Bug 864098 - Add "Disable Cache" to options panel r=jwalker
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Wed, 18 Dec 2013 09:34:49 +0000
changeset 161033 ae5d8b396299a497e7b22b91ce8e5626a9a30c6d
parent 161023 23dffe2643bc17ea8e84dcc005a4d0f0109f2a09
child 161034 86e63d21402f92edd317ed9b27bc8c815ccd1769
child 161108 b26925dd9232b714a0e6b50839d18da3a5c6fe15
push id3375
push usercbook@mozilla.com
push dateWed, 18 Dec 2013 15:11:08 +0000
treeherderb2g-inbound@e2a8732088de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalker
bugs864098
milestone29.0a1
Bug 864098 - Add "Disable Cache" to options panel r=jwalker
b2g/chrome/content/shell.js
browser/devtools/framework/test/browser.ini
browser/devtools/framework/test/browser_toolbox_options_disable_cache.js
browser/devtools/framework/test/browser_toolbox_options_disable_cache.sjs
browser/devtools/framework/test/browser_toolbox_options_disable_js.html
browser/devtools/framework/test/browser_toolbox_options_disable_js.js
browser/devtools/framework/test/browser_toolbox_options_disable_js_iframe.html
browser/devtools/framework/test/browser_toolbox_options_disablejs.html
browser/devtools/framework/test/browser_toolbox_options_disablejs.js
browser/devtools/framework/test/browser_toolbox_options_disablejs_iframe.html
browser/devtools/framework/test/browser_toolbox_tool_ready.js
browser/devtools/framework/toolbox-options.js
browser/devtools/framework/toolbox-options.xul
browser/devtools/framework/toolbox.js
browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
toolkit/devtools/client/dbg-client.jsm
toolkit/devtools/server/actors/webbrowser.js
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1060,17 +1060,17 @@ let RemoteDebugger = {
       if (Services.prefs.getBoolPref("devtools.debugger.enable-content-actors")) {
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/script.js");
         DebuggerServer.addGlobalActor(DebuggerServer.ChromeDebuggerActor, "chromeDebugger");
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webconsole.js");
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/gcli.js");
         if ("nsIProfiler" in Ci) {
           DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/profiler.js");
         }
-        DebuggerServer.registerModule("devtools/server/actors/inspector")
+        DebuggerServer.registerModule("devtools/server/actors/inspector");
         DebuggerServer.registerModule("devtools/server/actors/styleeditor");
         DebuggerServer.enableWebappsContentActor = true;
       }
       DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
       DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webapps.js");
       DebuggerServer.registerModule("devtools/server/actors/device");
 
 #ifdef MOZ_WIDGET_GONK
--- a/browser/devtools/framework/test/browser.ini
+++ b/browser/devtools/framework/test/browser.ini
@@ -1,24 +1,26 @@
 [DEFAULT]
 support-files =
-  browser_toolbox_options_disablejs.html
-  browser_toolbox_options_disablejs_iframe.html
+  browser_toolbox_options_disable_js.html
+  browser_toolbox_options_disable_js_iframe.html
+  browser_toolbox_options_disable_cache.sjs
   head.js
 
 [browser_devtools_api.js]
 [browser_dynamic_tool_enabling.js]
 [browser_keybindings.js]
 [browser_new_activation_workflow.js]
 [browser_target_events.js]
 [browser_toolbox_dynamic_registration.js]
 [browser_toolbox_highlight.js]
 [browser_toolbox_hosts.js]
 [browser_toolbox_options.js]
-[browser_toolbox_options_disablejs.js]
+[browser_toolbox_options_disable_cache.js]
+[browser_toolbox_options_disable_js.js]
 [browser_toolbox_raise.js]
 skip-if = os == "win"
 [browser_toolbox_ready.js]
 [browser_toolbox_select_event.js]
 [browser_toolbox_sidebar.js]
 [browser_toolbox_tabsswitch_shortcuts.js]
 [browser_toolbox_tool_ready.js]
 [browser_toolbox_window_shortcuts.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/test/browser_toolbox_options_disable_cache.js
@@ -0,0 +1,113 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that disabling JavaScript for a tab works as it should.
+
+const TEST_URI = "http://mochi.test:8888/browser/browser/devtools/framework/" +
+                 "test/browser_toolbox_options_disable_cache.sjs";
+
+let doc;
+let toolbox;
+
+function test() {
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
+
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
+    doc = content.document;
+    gDevTools.showToolbox(target).then(testSelectTool);
+  }, true);
+
+  content.location = TEST_URI;
+}
+
+function testSelectTool(aToolbox) {
+  toolbox = aToolbox;
+  toolbox.once("options-selected", testCacheEnabled);
+  toolbox.selectTool("options");
+}
+
+function testCacheEnabled() {
+  let prevTimestamp = getGUID();
+
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
+    doc = content.document;
+    is(prevTimestamp, getGUID(), "GUID has not changed (page is cached)");
+
+    testCacheEnabled2();
+  }, true);
+
+  doc.location.reload(false);
+}
+
+function testCacheEnabled2() {
+  let prevTimestamp = getGUID();
+
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
+    doc = content.document;
+    is(prevTimestamp, getGUID(),
+       "GUID has not changed after page refresh (page is cached)");
+
+    testCacheDisabled();
+  }, true);
+
+  doc.location.reload(false);
+}
+
+function testCacheDisabled() {
+  let prevTimestamp = getGUID();
+
+  let panel = toolbox.getCurrentPanel();
+  let cbx = panel.panelDoc.getElementById("devtools-disable-cache");
+  let browser = gBrowser.selectedBrowser;
+
+  browser.addEventListener("load", function onLoad(evt) {
+    browser.removeEventListener(evt.type, onLoad, true);
+    doc = content.document;
+    isnot(prevTimestamp, getGUID(), "GUID has changed (page is not cached)");
+    testCacheDisabled2();
+  }, true);
+
+  info("disabling cache");
+
+  cbx.scrollIntoView();
+
+  // After uising scrollIntoView() we need to use executeSoon() to wait for the
+  // browser to scroll.
+  executeSoon(function() {
+    EventUtils.synthesizeMouseAtCenter(cbx, {}, panel.panelWin);
+  });
+}
+
+function testCacheDisabled2() {
+  let prevTimestamp = getGUID();
+
+  let browser = gBrowser.selectedBrowser;
+
+  browser.addEventListener("load", function onLoad(evt) {
+    browser.removeEventListener(evt.type, onLoad, true);
+    doc = content.document;
+    isnot(prevTimestamp, getGUID(),
+       "GUID has changed after page refresh (page is not cached)");
+    finishUp();
+  }, true);
+
+  doc.location.reload(false);
+}
+
+function getGUID() {
+  return doc.querySelector("h1").textContent;
+}
+
+function finishUp() {
+  toolbox.destroy().then(() => {
+    gBrowser.removeCurrentTab();
+    toolbox = doc = null;
+    finish();
+  }).then(null, console.error);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/test/browser_toolbox_options_disable_cache.sjs
@@ -0,0 +1,28 @@
+  /* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(request, response) {
+  let Etag = '"4d881ab-b03-435f0a0f9ef00"';
+  let IfNoneMatch = request.hasHeader("If-None-Match")
+                    ? request.getHeader("If-None-Match")
+                    : "";
+
+  let guid = 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+    let r = Math.random() * 16 | 0;
+    let v = c === "x" ? r : (r & 0x3 | 0x8);
+
+    return v.toString(16);
+  });
+
+  let page = "<!DOCTYPE html><html><body><h1>" + guid + "</h1></body></html>";
+
+  response.setHeader("Etag", Etag, false);
+
+  if (IfNoneMatch === Etag) {
+    response.setStatusLine(request.httpVersion, "304", "Not Modified");
+  } else {
+    response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+    response.setHeader("Content-Length", page.length + "", false);
+    response.write(page);
+  }
+}
rename from browser/devtools/framework/test/browser_toolbox_options_disablejs.html
rename to browser/devtools/framework/test/browser_toolbox_options_disable_js.html
--- a/browser/devtools/framework/test/browser_toolbox_options_disablejs.html
+++ b/browser/devtools/framework/test/browser_toolbox_options_disable_js.html
@@ -1,8 +1,9 @@
+<!DOCTYPE html>
 <html>
   <head>
     <title>browser_toolbox_options_disablejs.html</title>
     <meta charset="UTF-8">
     <style>
       div {
         width: 260px;
         height: 24px;
@@ -35,11 +36,11 @@
            onclick="log('JavaScript Enabled')"/>
     <input id="logJSDisabled"
            type="button"
            value="Log JS Disabled"
            onclick="log('JavaScript Disabled')"/>
     <br>
     <div id="output">No output</div>
     <h1>Test in iframe</h1>
-    <iframe src="browser_toolbox_options_disablejs_iframe.html"></iframe>
+    <iframe src="browser_toolbox_options_disable_js_iframe.html"></iframe>
   </body>
 </html>
rename from browser/devtools/framework/test/browser_toolbox_options_disablejs.js
rename to browser/devtools/framework/test/browser_toolbox_options_disable_js.js
--- a/browser/devtools/framework/test/browser_toolbox_options_disablejs.js
+++ b/browser/devtools/framework/test/browser_toolbox_options_disable_js.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that disabling JavaScript for a tab works as it should.
 
 const TEST_URI = "http://example.com/browser/browser/devtools/framework/" +
-                 "test/browser_toolbox_options_disablejs.html";
+                 "test/browser_toolbox_options_disable_js.html";
 
 let doc;
 let toolbox;
 
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
@@ -118,11 +118,14 @@ function testJSDisabledIframe() {
   ok(output.textContent !== "JavaScript Disabled",
      'output is not "JavaScript Disabled" in iframe');
   toggleJS().then(function() {
     testJSEnabled(null, null, true);
   });
 }
 
 function finishUp() {
-  doc = toolbox = null;
-  finish();
+  toolbox.destroy().then(function() {
+    gBrowser.removeCurrentTab();
+    toolbox = doc = null;
+    finish();
+  });
 }
rename from browser/devtools/framework/test/browser_toolbox_options_disablejs_iframe.html
rename to browser/devtools/framework/test/browser_toolbox_options_disable_js_iframe.html
--- a/browser/devtools/framework/test/browser_toolbox_tool_ready.js
+++ b/browser/devtools/framework/test/browser_toolbox_tool_ready.js
@@ -15,18 +15,20 @@ function test() {
         ok(toolbox, "toolbox exists for " + toolId);
         is(toolbox.currentToolId, toolId, "currentToolId should be " + toolId);
 
         let panel = toolbox.getCurrentPanel();
         ok(panel.isReady, toolId + " panel should be ready");
 
         let nextIndex = index + 1;
         if (nextIndex >= toolIds.length) {
-          toolbox.destroy();
-          finish();
+          toolbox.destroy().then(function() {
+            gBrowser.removeCurrentTab();
+            finish();
+          });
         }
         else {
           open(nextIndex);
         }
       }, console.error);
     };
 
     open(0);
--- a/browser/devtools/framework/toolbox-options.js
+++ b/browser/devtools/framework/toolbox-options.js
@@ -36,39 +36,54 @@ XPCOMUtils.defineLazyGetter(this, "l10n"
  */
 function OptionsPanel(iframeWindow, toolbox) {
   this.panelDoc = iframeWindow.document;
   this.panelWin = iframeWindow;
   this.toolbox = toolbox;
   this.isReady = false;
 
   EventEmitter.decorate(this);
-};
+}
 
 OptionsPanel.prototype = {
 
   get target() {
     return this.toolbox.target;
   },
 
   open: function() {
-    let deferred = promise.defer();
+    let targetPromise;
 
-    this.setupToolsList();
-    this.populatePreferences();
+    // For local debugging we need to make the target remote.
+    if (!this.target.isRemote) {
+      targetPromise = this.target.makeRemote();
+    } else {
+      targetPromise = promise.resolve(this.target);
+    }
 
-    this._disableJSClicked = this._disableJSClicked.bind(this);
+    return targetPromise.then(() => {
+      this.setupToolsList();
+      this.populatePreferences();
 
-    let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript");
-    disableJSNode.addEventListener("click", this._disableJSClicked, false);
+      this._disableJSClicked = this._disableJSClicked.bind(this);
+      this._disableCacheClicked = this._disableCacheClicked.bind(this);
+
+      let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript");
+      disableJSNode.addEventListener("click", this._disableJSClicked, false);
 
-    this.isReady = true;
-    this.emit("ready");
-    deferred.resolve(this);
-    return deferred.promise;
+      let disableCacheNode = this.panelDoc.getElementById("devtools-disable-cache");
+      disableCacheNode.addEventListener("click", this._disableCacheClicked, false);
+    }).then(() => {
+      this.isReady = true;
+      this.emit("ready");
+      return this;
+    }).then(null, function onError(aReason) {
+      Cu.reportError("OptionsPanel open failed. " +
+                     aReason.error + ": " + aReason.message);
+    });
   },
 
   setupToolsList: function() {
     let defaultToolsBox = this.panelDoc.getElementById("default-tools-box");
     let additionalToolsBox = this.panelDoc.getElementById("additional-tools-box");
     let toolsNotSupportedLabel = this.panelDoc.getElementById("tools-not-supported-label");
     let atleastOneToolNotSupported = false;
 
@@ -191,41 +206,96 @@ OptionsPanel.prototype = {
           pref: this.getAttribute("data-pref"),
           newValue: this.value
         };
         data.oldValue = Services.prefs.getCharPref(data.pref);
         Services.prefs.setCharPref(data.pref, data.newValue);
         gDevTools.emit("pref-changed", data);
       }.bind(menulist));
     }
