Merge mozilla-central to autoland. a=merge CLOSED TREE

This commit is contained in:
Ciure Andrei 2019-01-26 15:52:08 +02:00
Родитель a27f06758a 849dbde8e4
Коммит 156ea01527
30 изменённых файлов: 633 добавлений и 760 удалений

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

@ -858,6 +858,8 @@ pref("browser.sessionstore.debug", false);
pref("browser.sessionstore.debug.no_auto_updates", false);
// Forget closed windows/tabs after two weeks
pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
// Maximum number of bytes of DOMSessionStorage data we collect per origin.
pref("browser.sessionstore.dom_storage_limit", 2048);
// Amount of failed SessionFile writes until we restart the worker.
pref("browser.sessionstore.max_write_failures", 5);

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

@ -7,14 +7,12 @@ const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/
const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
const TP_PREF = "privacy.trackingprotection.enabled";
const ANIMATIONS_PREF = "toolkit.cosmeticAnimations.enabled";
const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
// Test that the shield icon animation can be controlled by the cosmetic
// animations pref and that one of the icons is visible in each case.
add_task(async function testShieldAnimation() {
await UrlClassifierTestUtils.addTestTrackers();
Services.prefs.setBoolPref(TP_PREF, true);
Services.prefs.setBoolPref(DTSCBN_PREF, true);
let tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
@ -22,8 +20,7 @@ add_task(async function testShieldAnimation() {
let noAnimationIcon = document.getElementById("tracking-protection-icon");
Services.prefs.setBoolPref(ANIMATIONS_PREF, true);
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.linkedBrowser.ownerGlobal)]);
await promiseTabLoadEvent(tab, TRACKING_PAGE);
ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden when animations are enabled");
ok(BrowserTestUtils.is_visible(animationIcon), "the animated icon is shown when animations are enabled");
@ -32,14 +29,12 @@ add_task(async function testShieldAnimation() {
ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden");
Services.prefs.setBoolPref(ANIMATIONS_PREF, false);
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.linkedBrowser.ownerGlobal)]);
await promiseTabLoadEvent(tab, TRACKING_PAGE);
ok(BrowserTestUtils.is_visible(noAnimationIcon), "the default icon is shown when animations are disabled");
ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden when animations are disabled");
gBrowser.removeCurrentTab();
Services.prefs.clearUserPref(ANIMATIONS_PREF);
Services.prefs.clearUserPref(TP_PREF);
Services.prefs.clearUserPref(DTSCBN_PREF);
UrlClassifierTestUtils.cleanupTestTrackers();
});

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

