зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to m-i
This commit is contained in:
Коммит
78cfaae841
|
@ -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]
|
||||
|
|
Загрузка…
Ссылка в новой задаче