+
+    this.target.client.attachTab(this.target.client.activeTab._actor, (response) => {
+      this._origJavascriptEnabled = response.javascriptEnabled;
+      this._origCacheEnabled = response.cacheEnabled;
+
+      this._populateDisableJSCheckbox();
+      this._populateDisableCacheCheckbox();
+    });
+  },
+
+  _populateDisableJSCheckbox: function() {
+    let cbx = this.panelDoc.getElementById("devtools-disable-javascript");
+    cbx.checked = !this._origJavascriptEnabled;
+  },
+
+  _populateDisableCacheCheckbox: function() {
+    let cbx = this.panelDoc.getElementById("devtools-disable-cache");
+    cbx.checked = !this._origCacheEnabled;
   },
 
   /**
    * Disables JavaScript for the currently loaded tab. We force a page refresh
    * here because setting docShell.allowJavascript to true fails to block JS
    * execution from event listeners added using addEventListener(), AJAX calls
    * and timers. The page refresh prevents these things from being added in the
    * first place.
    *
    * @param {Event} event
    *        The event sent by checking / unchecking the disable JS checkbox.
    */
   _disableJSClicked: function(event) {
     let checked = event.target.checked;
-    let linkedBrowser = this.toolbox._host.hostTab.linkedBrowser;
-    let win = linkedBrowser.contentWindow;
-    let docShell = linkedBrowser.docShell;
+
+    let options = {
+      "javascriptEnabled": !checked
+    };
+
+    this.target.client.reconfigureTab(options);
+  },
 