@ -11,7 +11,6 @@ const NCB_PREF = "network.cookie.cookieBehavior";
const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
const COOKIE_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/cookiePage.html";
const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
requestLongerTimeout(2);
@ -20,13 +19,10 @@ registerCleanupFunction(function() {
Services.prefs.clearUserPref(TP_PREF);
Services.prefs.clearUserPref(TP_PB_PREF);
Services.prefs.clearUserPref(NCB_PREF);
Services.prefs.clearUserPref(DTSCBN_PREF);
Services.prefs.clearUserPref(ContentBlocking.prefIntroCount);
});
async function testTrackingProtectionAnimation(tabbrowser) {
Services.prefs.setBoolPref(DTSCBN_PREF, true);
info("Load a test page not containing tracking elements");
let benignTab = await BrowserTestUtils.openNewForegroundTab(tabbrowser, BENIGN_PAGE);
let ContentBlocking = tabbrowser.ownerGlobal.ContentBlocking;

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

@ -6,14 +6,12 @@
const TP_PB_PREF = "privacy.trackingprotection.enabled";
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
var TrackingProtection = null;
var ContentBlocking = null;
var browser = null;
registerCleanupFunction(function() {
Services.prefs.clearUserPref(TP_PB_PREF);
Services.prefs.clearUserPref(DTSCBN_PREF);
ContentBlocking = TrackingProtection = browser = null;
UrlClassifierTestUtils.cleanupTestTrackers();
});
@ -81,7 +79,6 @@ function testTrackingPageUnblocked() {
add_task(async function testExceptionAddition() {
await UrlClassifierTestUtils.addTestTrackers();
Services.prefs.setBoolPref(DTSCBN_PREF, true);
let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
browser = privateWin.gBrowser;
let tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser: browser, waitForLoad: true, waitForStateStop: true });
@ -95,8 +92,7 @@ add_task(async function testExceptionAddition() {
ok(TrackingProtection.enabled, "TP is enabled after setting the pref");
info("Load a test page containing tracking elements");
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.ownerGlobal)]);
await promiseTabLoadEvent(tab, TRACKING_PAGE);
testTrackingPage(tab.ownerGlobal);
@ -132,8 +128,7 @@ add_task(async function testExceptionPersistence() {
ok(TrackingProtection.enabled, "TP is still enabled");
info("Load a test page containing tracking elements");
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.ownerGlobal)]);
await promiseTabLoadEvent(tab, TRACKING_PAGE);
testTrackingPage(tab.ownerGlobal);
@ -142,8 +137,7 @@ add_task(async function testExceptionPersistence() {
clickButton("#tracking-action-unblock");
is(identityPopupState(), "closed", "Identity popup is closed");
await Promise.all([tabReloadPromise,
waitForContentBlockingEvent(2, tab.ownerGlobal)]);
await tabReloadPromise;
testTrackingPageUnblocked();
privateWin.close();

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

@ -17,7 +17,6 @@
const TP_PREF = "privacy.trackingprotection.enabled";
const TP_PB_PREF = "privacy.trackingprotection.pbmode.enabled";
const TPC_PREF = "network.cookie.cookieBehavior";
const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
const COOKIE_PAGE = "http://not-tracking.example.com/browser/browser/base/content/test/trackingUI/cookiePage.html";
@ -34,7 +33,6 @@ registerCleanupFunction(function() {
Services.prefs.clearUserPref(TP_PREF);
Services.prefs.clearUserPref(TP_PB_PREF);
Services.prefs.clearUserPref(TPC_PREF);
Services.prefs.clearUserPref(DTSCBN_PREF);
});
// This is a special version of "hidden" that doesn't check for item
@ -108,8 +106,8 @@ function testTrackingPage(window) {
ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected");
ok(!ContentBlocking.content.hasAttribute("hasException"), "content shows no exception");
let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
let blockedByTP = areTrackersBlocked(isWindowPrivate);
let isPrivateBrowsing = PrivateBrowsingUtils.isWindowPrivate(window);
let blockedByTP = areTrackersBlocked(isPrivateBrowsing);
is(BrowserTestUtils.is_visible(ContentBlocking.iconBox), blockedByTP,
"icon box is" + (blockedByTP ? "" : " not") + " visible");
is(ContentBlocking.iconBox.hasAttribute("active"), blockedByTP,
@ -121,6 +119,7 @@ function testTrackingPage(window) {
ok(hidden("#tracking-action-block"), "blockButton is hidden");
let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
if (isWindowPrivate) {
ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
is(!hidden("#tracking-action-unblock-private"), blockedByTP,
@ -219,8 +218,6 @@ async function testContentBlocking(tab) {
add_task(async function testNormalBrowsing() {
await UrlClassifierTestUtils.addTestTrackers();
Services.prefs.setBoolPref(DTSCBN_PREF, true);
tabbrowser = gBrowser;
let tab = tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser);

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

@ -8,7 +8,6 @@ const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/te
const TP_PREF = "privacy.trackingprotection.enabled";
const COOKIE_PREF = "network.cookie.cookieBehavior";
const ANIMATIONS_PREF = "toolkit.cosmeticAnimations.enabled";
const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
// Check that the shield icon is always hidden when all content blocking
// categories are turned off, even when content blocking is on.
@ -16,7 +15,6 @@ add_task(async function testContentBlockingAllDisabled() {
await SpecialPowers.pushPrefEnv({set: [
[TP_PREF, false],
[COOKIE_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT],
[DTSCBN_PREF, true],
]});
await UrlClassifierTestUtils.addTestTrackers();
@ -32,8 +30,7 @@ add_task(async function testContentBlockingAllDisabled() {
let animationIcon = document.getElementById("tracking-protection-icon-animatable-image");
let noAnimationIcon = document.getElementById("tracking-protection-icon");
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.ownerGlobal)]);
await promiseTabLoadEvent(tab, TRACKING_PAGE);
ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden");
ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden");
@ -50,7 +47,6 @@ add_task(async function testContentBlockingAllDisabled() {
await SpecialPowers.pushPrefEnv({set: [
[TP_PREF, true],
]});
await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
waitForContentBlockingEvent(2, tab.ownerGlobal)]);
await promiseTabLoadEvent(tab, TRACKING_PAGE);
ok(BrowserTestUtils.is_visible(noAnimationIcon), "the default icon is shown");
});

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

@ -8,8 +8,12 @@ var EXPORTED_SYMBOLS = ["ContentRestore"];
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
ChromeUtils.defineModuleGetter(this, "FormData",
"resource://gre/modules/FormData.jsm");
ChromeUtils.defineModuleGetter(this, "SessionHistory",
"resource://gre/modules/sessionstore/SessionHistory.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStorage",
"resource:///modules/sessionstore/SessionStorage.jsm");
ChromeUtils.defineModuleGetter(this, "Utils",
"resource://gre/modules/sessionstore/Utils.jsm");
@ -137,7 +141,7 @@ ContentRestoreInternal.prototype = {
if (tabData.storage && this.docShell instanceof Ci.nsIDocShell) {
SessionStoreUtils.restoreSessionStorage(this.docShell, tabData.storage);
SessionStorage.restore(this.docShell, tabData.storage);
delete tabData.storage;
}
@ -299,7 +303,7 @@ ContentRestoreInternal.prototype = {
// restore() will return false, and thus abort restoration for the
// current |frame| and its descendants, if |data.url| is given but
// doesn't match the loaded document's URL.
return SessionStoreUtils.restoreFormData(frame.document, data);
return FormData.restore(frame, data);
});
// Restore scroll data.

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

@ -18,6 +18,8 @@ ChromeUtils.defineModuleGetter(this, "ContentRestore",
"resource:///modules/sessionstore/ContentRestore.jsm");
ChromeUtils.defineModuleGetter(this, "SessionHistory",
"resource://gre/modules/sessionstore/SessionHistory.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStorage",
"resource:///modules/sessionstore/SessionStorage.jsm");
ChromeUtils.defineModuleGetter(this, "Utils",
"resource://gre/modules/sessionstore/Utils.jsm");
@ -531,10 +533,7 @@ class SessionStorageListener extends Handler {
// messages.
this.resetChanges();
this.messageQueue.push("storage", () => {
let data = SessionStoreUtils.collectSessionStorage(content);
return Object.keys(data).length ? data : null;
});
this.messageQueue.push("storage", () => SessionStorage.collect(content));
}
onPageLoadCompleted() {

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

@ -0,0 +1,211 @@
/* 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";
var EXPORTED_SYMBOLS = ["SessionStorage"];
ChromeUtils.import("resource://gre/modules/Services.jsm");
// A bound to the size of data to store for DOM Storage.
const DOM_STORAGE_LIMIT_PREF = "browser.sessionstore.dom_storage_limit";
// Returns the principal for a given |frame| contained in a given |docShell|.
function getPrincipalForFrame(docShell, frame) {
let ssm = Services.scriptSecurityManager;
let uri = frame.document.documentURIObject;
return ssm.getDocShellCodebasePrincipal(uri, docShell);
}
var SessionStorage = Object.freeze({
/**
* Updates all sessionStorage "super cookies"
* @param content
* A tab's global, i.e. the root frame we want to collect for.
* @return Returns a nested object that will have hosts as keys and per-origin
* session storage data as strings. For example:
* {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
*/
collect(content) {
return SessionStorageInternal.collect(content);
},
/**
* Restores all sessionStorage "super cookies".
* @param aDocShell
* A tab's docshell (containing the sessionStorage)
* @param aStorageData
* A nested object with storage data to be restored that has hosts as
* keys and per-origin session storage data as strings. For example:
* {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
*/
restore(aDocShell, aStorageData) {
SessionStorageInternal.restore(aDocShell, aStorageData);
},
});
/**
* Calls the given callback |cb|, passing |frame| and each of its descendants.
*/
function forEachNonDynamicChildFrame(frame, cb) {
// Call for current frame.
cb(frame);
// Call the callback recursively for each descendant.
SessionStoreUtils.forEachNonDynamicChildFrame(frame, subframe => {
return forEachNonDynamicChildFrame(subframe, cb);
});
}
var SessionStorageInternal = {
/**
* Reads all session storage data from the given docShell.
* @param content
* A tab's global, i.e. the root frame we want to collect for.
* @return Returns a nested object that will have hosts as keys and per-origin
* session storage data as strings. For example:
* {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
*/
collect(content) {
let data = {};
let visitedOrigins = new Set();
let docShell = content.docShell;
forEachNonDynamicChildFrame(content, frame => {
let principal = getPrincipalForFrame(docShell, frame);
if (!principal) {
return;
}
// Get the origin of the current history entry
// and use that as a key for the per-principal storage data.
let origin;
try {
// The origin getter may throw for about:blank iframes as of bug 1340710,
// but we should ignore them anyway.
origin = principal.origin;
} catch (e) {
return;
}
if (visitedOrigins.has(origin)) {
// Don't read a host twice.
return;
}
// Mark the current origin as visited.
visitedOrigins.add(origin);
let originData = this._readEntry(principal, docShell);
if (Object.keys(originData).length) {
data[origin] = originData;
}
});
return Object.keys(data).length ? data : null;
},
/**
* Writes session storage data to the given tab.
* @param aDocShell
* A tab's docshell (containing the sessionStorage)
* @param aStorageData
* A nested object with storage data to be restored that has hosts as
* keys and per-origin session storage data as strings. For example:
* {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
*/
restore(aDocShell, aStorageData) {
for (let origin of Object.keys(aStorageData)) {
let data = aStorageData[origin];
let principal;
try {
// NOTE: In capture() we record the full origin for the URI which the
// sessionStorage is being captured for. As of bug 1235657 this code
// stopped parsing any origins which have originattributes correctly, as
// it decided to use the origin attributes from the docshell, and try to
// interpret the origin as a URI. Since bug 1353844 this code now correctly
// parses the full origin, and then discards the origin attributes, to
// make the behavior line up with the original intentions in bug 1235657
// while preserving the ability to read all session storage from
// previous versions. In the future, if this behavior is desired, we may
// want to use the spec instead of the origin as the key, and avoid
// transmitting origin attribute information which we then discard when
// restoring.
//
// If changing this logic, make sure to also change the principal
// computation logic in SessionStore::_sendRestoreHistory.
let attrs = aDocShell.getOriginAttributes();
let dataPrincipal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
principal = Services.scriptSecurityManager.createCodebasePrincipal(dataPrincipal.URI, attrs);
} catch (e) {
console.error(e);
continue;
}
let storageManager = aDocShell.QueryInterface(Ci.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.
// Null window because the current window doesn't match the principal yet
// and loads about:blank.
let storage = storageManager.createStorage(null, principal, "", aDocShell.usePrivateBrowsing);
for (let key of Object.keys(data)) {
try {
storage.setItem(key, data[key]);
} catch (e) {
// throws e.g. for URIs that can't have sessionStorage
console.error(e);
}
}
}
},
/**
* Reads an entry in the session storage data contained in a tab's history.
* @param aURI
* That history entry uri
* @param aDocShell
* A tab's docshell (containing the sessionStorage)
*/
_readEntry(aPrincipal, aDocShell) {
let hostData = {};
let storage;
let window = aDocShell.domWindow;
try {
let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
storage = storageManager.getStorage(window, aPrincipal);
storage.length; // XXX: Bug 1232955 - storage.length can throw, catch that failure
} catch (e) {
// sessionStorage might throw if it's turned off, see bug 458954
storage = null;
}
if (!storage || !storage.length) {
return hostData;
}
// If the DOMSessionStorage contains too much data, ignore it.
let usage = window.windowUtils.getStorageUsage(storage);
if (usage > Services.prefs.getIntPref(DOM_STORAGE_LIMIT_PREF)) {
return hostData;
}
for (let i = 0; i < storage.length; i++) {
try {
let key = storage.key(i);
hostData[key] = storage.getItem(key);
} catch (e) {
// This currently throws for secured items (cf. bug 442048).
}
}
return hostData;
},
};

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

@ -20,6 +20,7 @@ EXTRA_JS_MODULES.sessionstore = [
'SessionMigration.jsm',
'SessionSaver.jsm',
'SessionStartup.jsm',
'SessionStorage.jsm',
'SessionStore.jsm',
'SessionWorker.js',
'SessionWorker.jsm',

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

@ -3575,8 +3575,6 @@ class Document : public nsINode,
void SetDocTreeHadAudibleMedia();
void SetDocTreeHadPlayRevoked();
mozilla::dom::XPathEvaluator* XPathEvaluator();
protected:
void DoUpdateSVGUseElementShadowTrees();
@ -3706,6 +3704,8 @@ class Document : public nsINode,
nsCString GetContentTypeInternal() const { return mContentType; }
mozilla::dom::XPathEvaluator* XPathEvaluator();
// Update our frame request callback scheduling state, if needed. This will
// schedule or unschedule them, if necessary, and update
// mFrameRequestCallbacksScheduled. aOldShell should only be passed when

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

@ -1274,7 +1274,7 @@ void nsGlobalWindowInner::FreeInnerObjects(bool aForDocumentOpen) {
}
if (mWindowGlobalChild && !mWindowGlobalChild->IsClosed()) {
mWindowGlobalChild->Destroy();
mWindowGlobalChild->Send__delete__(mWindowGlobalChild);
}
mWindowGlobalChild = nullptr;

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

@ -319,8 +319,6 @@ static LazyLogModule gDOMLeakPRLogOuter("DOMLeakOuter");
static int32_t gOpenPopupSpamCount = 0;
static bool gSyncContentBlockingNotifications = false;
nsGlobalWindowOuter::OuterWindowByIdTable*
nsGlobalWindowOuter::sOuterWindowsById = nullptr;
@ -5407,130 +5405,100 @@ void nsGlobalWindowOuter::NotifyContentBlockingEvent(unsigned aEvent,
nsCOMPtr<Document> doc = docShell->GetDocument();
NS_ENSURE_TRUE_VOID(doc);
nsCOMPtr<nsIURI> uri(aURIHint);
nsCOMPtr<nsIChannel> channel(aChannel);
static bool prefInitialized = false;
if (!prefInitialized) {
Preferences::AddBoolVarCache(
&gSyncContentBlockingNotifications,
"dom.testing.sync-content-blocking-notifications", false);
prefInitialized = true;
}
nsCOMPtr<nsIRunnable> func = NS_NewRunnableFunction(
"NotifyContentBlockingEventDelayed",
[doc, docShell, uri, channel, aEvent, aBlocked]() {
// This event might come after the user has navigated to another
// page. To prevent showing the TrackingProtection UI on the wrong
// page, we need to check that the loading URI for the channel is
// the same as the URI currently loaded in the document.
if (!SameLoadingURI(doc, channel) &&
aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
return;
}
// Notify nsIWebProgressListeners of this content blocking event.
// Can be used to change the UI state.
nsresult rv = NS_OK;
nsCOMPtr<nsISecurityEventSink> eventSink =
do_QueryInterface(docShell, &rv);
NS_ENSURE_SUCCESS_VOID(rv);
uint32_t event = 0;
nsCOMPtr<nsISecureBrowserUI> securityUI;
docShell->GetSecurityUI(getter_AddRefs(securityUI));
if (!securityUI) {
return;
}
securityUI->GetContentBlockingEvent(&event);
nsAutoCString origin;
nsContentUtils::GetASCIIOrigin(uri, origin);
bool blockedValue = aBlocked;
bool unblocked = false;
if (aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
doc->SetHasTrackingContentBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingContentBlocked();
}
} else if (aEvent ==
nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
doc->SetHasTrackingContentLoaded(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingContentLoaded();
}
} else if (aEvent == nsIWebProgressListener::
STATE_COOKIES_BLOCKED_BY_PERMISSION) {
doc->SetHasCookiesBlockedByPermission(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasCookiesBlockedByPermission();
}
} else if (aEvent ==
nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
doc->SetHasTrackingCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingCookiesBlocked();
}
} else if (aEvent ==
nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
doc->SetHasAllCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasAllCookiesBlocked();
}
} else if (aEvent ==
nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
doc->SetHasForeignCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasForeignCookiesBlocked();
}
} else if (aEvent == nsIWebProgressListener::STATE_COOKIES_LOADED) {
MOZ_ASSERT(!aBlocked,
"We don't expected to see blocked STATE_COOKIES_LOADED");
// Note that the logic in this branch is the logical negation of
// the logic in other branches, since the Document API we have is
// phrased in "loaded" terms as opposed to "blocked" terms.
blockedValue = !aBlocked;
doc->SetHasCookiesLoaded(blockedValue, origin);
if (!aBlocked) {
unblocked = !doc->GetHasCookiesLoaded();
}
} else {
// Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
}
const uint32_t oldEvent = event;
if (blockedValue) {
event |= aEvent;
} else if (unblocked) {
event &= ~aEvent;
}
if (event == oldEvent
#ifdef ANDROID
// GeckoView always needs to notify about blocked trackers,
// since the GeckoView API always needs to report the URI and
// type of any blocked tracker. We use a platform-dependent code
// path here because reporting this notification on desktop
// platforms isn't necessary and doing so can have a big
// performance cost.
&& aEvent != nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT
#endif
) {
// Avoid dispatching repeated notifications when nothing has
// changed
return;
}
eventSink->OnContentBlockingEvent(channel, event);
});
nsresult rv;
if (gSyncContentBlockingNotifications) {
rv = func->Run();
} else {
rv = NS_IdleDispatchToCurrentThread(func.forget(), 100);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
// This event might come after the user has navigated to another page.
// To prevent showing the TrackingProtection UI on the wrong page, we need to
// check that the loading URI for the channel is the same as the URI currently
// loaded in the document.
if (!SameLoadingURI(doc, aChannel) &&
aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
return;
}
// Notify nsIWebProgressListeners of this content blocking event.
// Can be used to change the UI state.
nsresult rv = NS_OK;
nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
NS_ENSURE_SUCCESS_VOID(rv);
uint32_t event = 0;
nsCOMPtr<nsISecureBrowserUI> securityUI;
docShell->GetSecurityUI(getter_AddRefs(securityUI));
if (!securityUI) {
return;
}
securityUI->GetContentBlockingEvent(&event);
nsAutoCString origin;
nsContentUtils::GetASCIIOrigin(aURIHint, origin);
bool blockedValue = aBlocked;
bool unblocked = false;
if (aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
doc->SetHasTrackingContentBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingContentBlocked();
}
} else if (aEvent == nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
doc->SetHasTrackingContentLoaded(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingContentLoaded();
}
} else if (aEvent ==
nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION) {
doc->SetHasCookiesBlockedByPermission(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasCookiesBlockedByPermission();
}
} else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
doc->SetHasTrackingCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasTrackingCookiesBlocked();
}
} else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
doc->SetHasAllCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasAllCookiesBlocked();
}
} else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
doc->SetHasForeignCookiesBlocked(aBlocked, origin);
if (!aBlocked) {
unblocked = !doc->GetHasForeignCookiesBlocked();
}
} else if (aEvent == nsIWebProgressListener::STATE_COOKIES_LOADED) {
MOZ_ASSERT(!aBlocked,
"We don't expected to see blocked STATE_COOKIES_LOADED");
// Note that the logic in this branch is the logical negation of the logic
// in other branches, since the Document API we have is phrased in
// "loaded" terms as opposed to "blocked" terms.
blockedValue = !aBlocked;
doc->SetHasCookiesLoaded(blockedValue, origin);
if (!aBlocked) {
unblocked = !doc->GetHasCookiesLoaded();
}
} else {
// Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
}
const uint32_t oldEvent = event;
if (blockedValue) {
event |= aEvent;
} else if (unblocked) {
event &= ~aEvent;
}
if (event == oldEvent
#ifdef ANDROID
// GeckoView always needs to notify about blocked trackers, since the
// GeckoView API always needs to report the URI and type of any blocked
// tracker.
// We use a platform-dependent code path here because reporting this
// notification on desktop platforms isn't necessary and doing so can have
// a big performance cost.
&& aEvent != nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT
#endif
) {
// Avoid dispatching repeated notifications when nothing has changed
return;
}
eventSink->OnContentBlockingEvent(aChannel, event);
}
// static

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

