By default, disable Super Dev Mode on https pages.
This should make it safer to leave the Super Dev Mode hook on all the time
when the production server uses https.

New API: CrossSiteIframeLinker.getJsDevModeRedirectHookPermitted() can be
overridden to change the JavaScript expression that does the check.

See issue 7538.

Change-Id: Ic603c9e511a766cd4c89b15f7561a254070c6ac5
Review-Link: https://gwt-review.googlesource.com/#/c/2480/

Review by: mdempsky@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@11590 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
index 8e59403..f629d1a 100644
--- a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
@@ -96,6 +96,8 @@
     includeJs(ss, logger, getJsLoadExternalStylesheets(context), "__LOAD_STYLESHEETS__");
     includeJs(ss, logger, getJsRunAsync(context), "__RUN_ASYNC__");
     includeJs(ss, logger, getJsDevModeRedirectHook(context), "__DEV_MODE_REDIRECT_HOOK__");
+    includeJs(ss, logger, getJsDevModeRedirectHookPermitted(context),
+        "__DEV_MODE_REDIRECT_HOOK_PERMITTED__"); // must be after DEV_MODE_REDIRECT_HOOK
 
     // This Linker does not support <script> tags in the gwt.xml
     SortedSet<ScriptReference> scripts = artifacts.find(ScriptReference.class);
@@ -223,7 +225,8 @@
   }
 
   /**
-   * Returns a code fragment to check for the new development mode.
+   * Returns a JavaScript fragment that starts Super Dev Mode, if enabled.
+   * (May return either the JavaScript itself or the name of a Java resource ending with ".js".)
    */
   protected String getJsDevModeRedirectHook(LinkerContext context) {
     // Enable Super Dev Mode for this app if the devModeRedirectEnabled config property is true.
@@ -236,6 +239,24 @@
   }
 
   /**
+   * Returns a JavaScript expression that determines whether Super Dev Mode may be turned on
+   * for the current page. (May return either the JavaScript itself or the name of a Java resource
+   * ending with ".js".)
+   *
+   * <p>The default implementation allows Super Dev Mode only on http: and file: pages. It could be
+   * overridden to implement a blacklist or whitelist of hostnames where Super Dev Mode may run.
+   * As a safety precaution, it's recommended to return false for https and for the hostnames in
+   * URL's visited by end users.
+   *
+   * <p>If you override this method to allow https, it probably won't work anyway because
+   * browsers often disallow loading JavaScript from http URL's into https pages. To make it work,
+   * you will also have to find a way to run the code server using https.
+   */
+  protected String getJsDevModeRedirectHookPermitted(LinkerContext context) {
+    return "$wnd.location.protocol == \"http:\" || $wnd.location.protocol == \"file:\"";
+  }
+
+  /**
    * Returns the name of the {@code JsInstallLocation} script.  By default,
    * returns {@code "com/google/gwt/core/ext/linker/impl/installLocationIframe.js"}.
    *
diff --git a/dev/core/src/com/google/gwt/core/linker/DevModeRedirectHook.js b/dev/core/src/com/google/gwt/core/linker/DevModeRedirectHook.js
index a5026f2..753e436 100644
--- a/dev/core/src/com/google/gwt/core/linker/DevModeRedirectHook.js
+++ b/dev/core/src/com/google/gwt/core/linker/DevModeRedirectHook.js
@@ -14,35 +14,38 @@
  * the License.
  */
 
-// A snippet of code that loads a different script if dev mode is enabled.
-
-// Declare our existence.
-// (This assumes that properties.js has already set up the map.)
+// A snippet of code that loads a different script if Super Dev Mode is enabled.
 if ($wnd) {
-  $wnd.__gwt_activeModules["__MODULE_NAME__"].canRedirect = true;
-}
+  var devModePermitted = !!(__DEV_MODE_REDIRECT_HOOK_PERMITTED__);
 
-// We use a different key for each module so that we can turn on dev mode
-// independently for each.
-var devModeKey = '__gwtDevModeHook:__MODULE_NAME__';
+  // Declare our existence.
+  // (This assumes that properties.js has already set up the map.)
+  $wnd.__gwt_activeModules["__MODULE_NAME__"].canRedirect = devModePermitted;
 
-// If dev mode is on, the Bookmarklet previously saved the code server's URL
-// to session storage.
-var devModeUrl = $wnd.sessionStorage[devModeKey];
+  if (devModePermitted) {
+    // We use a different key for each module so that we can turn on dev mode
+    // independently for each.
+    var devModeKey = '__gwtDevModeHook:__MODULE_NAME__';
 
-if (devModeUrl && !$wnd[devModeKey]) {
-  $wnd[devModeKey] = true; // Don't try to redirect more than once,
-  var script = $doc.createElement('script');
+    // If dev mode is on, the bookmarklet previously saved the code server's URL
+    // to session storage.
+    var devModeUrl = $wnd.sessionStorage[devModeKey];
 
-  // save original module base
-  $wnd[devModeKey + ':moduleBase'] = computeScriptBase();
+    if (devModeUrl && !$wnd[devModeKey]) {
+      $wnd[devModeKey] = true; // Don't try to redirect more than once,
 
-  script.src = devModeUrl;
-  var head = $doc.getElementsByTagName('head')[0];
+      // Save the original module base. (Returned by GWT.getModuleBaseURL.)
+      $wnd[devModeKey + ':moduleBase'] = computeScriptBase();
 
-  // The new script tag must come before the previous one so that
-  // computeScriptBase will see it.
-  head.insertBefore(script, head.firstElementChild || head.children[0]);
+      var devModeScript = $doc.createElement('script');
+      devModeScript.src = devModeUrl;
 
-  return false; // Skip the regular bootstrap.
+      // The new script tag must come before the previous one so that
+      // computeScriptBase will see it.
+      var head = $doc.getElementsByTagName('head')[0];
+      head.insertBefore(devModeScript, head.firstElementChild || head.children[0]);
+
+      return false; // Skip the regular bootstrap.
+    }
+  }
 }