-    if (typeof this.toolbox._origAllowJavascript == "undefined") {
-      this.toolbox._origAllowJavascript = docShell.allowJavascript;
+  /**
+   * Disables the cache for the currently loaded tab.
+   *
+   * @param {Event} event
+   *        The event sent by checking / unchecking the disable cache checkbox.
+   */
+  _disableCacheClicked: function(event) {
+    let checked = event.target.checked;
+
+    let options = {
+      "cacheEnabled": !checked
+    };
+
+    this.target.client.reconfigureTab(options);
+  },
+
+  destroy: function() {
+    if (this.destroyPromise) {
+      return this.destroyPromise;
     }
 
-    docShell.allowJavascript = !checked;
-    win.location.reload();
-  },
+    let deferred = promise.defer();
 
-  destroy: function OP_destroy() {
+    this.destroyPromise = deferred.promise;
+
     let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript");
     disableJSNode.removeEventListener("click", this._disableJSClicked, false);
 
-    this.panelWin = this.panelDoc = this.toolbox = this._disableJSClicked = null;
+    let disableCacheNode = this.panelDoc.getElementById("devtools-disable-cache");
+    disableCacheNode.removeEventListener("click", this._disableCacheClicked, false);
+
+    this.panelWin = this.panelDoc = null;
+    this._disableJSClicked = this._disableCacheClicked = null;
+
+    // If the cache or JavaScript is disabled we need to revert them to their
+    // original values.
+    let options = {
+      "cacheEnabled": this._origCacheEnabled,
+      "javascriptEnabled": this._origJavascriptEnabled
+    };
+    this.target.client.reconfigureTab(options, () => {
+      this.toolbox = null;
+      deferred.resolve();
+    }, true);
+
+    return deferred.promise;
   }
 };