@ -111,28 +111,6 @@ namespace SessionStoreUtils {
* Form data encoded in an object.
*/
CollectedFormData collectFormData(Document document);
boolean restoreFormData(Document document, optional CollectedFormData data);
/**
* Updates all sessionStorage "super cookies"
* @param content
* A tab's global, i.e. the root frame we want to collect for.
* @return Returns a nested object that will have hosts as keys and per-origin
* session storage data as strings. For example:
* {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
*/
record<DOMString, record<DOMString, DOMString>> collectSessionStorage(WindowProxy window);
/**
* Restores all sessionStorage "super cookies".
* @param aDocShell
* A tab's docshell (containing the sessionStorage)
* @param aStorageData
* A nested object with storage data to be restored that has hosts as
* keys and per-origin session storage data as strings. For example:
* {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
*/
void restoreSessionStorage(nsIDocShell docShell, record<DOMString, record<DOMString, DOMString>> data);
};
dictionary SSScrollPositionDict {
@ -152,7 +130,7 @@ dictionary CollectedNonMultipleSelectValue
};
// object contains either a CollectedFileListValue or a CollectedNonMultipleSelectValue or Sequence<DOMString>
typedef (DOMString or boolean or object) CollectedFormDataValue;
typedef (DOMString or boolean or long or object) CollectedFormDataValue;
dictionary CollectedFormData
{

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

@ -23,9 +23,6 @@ async protocol PWindowGlobal
{
manager PBrowser or PInProcess;
child:
async __delete__();
parent:
/// Update the URI of the document in this WindowGlobal.
async UpdateDocumentURI(nsIURI aUri);
@ -33,7 +30,7 @@ parent:
/// Notify the parent that this PWindowGlobal is now the current global.
async BecomeCurrentWindowGlobal();
async Destroy();
async __delete__();
};
} // namespace dom

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

