mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central and fx-team
This commit is contained in:
commit
4709550161
@ -860,6 +860,8 @@ pref("browser.sessionstore.restore_pinned_tabs_on_demand", false);
|
||||
pref("browser.sessionstore.upgradeBackup.latestBuildID", "");
|
||||
// End-users should not run sessionstore in debug mode
|
||||
pref("browser.sessionstore.debug", false);
|
||||
// Enable asynchronous data collection by default.
|
||||
pref("browser.sessionstore.async", true);
|
||||
|
||||
// allow META refresh by default
|
||||
pref("accessibility.blockautorefresh", false);
|
||||
|
@ -1435,7 +1435,6 @@ var gBrowserInit = {
|
||||
}
|
||||
|
||||
// Final window teardown, do this last.
|
||||
window.XULBrowserWindow.destroy();
|
||||
window.XULBrowserWindow = null;
|
||||
window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
@ -3690,14 +3689,6 @@ var XULBrowserWindow = {
|
||||
this.onSecurityChange(null, null, securityUI.state);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
// XXXjag to avoid leaks :-/, see bug 60729
|
||||
delete this.throbberElement;
|
||||
delete this.stopCommand;
|
||||
delete this.reloadCommand;
|
||||
delete this.statusText;
|
||||
},
|
||||
|
||||
setJSStatus: function () {
|
||||
// unsupported
|
||||
},
|
||||
|
@ -2,12 +2,19 @@
|
||||
* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
function debug(msg) {
|
||||
Services.console.logStringMessage("SessionStoreContent: " + msg);
|
||||
}
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
|
||||
"resource:///modules/sessionstore/SessionHistory.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
|
||||
"resource:///modules/sessionstore/SessionStorage.jsm");
|
||||
|
||||
/**
|
||||
* Listens for and handles content events that we need for the
|
||||
* session store service to be notified of state changes in content.
|
||||
@ -55,7 +62,37 @@ let EventListener = {
|
||||
}
|
||||
}
|
||||
};
|
||||
EventListener.init();
|
||||
|
||||
/**
|
||||
* Listens for and handles messages sent by the session store service.
|
||||
*/
|
||||
let MessageListener = {
|
||||
|
||||
MESSAGES: [
|
||||
"SessionStore:collectSessionHistory",
|
||||
"SessionStore:collectSessionStorage"
|
||||
],
|
||||
|
||||
init: function () {
|
||||
this.MESSAGES.forEach(m => addMessageListener(m, this));
|
||||
},
|
||||
|
||||
receiveMessage: function ({name, data: {id}}) {
|
||||
switch (name) {
|
||||
case "SessionStore:collectSessionHistory":
|
||||
let history = SessionHistory.read(docShell);
|
||||
sendAsyncMessage(name, {id: id, data: history});
|
||||
break;
|
||||
case "SessionStore:collectSessionStorage":
|
||||
let storage = SessionStorage.serialize(docShell);
|
||||
sendAsyncMessage(name, {id: id, data: storage});
|
||||
break;
|
||||
default:
|
||||
debug("received unknown message '" + name + "'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let ProgressListener = {
|
||||
init: function() {
|
||||
@ -74,4 +111,7 @@ let ProgressListener = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference])
|
||||
};
|
||||
|
||||
EventListener.init();
|
||||
MessageListener.init();
|
||||
ProgressListener.init();
|
||||
|
71
browser/components/sessionstore/src/Messenger.jsm
Normal file
71
browser/components/sessionstore/src/Messenger.jsm
Normal file
@ -0,0 +1,71 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Messenger"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
Cu.import("resource://gre/modules/Timer.jsm", this);
|
||||
|
||||
/**
|
||||
* The external API exported by this module.
|
||||
*/
|
||||
this.Messenger = Object.freeze({
|
||||
send: function (tab, type, options = {}) {
|
||||
return MessengerInternal.send(tab, type, options);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* A module that handles communication between the main and content processes.
|
||||
*/
|
||||
let MessengerInternal = {
|
||||
// The id of the last message we sent. This is used to assign a unique ID to
|
||||
// every message we send to handle multiple responses from the same browser.
|
||||
_latestMessageID: 0,
|
||||
|
||||
/**
|
||||
* Sends a message to the given tab and waits for a response.
|
||||
*
|
||||
* @param tab
|
||||
* tabbrowser tab
|
||||
* @param type
|
||||
* {string} the type of the message
|
||||
* @param options (optional)
|
||||
* {timeout: int} to set the timeout in milliseconds
|
||||
* @return {Promise} A promise that will resolve to the response message or
|
||||
* be reject when timing out.
|
||||
*/
|
||||
send: function (tab, type, options = {}) {
|
||||
let browser = tab.linkedBrowser;
|
||||
let mm = browser.messageManager;
|
||||
let deferred = Promise.defer();
|
||||
let id = ++this._latestMessageID;
|
||||
let timeout;
|
||||
|
||||
function onMessage({data: {id: mid, data}}) {
|
||||
if (mid == id) {
|
||||
mm.removeMessageListener(type, onMessage);
|
||||
clearTimeout(timeout);
|
||||
deferred.resolve(data);
|
||||
}
|
||||
}
|
||||
|
||||
mm.addMessageListener(type, onMessage);
|
||||
mm.sendAsyncMessage(type, {id: id});
|
||||
|
||||
function onTimeout() {
|
||||
mm.removeMessageListener(type, onMessage);
|
||||
deferred.reject(new Error("Timed out while waiting for a " + type + " " +
|
||||
"response message."));
|
||||
}
|
||||
|
||||
let delay = (options && options.timeout) || 5000;
|
||||
timeout = setTimeout(onTimeout, delay);
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
82
browser/components/sessionstore/src/PrivacyLevel.jsm
Normal file
82
browser/components/sessionstore/src/PrivacyLevel.jsm
Normal file
@ -0,0 +1,82 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PrivacyLevel"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const PREF_NORMAL = "browser.sessionstore.privacy_level";
|
||||
const PREF_DEFERRED = "browser.sessionstore.privacy_level_deferred";
|
||||
|
||||
// The following constants represent the different possible privacy levels that
|
||||
// can be set by the user and that we need to consider when collecting text
|
||||
// data, cookies, and POSTDATA.
|
||||
//
|
||||
// Collect data from all sites (http and https).
|
||||
const PRIVACY_NONE = 0;
|
||||
// Collect data from unencrypted sites (http), only.
|
||||
const PRIVACY_ENCRYPTED = 1;
|
||||
// Collect no data.
|
||||
const PRIVACY_FULL = 2;
|
||||
|
||||
/**
|
||||
* Returns whether we will resume the session automatically on next startup.
|
||||
*/
|
||||
function willResumeAutomatically() {
|
||||
return Services.prefs.getIntPref("browser.startup.page") == 3 ||
|
||||
Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the current privacy level as set by the user.
|
||||
*
|
||||
* @param isPinned
|
||||
* Whether to return the privacy level for pinned tabs.
|
||||
* @return {int} The privacy level as read from the user's preferences.
|
||||
*/
|
||||
function getCurrentLevel(isPinned) {
|
||||
let pref = PREF_NORMAL;
|
||||
|
||||
// If we're in the process of quitting and we're not autoresuming the session
|
||||
// then we will use the deferred privacy level for non-pinned tabs.
|
||||
if (!isPinned && Services.startup.shuttingDown && !willResumeAutomatically()) {
|
||||
pref = PREF_DEFERRED;
|
||||
}
|
||||
|
||||
return Services.prefs.getIntPref(pref);
|
||||
}
|
||||
|
||||
/**
|
||||
* The external API as exposed by this module.
|
||||
*/
|
||||
let PrivacyLevel = Object.freeze({
|
||||
/**
|
||||
* Checks whether we're allowed to save data for a specific site.
|
||||
*
|
||||
* @param {isHttps: boolean, isPinned: boolean}
|
||||
* An object that must have two properties: 'isHttps' and 'isPinned'.
|
||||
* 'isHttps' tells whether the site us secure communication (HTTPS).
|
||||
* 'isPinned' tells whether the site is loaded in a pinned tab.
|
||||
* @return {bool} Whether we can save data for the specified site.
|
||||
*/
|
||||
canSave: function ({isHttps, isPinned}) {
|
||||
let level = getCurrentLevel(isPinned);
|
||||
|
||||
// Never save any data when full privacy is requested.
|
||||
if (level == PRIVACY_FULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't save data for encrypted sites when requested.
|
||||
if (isHttps && level == PRIVACY_ENCRYPTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
@ -12,8 +12,8 @@ const Ci = Components.interfaces;
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
||||
"resource:///modules/sessionstore/PrivacyLevel.jsm");
|
||||
|
||||
// MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision.
|
||||
const MAX_EXPIRY = Math.pow(2, 62);
|
||||
@ -67,8 +67,8 @@ let SessionCookiesInternal = {
|
||||
for (let cookie of CookieStore.getCookiesForHost(host)) {
|
||||
// _getCookiesForHost() will only return hosts with the right privacy
|
||||
// rules, so there is no need to do anything special with this call
|
||||
// to checkPrivacyLevel().
|
||||
if (SessionStore.checkPrivacyLevel(cookie.secure, isPinned)) {
|
||||
// to PrivacyLevel.canSave().
|
||||
if (PrivacyLevel.canSave({isHttps: cookie.secure, isPinned: isPinned})) {
|
||||
cookies.push(cookie);
|
||||
}
|
||||
}
|
||||
@ -209,7 +209,7 @@ let SessionCookiesInternal = {
|
||||
// case testing scheme will be sufficient.
|
||||
if (/https?/.test(scheme) && !hosts[host] &&
|
||||
(!checkPrivacy ||
|
||||
SessionStore.checkPrivacyLevel(scheme == "https", isPinned))) {
|
||||
PrivacyLevel.canSave({isHttps: scheme == "https", isPinned: isPinned}))) {
|
||||
// By setting this to true or false, we can determine when looking at
|
||||
// the host in update() if we should check for privacy.
|
||||
hosts[host] = isPinned;
|
||||
|
272
browser/components/sessionstore/src/SessionHistory.jsm
Normal file
272
browser/components/sessionstore/src/SessionHistory.jsm
Normal file
@ -0,0 +1,272 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["SessionHistory"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
||||
"resource:///modules/sessionstore/PrivacyLevel.jsm");
|
||||
|
||||
function debug(msg) {
|
||||
Services.console.logStringMessage("SessionHistory: " + msg);
|
||||
}
|
||||
|
||||
// The preference value that determines how much post data to save.
|
||||
XPCOMUtils.defineLazyGetter(this, "gPostData", function () {
|
||||
const PREF = "browser.sessionstore.postdata";
|
||||
|
||||
// Observer that updates the cached value when the preference changes.
|
||||
Services.prefs.addObserver(PREF, () => {
|
||||
this.gPostData = Services.prefs.getIntPref(PREF);
|
||||
}, false);
|
||||
|
||||
return Services.prefs.getIntPref(PREF);
|
||||
});
|
||||
|
||||
/**
|
||||
* The external API exported by this module.
|
||||
*/
|
||||
this.SessionHistory = Object.freeze({
|
||||
read: function (docShell, includePrivateData) {
|
||||
return SessionHistoryInternal.read(docShell, includePrivateData);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The internal API for the SessionHistory module.
|
||||
*/
|
||||
let SessionHistoryInternal = {
|
||||
/**
|
||||
* Collects session history data for a given docShell.
|
||||
*
|
||||
* @param docShell
|
||||
* The docShell that owns the session history.
|
||||
* @param includePrivateData (optional)
|
||||
* True to always include private data and skip any privacy checks.
|
||||
*/
|
||||
read: function (docShell, includePrivateData = false) {
|
||||
let data = {entries: []};
|
||||
let isPinned = docShell.isAppTab;
|
||||
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let history = webNavigation.sessionHistory;
|
||||
|
||||
if (history && history.count > 0) {
|
||||
try {
|
||||
for (let i = 0; i < history.count; i++) {
|
||||
let shEntry = history.getEntryAtIndex(i, false);
|
||||
let entry = this._serializeEntry(shEntry, includePrivateData, isPinned);
|
||||
data.entries.push(entry);
|
||||
}
|
||||
} catch (ex) {
|
||||
// In some cases, getEntryAtIndex will throw. This seems to be due to
|
||||
// history.count being higher than it should be. By doing this in a
|
||||
// try-catch, we'll update history to where it breaks, print an error
|
||||
// message, and still save sessionstore.js.
|
||||
debug("SessionStore failed gathering complete history " +
|
||||
"for the focused window/tab. See bug 669196.");
|
||||
}
|
||||
data.index = history.index + 1;
|
||||
} else {
|
||||
let uri = webNavigation.currentURI.spec;
|
||||
// We landed here because the history is inaccessible or there are no
|
||||
// history entries. In that case we should at least record the docShell's
|
||||
// current URL as a single history entry. If the URL is not about:blank
|
||||
// or it's a blank tab that was modified (like a custom newtab page),
|
||||
// record it. For about:blank we explicitly want an empty array without
|
||||
// an 'index' property to denote that there are no history entries.
|
||||
if (uri != "about:blank" || webNavigation.document.body.hasChildNodes()) {
|
||||
data.entries.push({ url: uri });
|
||||
data.index = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get an object that is a serialized representation of a History entry.
|
||||
*
|
||||
* @param shEntry
|
||||
* nsISHEntry instance
|
||||
* @param includePrivateData
|
||||
* Always return privacy sensitive data (use with care).
|
||||
* @param isPinned
|
||||
* The tab is pinned and should be treated differently for privacy.
|
||||
* @return object
|
||||
*/
|
||||
_serializeEntry: function (shEntry, includePrivateData, isPinned) {
|
||||
let entry = { url: shEntry.URI.spec };
|
||||
|
||||
// Save some bytes and don't include the title property
|
||||
// if that's identical to the current entry's URL.
|
||||
if (shEntry.title && shEntry.title != entry.url) {
|
||||
entry.title = shEntry.title;
|
||||
}
|
||||
if (shEntry.isSubFrame) {
|
||||
entry.subframe = true;
|
||||
}
|
||||
|
||||
let cacheKey = shEntry.cacheKey;
|
||||
if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32 &&
|
||||
cacheKey.data != 0) {
|
||||
// XXXbz would be better to have cache keys implement
|
||||
// nsISerializable or something.
|
||||
entry.cacheKey = cacheKey.data;
|
||||
}
|
||||
entry.ID = shEntry.ID;
|
||||
entry.docshellID = shEntry.docshellID;
|
||||
|
||||
// We will include the property only if it's truthy to save a couple of
|
||||
// bytes when the resulting object is stringified and saved to disk.
|
||||
if (shEntry.referrerURI)
|
||||
entry.referrer = shEntry.referrerURI.spec;
|
||||
|
||||
if (shEntry.srcdocData)
|
||||
entry.srcdocData = shEntry.srcdocData;
|
||||
|
||||
if (shEntry.isSrcdocEntry)
|
||||
entry.isSrcdocEntry = shEntry.isSrcdocEntry;
|
||||
|
||||
if (shEntry.contentType)
|
||||
entry.contentType = shEntry.contentType;
|
||||
|
||||
let x = {}, y = {};
|
||||
shEntry.getScrollPosition(x, y);
|
||||
if (x.value != 0 || y.value != 0)
|
||||
entry.scroll = x.value + "," + y.value;
|
||||
|
||||
// Collect post data for the current history entry.
|
||||
try {
|
||||
let postdata = this._serializePostData(shEntry, isPinned);
|
||||
if (postdata) {
|
||||
entry.postdata_b64 = postdata;
|
||||
}
|
||||
} catch (ex) {
|
||||
// POSTDATA is tricky - especially since some extensions don't get it right
|
||||
debug("Failed serializing post data: " + ex);
|
||||
}
|
||||
|
||||
// Collect owner data for the current history entry.
|
||||
try {
|
||||
let owner = this._serializeOwner(shEntry);
|
||||
if (owner) {
|
||||
entry.owner_b64 = owner;
|
||||
}
|
||||
} catch (ex) {
|
||||
// Not catching anything specific here, just possible errors
|
||||
// from writeCompoundObject() and the like.
|
||||
debug("Failed serializing owner data: " + ex);
|
||||
}
|
||||
|
||||
entry.docIdentifier = shEntry.BFCacheEntry.ID;
|
||||
|
||||
if (shEntry.stateData != null) {
|
||||
entry.structuredCloneState = shEntry.stateData.getDataAsBase64();
|
||||
entry.structuredCloneVersion = shEntry.stateData.formatVersion;
|
||||
}
|
||||
|
||||
if (!(shEntry instanceof Ci.nsISHContainer)) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
if (shEntry.childCount > 0) {
|
||||
let children = [];
|
||||
for (let i = 0; i < shEntry.childCount; i++) {
|
||||
let child = shEntry.GetChildAt(i);
|
||||
|
||||
if (child) {
|
||||
// Don't try to restore framesets containing wyciwyg URLs.
|
||||
// (cf. bug 424689 and bug 450595)
|
||||
if (child.URI.schemeIs("wyciwyg")) {
|
||||
children.length = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
children.push(this._serializeEntry(child, includePrivateData, isPinned));
|
||||
}
|
||||
}
|
||||
|
||||
if (children.length) {
|
||||
entry.children = children;
|
||||
}
|
||||
}
|
||||
|
||||
return entry;
|
||||
},
|
||||
|
||||
/**
|
||||
* Serialize post data contained in the given session history entry.
|
||||
*
|
||||
* @param shEntry
|
||||
* The session history entry.
|
||||
* @param isPinned
|
||||
* Whether the docShell is owned by a pinned tab.
|
||||
* @return The base64 encoded post data.
|
||||
*/
|
||||
_serializePostData: function (shEntry, isPinned) {
|
||||
let isHttps = shEntry.URI.schemeIs("https");
|
||||
if (!shEntry.postData || !gPostData ||
|
||||
!PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned})) {
|
||||
return null;
|
||||
}
|
||||
|
||||
shEntry.postData.QueryInterface(Ci.nsISeekableStream)
|
||||
.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
|
||||
let stream = Cc["@mozilla.org/binaryinputstream;1"]
|
||||
.createInstance(Ci.nsIBinaryInputStream);
|
||||
stream.setInputStream(shEntry.postData);
|
||||
let postBytes = stream.readByteArray(stream.available());
|
||||
let postdata = String.fromCharCode.apply(null, postBytes);
|
||||
if (gPostData != -1 &&
|
||||
postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length > gPostData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// We can stop doing base64 encoding once our serialization into JSON
|
||||
// is guaranteed to handle all chars in strings, including embedded
|
||||
// nulls.
|
||||
return btoa(postdata);
|
||||
},
|
||||
|
||||
/**
|
||||
* Serialize owner data contained in the given session history entry.
|
||||
*
|
||||
* @param shEntry
|
||||
* The session history entry.
|
||||
* @return The base64 encoded owner data.
|
||||
*/
|
||||
_serializeOwner: function (shEntry) {
|
||||
if (!shEntry.owner) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
|
||||
createInstance(Ci.nsIObjectOutputStream);
|
||||
let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
|
||||
pipe.init(false, false, 0, 0xffffffff, null);
|
||||
binaryStream.setOutputStream(pipe.outputStream);
|
||||
binaryStream.writeCompoundObject(shEntry.owner, Ci.nsISupports, true);
|
||||
binaryStream.close();
|
||||
|
||||
// Now we want to read the data from the pipe's input end and encode it.
|
||||
let scriptableStream = Cc["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(Ci.nsIBinaryInputStream);
|
||||
scriptableStream.setInputStream(pipe.inputStream);
|
||||
let ownerBytes =
|
||||
scriptableStream.readByteArray(scriptableStream.available());
|
||||
|
||||
// We can stop doing base64 encoding once our serialization into JSON
|
||||
// is guaranteed to handle all chars in strings, including embedded
|
||||
// nulls.
|
||||
return btoa(String.fromCharCode.apply(null, ownerBytes));
|
||||
}
|
||||
};
|
@ -151,7 +151,7 @@ let SessionSaverInternal = {
|
||||
delay = Math.max(this._lastSaveTime + gInterval - Date.now(), delay, 0);
|
||||
|
||||
// Schedule a state save.
|
||||
this._timeoutID = setTimeout(() => this._saveState(), delay);
|
||||
this._timeoutID = setTimeout(() => this._saveStateAsync(), delay);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -186,8 +186,7 @@ let SessionSaverInternal = {
|
||||
* update the corresponding caches.
|
||||
*/
|
||||
_saveState: function (forceUpdateAllWindows = false) {
|
||||
// Cancel any pending timeouts or just clear
|
||||
// the timeout if this is why we've been called.
|
||||
// Cancel any pending timeouts.
|
||||
this.cancel();
|
||||
|
||||
stopWatchStart("COLLECT_DATA_MS", "COLLECT_DATA_LONGEST_OP_MS");
|
||||
@ -246,6 +245,33 @@ let SessionSaverInternal = {
|
||||
this._writeState(state);
|
||||
},
|
||||
|
||||
/**
|
||||
* Saves the current session state. Collects data asynchronously and calls
|
||||
* _saveState() to collect data again (with a cache hit rate of hopefully
|
||||
* 100%) and write to disk afterwards.
|
||||
*/
|
||||
_saveStateAsync: function () {
|
||||
// Allow scheduling delayed saves again.
|
||||
this._timeoutID = null;
|
||||
|
||||
// Check whether asynchronous data collection is disabled.
|
||||
if (!Services.prefs.getBoolPref("browser.sessionstore.async")) {
|
||||
this._saveState();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the last save time to make sure we wait at least another interval
|
||||
// length until we call _saveStateAsync() again.
|
||||
this.updateLastSaveTime();
|
||||
|
||||
// Save state synchronously after all tab caches have been filled. The data
|
||||
// for the tab caches is collected asynchronously. We will reuse this
|
||||
// cached data if the tab hasn't been invalidated in the meantime. In that
|
||||
// case we will just fall back to synchronous data collection for single
|
||||
// tabs.
|
||||
SessionStore.fillTabCachesAsynchronously().then(() => this._saveState());
|
||||
},
|
||||
|
||||
/**
|
||||
* Write the given state object to disk.
|
||||
*/
|
||||
|
@ -7,12 +7,13 @@
|
||||
this.EXPORTED_SYMBOLS = ["SessionStorage"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
||||
"resource:///modules/sessionstore/PrivacyLevel.jsm");
|
||||
|
||||
this.SessionStorage = {
|
||||
/**
|
||||
@ -51,16 +52,17 @@ let DomStorage = {
|
||||
read: function DomStorage_read(aDocShell, aFullData) {
|
||||
let data = {};
|
||||
let isPinned = aDocShell.isAppTab;
|
||||
let shistory = aDocShell.sessionHistory;
|
||||
let webNavigation = aDocShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let shistory = webNavigation.sessionHistory;
|
||||
|
||||
for (let i = 0; i < shistory.count; i++) {
|
||||
for (let i = 0; shistory && i < shistory.count; i++) {
|
||||
let principal = History.getPrincipalForEntry(shistory, i, aDocShell);
|
||||
if (!principal)
|
||||
continue;
|
||||
|
||||
// Check if we're allowed to store sessionStorage data.
|
||||
let isHTTPS = principal.URI && principal.URI.schemeIs("https");
|
||||
if (aFullData || SessionStore.checkPrivacyLevel(isHTTPS, isPinned)) {
|
||||
let isHttps = principal.URI && principal.URI.schemeIs("https");
|
||||
if (aFullData || PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned})) {
|
||||
let origin = principal.jarPrefix + principal.origin;
|
||||
|
||||
// Don't read a host twice.
|
||||
@ -90,8 +92,8 @@ let DomStorage = {
|
||||
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
|
||||
|
||||
// There is no need to pass documentURI, it's only used to fill documentURI property of
|
||||
// domstorage event, which in this case has no consumer. Prevention of events in case
|
||||
// of missing documentURI will be solved in a followup bug to bug 600307.
|
||||
// domstorage event, which in this case has no consumer. Prevention of events in case
|
||||
// of missing documentURI will be solved in a followup bug to bug 600307.
|
||||
let storage = storageManager.createStorage(principal, "", aDocShell.usePrivateBrowsing);
|
||||
|
||||
for (let [key, value] in Iterator(data)) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -34,6 +34,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
|
||||
"resource://gre/modules/TelemetryStopwatch.jsm");
|
||||
@ -148,6 +149,18 @@ let SessionFileInternal = {
|
||||
*/
|
||||
backupPath: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak"),
|
||||
|
||||
/**
|
||||
* The promise returned by the latest call to |write|.
|
||||
* We use it to ensure that AsyncShutdown.profileBeforeChange cannot
|
||||
* interrupt a call to |write|.
|
||||
*/
|
||||
_latestWrite: null,
|
||||
|
||||
/**
|
||||
* |true| once we have decided to stop receiving write instructiosn
|
||||
*/
|
||||
_isClosed: false,
|
||||
|
||||
/**
|
||||
* Utility function to safely read a file synchronously.
|
||||
* @param aPath
|
||||
@ -210,8 +223,11 @@ let SessionFileInternal = {
|
||||
},
|
||||
|
||||
write: function (aData) {
|
||||
if (this._isClosed) {
|
||||
return Promise.reject(new Error("_SessionFile is closed"));
|
||||
}
|
||||
let refObj = {};
|
||||
return TaskUtils.spawn(function task() {
|
||||
return this._latestWrite = TaskUtils.spawn(function task() {
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS", refObj);
|
||||
|
||||
try {
|
||||
@ -227,6 +243,15 @@ let SessionFileInternal = {
|
||||
Cu.reportError("Could not write session state file " + this.path
|
||||
+ ": " + ex);
|
||||
}
|
||||
// At this stage, we are done writing. If shutdown has started,
|
||||
// we will want to stop receiving write instructions.
|
||||
if (Services.startup.shuttingDown) {
|
||||
this._isClosed = true;
|
||||
}
|
||||
// In rare cases, we may already have other writes pending,
|
||||
// which we need to flush before shutdown proceeds. AsyncShutdown
|
||||
// uses _latestWrite to determine what needs to be flushed during
|
||||
// shutdown.
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
@ -277,3 +302,11 @@ let SessionWorker = (function () {
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// Ensure that we can write sessionstore.js cleanly before the profile
|
||||
// becomes unaccessible.
|
||||
AsyncShutdown.profileBeforeChange.addBlocker(
|
||||
"SessionFile: Finish writing the latest sessionstore.js",
|
||||
function() {
|
||||
return _SessionFile._latestWrite;
|
||||
});
|
||||
|
@ -14,7 +14,10 @@ JS_MODULES_PATH = 'modules/sessionstore'
|
||||
|
||||
EXTRA_JS_MODULES = [
|
||||
'DocumentUtils.jsm',
|
||||
'Messenger.jsm',
|
||||
'PrivacyLevel.jsm',
|
||||
'SessionCookies.jsm',
|
||||
'SessionHistory.jsm',
|
||||
'SessionMigration.jsm',
|
||||
'SessionStorage.jsm',
|
||||
'SessionWorker.js',
|
||||
|
@ -20,7 +20,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_input_sample.html \
|
||||
browser_pageshow.js \
|
||||
browser_sessionStorage.js \
|
||||
browser_upgrade_backup.js \
|
||||
browser_upgrade_backup.js \
|
||||
browser_windowRestore_perwindowpb.js \
|
||||
browser_248970_b_perwindowpb.js \
|
||||
browser_248970_b_sample.html \
|
||||
@ -135,6 +135,8 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_739805.js \
|
||||
browser_819510_perwindowpb.js \
|
||||
browser_833286_atomic_backup.js \
|
||||
browser_916390_form_data_loss.js \
|
||||
browser_916390_sample.html \
|
||||
$(filter disabled-for-intermittent-failures--bug-766044, browser_459906_empty.html) \
|
||||
$(filter disabled-for-intermittent-failures--bug-766044, browser_459906_sample.html) \
|
||||
$(filter disabled-for-intermittent-failures--bug-765389, browser_461743_sample.html) \
|
||||
|
@ -2,13 +2,15 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
let assertNumberOfTabs = function (num, msg) {
|
||||
function runTests() {
|
||||
function assertNumberOfTabs(num, msg) {
|
||||
is(gBrowser.tabs.length, num, msg);
|
||||
}
|
||||
|
||||
let assertNumberOfPinnedTabs = function (num, msg) {
|
||||
function assertNumberOfPinnedTabs(num, msg) {
|
||||
is(gBrowser._numPinnedTabs, num, msg);
|
||||
}
|
||||
|
||||
@ -27,13 +29,13 @@ function test() {
|
||||
assertNumberOfPinnedTabs(2, "both tabs are now pinned");
|
||||
|
||||
// run the test
|
||||
waitForBrowserState(
|
||||
yield waitForBrowserState(
|
||||
{ windows: [{ tabs: [{ url: "about:blank" }] }] },
|
||||
function () {
|
||||
assertNumberOfTabs(1, "one tab left after setBrowserState()");
|
||||
assertNumberOfPinnedTabs(0, "there are no pinned tabs");
|
||||
is(gBrowser.tabs[0].linkedBrowser, linkedBrowser, "first tab's browser got re-used");
|
||||
finish();
|
||||
next();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -58,7 +58,6 @@ function test() {
|
||||
ss.getBrowserState();
|
||||
|
||||
is(gBrowser.tabs[1], tab, "newly created tab should exist by now");
|
||||
ok(tab.linkedBrowser.__SS_data, "newly created tab should be in save state");
|
||||
|
||||
// Start a load and interrupt it by closing the tab
|
||||
tab.linkedBrowser.loadURI(URI_TO_LOAD);
|
||||
|
@ -0,0 +1,65 @@
|
||||
/* 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_916390_sample.html";
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
// Create a tab with some form fields.
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(URL);
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
yield waitForLoad(browser);
|
||||
|
||||
// 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.txt, "m", "txt's value is correct");
|
||||
|
||||
// Change the number of session history entries and modify
|
||||
// DOMSessionStorage data to invalidate the TabStateCache.
|
||||
browser.loadURI(URL + "#");
|
||||
browser.contentWindow.sessionStorage.foo = "bar";
|
||||
yield waitForStorageChange();
|
||||
|
||||
// Check that we'll save the form data state correctly.
|
||||
let state = JSON.parse(ss.getBrowserState());
|
||||
let {formdata} = state.windows[0].tabs[1].entries[1];
|
||||
is(formdata.id.txt, "m", "txt's value is correct");
|
||||
|
||||
// Clean up.
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
|
||||
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 onInput() {
|
||||
mm.removeMessageListener("SessionStore:input", onInput);
|
||||
executeSoon(next);
|
||||
});
|
||||
}
|
||||
|
||||
function waitForStorageChange() {
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
|
||||
mm.addMessageListener("SessionStore:MozStorageChanged", function onChanged() {
|
||||
mm.removeMessageListener("SessionStore:MozStorageChanged", onChanged);
|
||||
executeSoon(next);
|
||||
});
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>bug 916390</title>
|
||||
</head>
|
||||
<body>
|
||||
<input id="txt" />
|
||||
</body>
|
||||
</html>
|
@ -68,7 +68,11 @@ STUB_HOOK = $(NSINSTALL) -D "$(_ABS_DIST)/$(PKG_INST_PATH)"; \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows metro)
|
||||
SEARCHPLUGINS_NAMES = $(shell cat $(call MERGE_FILE,/searchplugins/metrolist.txt))
|
||||
else
|
||||
SEARCHPLUGINS_NAMES = $(shell cat $(call MERGE_FILE,/searchplugins/list.txt))
|
||||
endif
|
||||
SEARCHPLUGINS_PATH := $(FINAL_TARGET)/searchplugins
|
||||
SEARCHPLUGINS := $(addsuffix .xml,$(SEARCHPLUGINS_NAMES))
|
||||
PP_TARGETS += SEARCHPLUGINS
|
||||
|
18
browser/locales/en-US/searchplugins/googlemetrofx.xml
Normal file
18
browser/locales/en-US/searchplugins/googlemetrofx.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
|
||||
<ShortName>Google</ShortName>
|
||||
<Description>Google Search</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAAAAAB9AQAAJgAAACAgAAAAAAAA8gIAAKMBAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAFESURBVDjLpZNJSwNBEIXnt4lE4kHxovgT9BDwJHqPy0HEEOJBiAuCRg+KUdC4QS4KrpC4gCBGE3NQ48JsnZ6eZ3UOM6gjaePhQU93v6+qq2q0pqgeJj2S8EdJT1hr0OxBtKCD5iEd8QxDYpvhvOBAuMDKURX9C9aPu4GA1GEVkzvMg10UBfYveWAWgYAP00V01fa+R9M2bA51wJvhIn3qR+ybt3D3JNQBE5sMjCIOLFpoHzOwdsLRO22qA6R6kiZiWwxUvy/PUQZIhYZ1vFM9cvcOOsYNdcBgysISdSJBnZjJMlR0Fw8vAp0xoz5gao/h+NZBy4i/10XGwrPA+hmvDyhVRG2Avu/LwcrkFADZa16L1h330w1RNgc3DiJzCpPYRm1bpveXX11clQR28xwblHpk1vq1iP/5mcoS0CoXDZiL0vsJ+dzfl+3T/VYAAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAAIAAAACAIBgAAAHN6evQAAAK5SURBVFjDxVfrSxRRFJ9/Jta/oyWjF5XQm6D6EkHRgygIIgjUTcueVgqVWSRRkppEUQYWWB8ye1iGWilWlo/Ude489s7M6Zw7D9dlt53dmd29cFiWvXvO77x+51xpaaUsoSxBaUWZQ4ECy5xji2xKZDyCMlMEw6lCNiOSgwZKJK1SkcKeSealfP64t0mBjl4Ow39MkDUL0p2RSROOtqhZdeUEYM1pBl39XCg/fEeFtWcY7G9W4csvUxjlBkCsQ4Nt9QyWVfvT6RsAKXw3aoDGATZeYIt+W1kjw7cJG0RctWDTRebbKd8A6h5pwsDb70ba3w/eUr3wt/cmwgfw6Yft4TNMQaY7o1P2ncm4FT4ANQH/jQBJ2xv7kqIXEADDql8eS3+n8bku7oxNm+EDIM/dU92upb3T/NJGeaNbDx/AsbsLRUY5Xn92caWXY5d8RV6gWllxSg4fAEnTC90DQW13BLlgXR2D3dcUeDVkwOthA1bXspxILWcm3HdThcfvufB26LcJpkOEAz9NKI/lzqpSEC7feol5EWnpSeSlIxCALUkApmULdjUqxQVAQnl3D/X/yQda4QBEq2TYc12By091MQ17Bg3R88nHKlQbVmHvj89awNBLYrwT9zXY2aBAxTkGFdiSxP/Jp6FLDw+AS7GfsdJTJ2EqSO5khD43nGfBARy/ZxOQgZHe7GPM1jzUvChUtmnBAXQPcKGMJp3fdFGq6NByEhiAO4b/YptFfQJwNyQ/bZkVQGcf90Ja25ndIyrKBOa/f8wIpwi3X1G8UcxNu7ozUS7tiH0jBswwS3RIaF1w6LYKU/ML2+8sGnjygQswtKrVIy/Qd9qQP6LnO64q4fPAKpxyZIymHo1jWk6p1ag2BsdNwQMHcC+M5kHFJX+YlPxpVlbCx2mZ5DzPI04k4kUwHHdskU3pH76iftG8yWlkAAAAAElFTkSuQmCC</Image>
|
||||
<Url type="application/x-suggestions+json" method="GET" template="https://www.google.com/complete/search?client=metrofirefox&q={searchTerms}"/>
|
||||
<Url type="text/html" method="GET" template="https://www.google.com/search">
|
||||
<Param name="q" value="{searchTerms}"/>
|
||||
<Param name="ie" value="utf-8"/>
|
||||
<Param name="oe" value="utf-8"/>
|
||||
<Param name="client" value="metrofirefox"/>
|
||||
</Url>
|
||||
<SearchForm>https://www.google.com/</SearchForm>
|
||||
</SearchPlugin>
|
4
browser/locales/en-US/searchplugins/metrolist.txt
Normal file
4
browser/locales/en-US/searchplugins/metrolist.txt
Normal file
@ -0,0 +1,4 @@
|
||||
bing
|
||||
googlemetrofx
|
||||
wikipedia
|
||||
yahoo
|
@ -184,6 +184,7 @@ var ContextUI = {
|
||||
// Dismiss the navbar if visible.
|
||||
dismissNavbar: function dismissNavbar() {
|
||||
if (!BrowserUI.isStartTabVisible) {
|
||||
Elements.autocomplete.closePopup();
|
||||
Elements.navbar.dismiss();
|
||||
ContentAreaObserver.updateContentArea();
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ let Util = {
|
||||
*/
|
||||
getDownloadSize: function dv__getDownloadSize (aSize) {
|
||||
let [size, units] = DownloadUtils.convertByteUnits(aSize);
|
||||
if (size > 0)
|
||||
if (aSize > 0)
|
||||
return size + units;
|
||||
else
|
||||
return Strings.browser.GetStringFromName("downloadsUnknownSize");
|
||||
|
@ -44,7 +44,8 @@ HelperAppLauncherDialog.prototype = {
|
||||
|
||||
_getDownloadSize: function dv__getDownloadSize (aSize) {
|
||||
let displaySize = DownloadUtils.convertByteUnits(aSize);
|
||||
if (!isNaN(displaySize[0]) && displaySize[0] > 0) // [0] is size, [1] is units
|
||||
// displaySize[0] is formatted size, displaySize[1] is units
|
||||
if (aSize > 0)
|
||||
return displaySize.join("");
|
||||
else {
|
||||
let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
|
@ -713,7 +713,7 @@ IFACEMETHODIMP CExecuteCommandVerb::Execute()
|
||||
}
|
||||
|
||||
CComPtr<IApplicationActivationManager> activateMgr;
|
||||
if (!PrepareActivationManager(activateMgr)) {
|
||||
if (FAILED(PrepareActivationManager(activateMgr))) {
|
||||
LaunchDesktopBrowser();
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -182,16 +182,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
// fall through
|
||||
case SELECTED:
|
||||
if (Tabs.getInstance().isSelectedTab(tab)) {
|
||||
if (isAboutHome(tab)) {
|
||||
showHomePager(tab.getAboutHomePage());
|
||||
|
||||
if (isDynamicToolbarEnabled()) {
|
||||
// Show the toolbar.
|
||||
mLayerView.getLayerMarginsAnimator().showMargins(false);
|
||||
}
|
||||
} else {
|
||||
hideHomePager();
|
||||
}
|
||||
updateHomePagerForTab(tab);
|
||||
|
||||
if (mSiteIdentityPopup != null)
|
||||
mSiteIdentityPopup.dismiss();
|
||||
@ -1508,6 +1499,27 @@ abstract public class BrowserApp extends GeckoApp
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the home pager for the given tab.
|
||||
*/
|
||||
private void updateHomePagerForTab(Tab tab) {
|
||||
// Don't change the visibility of the home pager if we're in editing mode.
|
||||
if (mBrowserToolbar.isEditing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAboutHome(tab)) {
|
||||
showHomePager(tab.getAboutHomePage());
|
||||
|
||||
if (isDynamicToolbarEnabled()) {
|
||||
// Show the toolbar.
|
||||
mLayerView.getLayerMarginsAnimator().showMargins(false);
|
||||
}
|
||||
} else {
|
||||
hideHomePager();
|
||||
}
|
||||
}
|
||||
|
||||
private void showHomePager(HomePager.Page page) {
|
||||
showHomePagerWithAnimator(page, null);
|
||||
}
|
||||
@ -2125,7 +2137,18 @@ abstract public class BrowserApp extends GeckoApp
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri));
|
||||
}
|
||||
|
||||
if (!Intent.ACTION_MAIN.equals(action) || !mInitialized) {
|
||||
if (!mInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dismiss editing mode if the user is loading a URL from an external app.
|
||||
if (Intent.ACTION_VIEW.equals(action)) {
|
||||
dismissEditingMode();
|
||||
return;
|
||||
}
|
||||
|
||||
// Only solicit feedback when the app has been launched from the icon shortcut.
|
||||
if (!Intent.ACTION_MAIN.equals(action)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -49,8 +49,8 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
private static final int VERIFY_URL_TIMEOUT = 2000;
|
||||
private static final int MAX_LIST_ATTEMPTS = 3;
|
||||
private static final int MAX_WAIT_ENABLED_TEXT_MS = 10000;
|
||||
private static final int MAX_WAIT_HOME_PAGER_HIDDEN_MS = 10000;
|
||||
public static final int MAX_WAIT_MS = 3000;
|
||||
private static final int MAX_WAIT_HOME_PAGER_HIDDEN_MS = 15000;
|
||||
public static final int MAX_WAIT_MS = 4500;
|
||||
|
||||
// IDs for UI views
|
||||
private static final String BROWSER_TOOLBAR_ID = "browser_toolbar";
|
||||
|
@ -4,11 +4,12 @@ package @ANDROID_PACKAGE_NAME@.tests;
|
||||
import @ANDROID_PACKAGE_NAME@.*;
|
||||
|
||||
abstract class PixelTest extends BaseTest {
|
||||
private static final long PAINT_CLEAR_DELAY = 3000; // milliseconds
|
||||
private static final long PAINT_CLEAR_DELAY = 10000; // milliseconds
|
||||
|
||||
protected final PaintedSurface loadAndGetPainted(String url) {
|
||||
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();
|
||||
loadUrl(url);
|
||||
verifyHomePagerHidden();
|
||||
paintExpecter.blockUntilClear(PAINT_CLEAR_DELAY);
|
||||
paintExpecter.unregisterListener();
|
||||
PaintedSurface p = mDriver.getPaintedSurface();
|
||||
|
@ -12,7 +12,6 @@ public class testCheck2 extends PixelTest {
|
||||
|
||||
blockForGeckoReady();
|
||||
loadAndPaint(url);
|
||||
verifyHomePagerHidden();
|
||||
|
||||
mDriver.setupScrollHandling();
|
||||
|
||||
|
@ -6,6 +6,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from configobj import ConfigObj
|
||||
import re
|
||||
import os
|
||||
|
||||
|
||||
BUGZILLA_FINGERPRINT = '45:77:35:fd:6f:2c:1c:c2:90:4b:f7:b4:4d:60:c6:97:c5:5c:47:27'
|
||||
@ -15,9 +16,22 @@ HG_FINGERPRINT = '10:78:e8:57:2d:95:de:7c:de:90:bd:22:e1:38:17:67:c5:a7:9c:14'
|
||||
class MercurialConfig(object):
|
||||
"""Interface for manipulating a Mercurial config file."""
|
||||
|
||||
def __init__(self, infile=None):
|
||||
def __init__(self, infiles=None):
|
||||
"""Create a new instance, optionally from an existing hgrc file."""
|
||||
|
||||
if infiles:
|
||||
# If multiple files were specified, figure out which file we're using:
|
||||
if len(infiles) > 1:
|
||||
picky_infiles = filter(os.path.isfile, infiles)
|
||||
if picky_infiles:
|
||||
picky_infiles = [(os.path.getsize(path), path) for path in picky_infiles]
|
||||
infiles = [max(picky_infiles)[1]]
|
||||
|
||||
infile = infiles[0]
|
||||
self.config_path = infile
|
||||
else:
|
||||
infile = None
|
||||
|
||||
# write_empty_values is necessary to prevent built-in extensions (which
|
||||
# have no value) from being dropped on write.
|
||||
# list_values aren't needed by Mercurial and disabling them prevents
|
||||
|
@ -90,7 +90,7 @@ class MercurialSetupWizard(object):
|
||||
self.state_dir = state_dir
|
||||
self.ext_dir = os.path.join(state_dir, 'mercurial', 'extensions')
|
||||
|
||||
def run(self, config_path):
|
||||
def run(self, config_paths):
|
||||
try:
|
||||
os.makedirs(self.ext_dir)
|
||||
except OSError as e:
|
||||
@ -105,7 +105,7 @@ class MercurialSetupWizard(object):
|
||||
'up to date.')
|
||||
return 1
|
||||
|
||||
c = MercurialConfig(config_path)
|
||||
c = MercurialConfig(config_paths)
|
||||
|
||||
print(INITIAL_MESSAGE)
|
||||
raw_input()
|
||||
@ -225,6 +225,7 @@ class MercurialSetupWizard(object):
|
||||
new_lines = [line.rstrip() for line in b.getvalue().splitlines()]
|
||||
old_lines = []
|
||||
|
||||
config_path = c.config_path
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, 'rt') as fh:
|
||||
old_lines = [line.rstrip() for line in fh.readlines()]
|
||||
|
@ -26,7 +26,10 @@ class VersionControlCommands(object):
|
||||
from hgsetup.wizard import MercurialSetupWizard
|
||||
|
||||
wizard = MercurialSetupWizard(self._context.state_dir)
|
||||
result = wizard.run(os.path.expanduser('~/.hgrc'))
|
||||
config_paths = ['~/.hgrc']
|
||||
if sys.platform in ('win32', 'cygwin'):
|
||||
config_paths.insert(0, '~/mercurial.ini')
|
||||
result = wizard.run(map(os.path.expanduser, config_paths))
|
||||
|
||||
# Touch a file so we can periodically prompt to update extensions.
|
||||
state_path = os.path.join(self._context.state_dir,
|
||||
|
Loading…
Reference in New Issue
Block a user