--- a/browser/devtools/framework/toolbox-options.xul
+++ b/browser/devtools/framework/toolbox-options.xul
@@ -64,30 +64,31 @@
         <label value="&options.profiler.label;"/>
         <vbox id="profiler-options" class="options-groupbox">
           <checkbox label="&options.showPlatformData.label;"
                     tooltiptext="&options.showPlatformData.tooltip;"
                     data-pref="devtools.profiler.ui.show-platform-data"/>
         </vbox>
         <label value="&options.context.advancedSettings;"/>
         <vbox id="context-options" class="options-groupbox">
-          <hbox>
-            <checkbox id="devtools-disable-javascript"
-                      label="&options.disableJavaScript.label2;"
-                      tooltiptext="&options.disableJavaScript.tooltip;"/>
-            <label class="options-citation-label theme-comment"
-                   value="(&options.context.triggersPageRefresh2;)"/>
-          </hbox>
+          <checkbox id="devtools-disable-cache"
+                    label="&options.disableCache.label;"
+                    tooltiptext="&options.disableCache.tooltip;"/>
+          <checkbox id="devtools-disable-javascript"
+                    label="&options.disableJavaScript.label;"
+                    tooltiptext="&options.disableJavaScript.tooltip;"/>
           <hbox class="hidden-labels-box">
             <checkbox label="&options.enableChrome.label3;"
                       tooltiptext="&options.enableChrome.tooltip;"
                       data-pref="devtools.chrome.enabled"/>
           </hbox>
           <hbox class="hidden-labels-box">
             <checkbox label="&options.enableRemote.label3;"
                       tooltiptext="&options.enableRemote.tooltip;"
                       data-pref="devtools.debugger.remote-enabled"/>
           </hbox>