@ -90,24 +90,6 @@ already_AddRefed<WindowGlobalParent> WindowGlobalChild::GetParentActor() {
return do_AddRef(static_cast<WindowGlobalParent*>(otherSide));
}
already_AddRefed<TabChild> WindowGlobalChild::GetTabChild() {
if (IsInProcess() || mIPCClosed) {
return nullptr;
}
return do_AddRef(static_cast<TabChild*>(Manager()));
}
void WindowGlobalChild::Destroy() {
// Perform async IPC shutdown unless we're not in-process, and our TabChild is
// in the process of being destroyed, which will destroy us as well.
RefPtr<TabChild> tabChild = GetTabChild();
if (!tabChild || !tabChild->IsDestroyed()) {
SendDestroy();
}
mIPCClosed = true;
}
void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy) {
mIPCClosed = true;
gWindowGlobalChildById->Remove(mInnerWindowId);

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

@ -42,7 +42,6 @@ class WindowGlobalChild : public nsWrapperCache, public PWindowGlobalChild {
// Has this actor been shut down
bool IsClosed() { return mIPCClosed; }
void Destroy();
// Check if this actor is managed by PInProcess, as-in the document is loaded
// in the chrome process.
@ -58,10 +57,6 @@ class WindowGlobalChild : public nsWrapperCache, public PWindowGlobalChild {
// |nullptr| if the actor has been torn down, or is not in-process.
already_AddRefed<WindowGlobalParent> GetParentActor();
// Get this actor's manager if it is not an in-process actor. Returns
// |nullptr| if the actor has been torn down, or is in-process.
already_AddRefed<TabChild> GetTabChild();
// Create and initialize the WindowGlobalChild object.
static already_AddRefed<WindowGlobalChild> Create(
nsGlobalWindowInner* aWindow);

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

@ -116,13 +116,6 @@ already_AddRefed<WindowGlobalChild> WindowGlobalParent::GetChildActor() {
return do_AddRef(static_cast<WindowGlobalChild*>(otherSide));
}
already_AddRefed<TabParent> WindowGlobalParent::GetTabParent() {
if (IsInProcess() || mIPCClosed) {
return nullptr;
}
return do_AddRef(static_cast<TabParent*>(Manager()));
}
IPCResult WindowGlobalParent::RecvUpdateDocumentURI(nsIURI* aURI) {
// XXX(nika): Assert that the URI change was one which makes sense (either
// about:blank -> a real URI, or a legal push/popstate URI change?)
@ -135,16 +128,6 @@ IPCResult WindowGlobalParent::RecvBecomeCurrentWindowGlobal() {
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvDestroy() {
if (!mIPCClosed) {
RefPtr<TabParent> tabParent = GetTabParent();
if (!tabParent || !tabParent->IsDestroyed()) {
Unused << Send__delete__(this);
}
}
return IPC_OK();
}
bool WindowGlobalParent::IsCurrentGlobal() {
return !mIPCClosed && mBrowsingContext->GetCurrentWindowGlobal() == this;
}

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

@ -51,10 +51,6 @@ class WindowGlobalParent final : public nsISupports,
// |nullptr| if the actor has been torn down, or is not in-process.
already_AddRefed<WindowGlobalChild> GetChildActor();
// Get this actor's manager if it is not an in-process actor. Returns
// |nullptr| if the actor has been torn down, or is in-process.
already_AddRefed<TabParent> GetTabParent();
// The principal of this WindowGlobal. This value will not change over the
// lifetime of the WindowGlobal object, even to reflect changes in
// |document.domain|.
@ -94,7 +90,6 @@ class WindowGlobalParent final : public nsISupports,
// IPC messages
mozilla::ipc::IPCResult RecvUpdateDocumentURI(nsIURI* aURI) override;
mozilla::ipc::IPCResult RecvBecomeCurrentWindowGlobal() override;
mozilla::ipc::IPCResult RecvDestroy() override;
void ActorDestroy(ActorDestroyReason aWhy) override;

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

@ -48,9 +48,6 @@ class XPathEvaluator final : public NonRefcountedDOMObject {
ErrorResult& rv);
XPathExpression* CreateExpression(const nsAString& aExpression,
nsINode* aResolver, ErrorResult& aRv);
XPathExpression* CreateExpression(const nsAString& aExpression,
txIParseContext* aContext,
Document* aDocument, ErrorResult& aRv);
nsINode* CreateNSResolver(nsINode& aNodeResolver) { return &aNodeResolver; }
already_AddRefed<XPathResult> Evaluate(
JSContext* aCx, const nsAString& aExpression, nsINode& aContextNode,
@ -58,6 +55,10 @@ class XPathEvaluator final : public NonRefcountedDOMObject {
ErrorResult& rv);
private:
XPathExpression* CreateExpression(const nsAString& aExpression,
txIParseContext* aContext,
Document* aDocument, ErrorResult& aRv);
nsWeakPtr mDocument;
RefPtr<txResultRecycler> mRecycler;
};

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

@ -5,7 +5,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.dom += [
'txIXPathContext.h',
'XPathEvaluator.h',
'XPathExpression.h',
'XPathResult.h',

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

@ -6,9 +6,7 @@
#ifndef __TX_I_XPATH_CONTEXT
#define __TX_I_XPATH_CONTEXT
#include "nscore.h"
#include "nsISupportsImpl.h"
#include "nsStringFwd.h"
#include "txCore.h"
class FunctionCall;
class nsAtom;

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

@ -7,6 +7,7 @@ ChromeUtils.import("resource://gre/modules/GeckoViewChildModule.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
FormData: "resource://gre/modules/FormData.jsm",
FormLikeFactory: "resource://gre/modules/FormLikeFactory.jsm",
GeckoViewAutoFill: "resource://gre/modules/GeckoViewAutoFill.jsm",
PrivacyFilter: "resource://gre/modules/sessionstore/PrivacyFilter.jsm",
@ -202,7 +203,7 @@ class GeckoViewContentChild extends GeckoViewChildModule {
// restore() will return false, and thus abort restoration for the
// current |frame| and its descendants, if |data.url| is given but
// doesn't match the loaded document's URL.
return SessionStoreUtils.restoreFormData(frame.document, data);
return FormData.restore(frame, data);
});
}
}, {capture: true, mozSystemGroup: true, once: true});

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

@ -9,6 +9,7 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
EventDispatcher: "resource://gre/modules/Messaging.jsm",
FormData: "resource://gre/modules/FormData.jsm",
OS: "resource://gre/modules/osfile.jsm",
PrivacyFilter: "resource://gre/modules/sessionstore/PrivacyFilter.jsm",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
@ -1394,7 +1395,7 @@ SessionStore.prototype = {
// restore() will return false, and thus abort restoration for the
// current |frame| and its descendants, if |data.url| is given but
// doesn't match the loaded document's URL.
return SessionStoreUtils.restoreFormData(frame.document, data);
return FormData.restore(frame, data);
});
}
},

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

