This commit is contained in:
Phil Ringnalda 2013-04-21 19:43:19 -07:00
Родитель 7f0c97bfe7 12a533fa7b
Коммит 78cfaae841
21 изменённых файлов: 492 добавлений и 126 удалений

Просмотреть файл

@ -760,6 +760,7 @@ var gBrowserInit = {
window.addEventListener("AppCommand", HandleAppCommandEvent, true);
messageManager.loadFrameScript("chrome://browser/content/content.js", true);
messageManager.loadFrameScript("chrome://browser/content/content-sessionStore.js", true);
// initialize observers and listeners
// and give C++ access to gBrowser

Просмотреть файл

@ -0,0 +1,40 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function debug(msg) {
Services.console.logStringMessage("SessionStoreContent: " + msg);
}
/**
* Listens for and handles content events that we need for the
* session store service to be notified of state changes in content.
*/
let EventListener = {
DOM_EVENTS: [
"pageshow", "change", "input"
],
init: function () {
this.DOM_EVENTS.forEach(e => addEventListener(e, this, true));
},
handleEvent: function (event) {
switch (event.type) {
case "pageshow":
if (event.persisted)
sendAsyncMessage("SessionStore:pageshow");
break;
case "input":
case "change":
sendAsyncMessage("SessionStore:input");
break;
default:
debug("received unknown event '" + event.type + "'");
break;
}
}
};
EventListener.init();

Просмотреть файл

@ -5,3 +5,4 @@
browser.jar:
* content/browser/aboutSessionRestore.xhtml (content/aboutSessionRestore.xhtml)
* content/browser/aboutSessionRestore.js (content/aboutSessionRestore.js)
content/browser/content-sessionStore.js (content/content-sessionStore.js)

Просмотреть файл

@ -49,17 +49,16 @@ const WINDOW_HIDEABLE_FEATURES = [
"menubar", "toolbar", "locationbar", "personalbar", "statusbar", "scrollbars"
];
/*
docShell capabilities to (re)store
Restored in restoreHistory()
eg: browser.docShell["allow" + aCapability] = false;
const MESSAGES = [
// The content script tells us that its form data (or that of one of its
// subframes) might have changed. This can be the contents or values of
// standard form fields or of ContentEditables.
"SessionStore:input",
XXX keep these in sync with all the attributes starting
with "allow" in /docshell/base/nsIDocShell.idl
*/
const CAPABILITIES = [
"Subframes", "Plugins", "Javascript", "MetaRedirects", "Images",
"DNSPrefetch", "Auth", "WindowControl"
// The content script has received a pageshow event. This happens when a
// page is loaded from bfcache without any network activity, i.e. when
// clicking the back or forward button.
"SessionStore:pageshow"
];
// These are tab events that we listen to.
@ -85,6 +84,23 @@ Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", this);
XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
"@mozilla.org/browser/sessionstartup;1", "nsISessionStartup");
// List of docShell capabilities to (re)store. These are automatically
// retrieved from a given docShell if not already collected before.
// This is made so they're automatically in sync with all nsIDocShell.allow*
// properties.
let gDocShellCapabilities = (function () {
let caps;
return docShell => {
if (!caps) {
let keys = Object.keys(docShell);
caps = keys.filter(k => k.startsWith("allow")).map(k => k.slice(5));
}
return caps;
};
})();
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
@ -609,6 +625,29 @@ let SessionStoreInternal = {
}
},
/**
* This method handles incoming messages sent by the session store content
* script and thus enables communication with OOP tabs.
*/
receiveMessage: function ssi_receiveMessage(aMessage) {
var browser = aMessage.target;
var win = browser.ownerDocument.defaultView;
switch (aMessage.name) {
case "SessionStore:pageshow":
this.onTabLoad(win, browser);
break;
case "SessionStore:input":
this.onTabInput(win, browser);
break;
default:
debug("received unknown message '" + aMessage.name + "'");
break;
}
this._clearRestoringWindows();
},
/* ........ Window Event Handlers .............. */
/**
@ -621,16 +660,10 @@ let SessionStoreInternal = {
// If __SS_restore_data is set, then we need to restore the document
// (form data, scrolling, etc.). This will only happen when a tab is
// first restored.
if (aEvent.currentTarget.__SS_restore_data)
this.restoreDocument(win, aEvent.currentTarget, aEvent);
// We still need to call onTabLoad, so fall through to "pageshow" case.
case "pageshow":
this.onTabLoad(win, aEvent.currentTarget, aEvent);
break;
case "change":
case "input":
case "DOMAutoComplete":
this.onTabInput(win, aEvent.currentTarget);
let browser = aEvent.currentTarget;
if (browser.__SS_restore_data)
this.restoreDocument(win, browser, aEvent);
this.onTabLoad(win, browser);
break;
case "TabOpen":
this.onTabAdd(win, aEvent.originalTarget);
@ -1193,10 +1226,9 @@ let SessionStoreInternal = {
onTabAdd: function ssi_onTabAdd(aWindow, aTab, aNoNotification) {
let browser = aTab.linkedBrowser;
browser.addEventListener("load", this, true);
browser.addEventListener("pageshow", this, true);
browser.addEventListener("change", this, true);
browser.addEventListener("input", this, true);
browser.addEventListener("DOMAutoComplete", this, true);
let mm = browser.messageManager;
MESSAGES.forEach(msg => mm.addMessageListener(msg, this));
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
@ -1217,10 +1249,9 @@ let SessionStoreInternal = {
onTabRemove: function ssi_onTabRemove(aWindow, aTab, aNoNotification) {
let browser = aTab.linkedBrowser;
browser.removeEventListener("load", this, true);
browser.removeEventListener("pageshow", this, true);
browser.removeEventListener("change", this, true);
browser.removeEventListener("input", this, true);
browser.removeEventListener("DOMAutoComplete", this, true);
let mm = browser.messageManager;
MESSAGES.forEach(msg => mm.removeMessageListener(msg, this));
delete browser.__SS_data;
delete browser.__SS_tabStillLoading;
@ -1289,17 +1320,14 @@ let SessionStoreInternal = {
* Window reference
* @param aBrowser
* Browser reference
* @param aEvent
* Event obj
*/
onTabLoad: function ssi_onTabLoad(aWindow, aBrowser, aEvent) {
onTabLoad: function ssi_onTabLoad(aWindow, aBrowser) {
// react on "load" and solitary "pageshow" events (the first "pageshow"
// following "load" is too late for deleting the data caches)
// It's possible to get a load event after calling stop on a browser (when
// overwriting tabs). We want to return early if the tab hasn't been restored yet.
if ((aEvent.type != "load" && !aEvent.persisted) ||
(aBrowser.__SS_restoreState &&
aBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)) {
if (aBrowser.__SS_restoreState &&
aBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
return;
}
@ -1959,9 +1987,9 @@ let SessionStoreInternal = {
tabData.hidden = aTab.hidden;
var disallow = [];
for (var i = 0; i < CAPABILITIES.length; i++)
if (!browser.docShell["allow" + CAPABILITIES[i]])
disallow.push(CAPABILITIES[i]);
for (let cap of gDocShellCapabilities(browser.docShell))
if (!browser.docShell["allow" + cap])
disallow.push(cap);
if (disallow.length > 0)
tabData.disallow = disallow.join(",");
else if (tabData.disallow)
@ -2236,16 +2264,8 @@ let SessionStoreInternal = {
}
// designMode is undefined e.g. for XUL documents (as about:config)
if ((aContent.document.designMode || "") == "on" && aContent.document.body) {
if (aData.innerHTML === undefined && !aFullData) {
// we get no "input" events from iframes - listen for keypress here
let _this = this;
aContent.addEventListener("keypress", function(aEvent) {
_this.saveStateDelayed(aWindow, 3000);
}, true);
}
if ((aContent.document.designMode || "") == "on" && aContent.document.body)
aData.innerHTML = aContent.document.body.innerHTML;
}
}
// get scroll position from nsIDOMWindowUtils, since it allows avoiding a
@ -3090,10 +3110,10 @@ let SessionStoreInternal = {
}
// make sure to reset the capabilities and attributes, in case this tab gets reused
var disallow = (tabData.disallow)?tabData.disallow.split(","):[];
CAPABILITIES.forEach(function(aCapability) {
browser.docShell["allow" + aCapability] = disallow.indexOf(aCapability) == -1;
});
let disallow = new Set(tabData.disallow && tabData.disallow.split(","));
for (let cap of gDocShellCapabilities(browser.docShell))
browser.docShell["allow" + cap] = !disallow.has(cap);
for (let name in this.xulAttributes)
tab.removeAttribute(name);
for (let name in tabData.attributes)

Просмотреть файл

@ -21,10 +21,14 @@ XPCSHELL_TESTS = \
MOCHITEST_BROWSER_FILES = \
head.js \
browser_capabilities.js \
browser_form_restore_events.js \
browser_form_restore_events_sample.html \
browser_formdata_format.js \
browser_formdata_format_sample.html \
browser_input.js \
browser_input_sample.html \
browser_pageshow.js \
browser_248970_b_perwindowpb.js \
browser_248970_b_sample.html \
browser_339445.js \
@ -73,7 +77,6 @@ MOCHITEST_BROWSER_FILES = \
browser_490040.js \
browser_491168.js \
browser_491577.js \
browser_493467.js \
browser_495495.js \
browser_500328.js \
browser_514751.js \

Просмотреть файл

@ -1,35 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function test() {
/** Test for Bug 493467 **/
let tab = gBrowser.addTab();
tab.linkedBrowser.stop();
let tabState = JSON.parse(ss.getTabState(tab));
is(tabState.disallow || "", "", "Everything is allowed per default");
// collect all permissions that can be set on a docShell (i.e. all
// attributes starting with "allow" such as "allowJavascript") and
// disallow them all, as SessionStore only remembers disallowed ones
let permissions = [];
let docShell = tab.linkedBrowser.docShell;
for (let attribute in docShell) {
if (/^allow([A-Z].*)/.test(attribute)) {
permissions.push(RegExp.$1);
docShell[attribute] = false;
}
}
// make sure that all available permissions have been remembered
tabState = JSON.parse(ss.getTabState(tab));
let disallow = tabState.disallow.split(",");
permissions.forEach(function(aName) {
ok(disallow.indexOf(aName) > -1, "Saved state of allow" + aName);
});
// IF A TEST FAILS, please add the missing permission's name (without the
// leading "allow") to nsSessionStore.js's CAPABILITIES array. Thanks.
gBrowser.removeTab(tab);
}

Просмотреть файл

@ -88,11 +88,11 @@ function test_2() {
forceWriteState(function(state) {
is(state.windows.length, 1,
"sessionstore state: 1 windows in data being writted to disk");
"sessionstore state: 1 windows in data being written to disk");
is (state.selectedWindow, 1,
"Selected window is updated to match one of the saved windows");
is(state._closedWindows.length, 0,
"sessionstore state: no closed windows in data being writted to disk");
"sessionstore state: no closed windows in data being written to disk");
runNextTest();
});
});
@ -114,20 +114,25 @@ function test_3() {
is(curState.selectedWindow, 4, "Last window opened is the one selected");
waitForWindowClose(normalWindow, function() {
forceWriteState(function(state) {
is(state.windows.length, 2,
"sessionstore state: 2 windows in data being writted to disk");
is(state.selectedWindow, 2,
"Selected window is updated to match one of the saved windows");
state.windows.forEach(function(win) {
is(!win.isPrivate, true, "Saved window is not private");
// Load another tab before checking the written state so that
// the list of restoring windows gets cleared. Otherwise the
// window we just closed would be marked as not closed.
waitForTabLoad(aWindow, "http://www.example.com/", function() {
forceWriteState(function(state) {
is(state.windows.length, 2,
"sessionstore state: 2 windows in data being written to disk");
is(state.selectedWindow, 2,
"Selected window is updated to match one of the saved windows");
state.windows.forEach(function(win) {
is(!win.isPrivate, true, "Saved window is not private");
});
is(state._closedWindows.length, 1,
"sessionstore state: 1 closed window in data being written to disk");
state._closedWindows.forEach(function(win) {
is(!win.isPrivate, true, "Closed window is not private");
});
runNextTest();
});
is(state._closedWindows.length, 1,
"sessionstore state: 1 closed window in data being writted to disk");
state._closedWindows.forEach(function(win) {
is(!win.isPrivate, true, "Closed window is not private");
});
runNextTest();
});
});
});
@ -178,7 +183,7 @@ function testOnWindow(aIsPrivate, aCallback) {
function waitForTabLoad(aWin, aURL, aCallback) {
aWin.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
aWin.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
aCallback();
executeSoon(aCallback);
}, true);
aWin.gBrowser.selectedBrowser.loadURI(aURL);
}

Просмотреть файл

@ -0,0 +1,73 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
TestRunner.run();
}
/**
* This test ensures that disabling features by flipping nsIDocShell.allow*
* properties are (re)stored as disabled. Disallowed features must be
* re-enabled when the tab is re-used by another tab restoration.
*/
function runTests() {
// Create a tab that we're going to use for our tests.
let tab = gBrowser.selectedTab = gBrowser.addTab("about:mozilla");
let browser = tab.linkedBrowser;
let docShell = browser.docShell;
yield waitForLoad(browser);
// Get the list of capabilities for docShells.
let flags = Object.keys(docShell).filter(k => k.startsWith("allow"));
// Check that everything is allowed by default for new tabs.
let state = JSON.parse(ss.getTabState(tab));
ok(!("disallow" in state), "everything allowed by default");
ok(flags.every(f => docShell[f]), "all flags set to true");
// Flip a couple of allow* flags.
docShell.allowImages = false;
docShell.allowMetaRedirects = false;
// Check that we correctly save disallowed features.
let disallowedState = JSON.parse(ss.getTabState(tab));
let disallow = new Set(disallowedState.disallow.split(","));
ok(disallow.has("Images"), "images not allowed");
ok(disallow.has("MetaRedirects"), "meta redirects not allowed");
is(disallow.size, 2, "two capabilities disallowed");
// Reuse the tab to restore a new, clean state into it.
ss.setTabState(tab, JSON.stringify({ entries: [{url: "about:home"}] }));
yield waitForLoad(browser);
// After restoring disallowed features must be available again.
state = JSON.parse(ss.getTabState(tab));
ok(!("disallow" in state), "everything allowed again");
ok(flags.every(f => docShell[f]), "all flags set to true");
// Restore the state with disallowed features.
ss.setTabState(tab, JSON.stringify(disallowedState));
yield waitForLoad(browser);
// Check that docShell flags are set.
ok(!docShell.allowImages, "images not allowed");
ok(!docShell.allowMetaRedirects, "meta redirects not allowed")
// Check that we correctly restored features as disabled.
state = JSON.parse(ss.getTabState(tab));
disallow = new Set(state.disallow.split(","));
ok(disallow.has("Images"), "images not allowed anymore");
ok(disallow.has("MetaRedirects"), "meta redirects not allowed anymore");
is(disallow.size, 2, "two capabilities disallowed");
// Clean up after ourselves.
gBrowser.removeTab(tab);
}
function waitForLoad(aElement) {
aElement.addEventListener("load", function onLoad() {
aElement.removeEventListener("load", onLoad, true);
executeSoon(next);
}, true);
}

Просмотреть файл

@ -0,0 +1,121 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const URL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_input_sample.html";
function test() {
TestRunner.run();
}
/**
* This test ensures that modifying form input fields on a web page marks the
* window as dirty and causes the corresponding form data of the tab that
* changed to be re-collected.
*/
function runTests() {
// Create a dummy window that is regarded as active. We need to do this
// because we always collect data for tabs of active windows no matter if
// the window is dirty or not.
let win = OpenBrowserWindow();
yield waitForLoad(win);
// Create a tab with some form fields.
let tab = gBrowser.selectedTab = gBrowser.addTab(URL);
let browser = gBrowser.selectedBrowser;
yield waitForLoad(browser);
// All windows currently marked as dirty will be written to disk
// and thus marked clean afterwards.
yield forceWriteState();
// Modify the checkbox field's state.
let chk = browser.contentDocument.getElementById("chk");
EventUtils.sendMouseEvent({type: "click"}, chk);
yield waitForInput();
// Check that we'll save the form data state correctly.
let state = JSON.parse(ss.getBrowserState());
let {formdata} = state.windows[0].tabs[1].entries[0];
is(formdata.id.chk, true, "chk's value is correct");
// Clear dirty state of all windows again.
yield forceWriteState();
// Modify the text input field's state.
browser.contentDocument.getElementById("txt").focus();
EventUtils.synthesizeKey("m", {});
yield waitForInput();
// Check that we'll save the form data state correctly.
let state = JSON.parse(ss.getBrowserState());
let {formdata} = state.windows[0].tabs[1].entries[0];
is(formdata.id.chk, true, "chk's value is correct");
is(formdata.id.txt, "m", "txt's value is correct");
// Clear dirty state of all windows again.
yield forceWriteState();
// Modify the state of the checkbox contained in the iframe.
let cdoc = browser.contentDocument.getElementById("ifr").contentDocument;
EventUtils.sendMouseEvent({type: "click"}, cdoc.getElementById("chk"));
yield waitForInput();
// Modify the state of text field contained in the iframe.
cdoc.getElementById("txt").focus();
EventUtils.synthesizeKey("m", {});
yield waitForInput();
// Check that we'll save the iframe's form data correctly.
let state = JSON.parse(ss.getBrowserState());
let {formdata} = state.windows[0].tabs[1].entries[0].children[0];
is(formdata.id.chk, true, "iframe chk's value is correct");
is(formdata.id.txt, "m", "iframe txt's value is correct");
// Clear dirty state of all windows again.
yield forceWriteState();
// Modify the content editable's state.
browser.contentDocument.getElementById("ced").focus();
EventUtils.synthesizeKey("m", {});
yield waitForInput();
// Check the we'll correctly save the content editable's state.
let state = JSON.parse(ss.getBrowserState());
let {innerHTML} = state.windows[0].tabs[1].entries[0].children[1];
is(innerHTML, "m", "content editable's value is correct");
// Clean up after ourselves.
gBrowser.removeTab(tab);
win.close();
}
function forceWriteState() {
const PREF = "browser.sessionstore.interval";
const TOPIC = "sessionstore-state-write";
Services.obs.addObserver(function observe() {
Services.obs.removeObserver(observe, TOPIC);
Services.prefs.clearUserPref(PREF);
executeSoon(next);
}, TOPIC, false);
Services.prefs.setIntPref(PREF, 0);
}
function waitForLoad(aElement) {
aElement.addEventListener("load", function onLoad() {
aElement.removeEventListener("load", onLoad, true);
executeSoon(next);
}, true);
}
function waitForInput() {
let mm = gBrowser.selectedBrowser.messageManager;
mm.addMessageListener("SessionStore:input", function onPageShow() {
mm.removeMessageListener("SessionStore:input", onPageShow);
executeSoon(next);
});
}

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE HTML>
<html dir="ltr" xml:lang="en-US" lang="en-US">
<head>
<meta charset="utf-8">
<title>sessionstore input test</title>
</head>
<body>
<input id="chk" type="checkbox" /><input id="txt" />
<iframe id="ifr" src="data:text/html;charset=utf-8,<input id=chk type=checkbox /><input id=txt />"></iframe>
<iframe id="ced"></iframe>
<script type="text/javascript">
addEventListener("load", () => {
document.getElementById("ced").contentDocument.designMode = "on";
});
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,84 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
TestRunner.run();
}
/**
* This test ensures that loading a page from bfcache (by going back or forward
* in history) marks the window as dirty and causes data about the tab that
* changed to be re-collected.
*
* We will do this by creating a tab with two history entries and going back
* to the first. When we now request the current browser state from the
* session store service the first history entry must be selected.
*/
function runTests() {
// Create a dummy window that is regarded as active. We need to do this
// because we always collect data for tabs of active windows no matter if
// the window is dirty or not.
let win = OpenBrowserWindow();
yield waitForLoad(win);
// Create a tab with two history entries.
let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
yield loadURI("about:robots");
yield loadURI("about:mozilla");
// All windows currently marked as dirty will be written to disk
// and thus marked clean afterwards.
yield forceWriteState();
// Go back to 'about:robots' - which is loaded from the bfcache and thus
// will not fire a 'load' event but a 'pageshow' event with persisted=true.
waitForPageShow();
yield gBrowser.selectedBrowser.goBack();
is(tab.linkedBrowser.currentURI.spec, "about:robots", "url is about:robots");
// If by receiving the 'pageshow' event the first window has correctly
// been marked as dirty, getBrowserState() should return the tab we created
// with the right history entry (about:robots) selected.
let state = JSON.parse(ss.getBrowserState());
is(state.windows[0].tabs[1].index, 1, "first history entry is selected");
// Clean up after ourselves.
gBrowser.removeTab(tab);
win.close();
}
function forceWriteState() {
const PREF = "browser.sessionstore.interval";
const TOPIC = "sessionstore-state-write";
Services.obs.addObserver(function observe() {
Services.obs.removeObserver(observe, TOPIC);
Services.prefs.clearUserPref(PREF);
executeSoon(next);
}, TOPIC, false);
Services.prefs.setIntPref(PREF, 0);
}
function loadURI(aURI) {
let browser = gBrowser.selectedBrowser;
waitForLoad(browser);
browser.loadURI(aURI);
}
function waitForLoad(aElement) {
aElement.addEventListener("load", function onLoad() {
aElement.removeEventListener("load", onLoad, true);
executeSoon(next);
}, true);
}
function waitForPageShow() {
let mm = gBrowser.selectedBrowser.messageManager;
mm.addMessageListener("SessionStore:pageshow", function onPageShow() {
mm.removeMessageListener("SessionStore:pageshow", onPageShow);
executeSoon(next);
});
}