+          <label class="options-citation-label"
+                 value="&options.context.triggersPageRefresh;"/>
         </vbox>
       </vbox>
     </hbox>
   </hbox>
 </window>
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -987,26 +987,17 @@ Toolbox.prototype = {
 
     this._target.off("navigate", this._refreshHostTitle);
     this.off("select", this._refreshHostTitle);
     this.off("host-changed", this._refreshHostTitle);
 
     gDevTools.off("tool-registered", this._toolRegistered);
     gDevTools.off("tool-unregistered", this._toolUnregistered);
 
-    // Revert docShell.allowJavascript back to its original value if it was
-    // changed via the Disable JS option.
-    if (typeof this._origAllowJavascript != "undefined") {
-      let docShell = this._host.hostTab.linkedBrowser.docShell;
-      docShell.allowJavascript = this._origAllowJavascript;
-      this._origAllowJavascript = undefined;
-    }
-
     let outstanding = [];
-
     for (let [id, panel] of this._toolPanels) {
       try {
         outstanding.push(panel.destroy());
       } catch (e) {
         // We don't want to stop here if any panel fail to close.
         console.error(e);
       }
     }
--- a/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
@@ -47,39 +47,45 @@
   -  'Default color unit' dropdown list and is visible in the options panel. -->
 <!ENTITY options.defaultColorUnit.rgb "RGB(A)">
 
 <!-- LOCALIZATION NOTE (options.defaultColorUnit.name): This is used in
   -  the 'Default color unit' dropdown list and is visible in the options panel.
   -  -->
 <!ENTITY options.defaultColorUnit.name "Color Names">
 
-<!-- LOCALIZATION NOTE (options.context.triggersPageRefresh2): This is the
-  -  triggers page refresh label next to the settings in the advanced settings
-  -  group in the options panel which trigger page reload. -->
-<!ENTITY options.context.triggersPageRefresh2  "Current session only, reloads the page">
+<!-- LOCALIZATION NOTE (options.context.triggersPageRefresh): This is the
+  -  triggers page refresh footnote under the advanced settings group in the
+  -  options panel and is used for settings that trigger page reload. -->
+<!ENTITY options.context.triggersPageRefresh  "* Current session only, reloads the page">
 
 <!-- LOCALIZATION NOTE (options.enableChrome.label3): This is the label for the
   -  checkbox that toggles chrome debugging, i.e. devtools.chrome.enabled
   -  boolean preference in about:config, in the options panel. -->
 <!ENTITY options.enableChrome.label3    "Enable chrome debugging">
 <!ENTITY options.enableChrome.tooltip   "Turning this option on will allow you to use various developer tools in browser context">
 
 <!-- LOCALIZATION NOTE (options.enableRemote.label3): This is the label for the
   -  checkbox that toggles remote debugging, i.e. devtools.debugger.remote-enabled
   -  boolean preference in about:config, in the options panel. -->
 <!ENTITY options.enableRemote.label3    "Enable remote debugging">
 <!ENTITY options.enableRemote.tooltip   "Turning this option on will allow the developer tools to debug remote Firefox instance like Firefox OS">
 
-<!-- LOCALIZATION NOTE (options.disableJavaScript.label2,
+<!-- LOCALIZATION NOTE (options.disableJavaScript.label,
   -  options.disableJavaScript.tooltip): This is the options panel label and
   -  tooltip for the checkbox that toggles JavaScript on or off. -->
-<!ENTITY options.disableJavaScript.label2    "Disable JavaScript">
+<!ENTITY options.disableJavaScript.label     "Disable JavaScript *">
 <!ENTITY options.disableJavaScript.tooltip   "Turning this option on will disable JavaScript for the current tab. If the tab or the toolbox is closed then this setting will be forgotten.">
 
+<!-- LOCALIZATION NOTE (options.disableCache.label,
+  -  options.disableCache.tooltip): This is the options panel label and
+  -  tooltip for the checkbox that toggles the cache on or off. -->
+<!ENTITY options.disableCache.label     "Disable Cache *">
+<!ENTITY options.disableCache.tooltip   "Turning this option on will disable the cache for the current tab. If the tab or the toolbox is closed then this setting will be forgotten.">
+
 <!-- LOCALIZATION NOTE (options.selectDefaultTools.label): This is the label for
   -  the heading of group of checkboxes corresponding to the default developer
   -  tools. -->
 <!ENTITY options.selectDefaultTools.label     "Default Firefox Developer Tools">
 
 <!-- LOCALIZATION NOTE (options.selectAdditionalTools.label): This is the label for
   -  the heading of group of checkboxes corresponding to the developer tools
   -  added by add-ons. This heading is hidden when there is no developer tool
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -552,16 +552,33 @@ DebuggerClient.prototype = {
       to: this.activeThread._actor,
       type: "reconfigure",
       options: aOptions
     };
     this.request(packet, aOnResponse);
   },
 
   /**
+   * Reconfigure a tab actor.
+   *
+   * @param object aOptions
+   *        A dictionary object of the new options to use in the tab actor.
+   * @param function aOnResponse
+   *        Called with the response packet.
+   */
+  reconfigureTab: function(aOptions, aOnResponse) {
+    let packet = {
+      to: this.activeTab._actor,
+      type: "reconfigure",
+      options: aOptions
+    };
+    this.request(packet, aOnResponse);
+  },
+
+  /**
    * Release an object actor.
    *
    * @param string aActor
    *        The actor ID to send the request to.
    * @param aOnResponse function
    *        If specified, will be called with the response packet when
    *        debugging server responds.
    */
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -707,17 +707,22 @@ BrowserTabActor.prototype = {
 
   onAttach: function BTA_onAttach(aRequest) {
     if (this.exited) {
       return { type: "exited" };
     }
 
     this._attach();
 
-    return { type: "tabAttached", threadActor: this.threadActor.actorID };
+    return {
+      type: "tabAttached",
+      threadActor: this.threadActor.actorID,
+      cacheEnabled: this._getCacheEnabled(),
+      javascriptEnabled: this._getJavascriptEnabled()
+    };
   },
 
   onDetach: function BTA_onDetach(aRequest) {
     if (!this._detach()) {
       return { error: "wrongState" };
     }
 
     return { type: "detached" };
@@ -743,16 +748,109 @@ BrowserTabActor.prototype = {
     // subsequent navigation event packet.
     Services.tm.currentThread.dispatch(makeInfallible(() => {
       this.window.location = aRequest.url;
     }, "BrowserTabActor.prototype.onNavigateTo's delayed body"), 0);
     return {};
   },
 
   /**
+   * Reconfigure options.
+   */
+  onReconfigure: function (aRequest) {
+    let options = aRequest.options || {};
+
+    this._toggleJsOrCache(options);
+    return {};
+  },
+
+  /**
+   * Handle logic to enable/disable JS/cache.
+   */
+  _toggleJsOrCache: function(options) {
+    // Wait a tick so that the response packet can be dispatched before the
+    // subsequent navigation event packet.
+    let reload = false;
+
+    if (typeof options.javascriptEnabled !== "undefined" &&
+        options.javascriptEnabled !== this._getJavascriptEnabled()) {
+      this._setJavascriptEnabled(options.javascriptEnabled);
+      reload = true;
+    }
+    if (typeof options.cacheEnabled !== "undefined" &&
+        options.cacheEnabled !== this._getCacheEnabled()) {
+      this._setCacheEnabled(options.cacheEnabled);
+      reload = true;
+    }
+
+    if (reload) {
+      this.onReload();
+    }
+  },
+
+  /**
+   * Disable or enable the cache via docShell.
+   */
+  _setCacheEnabled: function(allow) {
+    let enable =  Ci.nsIRequest.LOAD_NORMAL;
+    let disable = Ci.nsIRequest.LOAD_BYPASS_CACHE |
+                  Ci.nsIRequest.INHIBIT_CACHING;
+    let docShell = this.window
+                       .QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDocShell);
+
+    docShell.defaultLoadFlags = allow ? enable : disable;
+  },
+
+  /**
+   * Disable or enable JS via docShell.
+   */
+  _setJavascriptEnabled: function(allow) {
+    let docShell = this.window
+                       .QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDocShell);
+
+    docShell.allowJavascript = allow;
+  },
+
+  /**
+   * Return cache allowed status.
+   */
+  _getCacheEnabled: function() {
+    if (!this.window) {
+      // The tab is already closed.
+      return null;
+    }
+
+    let disable = Ci.nsIRequest.LOAD_BYPASS_CACHE |
+                  Ci.nsIRequest.INHIBIT_CACHING;
+    let docShell = this.window
+                       .QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDocShell);
+
+    return docShell.defaultLoadFlags !== disable;
+  },
+
+  /**
+   * Return JS allowed status.
+   */
+  _getJavascriptEnabled: function() {
+    if (!this.window) {
+      // The tab is already closed.
+      return null;
+    }
+
+    let docShell = this.window
+                       .QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDocShell);
+
+    return docShell.allowJavascript;
+  },
+
+  /**
    * Prepare to enter a nested event loop by disabling debuggee events.
    */
   preNest: function BTA_preNest() {
     if (!this.window) {
       // The tab is already closed.
       return;
     }
     let windowUtils = this.window
@@ -833,17 +931,18 @@ BrowserTabActor.prototype = {
 
 /**
  * The request types this actor can handle.
  */
 BrowserTabActor.prototype.requestTypes = {
   "attach": BrowserTabActor.prototype.onAttach,
   "detach": BrowserTabActor.prototype.onDetach,
   "reload": BrowserTabActor.prototype.onReload,
-  "navigateTo": BrowserTabActor.prototype.onNavigateTo
+  "navigateTo": BrowserTabActor.prototype.onNavigateTo,
+  "reconfigure": BrowserTabActor.prototype.onReconfigure
 };
 
 function BrowserAddonList(aConnection)
 {
   this._connection = aConnection;
   this._actorByAddonId = new Map();
   this._onListChanged = null;
 }