@ -1801,16 +1801,6 @@ VARCACHE_PREF(
bool, false
)
//---------------------------------------------------------------------------
// ContentSessionStore prefs
//---------------------------------------------------------------------------
// Maximum number of bytes of DOMSessionStorage data we collect per origin.
VARCACHE_PREF(
"browser.sessionstore.dom_storage_limit",
browser_sessionstore_dom_storage_limit,
uint32_t, 2048
)
//---------------------------------------------------------------------------
// Preferences prefs
//---------------------------------------------------------------------------

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

@ -3,16 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "js/JSON.h"
#include "jsapi.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLSelectElement.h"
#include "mozilla/dom/HTMLTextAreaElement.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/txIXPathContext.h"
#include "mozilla/dom/WindowProxyHolder.h"
#include "mozilla/dom/XPathResult.h"
#include "mozilla/dom/XPathEvaluator.h"
#include "mozilla/dom/XPathExpression.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentList.h"
#include "nsContentUtils.h"
@ -327,11 +322,11 @@ static bool IsValidCCNumber(nsAString& aValue) {
static const uint16_t kMaxTraversedXPaths = 100;
// A helper function to append a element into mId or mXpath of CollectedFormData
static Record<nsString, OwningStringOrBooleanOrObject>::EntryType*
static Record<nsString, OwningStringOrBooleanOrLongOrObject>::EntryType*
AppendEntryToCollectedData(nsINode* aNode, const nsAString& aId,
uint16_t& aGeneratedCount,
CollectedFormData& aRetVal) {
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry;
Record<nsString, OwningStringOrBooleanOrLongOrObject>::EntryType* entry;
if (!aId.IsEmpty()) {
if (!aRetVal.mId.WasPassed()) {
aRetVal.mId.Construct();
@ -391,7 +386,7 @@ static void CollectFromTextAreaElement(Document& aDocument,
eCaseMatters)) {
continue;
}
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
Record<nsString, OwningStringOrBooleanOrLongOrObject>::EntryType* entry =
AppendEntryToCollectedData(textArea, id, aGeneratedCount, aRetVal);
entry->mValue.SetAsString() = value;
}
@ -446,7 +441,7 @@ static void CollectFromInputElement(JSContext* aCx, Document& aDocument,
if (checked == input->DefaultChecked()) {
continue;
}
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
Record<nsString, OwningStringOrBooleanOrLongOrObject>::EntryType* entry =
AppendEntryToCollectedData(input, id, aGeneratedCount, aRetVal);
entry->mValue.SetAsBoolean() = checked;
} else if (input->ControlType() == NS_FORM_INPUT_FILE) {
@ -465,7 +460,7 @@ static void CollectFromInputElement(JSContext* aCx, Document& aDocument,
JS_ClearPendingException(aCx);
continue;
}
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
Record<nsString, OwningStringOrBooleanOrLongOrObject>::EntryType* entry =
AppendEntryToCollectedData(input, id, aGeneratedCount, aRetVal);
entry->mValue.SetAsObject() = &jsval.toObject();
} else {
@ -491,7 +486,7 @@ static void CollectFromInputElement(JSContext* aCx, Document& aDocument,
JS::Rooted<JS::Value> jsval(aCx);
if (JS_ParseJSON(aCx, value.get(), value.Length(), &jsval) &&
jsval.isObject()) {
Record<nsString, OwningStringOrBooleanOrObject>::EntryType*
Record<nsString, OwningStringOrBooleanOrLongOrObject>::EntryType*
entry = AppendEntryToCollectedData(input, id, aGeneratedCount,
aRetVal);
entry->mValue.SetAsObject() = &jsval.toObject();
@ -502,7 +497,7 @@ static void CollectFromInputElement(JSContext* aCx, Document& aDocument,
}
}
}
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
Record<nsString, OwningStringOrBooleanOrLongOrObject>::EntryType* entry =
AppendEntryToCollectedData(input, id, aGeneratedCount, aRetVal);
entry->mValue.SetAsString() = value;
}
@ -553,7 +548,7 @@ static void CollectFromSelectElement(JSContext* aCx, Document& aDocument,
JS_ClearPendingException(aCx);
continue;
}
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
Record<nsString, OwningStringOrBooleanOrLongOrObject>::EntryType* entry =
AppendEntryToCollectedData(select, id, aGeneratedCount, aRetVal);
entry->mValue.SetAsObject() = &jsval.toObject();
} else {
@ -565,8 +560,8 @@ static void CollectFromSelectElement(JSContext* aCx, Document& aDocument,
}
bool hasDefaultValue = true;
nsTArray<nsString> selectslist;
uint32_t numOptions = options->Length();
for (uint32_t idx = 0; idx < numOptions; idx++) {
int numOptions = options->Length();
for (int idx = 0; idx < numOptions; idx++) {
HTMLOptionElement* option = options->ItemAsOption(idx);
bool selected = option->Selected();
if (!selected) {
@ -586,7 +581,7 @@ static void CollectFromSelectElement(JSContext* aCx, Document& aDocument,
JS_ClearPendingException(aCx);
continue;
}
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
Record<nsString, OwningStringOrBooleanOrLongOrObject>::EntryType* entry =
AppendEntryToCollectedData(select, id, aGeneratedCount, aRetVal);
entry->mValue.SetAsObject() = &jsval.toObject();
}
@ -635,7 +630,7 @@ static void CollectFromXULTextbox(Document& aDocument,
continue;
}
uint16_t generatedCount = 0;
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
Record<nsString, OwningStringOrBooleanOrLongOrObject>::EntryType* entry =
AppendEntryToCollectedData(input, id, generatedCount, aRetVal);
entry->mValue.SetAsString() = value;
return;
@ -673,474 +668,3 @@ static void CollectFromXULTextbox(Document& aDocument,
uri->GetSpecIgnoringRef(aRetVal.mUrl.Construct());
}
}
MOZ_CAN_RUN_SCRIPT
static void SetElementAsString(Element* aElement, const nsAString& aValue) {
IgnoredErrorResult rv;
HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromNodeOrNull(aElement);
if (textArea) {
textArea->SetValue(aValue, rv);
if (!rv.Failed()) {
nsContentUtils::DispatchInputEvent(aElement);
}
return;
}
HTMLInputElement* input = HTMLInputElement::FromNodeOrNull(aElement);
if (input) {
input->SetValue(aValue, CallerType::NonSystem, rv);
if (!rv.Failed()) {
nsContentUtils::DispatchInputEvent(aElement);
return;
}
}
input = HTMLInputElement::FromNodeOrNull(nsFocusManager::GetRedirectedFocus(aElement));
if (input) {
input->SetValue(aValue, CallerType::NonSystem, rv);
if (!rv.Failed()) {
nsContentUtils::DispatchInputEvent(aElement);
}
}
}
MOZ_CAN_RUN_SCRIPT
static void SetElementAsBool(Element* aElement, bool aValue) {
HTMLInputElement* input = HTMLInputElement::FromNodeOrNull(aElement);
if (input) {
bool checked = input->Checked();
if (aValue != checked) {
input->SetChecked(aValue);
nsContentUtils::DispatchInputEvent(aElement);
}
}
}
MOZ_CAN_RUN_SCRIPT
static void SetElementAsFiles(HTMLInputElement* aElement,
const CollectedFileListValue& aValue) {
nsTArray<nsString> fileList;
IgnoredErrorResult rv;
aElement->MozSetFileNameArray(aValue.mFileList, rv);
if (rv.Failed()) {
return;
}
nsContentUtils::DispatchInputEvent(aElement);
}
MOZ_CAN_RUN_SCRIPT
static void SetElementAsSelect(HTMLSelectElement* aElement,
const CollectedNonMultipleSelectValue& aValue) {
HTMLOptionsCollection* options = aElement->GetOptions();
if (!options) {
return;
}
int32_t selectIdx = options->SelectedIndex();
if (selectIdx >= 0) {
nsAutoString selectOptionVal;
options->ItemAsOption(selectIdx)->GetValue(selectOptionVal);
if (aValue.mValue.Equals(selectOptionVal)) {
return;
}
}
uint32_t numOptions = options->Length();
for (uint32_t idx = 0; idx < numOptions; idx++) {
HTMLOptionElement* option = options->ItemAsOption(idx);
nsAutoString optionValue;
option->GetValue(optionValue);
if (aValue.mValue.Equals(optionValue)) {
aElement->SetSelectedIndex(idx);
nsContentUtils::DispatchInputEvent(aElement);
}
}
}
MOZ_CAN_RUN_SCRIPT
static void SetElementAsMultiSelect(HTMLSelectElement* aElement,
const nsTArray<nsString>& aValueArray) {
bool fireEvent = false;
HTMLOptionsCollection* options = aElement->GetOptions();
if (!options) {
return;
}
uint32_t numOptions = options->Length();
for (uint32_t idx = 0; idx < numOptions; idx++) {
HTMLOptionElement* option = options->ItemAsOption(idx);
nsAutoString optionValue;
option->GetValue(optionValue);
for (uint32_t i = 0, l = aValueArray.Length(); i < l; ++i) {
if (optionValue.Equals(aValueArray[i])) {
option->SetSelected(true);
if (!option->DefaultSelected()) {
fireEvent = true;
}
}
}
}
if (fireEvent) {
nsContentUtils::DispatchInputEvent(aElement);
}
}
MOZ_CAN_RUN_SCRIPT
static void SetElementAsObject(JSContext* aCx, Element* aElement,
JS::Handle<JS::Value> aObject) {
RefPtr<HTMLInputElement> input = HTMLInputElement::FromNodeOrNull(aElement);
if (input) {
if (input->ControlType() == NS_FORM_INPUT_FILE) {
CollectedFileListValue value;
if (value.Init(aCx, aObject)) {
SetElementAsFiles(input, value);
} else {
JS_ClearPendingException(aCx);
}
}
return;
}
RefPtr<HTMLSelectElement> select = HTMLSelectElement::FromNodeOrNull(aElement);
if (select) {
// For Single Select Element
if (!select->Multiple()) {
CollectedNonMultipleSelectValue value;
if (value.Init(aCx, aObject)) {
SetElementAsSelect(select, value);
} else {
JS_ClearPendingException(aCx);
}
return;
}
// For Multiple Selects Element
bool isArray = false;
JS_IsArrayObject(aCx, aObject, &isArray);
if (!isArray) {
return;
}
JS::Rooted<JSObject*> arrayObj(aCx, &aObject.toObject());
uint32_t arrayLength = 0;
if (!JS_GetArrayLength(aCx, arrayObj, &arrayLength)) {
JS_ClearPendingException(aCx);
return;
}
nsTArray<nsString> array(arrayLength);
for (uint32_t arrayIdx = 0; arrayIdx < arrayLength; arrayIdx++) {
JS::Rooted<JS::Value> element(aCx);
if (!JS_GetElement(aCx, arrayObj, arrayIdx, &element)) {
JS_ClearPendingException(aCx);
return;
}
if (!element.isString()) {
return;
}
nsAutoJSString value;
if (!value.init(aCx, element)) {
JS_ClearPendingException(aCx);
return;
}
array.AppendElement(value);
}
SetElementAsMultiSelect(select, array);
}
}
MOZ_CAN_RUN_SCRIPT
static void SetRestoreData(JSContext* aCx, Element* aElement,
JS::MutableHandle<JS::Value> aObject) {
nsAutoString data;
if (nsContentUtils::StringifyJSON(aCx, aObject, data)) {
SetElementAsString(aElement, data);
} else {
JS_ClearPendingException(aCx);
}
}
MOZ_CAN_RUN_SCRIPT
static void SetInnerHTML(Document& aDocument, const CollectedFormData& aData) {
RefPtr<Element> bodyElement = aDocument.GetBody();
if (aDocument.HasFlag(NODE_IS_EDITABLE) && bodyElement) {
IgnoredErrorResult rv;
bodyElement->SetInnerHTML(aData.mInnerHTML.Value(),
aDocument.NodePrincipal(), rv);
if (!rv.Failed()) {
nsContentUtils::DispatchInputEvent(bodyElement);
}
}
}
class FormDataParseContext : public txIParseContext {
public:
explicit FormDataParseContext(bool aCaseInsensitive)
: mIsCaseInsensitive(aCaseInsensitive) {}
nsresult resolveNamespacePrefix(nsAtom* aPrefix, int32_t& aID) override {
if (aPrefix == nsGkAtoms::xul) {
aID = kNameSpaceID_XUL;
} else {
MOZ_ASSERT(nsDependentAtomString(aPrefix).EqualsLiteral("xhtml"));
aID = kNameSpaceID_XHTML;
}
return NS_OK;
}
nsresult resolveFunctionCall(nsAtom* aName, int32_t aID,
FunctionCall** aFunction) override {
return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
}
bool caseInsensitiveNameTests() override { return mIsCaseInsensitive; }
void SetErrorOffset(uint32_t aOffset) override {}
private:
bool mIsCaseInsensitive;
};
static Element* FindNodeByXPath(JSContext* aCx, Document& aDocument,
const nsAString& aExpression) {
FormDataParseContext parsingContext(aDocument.IsHTMLDocument());
IgnoredErrorResult rv;
nsAutoPtr<XPathExpression> expression(
aDocument.XPathEvaluator()->CreateExpression(aExpression, &parsingContext,
&aDocument, rv));
if (rv.Failed()) {
return nullptr;
}
RefPtr<XPathResult> result = expression->Evaluate(
aCx, aDocument, XPathResult::FIRST_ORDERED_NODE_TYPE, nullptr, rv);
if (rv.Failed()) {
return nullptr;
}
return Element::FromNodeOrNull(result->GetSingleNodeValue(rv));
}
MOZ_CAN_RUN_SCRIPT_BOUNDARY
/* static */ bool SessionStoreUtils::RestoreFormData(
const GlobalObject& aGlobal, Document& aDocument,
const CollectedFormData& aData) {
if (!aData.mUrl.WasPassed()) {
return true;
}
// Don't restore any data for the given frame if the URL
// stored in the form data doesn't match its current URL.
nsAutoCString url;
Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
if (!aData.mUrl.Value().Equals(url)) {
return false;
}
if (aData.mInnerHTML.WasPassed()) {
SetInnerHTML(aDocument, aData);
}
if (aData.mId.WasPassed()) {
for (auto& entry : aData.mId.Value().Entries()) {
RefPtr<Element> node = aDocument.GetElementById(entry.mKey);
if (entry.mValue.IsString()) {
SetElementAsString(node, entry.mValue.GetAsString());
} else if (entry.mValue.IsBoolean()) {
SetElementAsBool(node, entry.mValue.GetAsBoolean());
} else {
// For about:{sessionrestore,welcomeback} we saved the field as JSON to
// avoid nested instances causing humongous sessionstore.js files.
// cf. bug 467409
JSContext* cx = aGlobal.Context();
if (entry.mKey.EqualsLiteral("sessionData")) {
nsAutoCString url;
Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
if (url.EqualsLiteral("about:sessionrestore") ||
url.EqualsLiteral("about:welcomeback")) {
JS::Rooted<JS::Value> object(
cx, JS::ObjectValue(*entry.mValue.GetAsObject()));
SetRestoreData(cx, node, &object);
continue;
}
}
JS::Rooted<JS::Value> object(
cx, JS::ObjectValue(*entry.mValue.GetAsObject()));
SetElementAsObject(cx, node, object);
}
}
}
if (aData.mXpath.WasPassed()) {
for (auto& entry : aData.mXpath.Value().Entries()) {
RefPtr<Element> node = FindNodeByXPath(aGlobal.Context(), aDocument, entry.mKey);
if (entry.mValue.IsString()) {
SetElementAsString(node, entry.mValue.GetAsString());
} else if (entry.mValue.IsBoolean()) {
SetElementAsBool(node, entry.mValue.GetAsBoolean());
} else {
JS::Rooted<JS::Value> object(
aGlobal.Context(), JS::ObjectValue(*entry.mValue.GetAsObject()));
SetElementAsObject(aGlobal.Context(), node, object);
}
}
}
return true;
}
/* Read entries in the session storage data contained in a tab's history. */
static void ReadAllEntriesFromStorage(
nsPIDOMWindowOuter* aWindow,
nsTHashtable<nsCStringHashKey>& aVisitedOrigins,
Record<nsString, Record<nsString, nsString>>& aRetVal) {
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
if (!docShell) {
return;
}
Document* doc = aWindow->GetDoc();
if (!doc) {
return;
}
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
if (!principal) {
return;
}
nsAutoCString origin;
nsresult rv = principal->GetOrigin(origin);
if (NS_FAILED(rv) || aVisitedOrigins.Contains(origin)) {
// Don't read a host twice.
return;
}
/* Completed checking for recursion and is about to read storage*/
nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell);
if (!storageManager) {
return;
}
RefPtr<Storage> storage;
storageManager->GetStorage(aWindow->GetCurrentInnerWindow(), principal, false,
getter_AddRefs(storage));
if (!storage) {
return;
}
mozilla::IgnoredErrorResult result;
uint32_t len = storage->GetLength(*principal, result);
if (result.Failed() || len == 0) {
return;
}
int64_t storageUsage = storage->GetOriginQuotaUsage();
if (storageUsage > StaticPrefs::browser_sessionstore_dom_storage_limit()) {
return;
}
Record<nsString, Record<nsString, nsString>>::EntryType* recordEntry = nullptr;
for (uint32_t i = 0; i < len; i++) {
Record<nsString, nsString>::EntryType entry;
mozilla::IgnoredErrorResult res;
storage->Key(i, entry.mKey, *principal, res);
if (res.Failed()) {
continue;
}
storage->GetItem(entry.mKey, entry.mValue, *principal, res);
if (res.Failed()) {
continue;
}
if (!recordEntry) {
recordEntry = aRetVal.Entries().AppendElement();
recordEntry->mKey = NS_ConvertUTF8toUTF16(origin);
aVisitedOrigins.PutEntry(origin);
}
recordEntry->mValue.Entries().AppendElement(std::move(entry));
}
}
/* Collect Collect session storage from current frame and all child frame */
static void CollectedSessionStorageInternal(
JSContext* aCx, BrowsingContext* aBrowsingContext,
nsTHashtable<nsCStringHashKey>& aVisitedOrigins,
Record<nsString, Record<nsString, nsString>>& aRetVal) {
/* Collect session store from current frame */
nsPIDOMWindowOuter* window = aBrowsingContext->GetDOMWindow();
if (!window) {
return;
}
ReadAllEntriesFromStorage(window, aVisitedOrigins, aRetVal);
/* Collect session storage from all child frame */
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
if (!docShell) {
return;
}
int32_t length;
nsresult rv = docShell->GetChildCount(&length);
if (NS_FAILED(rv)) {
return;
}
for (int32_t i = 0; i < length; ++i) {
nsCOMPtr<nsIDocShellTreeItem> item;
docShell->GetChildAt(i, getter_AddRefs(item));
if (!item) {
return;
}
nsCOMPtr<nsIDocShell> childDocShell(do_QueryInterface(item));
if (!childDocShell) {
return;
}
bool isDynamic = false;
rv = childDocShell->GetCreatedDynamically(&isDynamic);
if (NS_SUCCEEDED(rv) && isDynamic) {
continue;
}
CollectedSessionStorageInternal(
aCx, nsDocShell::Cast(childDocShell)->GetBrowsingContext(),
aVisitedOrigins, aRetVal);
}
}
/* static */ void SessionStoreUtils::CollectSessionStorage(
const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
Record<nsString, Record<nsString, nsString>>& aRetVal) {
nsTHashtable<nsCStringHashKey> visitedOrigins;
CollectedSessionStorageInternal(aGlobal.Context(), aWindow.get(),
visitedOrigins, aRetVal);
}
/* static */ void SessionStoreUtils::RestoreSessionStorage(
const GlobalObject& aGlobal, nsIDocShell* aDocShell,
const Record<nsString, Record<nsString, nsString>>& aData) {
for (auto& entry : aData.Entries()) {
// NOTE: In capture() we record the full origin for the URI which the
// sessionStorage is being captured for. As of bug 1235657 this code
// stopped parsing any origins which have originattributes correctly, as
// it decided to use the origin attributes from the docshell, and try to
// interpret the origin as a URI. Since bug 1353844 this code now correctly
// parses the full origin, and then discards the origin attributes, to
// make the behavior line up with the original intentions in bug 1235657
// while preserving the ability to read all session storage from
// previous versions. In the future, if this behavior is desired, we may
// want to use the spec instead of the origin as the key, and avoid
// transmitting origin attribute information which we then discard when
// restoring.
//
// If changing this logic, make sure to also change the principal
// computation logic in SessionStore::_sendRestoreHistory.
// OriginAttributes are always after a '^' character
int32_t pos = entry.mKey.RFindChar('^');
nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateCodebasePrincipal(
NS_ConvertUTF16toUTF8(Substring(entry.mKey, 0, pos)));
nsresult rv;
nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(aDocShell, &rv);
if (NS_FAILED(rv)) {
return;
}
RefPtr<Storage> storage;
// 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.
// Null window because the current window doesn't match the principal yet
// and loads about:blank.
storageManager->CreateStorage(nullptr, principal, EmptyString(), false, getter_AddRefs(storage));
if (!storage) {
continue;
}
for (auto& InnerEntry : entry.mValue.Entries()) {
IgnoredErrorResult result;
storage->SetItem(InnerEntry.mKey, InnerEntry.mValue, *principal, result);
if (result.Failed()) {
NS_WARNING("storage set item failed!");
}
}
}
}

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

@ -54,18 +54,6 @@ class SessionStoreUtils {
static void CollectFormData(const GlobalObject& aGlobal, Document& aDocument,
CollectedFormData& aRetVal);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
static bool RestoreFormData(const GlobalObject& aGlobal, Document& aDocument,
const CollectedFormData& aData);
static void CollectSessionStorage(
const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
Record<nsString, Record<nsString, nsString>>& aRetVal);
static void RestoreSessionStorage(
const GlobalObject& aGlobal, nsIDocShell* aDocShell,
const Record<nsString, Record<nsString, nsString>>& aData);
};
} // namespace dom

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

@ -210,10 +210,7 @@ function testOnWindow(aTestData) {
});
}
SpecialPowers.pushPrefEnv(
{"set": [
["browser.safebrowsing.phishing.enabled", true],
["dom.testing.sync-content-blocking-notifications", true],
]},
{"set": [["browser.safebrowsing.phishing.enabled", true]]},
test);
function test() {

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

@ -241,6 +241,7 @@ EXTRA_JS_MODULES += [
'SelectParentHelper.jsm',
'ServiceRequest.jsm',
'Services.jsm',
'sessionstore/FormData.jsm',
'ShortcutUtils.jsm',
'Sqlite.jsm',
'Timer.jsm',

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

@ -0,0 +1,280 @@
/* 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";
var EXPORTED_SYMBOLS = ["FormData"];
/**
* Returns whether the given URL very likely has input
* fields that contain serialized session store data.
*/
function isRestorationPage(url) {
return url == "about:sessionrestore" || url == "about:welcomeback";
}
/**
* Returns whether the given form |data| object contains nested restoration
* data for a page like about:sessionrestore or about:welcomeback.
*/
function hasRestorationData(data) {
if (isRestorationPage(data.url) && data.id) {
return typeof(data.id.sessionData) == "object";
}
return false;
}
/**
* Returns the given document's current URI and strips
* off the URI's anchor part, if any.
*/
function getDocumentURI(doc) {
return doc.documentURI.replace(/#.*$/, "");
}
/**
* The public API exported by this module that allows to collect
* and restore form data for a document and its subframes.
*/
var FormData = Object.freeze({
restore(frame, data) {
return FormDataInternal.restore(frame, data);
},
restoreTree(root, data) {
FormDataInternal.restoreTree(root, data);
},
});
/**
* This module's internal API.
*/
var FormDataInternal = {
namespaceURIs: {
"xhtml": "http://www.w3.org/1999/xhtml",
"xul": "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
},
/**
* Resolves an XPath query generated by node.generateXPath.
*/
resolve(aDocument, aQuery) {
let xptype = aDocument.defaultView.XPathResult.FIRST_ORDERED_NODE_TYPE;
return aDocument.evaluate(aQuery, aDocument, this.resolveNS.bind(this), xptype, null).singleNodeValue;
},
/**
* Namespace resolver for the above XPath resolver.
*/
resolveNS(aPrefix) {
return this.namespaceURIs[aPrefix] || null;
},
/**
* @returns an XPath query to all savable form field nodes
*/
get restorableFormNodesXPath() {
let formNodesXPath = "//textarea|//xhtml:textarea|" +
"//select|//xhtml:select|" +
"//input|//xhtml:input" +
// Special case for about:config's search field.
"|/xul:window[@id='config']//xul:textbox[@id='textbox']";
delete this.restorableFormNodesXPath;
return (this.restorableFormNodesXPath = formNodesXPath);
},
/**
* Restores form |data| for the given frame. The data is expected to be in
* the same format that FormData.collect() returns.
*
* @param frame (DOMWindow)
* The frame to restore form data to.
* @param data (object)
* An object holding form data.
*/
restore({document: doc}, data) {
if (!data.url) {
return true;
}
// Don't restore any data for the given frame if the URL
// stored in the form data doesn't match its current URL.
if (data.url != getDocumentURI(doc)) {
return false;
}
// For about:{sessionrestore,welcomeback} we saved the field as JSON to
// avoid nested instances causing humongous sessionstore.js files.
// cf. bug 467409
if (hasRestorationData(data)) {
data.id.sessionData = JSON.stringify(data.id.sessionData);
}
if ("id" in data) {
let retrieveNode = id => doc.getElementById(id);
this.restoreManyInputValues(data.id, retrieveNode);
}
if ("xpath" in data) {
let retrieveNode = xpath => this.resolve(doc, xpath);
this.restoreManyInputValues(data.xpath, retrieveNode);
}
if ("innerHTML" in data) {
if (doc.body && doc.designMode == "on") {
// eslint-disable-next-line no-unsanitized/property
doc.body.innerHTML = data.innerHTML;
this.fireInputEvent(doc.body);
}
}
return true;
},
/**
* Iterates the given form data, retrieving nodes for all the keys and
* restores their appropriate values.
*
* @param data (object)
* A subset of the form data as collected by FormData.collect(). This
* is either data stored under "id" or under "xpath".
* @param retrieve (function)
* The function used to retrieve the input field belonging to a key
* in the given |data| object.
*/
restoreManyInputValues(data, retrieve) {
for (let key of Object.keys(data)) {
let input = retrieve(key);
if (input) {
this.restoreSingleInputValue(input, data[key]);
}
}
},
/**
* Restores a given form value to a given DOMNode and takes care of firing
* the appropriate DOM event should the input's value change.
*
* @param aNode
* DOMNode to set form value on.
* @param aValue
* Value to set form element to.
*/
restoreSingleInputValue(aNode, aValue) {
let fireEvent = false;
if (typeof aValue == "string" && aNode.type != "file") {
// Don't dispatch an input event if there is no change.
if (aNode.value == aValue) {
return;
}
aNode.value = aValue;
fireEvent = true;
} else if (typeof aValue == "boolean") {
// Don't dispatch a change event for no change.
if (aNode.checked == aValue) {
return;
}
aNode.checked = aValue;
fireEvent = true;
} else if (aValue && aValue.selectedIndex >= 0 && aValue.value) {
// Don't dispatch a change event for no change
if (aNode.options[aNode.selectedIndex].value == aValue.value) {
return;
}
// find first option with matching aValue if possible
for (let i = 0; i < aNode.options.length; i++) {
if (aNode.options[i].value == aValue.value) {
aNode.selectedIndex = i;
fireEvent = true;
break;
}
}
} else if (aValue && aValue.fileList && aValue.type == "file" &&
aNode.type == "file") {
try {
// FIXME (bug 1122855): This won't work in content processes.
aNode.mozSetFileNameArray(aValue.fileList, aValue.fileList.length);
} catch (e) {
Cu.reportError("mozSetFileNameArray: " + e);
}
fireEvent = true;
} else if (Array.isArray(aValue) && aNode.options) {
Array.forEach(aNode.options, function(opt, index) {
// don't worry about malformed options with same values
opt.selected = aValue.indexOf(opt.value) > -1;
// Only fire the event here if this wasn't selected by default
if (!opt.defaultSelected) {
fireEvent = true;
}
});
}
// Fire events for this node if applicable
if (fireEvent) {
this.fireInputEvent(aNode);
}
},
/**
* Dispatches an event of type "input" to the given |node|.
*
* @param node (DOMNode)
*/
fireInputEvent(node) {
// "inputType" value hasn't been decided for session restor:
// https://github.com/w3c/input-events/issues/30#issuecomment-438693664
let event = node.isInputEventTarget ?
new node.ownerGlobal.InputEvent("input", {bubbles: true, inputType: ""}) :
new node.ownerGlobal.Event("input", {bubbles: true});
node.dispatchEvent(event);
},
/**
* Restores form data for the current frame hierarchy starting at |root|
* using the given form |data|.
*
* If the given |root| frame's hierarchy doesn't match that of the given
* |data| object we will silently discard data for unreachable frames. For
* security reasons we will never restore form data to the wrong frames as
* we bail out silently if the stored URL doesn't match the frame's current
* URL.
*
* @param root (DOMWindow)
* @param data (object)
* {
* formdata: {id: {input1: "value1"}},
* children: [
* {formdata: {id: {input2: "value2"}}},
* null,
* {formdata: {xpath: { ... }}, children: [ ... ]}
* ]
* }
*/
restoreTree(root, data) {
// Restore data for the given |root| frame and its descendants. If restore()
// returns false this indicates the |data.url| doesn't match the loaded
// document URI. We then must ignore this branch for security reasons.
if (this.restore(root, data) === false) {
return;
}
if (!data.hasOwnProperty("children")) {
return;
}
let frames = root.frames;
for (let index of Object.keys(data.children)) {
if (index < frames.length) {
this.restoreTree(frames[index], data.children[index]);
}
}
},
};