Просмотреть файл

@ -201,13 +201,10 @@ this.defaultTools = [
webConsoleDefinition,
debuggerDefinition,
inspectorDefinition,
profilerDefinition,
netMonitorDefinition
];
if (Services.prefs.getBoolPref("devtools.profiler.enabled")) {
defaultTools.push(profilerDefinition);
}
/**
* Lookup l10n string from a string bundle.
*

Просмотреть файл

@ -670,10 +670,15 @@ Toolbox.prototype = {
* Handler for the tool-unregistered event.
* @param {string} event
* Name of the event ("tool-unregistered")
* @param {string} toolId
* Id of the tool that was unregistered
* @param {string|object} toolId
* Definition or id of the tool that was unregistered. Passing the
* tool id should be avoided as it is a temporary measure.
*/
_toolUnregistered: function TBOX_toolUnregistered(event, toolId) {
if (typeof toolId != "string") {
toolId = toolId.id;
}
if (this._toolPanels.has(toolId)) {
let instance = this._toolPanels.get(toolId);
instance.destroy();

Просмотреть файл

@ -83,17 +83,26 @@ DevTools.prototype = {
* Removes all tools that match the given |toolId|
* Needed so that add-ons can remove themselves when they are deactivated
*
* @param {string} toolId
* id of the tool to unregister
* @param {string|object} tool
* Definition or the id of the tool to unregister. Passing the
* tool id should be avoided as it is a temporary measure.
* @param {boolean} isQuitApplication
* true to indicate that the call is due to app quit, so we should not
* cause a cascade of costly events
*/
unregisterTool: function DT_unregisterTool(toolId, isQuitApplication) {
unregisterTool: function DT_unregisterTool(tool, isQuitApplication) {
let toolId = null;
if (typeof tool == "string") {
toolId = tool;
tool = this._tools.get(tool);
}
else {
toolId = tool.id;
}
this._tools.delete(toolId);
if (!isQuitApplication) {
this.emit("tool-unregistered", toolId);
this.emit("tool-unregistered", tool);
}
},
@ -382,12 +391,16 @@ let gDevToolsBrowser = {
},
/**
* Add the menuitem for a tool to all open browser windows.
* Add the menuitem for a tool to all open browser windows. Also toggles the
* kill switch preference of the tool.
*
* @param {object} toolDefinition
* properties of the tool to add
*/
_addToolToWindows: function DT_addToolToWindows(toolDefinition) {
// Set the kill switch pref boolean to true
Services.prefs.setBoolPref(toolDefinition.killswitch, true);
// We need to insert the new tool in the right place, which means knowing
// the tool that comes before the tool that we're trying to add
let allDefs = gDevTools.getToolDefinitionArray();
@ -567,12 +580,16 @@ let gDevToolsBrowser = {
},
/**
* Remove the menuitem for a tool to all open browser windows.
* Remove the menuitem for a tool to all open browser windows. Also sets the
* kill switch boolean pref to false.
*
* @param {object} toolId
* id of the tool to remove
* @param {string} killswitch
* The kill switch preference string of the tool
*/
_removeToolFromWindows: function DT_removeToolFromWindows(toolId) {
_removeToolFromWindows: function DT_removeToolFromWindows(toolId, killswitch) {
Services.prefs.setBoolPref(killswitch, false);
for (let win of gDevToolsBrowser._trackedBrowserWindows) {
gDevToolsBrowser._removeToolFromMenu(toolId, win.document);
}
@ -650,7 +667,15 @@ gDevTools.on("tool-registered", function(ev, toolId) {
});
gDevTools.on("tool-unregistered", function(ev, toolId) {
gDevToolsBrowser._removeToolFromWindows(toolId);
let killswitch;
if (typeof toolId == "string") {
killswitch = "devtools." + toolId + ".enabled";
}
else {
killswitch = toolId.killswitch;
toolId = toolId.id;
}
gDevToolsBrowser._removeToolFromWindows(toolId, killswitch);
});
gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);

Просмотреть файл

@ -77,8 +77,9 @@ function testUnregister()
gDevTools.unregisterTool("test-tool");
}
function toolUnregistered(event, toolId)
function toolUnregistered(event, toolDefinition)
{
let toolId = toolDefinition.id;
is(toolId, "test-tool", "tool-unregistered event handler sent tool id");
ok(!gDevTools.getToolDefinitionMap().has(toolId), "tool removed from map");

Просмотреть файл

@ -109,16 +109,17 @@ function toggleTools() {
}
function checkUnregistered(event, data) {
if (data == prefNodes[index].getAttribute("id")) {
if (data.id == prefNodes[index].getAttribute("id")) {
ok(true, "Correct tool removed");
// checking tab on the toolbox
ok(!doc.getElementById("toolbox-tab-" + data), "Tab removed for " + data);
ok(!doc.getElementById("toolbox-tab-" + data.id), "Tab removed for " +
data.id);
index++;
// Wait for the next turn of the event loop to avoid stack overflow errors.
executeSoon(toggleTools);
return;
}
ok(false, "Something went wrong, " + data + " was not unregistered");
ok(false, "Something went wrong, " + data.id + " was not unregistered");
cleanup();
}

Просмотреть файл

@ -32,7 +32,7 @@ function setupToolsList() {
else {
disabledTools.push(id);
Services.prefs.setCharPref(DISABLED_TOOLS, JSON.stringify(disabledTools));
gDevTools.emit("tool-unregistered", id);
gDevTools.emit("tool-unregistered", gDevTools._tools.get(id));
}
};

Просмотреть файл

@ -1101,8 +1101,7 @@ RuleEditor.prototype = {
// Add the source link.
let source = createChild(this.element, "div", {
class: "ruleview-rule-source theme-link",
textContent: this.rule.title
class: "ruleview-rule-source theme-link"
});
source.addEventListener("click", function() {
let rule = this.rule;
@ -1112,6 +1111,11 @@ RuleEditor.prototype = {
});
this.element.dispatchEvent(evt);
}.bind(this));
let sourceLabel = this.doc.createElementNS(XUL_NS, "label");
sourceLabel.setAttribute("crop", "center");
sourceLabel.setAttribute("value", this.rule.title);
sourceLabel.setAttribute("tooltiptext", this.rule.title);
source.appendChild(sourceLabel);
let code = createChild(this.element, "div", {
class: "ruleview-code"

Просмотреть файл

@ -133,9 +133,10 @@ function testEditProperty()
}
});
EventUtils.synthesizeMouse(propEditor.nameSpan, 1, 1,
EventUtils.synthesizeMouse(propEditor.nameSpan, 32, 1,
{ },
ruleDialog);}
ruleDialog);
}
function finishTest()
{

Просмотреть файл

@ -177,7 +177,7 @@ function testEditProperty()
}
});
EventUtils.synthesizeMouse(propEditor.nameSpan, 1, 1,
EventUtils.synthesizeMouse(propEditor.nameSpan, 32, 1,
{ },
ruleDialog);
}

Просмотреть файл

@ -6,6 +6,7 @@
[include:dom/mms/tests/xpcshell.ini]
[include:dom/system/gonk/tests/xpcshell.ini]
[include:toolkit/devtools/debugger/tests/unit/xpcshell.ini]
[include:toolkit/devtools/sourcemap/tests/unit/xpcshell.ini]
[include:toolkit/mozapps/downloads/tests/unit/xpcshell.ini]
[include:toolkit/mozapps/update/test_timermanager/unit/xpcshell.ini]
[include:toolkit/mozapps/update/test_svc/unit/xpcshell.ini]