зеркало из https://github.com/mozilla/gecko-dev.git
merge m-c to fx-team
This commit is contained in:
Коммит
e354fcec6e
|
@ -117,11 +117,6 @@ const CAPABILITIES = [
|
|||
"DNSPrefetch", "Auth", "WindowControl"
|
||||
];
|
||||
|
||||
// These keys are for internal use only - they shouldn't be part of the JSON
|
||||
// that gets saved to disk nor part of the strings returned by the API.
|
||||
const INTERNAL_KEYS = ["_tabStillLoading", "_hosts", "_formDataSaved",
|
||||
"_shouldRestore", "_host", "_scheme"];
|
||||
|
||||
// These are tab events that we listen to.
|
||||
const TAB_EVENTS = ["TabOpen", "TabClose", "TabSelect", "TabShow", "TabHide",
|
||||
"TabPinned", "TabUnpinned"];
|
||||
|
@ -216,6 +211,9 @@ SessionStoreService.prototype = {
|
|||
// states for all currently opened windows
|
||||
_windows: {},
|
||||
|
||||
// internal states for all open windows (data we need to associate, but not write to disk)
|
||||
_internalWindows: {},
|
||||
|
||||
// states for all recently closed windows
|
||||
_closedWindows: [],
|
||||
|
||||
|
@ -379,6 +377,11 @@ SessionStoreService.prototype = {
|
|||
// We don't want to minimize and then open a window at startup.
|
||||
if (this._initialState.windows[0].sizemode == "minimized")
|
||||
this._initialState.windows[0].sizemode = "normal";
|
||||
// clear any lastSessionWindowID attributes since those don't matter
|
||||
// during normal restore
|
||||
this._initialState.windows.forEach(function(aWindow) {
|
||||
delete aWindow.__lastSessionWindowID;
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (ex) { debug("The session file is invalid: " + ex); }
|
||||
|
@ -550,6 +553,9 @@ SessionStoreService.prototype = {
|
|||
this._forEachBrowserWindow(function(aWindow) {
|
||||
Array.forEach(aWindow.gBrowser.tabs, function(aTab) {
|
||||
delete aTab.linkedBrowser.__SS_data;
|
||||
delete aTab.linkedBrowser.__SS_tabStillLoading;
|
||||
delete aTab.linkedBrowser.__SS_formDataSaved;
|
||||
delete aTab.linkedBrowser.__SS_hostSchemeData;
|
||||
if (aTab.linkedBrowser.__SS_restoreState)
|
||||
this._resetTabRestoringState(aTab);
|
||||
});
|
||||
|
@ -557,10 +563,13 @@ SessionStoreService.prototype = {
|
|||
});
|
||||
// also clear all data about closed tabs and windows
|
||||
for (let ix in this._windows) {
|
||||
if (ix in openWindows)
|
||||
if (ix in openWindows) {
|
||||
this._windows[ix]._closedTabs = [];
|
||||
else
|
||||
}
|
||||
else {
|
||||
delete this._windows[ix];
|
||||
delete this._internalWindows[ix];
|
||||
}
|
||||
}
|
||||
// also clear all data about closed windows
|
||||
this._closedWindows = [];
|
||||
|
@ -797,6 +806,10 @@ SessionStoreService.prototype = {
|
|||
|
||||
// and create its data object
|
||||
this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false };
|
||||
|
||||
// and create its internal data object
|
||||
this._internalWindows[aWindow.__SSi] = { hosts: {} }
|
||||
|
||||
if (!this._isWindowLoaded(aWindow))
|
||||
this._windows[aWindow.__SSi]._restoring = true;
|
||||
if (!aWindow.toolbar.visible)
|
||||
|
@ -968,7 +981,9 @@ SessionStoreService.prototype = {
|
|||
winData.title = aWindow.content.document.title || tabbrowser.selectedTab.label;
|
||||
winData.title = this._replaceLoadingTitle(winData.title, tabbrowser,
|
||||
tabbrowser.selectedTab);
|
||||
this._updateCookies([winData]);
|
||||
let windows = {};
|
||||
windows[aWindow.__SSi] = winData;
|
||||
this._updateCookies(windows);
|
||||
}
|
||||
|
||||
#ifndef XP_MACOSX
|
||||
|
@ -989,6 +1004,7 @@ SessionStoreService.prototype = {
|
|||
|
||||
// clear this window from the list
|
||||
delete this._windows[aWindow.__SSi];
|
||||
delete this._internalWindows[aWindow.__SSi];
|
||||
|
||||
// save the state without this window to disk
|
||||
this.saveStateDelayed();
|
||||
|
@ -1046,6 +1062,9 @@ SessionStoreService.prototype = {
|
|||
browser.removeEventListener("DOMAutoComplete", this, true);
|
||||
|
||||
delete browser.__SS_data;
|
||||
delete browser.__SS_tabStillLoading;
|
||||
delete browser.__SS_formDataSaved;
|
||||
delete browser.__SS_hostSchemeData;
|
||||
|
||||
// If this tab was in the middle of restoring or still needs to be restored,
|
||||
// we need to reset that state. If the tab was restoring, we will attempt to
|
||||
|
@ -1125,6 +1144,8 @@ SessionStoreService.prototype = {
|
|||
}
|
||||
|
||||
delete aBrowser.__SS_data;
|
||||
delete aBrowser.__SS_tabStillLoading;
|
||||
delete aBrowser.__SS_formDataSaved;
|
||||
this.saveStateDelayed(aWindow);
|
||||
|
||||
// attempt to update the current URL we send in a crash report
|
||||
|
@ -1139,9 +1160,9 @@ SessionStoreService.prototype = {
|
|||
* Browser reference
|
||||
*/
|
||||
onTabInput: function sss_onTabInput(aWindow, aBrowser) {
|
||||
if (aBrowser.__SS_data)
|
||||
delete aBrowser.__SS_data._formDataSaved;
|
||||
|
||||
// deleting __SS_formDataSaved will cause us to recollect form data
|
||||
delete aBrowser.__SS_formDataSaved;
|
||||
|
||||
this.saveStateDelayed(aWindow, 3000);
|
||||
},
|
||||
|
||||
|
@ -1708,7 +1729,7 @@ SessionStoreService.prototype = {
|
|||
if (!browser || !browser.currentURI)
|
||||
// can happen when calling this function right after .addTab()
|
||||
return tabData;
|
||||
else if (browser.__SS_data && browser.__SS_data._tabStillLoading) {
|
||||
else if (browser.__SS_data && browser.__SS_tabStillLoading) {
|
||||
// use the data to be restored when the tab hasn't been completely loaded
|
||||
tabData = browser.__SS_data;
|
||||
if (aTab.pinned)
|
||||
|
@ -1743,10 +1764,11 @@ SessionStoreService.prototype = {
|
|||
tabData.index = history.index + 1;
|
||||
}
|
||||
else if (history && history.count > 0) {
|
||||
browser.__SS_hostSchemeData = [];
|
||||
try {
|
||||
for (var j = 0; j < history.count; j++) {
|
||||
let entry = this._serializeHistoryEntry(history.getEntryAtIndex(j, false),
|
||||
aFullData, aTab.pinned);
|
||||
aFullData, aTab.pinned, browser.__SS_hostSchemeData);
|
||||
tabData.entries.push(entry);
|
||||
}
|
||||
// If we make it through the for loop, then we're ok and we should clear
|
||||
|
@ -1835,15 +1857,16 @@ SessionStoreService.prototype = {
|
|||
* always return privacy sensitive data (use with care)
|
||||
* @param aIsPinned
|
||||
* the tab is pinned and should be treated differently for privacy
|
||||
* @param aHostSchemeData
|
||||
* an array of objects with host & scheme keys
|
||||
* @returns object
|
||||
*/
|
||||
_serializeHistoryEntry:
|
||||
function sss_serializeHistoryEntry(aEntry, aFullData, aIsPinned) {
|
||||
function sss_serializeHistoryEntry(aEntry, aFullData, aIsPinned, aHostSchemeData) {
|
||||
var entry = { url: aEntry.URI.spec };
|
||||
|
||||
try {
|
||||
entry._host = aEntry.URI.host;
|
||||
entry._scheme = aEntry.URI.scheme;
|
||||
aHostSchemeData.push({ host: aEntry.URI.host, scheme: aEntry.URI.scheme });
|
||||
}
|
||||
catch (ex) {
|
||||
// We just won't attempt to get cookies for this entry.
|
||||
|
@ -1946,7 +1969,7 @@ SessionStoreService.prototype = {
|
|||
var child = aEntry.GetChildAt(i);
|
||||
if (child) {
|
||||
entry.children.push(this._serializeHistoryEntry(child, aFullData,
|
||||
aIsPinned));
|
||||
aIsPinned, aHostSchemeData));
|
||||
}
|
||||
else { // to maintain the correct frame order, insert a dummy entry
|
||||
entry.children.push({ url: "about:blank" });
|
||||
|
@ -2044,7 +2067,7 @@ SessionStoreService.prototype = {
|
|||
var browsers = aWindow.gBrowser.browsers;
|
||||
this._windows[aWindow.__SSi].tabs.forEach(function (tabData, i) {
|
||||
if (browsers[i].__SS_data &&
|
||||
browsers[i].__SS_data._tabStillLoading)
|
||||
browsers[i].__SS_tabStillLoading)
|
||||
return; // ignore incompletely initialized tabs
|
||||
try {
|
||||
this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
|
||||
|
@ -2081,9 +2104,9 @@ SessionStoreService.prototype = {
|
|||
|
||||
this._updateTextAndScrollDataForFrame(aWindow, aBrowser.contentWindow,
|
||||
aTabData.entries[tabIndex],
|
||||
!aTabData._formDataSaved, aFullData,
|
||||
!aBrowser.__SS_formDataSaved, aFullData,
|
||||
!!aTabData.pinned);
|
||||
aTabData._formDataSaved = true;
|
||||
aBrowser.__SS_formDataSaved = true;
|
||||
if (aBrowser.currentURI.spec == "about:config")
|
||||
aTabData.entries[tabIndex].formdata = {
|
||||
"#textbox": aBrowser.contentDocument.getElementById("textbox").value
|
||||
|
@ -2260,8 +2283,8 @@ SessionStoreService.prototype = {
|
|||
* is the entry we're evaluating for a pinned tab; used only if
|
||||
* aCheckPrivacy
|
||||
*/
|
||||
_extractHostsForCookies:
|
||||
function sss__extractHostsForCookies(aEntry, aHosts, aCheckPrivacy, aIsPinned) {
|
||||
_extractHostsForCookiesFromEntry:
|
||||
function sss__extractHostsForCookiesFromEntry(aEntry, aHosts, aCheckPrivacy, aIsPinned) {
|
||||
|
||||
let host = aEntry._host,
|
||||
scheme = aEntry._scheme;
|
||||
|
@ -2275,23 +2298,11 @@ SessionStoreService.prototype = {
|
|||
let uri = this._getURIFromString(aEntry.url);
|
||||
host = uri.host;
|
||||
scheme = uri.scheme;
|
||||
this._extractHostsForCookiesFromHostScheme(host, scheme, aHosts, aCheckPrivacy, aIsPinned);
|
||||
}
|
||||
catch(ex) { }
|
||||
}
|
||||
|
||||
// host and scheme may not be set (for about: urls for example), in which
|
||||
// case testing scheme will be sufficient.
|
||||
if (/https?/.test(scheme) && !aHosts[host] &&
|
||||
(!aCheckPrivacy ||
|
||||
this._checkPrivacyLevel(scheme == "https", aIsPinned))) {
|
||||
// By setting this to true or false, we can determine when looking at
|
||||
// the host in _updateCookies if we should check for privacy.
|
||||
aHosts[host] = aIsPinned;
|
||||
}
|
||||
else if (scheme == "file") {
|
||||
aHosts[host] = true;
|
||||
}
|
||||
|
||||
if (aEntry.children) {
|
||||
aEntry.children.forEach(function(entry) {
|
||||
this._extractHostsForCookies(entry, aHosts, aCheckPrivacy, aIsPinned);
|
||||
|
@ -2299,25 +2310,63 @@ SessionStoreService.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* extract the base domain from a host & scheme
|
||||
* @param aHost
|
||||
* the host of a uri (usually via nsIURI.host)
|
||||
* @param aScheme
|
||||
* the scheme of a uri (usually via nsIURI.scheme)
|
||||
* @param aHosts
|
||||
* the hash that will be used to store hosts eg, { hostname: true }
|
||||
* @param aCheckPrivacy
|
||||
* should we check the privacy level for https
|
||||
* @param aIsPinned
|
||||
* is the entry we're evaluating for a pinned tab; used only if
|
||||
* aCheckPrivacy
|
||||
*/
|
||||
_extractHostsForCookiesFromHostScheme:
|
||||
function sss__extractHostsForCookiesFromHostScheme(aHost, aScheme, aHosts, aCheckPrivacy, aIsPinned) {
|
||||
// host and scheme may not be set (for about: urls for example), in which
|
||||
// case testing scheme will be sufficient.
|
||||
if (/https?/.test(aScheme) && !aHosts[aHost] &&
|
||||
(!aCheckPrivacy ||
|
||||
this._checkPrivacyLevel(aScheme == "https", aIsPinned))) {
|
||||
// By setting this to true or false, we can determine when looking at
|
||||
// the host in _updateCookies if we should check for privacy.
|
||||
aHosts[aHost] = aIsPinned;
|
||||
}
|
||||
else if (aScheme == "file") {
|
||||
aHosts[aHost] = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* store all hosts for a URL
|
||||
* @param aWindow
|
||||
* Window reference
|
||||
*/
|
||||
_updateCookieHosts: function sss_updateCookieHosts(aWindow) {
|
||||
var hosts = this._windows[aWindow.__SSi]._hosts = {};
|
||||
var hosts = this._internalWindows[aWindow.__SSi].hosts = {};
|
||||
|
||||
this._windows[aWindow.__SSi].tabs.forEach(function(aTabData) {
|
||||
aTabData.entries.forEach(function(entry) {
|
||||
this._extractHostsForCookies(entry, hosts, true, !!aTabData.pinned);
|
||||
}, this);
|
||||
}, this);
|
||||
// Since _updateCookiesHosts is only ever called for open windows during a
|
||||
// session, we can call into _extractHostsForCookiesFromHostScheme directly
|
||||
// using data that is attached to each browser.
|
||||
for (let i = 0; i < aWindow.gBrowser.tabs.length; i++) {
|
||||
let tab = aWindow.gBrowser.tabs[i];
|
||||
let hostSchemeData = tab.linkedBrowser.__SS_hostSchemeData || [];
|
||||
for (let j = 0; j < hostSchemeData.length; j++) {
|
||||
this._extractHostsForCookiesFromHostScheme(hostSchemeData[j].host,
|
||||
hostSchemeData[j].scheme,
|
||||
hosts, true, tab.pinned);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Serialize cookie data
|
||||
* @param aWindows
|
||||
* array of Window references
|
||||
* JS object containing window data references
|
||||
* { id: winData, etc. }
|
||||
*/
|
||||
_updateCookies: function sss_updateCookies(aWindows) {
|
||||
function addCookieToHash(aHash, aHost, aPath, aName, aCookie) {
|
||||
|
@ -2330,18 +2379,17 @@ SessionStoreService.prototype = {
|
|||
aHash[aHost][aPath][aName] = aCookie;
|
||||
}
|
||||
|
||||
// collect the cookies per window
|
||||
for (var i = 0; i < aWindows.length; i++)
|
||||
aWindows[i].cookies = [];
|
||||
|
||||
var jscookies = {};
|
||||
var _this = this;
|
||||
// MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
|
||||
var MAX_EXPIRY = Math.pow(2, 62);
|
||||
aWindows.forEach(function(aWindow) {
|
||||
if (!aWindow._hosts)
|
||||
|
||||
for (let [id, window] in Iterator(aWindows)) {
|
||||
window.cookies = [];
|
||||
let internalWindow = this._internalWindows[id];
|
||||
if (!internalWindow.hosts)
|
||||
return;
|
||||
for (var [host, isPinned] in Iterator(aWindow._hosts)) {
|
||||
for (var [host, isPinned] in Iterator(internalWindow.hosts)) {
|
||||
let list;
|
||||
try {
|
||||
list = CookieSvc.getCookiesFromHost(host);
|
||||
|
@ -2351,7 +2399,7 @@ SessionStoreService.prototype = {
|
|||
}
|
||||
while (list && list.hasMoreElements()) {
|
||||
var cookie = list.getNext().QueryInterface(Ci.nsICookie2);
|
||||
// aWindow._hosts will only have hosts with the right privacy rules,
|
||||
// window._hosts will only have hosts with the right privacy rules,
|
||||
// so there is no need to do anything special with this call to
|
||||
// _checkPrivacyLevel.
|
||||
if (cookie.isSession && _this._checkPrivacyLevel(cookie.isSecure, isPinned)) {
|
||||
|
@ -2370,16 +2418,15 @@ SessionStoreService.prototype = {
|
|||
|
||||
addCookieToHash(jscookies, cookie.host, cookie.path, cookie.name, jscookie);
|
||||
}
|
||||
aWindow.cookies.push(jscookies[cookie.host][cookie.path][cookie.name]);
|
||||
window.cookies.push(jscookies[cookie.host][cookie.path][cookie.name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// don't include empty cookie sections
|
||||
for (i = 0; i < aWindows.length; i++)
|
||||
if (aWindows[i].cookies.length == 0)
|
||||
delete aWindows[i].cookies;
|
||||
// don't include empty cookie sections
|
||||
if (!window.cookies.length)
|
||||
delete window.cookies;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2438,18 +2485,19 @@ SessionStoreService.prototype = {
|
|||
}
|
||||
|
||||
// collect the data for all windows
|
||||
var total = [], windows = [];
|
||||
var total = [], windows = {}, ids = [];
|
||||
var nonPopupCount = 0;
|
||||
var ix;
|
||||
for (ix in this._windows) {
|
||||
if (this._windows[ix]._restoring) // window data is still in _statesToRestore
|
||||
continue;
|
||||
total.push(this._windows[ix]);
|
||||
windows.push(ix);
|
||||
ids.push(ix);
|
||||
windows[ix] = this._windows[ix];
|
||||
if (!this._windows[ix].isPopup)
|
||||
nonPopupCount++;
|
||||
}
|
||||
this._updateCookies(total);
|
||||
this._updateCookies(windows);
|
||||
|
||||
// collect the data for all windows yet to be restored
|
||||
for (ix in this._statesToRestore) {
|
||||
|
@ -2500,7 +2548,7 @@ SessionStoreService.prototype = {
|
|||
if (activeWindow) {
|
||||
this.activeWindowSSiCache = activeWindow.__SSi || "";
|
||||
}
|
||||
ix = windows.indexOf(this.activeWindowSSiCache);
|
||||
ix = ids.indexOf(this.activeWindowSSiCache);
|
||||
// We don't want to restore focus to a minimized window or a window which had all its
|
||||
// tabs stripped out (doesn't exist).
|
||||
if (ix != -1 && total[ix] && total[ix].sizemode == "minimized")
|
||||
|
@ -2539,10 +2587,12 @@ SessionStoreService.prototype = {
|
|||
this._collectWindowData(aWindow);
|
||||
}
|
||||
|
||||
var total = [this._windows[aWindow.__SSi]];
|
||||
this._updateCookies(total);
|
||||
var winData = this._windows[aWindow.__SSi];
|
||||
let windows = {};
|
||||
windows[aWindow.__SSi] = winData;
|
||||
this._updateCookies(windows);
|
||||
|
||||
return { windows: total };
|
||||
return { windows: [winData] };
|
||||
},
|
||||
|
||||
_collectWindowData: function sss_collectWindowData(aWindow) {
|
||||
|
@ -2864,7 +2914,7 @@ SessionStoreService.prototype = {
|
|||
for (let name in tabData.attributes)
|
||||
this.xulAttributes[name] = true;
|
||||
|
||||
tabData._tabStillLoading = true;
|
||||
browser.__SS_tabStillLoading = true;
|
||||
|
||||
// keep the data around to prevent dataloss in case
|
||||
// a tab gets closed before it's been properly restored
|
||||
|
@ -2931,7 +2981,7 @@ SessionStoreService.prototype = {
|
|||
restoreHistory:
|
||||
function sss_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap) {
|
||||
var _this = this;
|
||||
while (aTabs.length > 0 && (!aTabData[0]._tabStillLoading || !aTabs[0].parentNode)) {
|
||||
while (aTabs.length > 0 && (!aTabs[0].linkedBrowser.__SS_tabStillLoading || !aTabs[0].parentNode)) {
|
||||
aTabs.shift(); // this tab got removed before being completely restored
|
||||
aTabData.shift();
|
||||
}
|
||||
|
@ -3327,31 +3377,53 @@ SessionStoreService.prototype = {
|
|||
if (!node)
|
||||
continue;
|
||||
|
||||
let eventType;
|
||||
let value = aData[key];
|
||||
if (typeof value == "string" && node.type != "file") {
|
||||
if (node.value == value)
|
||||
continue; // don't dispatch an input event for no change
|
||||
|
||||
node.value = value;
|
||||
|
||||
let event = aDocument.createEvent("UIEvents");
|
||||
event.initUIEvent("input", true, true, aDocument.defaultView, 0);
|
||||
node.dispatchEvent(event);
|
||||
eventType = "input";
|
||||
}
|
||||
else if (typeof value == "boolean")
|
||||
else if (typeof value == "boolean") {
|
||||
if (node.checked == value)
|
||||
continue; // don't dispatch a change event for no change
|
||||
|
||||
node.checked = value;
|
||||
else if (typeof value == "number")
|
||||
eventType = "change";
|
||||
}
|
||||
else if (typeof value == "number") {
|
||||
// We saved the value blindly since selects take more work to determine
|
||||
// default values. So now we should check to avoid unnecessary events.
|
||||
if (node.selectedIndex == value)
|
||||
continue;
|
||||
|
||||
try {
|
||||
node.selectedIndex = value;
|
||||
eventType = "change";
|
||||
} catch (ex) { /* throws for invalid indices */ }
|
||||
else if (value && value.fileList && value.type == "file" && node.type == "file")
|
||||
}
|
||||
else if (value && value.fileList && value.type == "file" && node.type == "file") {
|
||||
node.mozSetFileNameArray(value.fileList, value.fileList.length);
|
||||
eventType = "input";
|
||||
}
|
||||
else if (value && typeof value.indexOf == "function" && node.options) {
|
||||
Array.forEach(node.options, function(aOpt, aIx) {
|
||||
aOpt.selected = value.indexOf(aIx) > -1;
|
||||
|
||||
// Only fire the event here if this wasn't selected by default
|
||||
if (!aOpt.defaultSelected)
|
||||
eventType = "change";
|
||||
});
|
||||
}
|
||||
// NB: dispatching "change" events might have unintended side-effects
|
||||
|
||||
// Fire events for this node if applicable
|
||||
if (eventType) {
|
||||
let event = aDocument.createEvent("UIEvents");
|
||||
event.initUIEvent(eventType, true, true, aDocument.defaultView, 0);
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3563,6 +3635,7 @@ SessionStoreService.prototype = {
|
|||
while (oState._closedWindows.length) {
|
||||
let i = oState._closedWindows.length - 1;
|
||||
if (oState._closedWindows[i]._shouldRestore) {
|
||||
delete oState._closedWindows[i]._shouldRestore;
|
||||
oState.windows.unshift(oState._closedWindows.pop());
|
||||
}
|
||||
else {
|
||||
|
@ -4031,7 +4104,7 @@ SessionStoreService.prototype = {
|
|||
let cookieHosts = {};
|
||||
aTargetWinState.tabs.forEach(function(tab) {
|
||||
tab.entries.forEach(function(entry) {
|
||||
this._extractHostsForCookies(entry, cookieHosts, false)
|
||||
this._extractHostsForCookiesFromEntry(entry, cookieHosts, false);
|
||||
}, this);
|
||||
}, this);
|
||||
|
||||
|
@ -4062,18 +4135,7 @@ SessionStoreService.prototype = {
|
|||
* @returns the object's JSON representation
|
||||
*/
|
||||
_toJSONString: function sss_toJSONString(aJSObject) {
|
||||
// We never want to save __lastSessionWindowID across sessions, but we do
|
||||
// want it exported to consumers when running (eg. Private Browsing).
|
||||
let internalKeys = INTERNAL_KEYS;
|
||||
if (this._loadState == STATE_QUITTING) {
|
||||
internalKeys = internalKeys.slice();
|
||||
internalKeys.push("__lastSessionWindowID");
|
||||
}
|
||||
function exclude(key, value) {
|
||||
// returning undefined results in the exclusion of that key
|
||||
return internalKeys.indexOf(key) == -1 ? value : undefined;
|
||||
}
|
||||
return JSON.stringify(aJSObject, exclude);
|
||||
return JSON.stringify(aJSObject);
|
||||
},
|
||||
|
||||
_sendRestoreCompletedNotifications: function sss_sendRestoreCompletedNotifications() {
|
||||
|
|
|
@ -50,6 +50,8 @@ include $(topsrcdir)/config/rules.mk
|
|||
|
||||
_BROWSER_TEST_FILES = \
|
||||
head.js \
|
||||
browser_form_restore_events.js \
|
||||
browser_form_restore_events_sample.html \
|
||||
browser_248970_a.js \
|
||||
browser_248970_b.js \
|
||||
browser_248970_b_sample.html \
|
||||
|
@ -98,8 +100,6 @@ _BROWSER_TEST_FILES = \
|
|||
browser_465223.js \
|
||||
browser_466937.js \
|
||||
browser_466937_sample.html \
|
||||
browser_476161.js \
|
||||
browser_476161_sample.html \
|
||||
browser_477657.js \
|
||||
browser_480148.js \
|
||||
browser_480893.js \
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Test for bug 476161</title>
|
||||
|
||||
<script>
|
||||
|
||||
document.addEventListener("input", function(aEvent) {
|
||||
var inputEl = aEvent.originalTarget;
|
||||
var changedEl = document.getElementById("changed");
|
||||
|
||||
changedEl.textContent += " " + inputEl.id;
|
||||
}, false);
|
||||
|
||||
</script>
|
||||
|
||||
<h3>Text fields with changed text</h3>
|
||||
<input type="text" id="modify1">
|
||||
<input type="text" id="modify2" value="preset value">
|
||||
|
||||
<h3>Text fields with unchanged text</h3>
|
||||
<input type="text" id="unchanged1">
|
||||
<input type="text" id="unchanged2" value="preset value">
|
||||
|
||||
<h3>Changed field IDs</h3>
|
||||
<div id="changed"></div>
|
|
@ -50,7 +50,7 @@ function test() {
|
|||
// Undo pinning
|
||||
gBrowser.unpinTab(tab1);
|
||||
|
||||
is(tab1.linkedBrowser.__SS_data._tabStillLoading, true,
|
||||
is(tab1.linkedBrowser.__SS_tabStillLoading, true,
|
||||
"_tabStillLoading should be true.");
|
||||
|
||||
// Close and restore tab
|
||||
|
|
|
@ -35,33 +35,64 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function test() {
|
||||
/** Test for Bug 476161 **/
|
||||
|
||||
/** Originally a test for Bug 476161, but then expanded to include all input types in bug 640136 **/
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
|
||||
let file = Components.classes["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Components.interfaces.nsIProperties)
|
||||
.get("TmpD", Components.interfaces.nsIFile);
|
||||
|
||||
let testURL = "http://mochi.test:8888/browser/" +
|
||||
"browser/components/sessionstore/test/browser/browser_476161_sample.html";
|
||||
"browser/components/sessionstore/test/browser/browser_form_restore_events_sample.html";
|
||||
let tab = gBrowser.addTab(testURL);
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
|
||||
doc.getElementById("modify1").value += Math.random();
|
||||
doc.getElementById("modify2").value += " " + Date.now();
|
||||
|
||||
|
||||
// text fields
|
||||
doc.getElementById("modify01").value += Math.random();
|
||||
doc.getElementById("modify02").value += " " + Date.now();
|
||||
|
||||
// textareas
|
||||
doc.getElementById("modify03").value += Math.random();
|
||||
doc.getElementById("modify04").value += " " + Date.now();
|
||||
|
||||
// file
|
||||
doc.getElementById("modify05").value = file.path;
|
||||
|
||||
// select
|
||||
doc.getElementById("modify06").selectedIndex = 1;
|
||||
var multipleChange = doc.getElementById("modify07");
|
||||
Array.forEach(multipleChange.options, function(option) option.selected = true);
|
||||
|
||||
// checkbox
|
||||
doc.getElementById("modify08").checked = true;
|
||||
doc.getElementById("modify09").checked = false;
|
||||
|
||||
// radio
|
||||
// select one then another in the same group - only last one should get event on restore
|
||||
doc.getElementById("modify10").checked = true;
|
||||
doc.getElementById("modify11").checked = true;
|
||||
|
||||
|
||||
let tab2 = gBrowser.duplicateTab(tab);
|
||||
tab2.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
let doc = tab2.linkedBrowser.contentDocument;
|
||||
let changed = doc.getElementById("changed").textContent.trim().split();
|
||||
|
||||
is(changed.sort().join(" "), "modify1 modify2",
|
||||
"input events were only dispatched for modified text fields");
|
||||
|
||||
let inputFired = doc.getElementById("inputFired").textContent.trim().split();
|
||||
let changeFired = doc.getElementById("changeFired").textContent.trim().split();
|
||||
|
||||
is(inputFired.sort().join(" "), "modify01 modify02 modify03 modify04 modify05",
|
||||
"input events were only dispatched for modified input, textarea fields");
|
||||
|
||||
is(changeFired.sort().join(" "), "modify06 modify07 modify08 modify09 modify11",
|
||||
"change events were only dispatched for modified select, checkbox, radio fields");
|
||||
|
||||
// clean up
|
||||
gBrowser.removeTab(tab2);
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
|
||||
finish();
|
||||
}, true);
|
||||
}, true);
|
|
@ -0,0 +1,98 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Test for form restore events (originally bug 476161)</title>
|
||||
|
||||
<script>
|
||||
|
||||
document.addEventListener("input", function(aEvent) {
|
||||
var inputEl = aEvent.originalTarget;
|
||||
var changedEl = document.getElementById("inputFired");
|
||||
changedEl.textContent += " " + inputEl.id;
|
||||
}, false);
|
||||
|
||||
document.addEventListener("change", function(aEvent) {
|
||||
var inputEl = aEvent.originalTarget;
|
||||
var changedEl = document.getElementById("changeFired");
|
||||
changedEl.textContent += " " + inputEl.id;
|
||||
}, false);
|
||||
|
||||
</script>
|
||||
|
||||
<!-- input events -->
|
||||
<h3>Text fields with changed text</h3>
|
||||
<input type="text" id="modify1">
|
||||
<input type="text" id="modify2" value="preset value">
|
||||
<input type="text" id="modify01">
|
||||
<input type="text" id="modify02" value="preset value">
|
||||
|
||||
<h3>Text fields with unchanged text</h3>
|
||||
<input type="text" id="unchanged1">
|
||||
<input type="text" id="unchanged2" value="preset value">
|
||||
<input type="text" id="unchanged01">
|
||||
<input type="text" id="unchanged02" value="preset value">
|
||||
|
||||
<h3>Textarea with changed text</h3>
|
||||
<textarea id="modify03"></textarea>
|
||||
<textarea id="modify04">preset value</textarea>
|
||||
|
||||
<h3>Textarea with unchanged text</h3>
|
||||
<textarea id="unchanged03"></textarea>
|
||||
<textarea id="unchanged04">preset value</textarea>
|
||||
|
||||
<h3>file field with changed value</h3>
|
||||
<input type="file" id="modify05">
|
||||
|
||||
<h3>file field with unchanged value</h3>
|
||||
<input type="file" id="unchanged05">
|
||||
|
||||
<!-- change events -->
|
||||
|
||||
<h3>Select menu with changed selection</h3>
|
||||
<select id="modify06">
|
||||
<option value="one">one</option>
|
||||
<option value="two">two</option>
|
||||
<option value="three">three</option>
|
||||
</select>
|
||||
|
||||
<h3>Select menu with unchanged selection (change event still fires)</h3>
|
||||
<select id="unchanged06">
|
||||
<option value="one">one</option>
|
||||
<option value="two" selected>two</option>
|
||||
<option value="three">three</option>
|
||||
</select>
|
||||
|
||||
<h3>Multiple Select menu with changed selection</h3>
|
||||
<select id="modify07" multiple>
|
||||
<option value="one">one</option>
|
||||
<option value="two" selected>two</option>
|
||||
<option value="three">three</option>
|
||||
</select>
|
||||
|
||||
<h3>Select menu with unchanged selection</h3>
|
||||
<select id="unchanged07" multiple>
|
||||
<option value="one">one</option>
|
||||
<option value="two" selected>two</option>
|
||||
<option value="three" selected>three</option>
|
||||
</select>
|
||||
|
||||
<h3>checkbox with changed value</h3>
|
||||
<input type="checkbox" id="modify08">
|
||||
<input type="checkbox" id="modify09" checked>
|
||||
|
||||
<h3>checkbox with unchanged value</h3>
|
||||
<input type="checkbox" id="unchanged08">
|
||||
<input type="checkbox" id="unchanged09" checked>
|
||||
|
||||
<h3>radio with changed value</h3>
|
||||
<input type="radio" id="modify10" name="group">Radio 1</input>
|
||||
<input type="radio" id="modify11" name="group">Radio 2</input>
|
||||
<input type="radio" id="modify12" name="group" checked>Radio 3</input>
|
||||
|
||||
<h3>radio with unchanged value</h3>
|
||||
<input type="radio" id="unchanged10" name="group2">Radio 4</input>
|
||||
<input type="radio" id="unchanged11" name="group2">Radio 5</input>
|
||||
<input type="radio" id="unchanged12" name="group2" checked>Radio 6</input>
|
||||
|
||||
<h3>Changed field IDs</h3>
|
||||
<div id="changed"></div>
|
||||
<div id="inputFired"></div>
|
||||
<div id="changeFired"></div>
|
|
@ -2096,6 +2096,7 @@ nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
|
|||
if (!targetContent)
|
||||
return;
|
||||
|
||||
sLastDragOverFrame = nsnull;
|
||||
nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetNearestWidget();
|
||||
|
||||
// get the widget from the target frame
|
||||
|
@ -3422,6 +3423,7 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
|||
targetContent, &status);
|
||||
}
|
||||
}
|
||||
sLastDragOverFrame = nsnull;
|
||||
ClearGlobalActiveContent(this);
|
||||
break;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче