зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge on a CLOSED TREE
--HG-- extra : rebase_source : 81b5f62e48ccbe6c941a80b72a4eebe0ed942ccf
This commit is contained in:
Коммит
d4b090ea42
|
@ -1532,6 +1532,9 @@ pref("browser.contentblocking.allowlist.storage.enabled", true);
|
|||
pref("dom.storage_access.enabled", true);
|
||||
#endif
|
||||
|
||||
pref("dom.storage_access.auto_grants", true);
|
||||
pref("dom.storage_access.max_concurrent_auto_grants", 5);
|
||||
|
||||
// Define a set of default features for the Content Blocking UI.
|
||||
pref("browser.contentblocking.trackingprotection.control-center.ui.enabled", true);
|
||||
pref("browser.contentblocking.rejecttrackers.control-center.ui.enabled", true);
|
||||
|
@ -1795,3 +1798,5 @@ pref("prio.enabled", true);
|
|||
pref("browser.discovery.enabled", false);
|
||||
pref("browser.discovery.containers.enabled", true);
|
||||
pref("browser.discovery.sites", "addons.mozilla.org");
|
||||
|
||||
pref("browser.engagement.recent_visited_origins.expiry", 86400); // 24 * 60 * 60 (24 hours in seconds)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
var EXPORTED_SYMBOLS = [
|
||||
"BrowserUsageTelemetry",
|
||||
"URICountListener",
|
||||
"URLBAR_SELECTED_RESULT_TYPES",
|
||||
"URLBAR_SELECTED_RESULT_METHODS",
|
||||
"MINIMUM_TAB_COUNT_INTERVAL_MS",
|
||||
|
@ -18,8 +19,14 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
SearchTelemetry: "resource:///modules/SearchTelemetry.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
setTimeout: "resource://gre/modules/Timer.jsm",
|
||||
});
|
||||
|
||||
// This pref is in seconds!
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this,
|
||||
"gRecentVisitedOriginsExpiry",
|
||||
"browser.engagement.recent_visited_origins.expiry");
|
||||
|
||||
// The upper bound for the count of the visited unique domain names.
|
||||
const MAX_UNIQUE_VISITED_DOMAINS = 100;
|
||||
|
||||
|
@ -128,6 +135,8 @@ function shouldRecordSearchCount(tabbrowser) {
|
|||
let URICountListener = {
|
||||
// A set containing the visited domains, see bug 1271310.
|
||||
_domainSet: new Set(),
|
||||
// A set containing the visited origins during the last 24 hours (similar to domains, but not quite the same)
|
||||
_origin24hrSet: new Set(),
|
||||
// A map to keep track of the URIs loaded from the restored tabs.
|
||||
_restoredURIsMap: new WeakMap(),
|
||||
|
||||
|
@ -230,13 +239,26 @@ let URICountListener = {
|
|||
|
||||
// Unique domains should be aggregated by (eTLD + 1): x.test.com and y.test.com
|
||||
// are counted once as test.com.
|
||||
let baseDomain;
|
||||
try {
|
||||
// Even if only considering http(s) URIs, |getBaseDomain| could still throw
|
||||
// due to the URI containing invalid characters or the domain actually being
|
||||
// an ipv4 or ipv6 address.
|
||||
this._domainSet.add(Services.eTLD.getBaseDomain(uri));
|
||||
baseDomain = Services.eTLD.getBaseDomain(uri);
|
||||
this._domainSet.add(baseDomain);
|
||||
} catch (e) {
|
||||
return;
|
||||
baseDomain = uri.host;
|
||||
}
|
||||
|
||||
// Record the origin, but with the base domain (eTLD + 1).
|
||||
let baseDomainURI = uri.mutate()
|
||||
.setHost(baseDomain)
|
||||
.finalize();
|
||||
this._origin24hrSet.add(baseDomainURI.prePath);
|
||||
if (gRecentVisitedOriginsExpiry) {
|
||||
setTimeout(() => {
|
||||
this._origin24hrSet.delete(baseDomainURI.prePath);
|
||||
}, gRecentVisitedOriginsExpiry * 1000);
|
||||
}
|
||||
|
||||
Services.telemetry.scalarSet(UNIQUE_DOMAINS_COUNT_SCALAR_NAME, this._domainSet.size);
|
||||
|
@ -249,6 +271,21 @@ let URICountListener = {
|
|||
this._domainSet.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the number of unique origins visited in this session during the
|
||||
* last 24 hours.
|
||||
*/
|
||||
get uniqueOriginsVisitedInPast24Hours() {
|
||||
return this._origin24hrSet.size;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the number of unique origins visited in this session.
|
||||
*/
|
||||
resetUniqueOriginsVisitedInPast24Hours() {
|
||||
this._origin24hrSet.clear();
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
};
|
||||
|
|
|
@ -68,6 +68,8 @@ ChromeUtils.defineModuleGetter(this, "SitePermissions",
|
|||
"resource:///modules/SitePermissions.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "URICountListener",
|
||||
"resource:///modules/BrowserUsageTelemetry.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
|
||||
return Services.strings
|
||||
|
@ -253,8 +255,12 @@ var PermissionPromptPrototype = {
|
|||
* be called just before. Subclasses may want to override this
|
||||
* in order to, for example, bump a counter Telemetry probe for
|
||||
* how often a particular permission request is seen.
|
||||
*
|
||||
* If this returns false, it cancels the process of showing the prompt. In
|
||||
* that case, it is the responsibility of the onBeforeShow() implementation
|
||||
* to ensure that allow() or cancel() are called on the object appropriately.
|
||||
*/
|
||||
onBeforeShow() {},
|
||||
onBeforeShow() { return true; },
|
||||
|
||||
/**
|
||||
* If the prompt was shown to the user, this callback will be called just
|
||||
|
@ -440,7 +446,7 @@ var PermissionPromptPrototype = {
|
|||
return false;
|
||||
};
|
||||
|
||||
this.onBeforeShow();
|
||||
if (this.onBeforeShow() !== false) {
|
||||
chromeWin.PopupNotifications.show(this.browser,
|
||||
this.notificationID,
|
||||
this.message,
|
||||
|
@ -448,6 +454,7 @@ var PermissionPromptPrototype = {
|
|||
mainAction,
|
||||
secondaryActions,
|
||||
options);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -590,6 +597,7 @@ GeolocationPermissionPrompt.prototype = {
|
|||
let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
|
||||
const SHOW_REQUEST = Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST;
|
||||
secHistogram.add(SHOW_REQUEST);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -826,9 +834,6 @@ MIDIPermissionPrompt.prototype = {
|
|||
action: Ci.nsIPermissionManager.DENY_ACTION,
|
||||
}];
|
||||
},
|
||||
|
||||
onBeforeShow() {
|
||||
},
|
||||
};
|
||||
|
||||
PermissionUI.MIDIPermissionPrompt = MIDIPermissionPrompt;
|
||||
|
@ -911,6 +916,7 @@ AutoplayPermissionPrompt.prototype = {
|
|||
};
|
||||
this.browser.addEventListener(
|
||||
"DOMAudioPlaybackStarted", this.handlePlaybackStart);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -918,6 +924,11 @@ PermissionUI.AutoplayPermissionPrompt = AutoplayPermissionPrompt;
|
|||
|
||||
function StorageAccessPermissionPrompt(request) {
|
||||
this.request = request;
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "_autoGrants",
|
||||
"dom.storage_access.auto_grants");
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "_maxConcurrentAutoGrants",
|
||||
"dom.storage_access.max_concurrent_auto_grants");
|
||||
}
|
||||
|
||||
StorageAccessPermissionPrompt.prototype = {
|
||||
|
@ -1008,6 +1019,38 @@ StorageAccessPermissionPrompt.prototype = {
|
|||
get topLevelPrincipal() {
|
||||
return this.request.topLevelPrincipal;
|
||||
},
|
||||
|
||||
get maxConcurrentAutomaticGrants() {
|
||||
// one percent of the number of top-levels origins visited in the current
|
||||
// session (but not to exceed 24 hours), or the value of the
|
||||
// dom.storage_access.max_concurrent_auto_grants preference, whichever is
|
||||
// higher.
|
||||
return Math.max(Math.max(Math.floor(URICountListener.uniqueOriginsVisitedInPast24Hours / 100),
|
||||
this._maxConcurrentAutoGrants), 0);
|
||||
},
|
||||
|
||||
getOriginsThirdPartyHasAccessTo(thirdPartyOrigin) {
|
||||
let prefix = `3rdPartyStorage^${thirdPartyOrigin}`;
|
||||
let perms = Services.perms.getAllWithTypePrefix(prefix);
|
||||
let origins = new Set();
|
||||
while (perms.length) {
|
||||
let perm = perms.shift();
|
||||
origins.add(perm.principal.origin);
|
||||
}
|
||||
return origins.size;
|
||||
},
|
||||
|
||||
onBeforeShow() {
|
||||
let thirdPartyOrigin = this.request.principal.origin;
|
||||
if (this._autoGrants &&
|
||||
this.getOriginsThirdPartyHasAccessTo(thirdPartyOrigin) <
|
||||
this.maxConcurrentAutomaticGrants) {
|
||||
// Automatically accept the prompt
|
||||
this.allow({"storage-access": "allow-auto-grant"});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
PermissionUI.StorageAccessPermissionPrompt = StorageAccessPermissionPrompt;
|
||||
|
|
|
@ -39,6 +39,7 @@ run-if = crashreporter
|
|||
[browser_UsageTelemetry_domains.js]
|
||||
[browser_UsageTelemetry_private_and_restore.js]
|
||||
skip-if = verify && debug
|
||||
[browser_UsageTelemetry_uniqueOriginsVisitedInPast24Hours.js]
|
||||
[browser_UsageTelemetry_urlbar.js]
|
||||
support-files =
|
||||
usageTelemetrySearchSuggestions.sjs
|
||||
|
|
|
@ -294,6 +294,7 @@ add_task(async function test_on_before_show() {
|
|||
promptActions: [mainAction],
|
||||
onBeforeShow() {
|
||||
beforeShown = true;
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -353,6 +354,7 @@ add_task(async function test_no_request() {
|
|||
promptActions: [mainAction, secondaryAction],
|
||||
onBeforeShow() {
|
||||
beforeShown = true;
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -39,7 +39,9 @@ add_task(async function test_autoplay_permission_prompt() {
|
|||
|
||||
// Tests that AutoplayPermissionPrompt works as expected
|
||||
add_task(async function test_storage_access_permission_prompt() {
|
||||
Services.prefs.setBoolPref("dom.storage_access.auto_grants", false);
|
||||
await testPrompt(PermissionUI.StorageAccessPermissionPrompt);
|
||||
Services.prefs.clearUserPref("dom.storage_access.auto_grants");
|
||||
});
|
||||
|
||||
async function testPrompt(Prompt) {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||
/* 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";
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "URICountListener",
|
||||
"resource:///modules/BrowserUsageTelemetry.jsm");
|
||||
|
||||
add_task(async function test_uniqueOriginsVisitedInPast24Hours() {
|
||||
registerCleanupFunction(async () => {
|
||||
info("Cleaning up");
|
||||
URICountListener.resetUniqueOriginsVisitedInPast24Hours();
|
||||
});
|
||||
|
||||
URICountListener.resetUniqueOriginsVisitedInPast24Hours();
|
||||
let startingCount = URICountListener.uniqueOriginsVisitedInPast24Hours;
|
||||
is(startingCount, 0, "We should have no origins recorded in the history right after resetting");
|
||||
|
||||
// Add a new window and then some tabs in it.
|
||||
let win = await BrowserTestUtils.openNewBrowserWindow();
|
||||
await BrowserTestUtils.openNewForegroundTab(win.gBrowser, "http://example.com");
|
||||
|
||||
await BrowserTestUtils.openNewForegroundTab(win.gBrowser, "http://test1.example.com");
|
||||
is(URICountListener.uniqueOriginsVisitedInPast24Hours, startingCount + 1,
|
||||
"test1.example.com should only count as a unique visit if example.com wasn't visited before");
|
||||
|
||||
// http://www.exämple.test
|
||||
await BrowserTestUtils.openNewForegroundTab(win.gBrowser, "http://xn--exmple-cua.test");
|
||||
is(URICountListener.uniqueOriginsVisitedInPast24Hours, startingCount + 2,
|
||||
"www.exämple.test should count as a unique visit");
|
||||
|
||||
// Set the expiry time to 1 second
|
||||
await SpecialPowers.pushPrefEnv({set: [["browser.engagement.recent_visited_origins.expiry", 1]]});
|
||||
|
||||
await BrowserTestUtils.openNewForegroundTab(win.gBrowser, "http://127.0.0.1");
|
||||
is(URICountListener.uniqueOriginsVisitedInPast24Hours, startingCount + 3,
|
||||
"127.0.0.1 should count as a unique visit");
|
||||
|
||||
let countBefore = URICountListener.uniqueOriginsVisitedInPast24Hours;
|
||||
|
||||
await new Promise(resolve => {
|
||||
setTimeout(_ => {
|
||||
let countAfter = URICountListener.uniqueOriginsVisitedInPast24Hours;
|
||||
is(countAfter, countBefore - 1,
|
||||
"The expiry should work correctly");
|
||||
resolve();
|
||||
}, 1100);
|
||||
});
|
||||
|
||||
BrowserTestUtils.removeTab(win.gBrowser.selectedTab);
|
||||
BrowserTestUtils.removeTab(win.gBrowser.selectedTab);
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
|
@ -36,6 +36,7 @@ function PageError(props) {
|
|||
} = props;
|
||||
const {
|
||||
id: messageId,
|
||||
executionPoint,
|
||||
indent,
|
||||
source,
|
||||
type,
|
||||
|
@ -58,6 +59,7 @@ function PageError(props) {
|
|||
return Message({
|
||||
dispatch,
|
||||
messageId,
|
||||
executionPoint,
|
||||
isPaused,
|
||||
open,
|
||||
collapsible: Array.isArray(stacktrace),
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "mozilla/dom/Text.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
#include "nsAtom.h"
|
||||
#include "nsCSSFrameConstructor.h"
|
||||
#include "nsDOMAttributeMap.h"
|
||||
#include "nsIContentInlines.h"
|
||||
#include "mozilla/dom/NodeInfo.h"
|
||||
|
|
|
@ -19,12 +19,14 @@ StorageAccessPermissionRequest::StorageAccessPermissionRequest(
|
|||
nsPIDOMWindowInner* aWindow,
|
||||
nsIPrincipal* aNodePrincipal,
|
||||
AllowCallback&& aAllowCallback,
|
||||
AllowAutoGrantCallback&& aAllowAutoGrantCallback,
|
||||
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||
CancelCallback&& aCancelCallback)
|
||||
: ContentPermissionRequestBase(aNodePrincipal, false, aWindow,
|
||||
NS_LITERAL_CSTRING("dom.storage_access"),
|
||||
NS_LITERAL_CSTRING("storage-access")),
|
||||
mAllowCallback(std::move(aAllowCallback)),
|
||||
mAllowAutoGrantCallback(std::move(aAllowAutoGrantCallback)),
|
||||
mAllowAnySiteCallback(std::move(aAllowAnySiteCallback)),
|
||||
mCancelCallback(std::move(aCancelCallback)),
|
||||
mCallbackCalled(false)
|
||||
|
@ -61,6 +63,9 @@ StorageAccessPermissionRequest::Allow(JS::HandleValue aChoices)
|
|||
if (choices.Length() == 1 &&
|
||||
choices[0].choice().EqualsLiteral("allow-on-any-site")) {
|
||||
mAllowAnySiteCallback();
|
||||
} else if (choices.Length() == 1 &&
|
||||
choices[0].choice().EqualsLiteral("allow-auto-grant")) {
|
||||
mAllowAutoGrantCallback();
|
||||
} else {
|
||||
mAllowCallback();
|
||||
}
|
||||
|
@ -71,6 +76,7 @@ StorageAccessPermissionRequest::Allow(JS::HandleValue aChoices)
|
|||
already_AddRefed<StorageAccessPermissionRequest>
|
||||
StorageAccessPermissionRequest::Create(nsPIDOMWindowInner* aWindow,
|
||||
AllowCallback&& aAllowCallback,
|
||||
AllowAutoGrantCallback&& aAllowAutoGrantCallback,
|
||||
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||
CancelCallback&& aCancelCallback)
|
||||
{
|
||||
|
@ -85,6 +91,7 @@ StorageAccessPermissionRequest::Create(nsPIDOMWindowInner* aWindow,
|
|||
new StorageAccessPermissionRequest(aWindow,
|
||||
win->GetPrincipal(),
|
||||
std::move(aAllowCallback),
|
||||
std::move(aAllowAutoGrantCallback),
|
||||
std::move(aAllowAnySiteCallback),
|
||||
std::move(aCancelCallback));
|
||||
return request.forget();
|
||||
|
|
|
@ -28,12 +28,14 @@ public:
|
|||
NS_IMETHOD Allow(JS::HandleValue choices) override;
|
||||
|
||||
typedef std::function<void()> AllowCallback;
|
||||
typedef std::function<void()> AllowAutoGrantCallback;
|
||||
typedef std::function<void()> AllowAnySiteCallback;
|
||||
typedef std::function<void()> CancelCallback;
|
||||
|
||||
static already_AddRefed<StorageAccessPermissionRequest> Create(
|
||||
nsPIDOMWindowInner* aWindow,
|
||||
AllowCallback&& aAllowCallback,
|
||||
AllowAutoGrantCallback&& aAllowAutoGrantCallback,
|
||||
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||
CancelCallback&& aCancelCallback);
|
||||
|
||||
|
@ -41,11 +43,13 @@ private:
|
|||
StorageAccessPermissionRequest(nsPIDOMWindowInner* aWindow,
|
||||
nsIPrincipal* aNodePrincipal,
|
||||
AllowCallback&& aAllowCallback,
|
||||
AllowAutoGrantCallback&& aAllowAutoGrantCallback,
|
||||
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||
CancelCallback&& aCancelCallback);
|
||||
~StorageAccessPermissionRequest();
|
||||
|
||||
AllowCallback mAllowCallback;
|
||||
AllowAutoGrantCallback mAllowAutoGrantCallback;
|
||||
AllowAnySiteCallback mAllowAnySiteCallback;
|
||||
CancelCallback mCancelCallback;
|
||||
nsTArray<PermissionRequest> mPermissionRequests;
|
||||
|
|
|
@ -13988,9 +13988,11 @@ nsIDocument::RequestStorageAccess(mozilla::ErrorResult& aRv)
|
|||
RefPtr<StorageAccessPermissionRequest> sapr =
|
||||
StorageAccessPermissionRequest::Create(inner,
|
||||
// Allow
|
||||
[p] { p->Resolve(false, __func__); },
|
||||
[p] { p->Resolve(AntiTrackingCommon::eAllow, __func__); },
|
||||
// Allow auto grant
|
||||
[p] { p->Resolve(AntiTrackingCommon::eAllowAutoGrant, __func__); },
|
||||
// Allow on any site
|
||||
[p] { p->Resolve(true, __func__); },
|
||||
[p] { p->Resolve(AntiTrackingCommon::eAllowOnAnySite, __func__); },
|
||||
// Block
|
||||
[p] { p->Reject(false, __func__); });
|
||||
|
||||
|
@ -14011,7 +14013,8 @@ nsIDocument::RequestStorageAccess(mozilla::ErrorResult& aRv)
|
|||
pr == PromptResult::Denied);
|
||||
if (pr == PromptResult::Granted) {
|
||||
return AntiTrackingCommon::StorageAccessFinalCheckPromise::
|
||||
CreateAndResolve(onAnySite, __func__);
|
||||
CreateAndResolve(onAnySite ? AntiTrackingCommon::eAllowOnAnySite :
|
||||
AntiTrackingCommon::eAllow, __func__);
|
||||
}
|
||||
return AntiTrackingCommon::StorageAccessFinalCheckPromise::
|
||||
CreateAndReject(false, __func__);
|
||||
|
|
|
@ -7632,10 +7632,6 @@ nsGlobalWindowInner::GetSidebar(OwningExternalOrWindowProxy& aResult,
|
|||
void
|
||||
nsGlobalWindowInner::ClearDocumentDependentSlots(JSContext* aCx)
|
||||
{
|
||||
if (js::GetContextCompartment(aCx) != js::GetObjectCompartment(GetWrapperPreserveColor())) {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with ClearDocumentDependentSlots in a bogus compartment");
|
||||
}
|
||||
|
||||
// If JSAPI OOMs here, there is basically nothing we can do to recover safely.
|
||||
if (!Window_Binding::ClearCachedDocumentValue(aCx, this) ||
|
||||
!Window_Binding::ClearCachedPerformanceValue(aCx, this)) {
|
||||
|
|
|
@ -2886,9 +2886,6 @@ nsINode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
|||
if (!OwnerDoc()->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
|
||||
!hasHadScriptHandlingObject &&
|
||||
!nsContentUtils::IsSystemCaller(aCx)) {
|
||||
if (IsDocument()) {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with a document that lost its script handling object");
|
||||
}
|
||||
Throw(aCx, NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -2897,9 +2894,6 @@ nsINode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
|||
MOZ_ASSERT_IF(obj && ChromeOnlyAccess(),
|
||||
xpc::IsInContentXBLScope(obj) ||
|
||||
!xpc::UseContentXBLScope(JS::GetObjectRealmOrNull(obj)));
|
||||
if (!obj && IsDocument()) {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with WrapNode on a document returning null");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -689,48 +689,16 @@ DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
|
|||
}
|
||||
|
||||
static inline bool
|
||||
Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSFunctionSpec* spec)
|
||||
{
|
||||
bool ok = JS_DefineFunctions(cx, obj, spec);
|
||||
if (ok) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(js::GetObjectClass(obj)->name, "DocumentPrototype")) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: JS_DefineFunctions failed for Document.prototype");
|
||||
}
|
||||
|
||||
return false;
|
||||
Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSFunctionSpec* spec) {
|
||||
return JS_DefineFunctions(cx, obj, spec);
|
||||
}
|
||||
static inline bool
|
||||
Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSPropertySpec* spec)
|
||||
{
|
||||
bool ok = JS_DefineProperties(cx, obj, spec);
|
||||
if (ok) {
|
||||
return true;
|
||||
Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSPropertySpec* spec) {
|
||||
return JS_DefineProperties(cx, obj, spec);
|
||||
}
|
||||
|
||||
if (!strcmp(js::GetObjectClass(obj)->name, "DocumentPrototype")) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: JS_DefineProperties failed for Document.prototype");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
Define(JSContext* cx, JS::Handle<JSObject*> obj, const ConstantSpec* spec)
|
||||
{
|
||||
bool ok = DefineConstants(cx, obj, spec);
|
||||
if (ok) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(js::GetObjectClass(obj)->name, "DocumentPrototype")) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: DefineConstants failed for Document.prototype");
|
||||
}
|
||||
|
||||
return false;
|
||||
Define(JSContext* cx, JS::Handle<JSObject*> obj, const ConstantSpec* spec) {
|
||||
return DefineConstants(cx, obj, spec);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -966,13 +934,6 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
// properties go on the global itself.
|
||||
(!isGlobal &&
|
||||
!DefineProperties(cx, ourProto, properties, chromeOnlyProperties))) {
|
||||
if (!strcmp(protoClass->name, "DocumentPrototype")) {
|
||||
if (!ourProto) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: JS_NewObjectWithUniqueType failed for Document.prototype");
|
||||
} else {
|
||||
MOZ_CRASH("Bug 1405521/1488480: DefineProperties failed for Document.prototype");
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -980,18 +941,12 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
JS::Rooted<JSObject*> unscopableObj(cx,
|
||||
JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
|
||||
if (!unscopableObj) {
|
||||
if (!strcmp(protoClass->name, "DocumentPrototype")) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: Unscopable object creation failed for Document.prototype");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (; *unscopableNames; ++unscopableNames) {
|
||||
if (!JS_DefineProperty(cx, unscopableObj, *unscopableNames,
|
||||
JS::TrueHandleValue, JSPROP_ENUMERATE)) {
|
||||
if (!strcmp(protoClass->name, "DocumentPrototype")) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: Defining property on unscopable object failed for Document.prototype");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -1001,9 +956,6 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
// Readonly and non-enumerable to match Array.prototype.
|
||||
if (!JS_DefinePropertyById(cx, ourProto, unscopableId, unscopableObj,
|
||||
JSPROP_READONLY)) {
|
||||
if (!strcmp(protoClass->name, "DocumentPrototype")) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: Defining @@unscopables failed for Document.prototype");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -1012,9 +964,6 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
JS::Rooted<JSString*> toStringTagStr(cx,
|
||||
JS_NewStringCopyZ(cx, toStringTag));
|
||||
if (!toStringTagStr) {
|
||||
if (!strcmp(protoClass->name, "DocumentPrototype")) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: Copying string tag failed for Document.prototype");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1022,9 +971,6 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::toStringTag)));
|
||||
if (!JS_DefinePropertyById(cx, ourProto, toStringTagId, toStringTagStr,
|
||||
JSPROP_READONLY)) {
|
||||
if (!strcmp(protoClass->name, "DocumentPrototype")) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: Defining @@toStringTag failed for Document.prototype");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -1129,9 +1075,6 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
isChrome ? chromeOnlyProperties : nullptr,
|
||||
unscopableNames, toStringTag, isGlobal);
|
||||
if (!proto) {
|
||||
if (name && !strcmp(name, "Document")) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: CreateInterfacePrototypeObject failed for Document.prototype");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1150,9 +1093,6 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
isChrome,
|
||||
defineOnGlobal);
|
||||
if (!interface) {
|
||||
if (name && !strcmp(name, "Document")) {
|
||||
MOZ_CRASH("Bug 1405521/1488480: CreateInterfaceObject failed for Document");
|
||||
}
|
||||
if (protoCache) {
|
||||
// If we fail we need to make sure to clear the value of protoCache we
|
||||
// set above.
|
||||
|
@ -4299,10 +4239,6 @@ GetPerInterfaceObjectHandle(JSContext* aCx,
|
|||
/* Make sure our global is sane. Hopefully we can remove this sometime */
|
||||
JSObject* global = JS::CurrentGlobalOrNull(aCx);
|
||||
if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
|
||||
if (aSlotId == prototypes::id::HTMLDocument ||
|
||||
aSlotId == prototypes::id::Document) {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with a non-DOM global in GetPerInterfaceObjectHandle");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -4327,30 +4263,6 @@ GetPerInterfaceObjectHandle(JSContext* aCx,
|
|||
const JS::Heap<JSObject*>& entrySlot =
|
||||
protoAndIfaceCache.EntrySlotMustExist(aSlotId);
|
||||
MOZ_ASSERT(JS::ObjectIsNotGray(entrySlot));
|
||||
|
||||
if (!entrySlot) {
|
||||
switch (aSlotId) {
|
||||
case prototypes::id::HTMLDocument: {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with aCreator failing to create HTMLDocument.prototype");
|
||||
break;
|
||||
}
|
||||
case prototypes::id::Document: {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with aCreator failing to create Document.prototype");
|
||||
break;
|
||||
}
|
||||
case prototypes::id::Node: {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with aCreator failing to create Node.prototype");
|
||||
break;
|
||||
}
|
||||
case prototypes::id::EventTarget: {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with aCreator failing to create EventTarget.prototype");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.address());
|
||||
}
|
||||
|
||||
|
|
|
@ -2979,20 +2979,13 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
# if we don't need to create anything, why are we generating this?
|
||||
assert needInterfaceObject or needInterfacePrototypeObject
|
||||
|
||||
def maybecrash(reason):
|
||||
if self.descriptor.name == "Document":
|
||||
return 'MOZ_CRASH("Bug 1405521/1488480: %s");\n' % reason
|
||||
return ""
|
||||
|
||||
getParentProto = fill(
|
||||
"""
|
||||
JS::${type}<JSObject*> parentProto(${getParentProto});
|
||||
if (!parentProto) {
|
||||
$*{maybeCrash}
|
||||
return;
|
||||
}
|
||||
""",
|
||||
maybeCrash=maybecrash("Can't get Node.prototype"),
|
||||
type=parentProtoType,
|
||||
getParentProto=getParentProto)
|
||||
|
||||
|
@ -3000,11 +2993,9 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
"""
|
||||
JS::${type}<JSObject*> constructorProto(${getConstructorProto});
|
||||
if (!constructorProto) {
|
||||
$*{maybeCrash}
|
||||
return;
|
||||
}
|
||||
""",
|
||||
maybeCrash=maybecrash("Can't get Node"),
|
||||
type=constructorProtoType,
|
||||
getConstructorProto=getConstructorProto)
|
||||
|
||||
|
@ -3020,12 +3011,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
for properties in idsToInit]
|
||||
idsInitedFlag = CGGeneric("static bool sIdsInited = false;\n")
|
||||
setFlag = CGGeneric("sIdsInited = true;\n")
|
||||
initIdConditionals = [CGIfWrapper(CGGeneric(fill(
|
||||
"""
|
||||
$*{maybeCrash}
|
||||
return;
|
||||
""",
|
||||
maybeCrash=maybecrash("Can't init IDs"))), call)
|
||||
initIdConditionals = [CGIfWrapper(CGGeneric("return;\n"), call)
|
||||
for call in initIdCalls]
|
||||
initIds = CGList([idsInitedFlag,
|
||||
CGIfWrapper(CGList(initIdConditionals + [setFlag]),
|
||||
|
@ -3112,9 +3098,6 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
${name}, aDefineOnGlobal,
|
||||
${unscopableNames},
|
||||
${isGlobal});
|
||||
if (protoCache && !*protoCache) {
|
||||
$*{maybeCrash}
|
||||
}
|
||||
""",
|
||||
protoClass=protoClass,
|
||||
parentProto=parentProto,
|
||||
|
@ -3129,8 +3112,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
chromeProperties=chromeProperties,
|
||||
name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr",
|
||||
unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr",
|
||||
isGlobal=toStringBool(isGlobal),
|
||||
maybeCrash=maybecrash("dom::CreateInterfaceObjects failed for Document"))
|
||||
isGlobal=toStringBool(isGlobal))
|
||||
|
||||
# If we fail after here, we must clear interface and prototype caches
|
||||
# using this code: intermediate failure must not expose the interface in
|
||||
|
@ -3232,12 +3214,10 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|||
JS::Rooted<JSObject*> holderProto(aCx, ${holderProto});
|
||||
unforgeableHolder = JS_NewObjectWithoutMetadata(aCx, ${holderClass}, holderProto);
|
||||
if (!unforgeableHolder) {
|
||||
$*{maybeCrash}
|
||||
$*{failureCode}
|
||||
}
|
||||
}
|
||||
""",
|
||||
maybeCrash=maybecrash("Can't create unforgeable holder"),
|
||||
holderProto=holderProto,
|
||||
holderClass=holderClass,
|
||||
failureCode=failureCode))
|
||||
|
@ -3522,7 +3502,7 @@ class CGConstructorEnabled(CGAbstractMethod):
|
|||
return body.define()
|
||||
|
||||
|
||||
def CreateBindingJSObject(descriptor, properties, failureCode = ""):
|
||||
def CreateBindingJSObject(descriptor, properties):
|
||||
objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType
|
||||
|
||||
# We don't always need to root obj, but there are a variety
|
||||
|
@ -3547,14 +3527,12 @@ def CreateBindingJSObject(descriptor, properties, failureCode = ""):
|
|||
"""
|
||||
creator.CreateObject(aCx, sClass.ToJSClass(), proto, aObject, aReflector);
|
||||
""")
|
||||
return objDecl + create + fill(
|
||||
return objDecl + create + dedent(
|
||||
"""
|
||||
if (!aReflector) {
|
||||
$*{failureCode}
|
||||
return false;
|
||||
}
|
||||
""",
|
||||
failureCode=failureCode)
|
||||
""")
|
||||
|
||||
|
||||
def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode,
|
||||
|
@ -3573,22 +3551,12 @@ def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode,
|
|||
|
||||
unforgeables = []
|
||||
|
||||
if descriptor.name == "Document":
|
||||
maybeCrash = dedent(
|
||||
"""
|
||||
MOZ_CRASH("Bug 1405521/1488480: Can't define unforgeable attributes");
|
||||
""");
|
||||
else:
|
||||
maybeCrash = "";
|
||||
|
||||
defineUnforgeableAttrs = fill(
|
||||
"""
|
||||
if (!DefineUnforgeableAttributes(aCx, ${holderName}, %s)) {
|
||||
$*{maybeCrash}
|
||||
$*{failureCode}
|
||||
}
|
||||
""",
|
||||
maybeCrash=maybeCrash,
|
||||
failureCode=failureCode,
|
||||
holderName=holderName)
|
||||
defineUnforgeableMethods = fill(
|
||||
|
@ -3734,15 +3702,14 @@ def SetImmutablePrototype(descriptor, failureCode):
|
|||
failureCode=failureCode)
|
||||
|
||||
|
||||
def DeclareProto(noProto = "", wrapFail = ""):
|
||||
def DeclareProto():
|
||||
"""
|
||||
Declare the canonicalProto and proto we have for our wrapping operation.
|
||||
"""
|
||||
return fill(
|
||||
return dedent(
|
||||
"""
|
||||
JS::Handle<JSObject*> canonicalProto = GetProtoObjectHandle(aCx);
|
||||
if (!canonicalProto) {
|
||||
$*{noProto}
|
||||
return false;
|
||||
}
|
||||
JS::Rooted<JSObject*> proto(aCx);
|
||||
|
@ -3753,16 +3720,13 @@ def DeclareProto(noProto = "", wrapFail = ""):
|
|||
// to wrap the proto here.
|
||||
if (js::GetContextCompartment(aCx) != js::GetObjectCompartment(proto)) {
|
||||
if (!JS_WrapObject(aCx, &proto)) {
|
||||
$*{wrapFail}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
proto = canonicalProto;
|
||||
}
|
||||
""",
|
||||
noProto=noProto,
|
||||
wrapFail=wrapFail)
|
||||
""")
|
||||
|
||||
|
||||
class CGWrapWithCacheMethod(CGAbstractMethod):
|
||||
|
@ -3789,47 +3753,6 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
|
|||
return false;
|
||||
""")
|
||||
|
||||
isDocument = False
|
||||
iface = self.descriptor.interface
|
||||
while iface:
|
||||
if iface.identifier.name == "Document":
|
||||
isDocument = True
|
||||
break
|
||||
iface = iface.parent
|
||||
|
||||
if isDocument:
|
||||
noGlobal = fill(
|
||||
"""
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with ${name} not having a global");
|
||||
""",
|
||||
name = self.descriptor.name)
|
||||
noProto = fill(
|
||||
"""
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with ${name} not having a proto");
|
||||
""",
|
||||
name = self.descriptor.name)
|
||||
protoWrapFail = fill(
|
||||
"""
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with ${name} failing to wrap a custom proto");
|
||||
""",
|
||||
name = self.descriptor.name)
|
||||
createObjectFailed = fill(
|
||||
"""
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with ${name} failing to CreateObject/CreateProxyObject");
|
||||
""",
|
||||
name = self.descriptor.name)
|
||||
expandoAllocFail = fill(
|
||||
"""
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with ${name} failing to EnsureExpandoObject or JS_InitializePropertiesFromCompatibleNativeObject");
|
||||
""",
|
||||
name = self.descriptor.name)
|
||||
else:
|
||||
noGlobal = ""
|
||||
noProto = ""
|
||||
protoWrapFail = ""
|
||||
createObjectFailed = ""
|
||||
expandoAllocFail = ""
|
||||
|
||||
return fill(
|
||||
"""
|
||||
static_assert(!IsBaseOf<NonRefcountedDOMObject, ${nativeType}>::value,
|
||||
|
@ -3845,7 +3768,6 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
|
|||
|
||||
JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject()));
|
||||
if (!global) {
|
||||
$*{noGlobal}
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(JS_IsGlobalObject(global));
|
||||
|
@ -3886,14 +3808,11 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
|
|||
|
||||
return true;
|
||||
""",
|
||||
noGlobal=noGlobal,
|
||||
nativeType=self.descriptor.nativeType,
|
||||
assertInheritance=AssertInheritanceChain(self.descriptor),
|
||||
declareProto=DeclareProto(noProto, protoWrapFail),
|
||||
createObject=CreateBindingJSObject(self.descriptor, self.properties,
|
||||
createObjectFailed),
|
||||
declareProto=DeclareProto(),
|
||||
createObject=CreateBindingJSObject(self.descriptor, self.properties),
|
||||
unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor,
|
||||
expandoAllocFail +
|
||||
failureCode),
|
||||
slots=InitMemberSlots(self.descriptor, failureCode),
|
||||
setImmutablePrototype=SetImmutablePrototype(self.descriptor,
|
||||
|
|
|
@ -203,11 +203,7 @@ ImageDocument::Init()
|
|||
JSObject*
|
||||
ImageDocument::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
JSObject* obj = ImageDocument_Binding::Wrap(aCx, this, aGivenProto);
|
||||
if (!obj) {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with ImageDocument::WrapNode failing");
|
||||
}
|
||||
return obj;
|
||||
return ImageDocument_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -203,11 +203,7 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(nsHTMLDocument,
|
|||
JSObject*
|
||||
nsHTMLDocument::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
JSObject* obj = HTMLDocument_Binding::Wrap(aCx, this, aGivenProto);
|
||||
if (!obj) {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with nsHTMLDocument::WrapNode failing");
|
||||
}
|
||||
return obj;
|
||||
return HTMLDocument_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
#include "mozilla/TouchEvents.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCSSFrameConstructor.h"
|
||||
#include "nsDocShell.h"
|
||||
#include "nsEmbedCID.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* 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/. */
|
||||
|
||||
#include "nsCSSFrameConstructor.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIDocument.h"
|
||||
|
|
|
@ -628,16 +628,11 @@ XMLDocument::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const
|
|||
JSObject*
|
||||
XMLDocument::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
JSObject* obj;
|
||||
if (mIsPlainDocument) {
|
||||
obj = Document_Binding::Wrap(aCx, this, aGivenProto);
|
||||
} else {
|
||||
obj = XMLDocument_Binding::Wrap(aCx, this, aGivenProto);
|
||||
return Document_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
if (!obj) {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with XMLDocument::WrapNode failing");
|
||||
}
|
||||
return obj;
|
||||
|
||||
return XMLDocument_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -1648,11 +1648,7 @@ XULDocument::DirectionChanged(const char* aPrefName, XULDocument* aDoc)
|
|||
JSObject*
|
||||
XULDocument::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
JSObject* obj = XULDocument_Binding::Wrap(aCx, this, aGivenProto);
|
||||
if (!obj) {
|
||||
MOZ_CRASH("Looks like bug 1488480/1405521, with XULDocument_Binding::Wrap failing");
|
||||
}
|
||||
return obj;
|
||||
return XULDocument_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
var verified = false;
|
||||
function f(a) {
|
||||
if (a < 10000)
|
||||
return 5;
|
||||
assertEq(g_fwd.caller.arguments.length, 0);
|
||||
assertEq(h_fwd.caller.arguments.length, 0);
|
||||
verified = true;
|
||||
return 6;
|
||||
}
|
||||
|
||||
function g_fwd(x) {
|
||||
with({}) {};
|
||||
return f(x);
|
||||
}
|
||||
function g(a) {
|
||||
var x = a;
|
||||
function inline() {
|
||||
return g_fwd(x);
|
||||
}
|
||||
return inline();
|
||||
}
|
||||
|
||||
function h_fwd(x) {
|
||||
with({}) {};
|
||||
return g(x);
|
||||
}
|
||||
function h(a) {
|
||||
var x = a;
|
||||
function inline() {
|
||||
return h_fwd(x);
|
||||
}
|
||||
return inline();
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
while (!verified) {
|
||||
h(i++);
|
||||
}
|
|
@ -1103,17 +1103,42 @@ JitRuntime::getBaselineDebugModeOSRHandlerAddress(JSContext* cx, bool popFrameRe
|
|||
: baselineDebugModeOSRHandlerNoFrameRegPopAddr_.ref();
|
||||
}
|
||||
|
||||
static void
|
||||
PushCallVMOutputRegisters(MacroAssembler& masm)
|
||||
{
|
||||
// callVMs can use several different output registers, depending on the
|
||||
// type of their outparam.
|
||||
masm.push(ReturnReg);
|
||||
masm.push(ReturnDoubleReg);
|
||||
masm.Push(JSReturnOperand);
|
||||
}
|
||||
|
||||
static void
|
||||
PopCallVMOutputRegisters(MacroAssembler& masm)
|
||||
{
|
||||
masm.Pop(JSReturnOperand);
|
||||
masm.pop(ReturnDoubleReg);
|
||||
masm.pop(ReturnReg);
|
||||
}
|
||||
|
||||
static void
|
||||
TakeCallVMOutputRegisters(AllocatableGeneralRegisterSet& regs)
|
||||
{
|
||||
regs.take(ReturnReg);
|
||||
regs.take(JSReturnOperand);
|
||||
}
|
||||
|
||||
static void
|
||||
EmitBaselineDebugModeOSRHandlerTail(MacroAssembler& masm, Register temp, bool returnFromCallVM)
|
||||
{
|
||||
// Save real return address on the stack temporarily.
|
||||
//
|
||||
// If we're returning from a callVM, we don't need to worry about R0 and
|
||||
// R1 but do need to propagate the original ReturnReg value. Otherwise we
|
||||
// need to worry about R0 and R1 but can clobber ReturnReg. Indeed, on
|
||||
// x86, R1 contains ReturnReg.
|
||||
// R1 but do need to propagate the registers the call might write to.
|
||||
// Otherwise we need to worry about R0 and R1 but can clobber ReturnReg.
|
||||
// Indeed, on x86, R1 contains ReturnReg.
|
||||
if (returnFromCallVM) {
|
||||
masm.push(ReturnReg);
|
||||
PushCallVMOutputRegisters(masm);
|
||||
} else {
|
||||
masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR0)));
|
||||
masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR1)));
|
||||
|
@ -1130,7 +1155,7 @@ EmitBaselineDebugModeOSRHandlerTail(MacroAssembler& masm, Register temp, bool re
|
|||
// Restore saved values.
|
||||
AllocatableGeneralRegisterSet jumpRegs(GeneralRegisterSet::All());
|
||||
if (returnFromCallVM) {
|
||||
jumpRegs.take(ReturnReg);
|
||||
TakeCallVMOutputRegisters(jumpRegs);
|
||||
} else {
|
||||
jumpRegs.take(R0);
|
||||
jumpRegs.take(R1);
|
||||
|
@ -1141,7 +1166,7 @@ EmitBaselineDebugModeOSRHandlerTail(MacroAssembler& masm, Register temp, bool re
|
|||
masm.pop(target);
|
||||
masm.pop(BaselineFrameReg);
|
||||
if (returnFromCallVM) {
|
||||
masm.pop(ReturnReg);
|
||||
PopCallVMOutputRegisters(masm);
|
||||
} else {
|
||||
masm.popValue(R1);
|
||||
masm.popValue(R0);
|
||||
|
@ -1157,7 +1182,7 @@ JitRuntime::generateBaselineDebugModeOSRHandler(JSContext* cx, uint32_t* noFrame
|
|||
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(BaselineFrameReg);
|
||||
regs.take(ReturnReg);
|
||||
TakeCallVMOutputRegisters(regs);
|
||||
Register temp = regs.takeAny();
|
||||
Register syncedStackStart = regs.takeAny();
|
||||
|
||||
|
@ -1170,7 +1195,7 @@ JitRuntime::generateBaselineDebugModeOSRHandler(JSContext* cx, uint32_t* noFrame
|
|||
|
||||
// Record the stack pointer for syncing.
|
||||
masm.moveStackPtrTo(syncedStackStart);
|
||||
masm.push(ReturnReg);
|
||||
PushCallVMOutputRegisters(masm);
|
||||
masm.push(BaselineFrameReg);
|
||||
|
||||
// Call a stub to fully initialize the info.
|
||||
|
@ -1186,7 +1211,7 @@ JitRuntime::generateBaselineDebugModeOSRHandler(JSContext* cx, uint32_t* noFrame
|
|||
// a callVM, and prepareCallVM in BaselineCompiler always fully syncs the
|
||||
// stack.
|
||||
masm.pop(BaselineFrameReg);
|
||||
masm.pop(ReturnReg);
|
||||
PopCallVMOutputRegisters(masm);
|
||||
masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScratchValue()), temp);
|
||||
masm.addToStackPtr(Address(temp, offsetof(BaselineDebugModeOSRInfo, stackAdjust)));
|
||||
|
||||
|
|
|
@ -1835,7 +1835,7 @@ jit::JitActivation::registerIonFrameRecovery(RInstructionResults&& results)
|
|||
jit::RInstructionResults*
|
||||
jit::JitActivation::maybeIonFrameRecovery(JitFrameLayout* fp)
|
||||
{
|
||||
for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end(); ) {
|
||||
for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end(); it++) {
|
||||
if (it->frame() == fp) {
|
||||
return it;
|
||||
}
|
||||
|
|
|
@ -11030,13 +11030,6 @@ nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder,
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsIFrame::IsSelected() const
|
||||
{
|
||||
return (GetContent() && GetContent()->IsSelectionDescendant()) ?
|
||||
IsFrameSelected() : false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsIFrame::IsStackingContext(EffectSet* aEffectSet,
|
||||
const nsStyleDisplay* aStyleDisplay,
|
||||
|
|
|
@ -3232,7 +3232,10 @@ public:
|
|||
/**
|
||||
* @returns true if this frame is selected.
|
||||
*/
|
||||
bool IsSelected() const;
|
||||
bool IsSelected() const {
|
||||
return (GetContent() && GetContent()->IsSelectionDescendant()) ?
|
||||
IsFrameSelected() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* called to discover where this frame, or a parent frame has user-select style
|
||||
|
|
|
@ -72,7 +72,7 @@ public:
|
|||
|
||||
// nsIFrame
|
||||
void BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayListSet& aLists) override;
|
||||
const nsDisplayListSet& aLists) final;
|
||||
|
||||
void Init(nsIContent* aContent,
|
||||
nsContainerFrame* aParent,
|
||||
|
@ -80,7 +80,7 @@ public:
|
|||
|
||||
void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) override;
|
||||
|
||||
nsresult GetCursor(const nsPoint& aPoint, nsIFrame::Cursor& aCursor) override;
|
||||
nsresult GetCursor(const nsPoint& aPoint, nsIFrame::Cursor& aCursor) final;
|
||||
|
||||
nsresult CharacterDataChanged(const CharacterDataChangeInfo&) final;
|
||||
|
||||
|
@ -103,7 +103,7 @@ public:
|
|||
GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
|
||||
}
|
||||
}
|
||||
nsIFrame* GetNextInFlowVirtual() const override { return GetNextInFlow(); }
|
||||
nsIFrame* GetNextInFlowVirtual() const final { return GetNextInFlow(); }
|
||||
nsTextFrame* GetNextInFlow() const
|
||||
{
|
||||
return mNextContinuation &&
|
||||
|
@ -156,20 +156,20 @@ public:
|
|||
return Style()->ShouldSuppressLineBreak();
|
||||
}
|
||||
|
||||
void InvalidateFrame(uint32_t aDisplayItemKey = 0, bool aRebuildDisplayItems = true) override;
|
||||
void InvalidateFrame(uint32_t aDisplayItemKey = 0, bool aRebuildDisplayItems = true) final;
|
||||
void InvalidateFrameWithRect(const nsRect& aRect,
|
||||
uint32_t aDisplayItemKey = 0,
|
||||
bool aRebuildDisplayItems = true) override;
|
||||
bool aRebuildDisplayItems = true) final;
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
void List(FILE* out = stderr,
|
||||
const char* aPrefix = "",
|
||||
uint32_t aFlags = 0) const override;
|
||||
nsresult GetFrameName(nsAString& aResult) const override;
|
||||
uint32_t aFlags = 0) const final;
|
||||
nsresult GetFrameName(nsAString& aResult) const final;
|
||||
void ToCString(nsCString& aBuf, int32_t* aTotalContentLength) const;
|
||||
#endif
|
||||
|
||||
ContentOffsets CalcContentOffsetsFromFramePoint(const nsPoint& aPoint) override;
|
||||
ContentOffsets CalcContentOffsetsFromFramePoint(const nsPoint& aPoint) final;
|
||||
ContentOffsets GetCharacterOffsetAtFramePoint(const nsPoint& aPoint);
|
||||
|
||||
/**
|
||||
|
@ -187,24 +187,24 @@ public:
|
|||
SelectionType aSelectionType);
|
||||
|
||||
FrameSearchResult PeekOffsetNoAmount(bool aForward,
|
||||
int32_t* aOffset) override;
|
||||
int32_t* aOffset) final;
|
||||
FrameSearchResult
|
||||
PeekOffsetCharacter(bool aForward,
|
||||
int32_t* aOffset,
|
||||
PeekOffsetCharacterOptions aOptions =
|
||||
PeekOffsetCharacterOptions()) override;
|
||||
PeekOffsetCharacterOptions()) final;
|
||||
FrameSearchResult PeekOffsetWord(bool aForward,
|
||||
bool aWordSelectEatSpace,
|
||||
bool aIsKeyboardSelect,
|
||||
int32_t* aOffset,
|
||||
PeekWordState* aState) override;
|
||||
PeekWordState* aState) final;
|
||||
|
||||
nsresult CheckVisibility(nsPresContext* aContext,
|
||||
int32_t aStartIndex,
|
||||
int32_t aEndIndex,
|
||||
bool aRecurse,
|
||||
bool* aFinished,
|
||||
bool* _retval) override;
|
||||
bool* _retval) final;
|
||||
|
||||
// Flags for aSetLengthFlags
|
||||
enum
|
||||
|
@ -217,27 +217,27 @@ public:
|
|||
nsLineLayout* aLineLayout,
|
||||
uint32_t aSetLengthFlags = 0);
|
||||
|
||||
nsresult GetOffsets(int32_t& start, int32_t& end) const override;
|
||||
nsresult GetOffsets(int32_t& start, int32_t& end) const final;
|
||||
|
||||
void AdjustOffsetsForBidi(int32_t start, int32_t end) override;
|
||||
void AdjustOffsetsForBidi(int32_t start, int32_t end) final;
|
||||
|
||||
nsresult GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) override;
|
||||
nsresult GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) final;
|
||||
nsresult GetCharacterRectsInRange(int32_t aInOffset,
|
||||
int32_t aLength,
|
||||
nsTArray<nsRect>& aRects) override;
|
||||
nsTArray<nsRect>& aRects) final;
|
||||
|
||||
nsresult GetChildFrameContainingOffset(int32_t inContentOffset,
|
||||
bool inHint,
|
||||
int32_t* outFrameContentOffset,
|
||||
nsIFrame** outChildFrame) override;
|
||||
nsIFrame** outChildFrame) final;
|
||||
|
||||
bool IsVisibleInSelection(mozilla::dom::Selection* aSelection) override;
|
||||
bool IsVisibleInSelection(mozilla::dom::Selection* aSelection) final;
|
||||
|
||||
bool IsEmpty() override;
|
||||
bool IsSelfEmpty() override { return IsEmpty(); }
|
||||
bool IsEmpty() final;
|
||||
bool IsSelfEmpty() final { return IsEmpty(); }
|
||||
nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const final;
|
||||
|
||||
bool HasSignificantTerminalNewline() const override;
|
||||
bool HasSignificantTerminalNewline() const final;
|
||||
|
||||
/**
|
||||
* Returns true if this text frame is logically adjacent to the end of the
|
||||
|
@ -255,7 +255,7 @@ public:
|
|||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
mozilla::a11y::AccType AccessibleType() override;
|
||||
mozilla::a11y::AccType AccessibleType() final;
|
||||
#endif
|
||||
|
||||
float GetFontSizeInflation() const;
|
||||
|
@ -266,9 +266,9 @@ public:
|
|||
}
|
||||
void SetFontSizeInflation(float aInflation);
|
||||
|
||||
void MarkIntrinsicISizesDirty() override;
|
||||
nscoord GetMinISize(gfxContext* aRenderingContext) override;
|
||||
nscoord GetPrefISize(gfxContext* aRenderingContext) override;
|
||||
void MarkIntrinsicISizesDirty() final;
|
||||
nscoord GetMinISize(gfxContext* aRenderingContext) final;
|
||||
nscoord GetPrefISize(gfxContext* aRenderingContext) final;
|
||||
void AddInlineMinISize(gfxContext* aRenderingContext,
|
||||
InlineMinISizeData* aData) override;
|
||||
void AddInlinePrefISize(gfxContext* aRenderingContext,
|
||||
|
@ -280,16 +280,16 @@ public:
|
|||
const mozilla::LogicalSize& aMargin,
|
||||
const mozilla::LogicalSize& aBorder,
|
||||
const mozilla::LogicalSize& aPadding,
|
||||
ComputeSizeFlags aFlags) override;
|
||||
nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override;
|
||||
ComputeSizeFlags aFlags) final;
|
||||
nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const final;
|
||||
nsresult GetPrefWidthTightBounds(gfxContext* aContext,
|
||||
nscoord* aX,
|
||||
nscoord* aXMost) override;
|
||||
nscoord* aXMost) final;
|
||||
void Reflow(nsPresContext* aPresContext,
|
||||
ReflowOutput& aMetrics,
|
||||
const ReflowInput& aReflowInput,
|
||||
nsReflowStatus& aStatus) override;
|
||||
bool CanContinueTextRun() const override;
|
||||
nsReflowStatus& aStatus) final;
|
||||
bool CanContinueTextRun() const final;
|
||||
// Method that is called for a text frame that is logically
|
||||
// adjacent to the end of the line (i.e. followed only by empty text frames,
|
||||
// placeholders or inlines containing such).
|
||||
|
@ -307,7 +307,7 @@ public:
|
|||
uint32_t aEndOffset = UINT32_MAX,
|
||||
TextOffsetType aOffsetType = TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
|
||||
TrailingWhitespace aTrimTrailingWhitespace =
|
||||
TrailingWhitespace::TRIM_TRAILING_WHITESPACE) override;
|
||||
TrailingWhitespace::TRIM_TRAILING_WHITESPACE) final;
|
||||
|
||||
nsOverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame);
|
||||
|
||||
|
@ -544,7 +544,7 @@ public:
|
|||
const nscolor* aDecorationOverrideColor,
|
||||
PropertyProvider* aProvider);
|
||||
|
||||
nscolor GetCaretColorAt(int32_t aOffset) override;
|
||||
nscolor GetCaretColorAt(int32_t aOffset) final;
|
||||
|
||||
int16_t GetSelectionStatus(int16_t* aSelectionFlags);
|
||||
|
||||
|
@ -655,14 +655,14 @@ public:
|
|||
|
||||
bool IsInitialLetterChild() const;
|
||||
|
||||
bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override;
|
||||
bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) final;
|
||||
|
||||
void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign);
|
||||
mozilla::JustificationAssignment GetJustificationAssignment() const;
|
||||
|
||||
uint32_t CountGraphemeClusters() const;
|
||||
|
||||
bool HasAnyNoncollapsedCharacters() override;
|
||||
bool HasAnyNoncollapsedCharacters() final;
|
||||
|
||||
/**
|
||||
* Call this after you have manually changed the text node contents without
|
||||
|
@ -704,7 +704,7 @@ protected:
|
|||
* Return true if the frame is part of a Selection.
|
||||
* Helper method to implement the public IsSelected() API.
|
||||
*/
|
||||
bool IsFrameSelected() const override;
|
||||
bool IsFrameSelected() const final;
|
||||
|
||||
mozilla::UniquePtr<SelectionDetails> GetSelectionDetails();
|
||||
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
#include "nsSVGMaskFrame.h"
|
||||
#include "nsTableCellFrame.h"
|
||||
#include "nsTableColFrame.h"
|
||||
#include "nsTextFrame.h"
|
||||
#include "nsSliderFrame.h"
|
||||
#include "ClientLayerManager.h"
|
||||
#include "mozilla/layers/StackingContextHelper.h"
|
||||
|
@ -9614,6 +9615,18 @@ nsCharClipDisplayItem::ComputeInvalidationRegion(
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsCharClipDisplayItem::IsSelected() const
|
||||
{
|
||||
if (mIsFrameSelected.isNothing()) {
|
||||
MOZ_ASSERT((nsTextFrame*)do_QueryFrame(mFrame));
|
||||
auto* f = static_cast<nsTextFrame*>(mFrame);
|
||||
mIsFrameSelected.emplace(f->IsSelected());
|
||||
}
|
||||
|
||||
return mIsFrameSelected.value();
|
||||
}
|
||||
|
||||
nsDisplayEffectsBase::nsDisplayEffectsBase(
|
||||
nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
|
|
|
@ -7798,14 +7798,7 @@ public:
|
|||
: nullptr;
|
||||
}
|
||||
|
||||
bool IsSelected() const
|
||||
{
|
||||
if (mIsFrameSelected.isNothing()) {
|
||||
mIsFrameSelected.emplace(mFrame->IsSelected());
|
||||
}
|
||||
|
||||
return mIsFrameSelected.value();
|
||||
}
|
||||
bool IsSelected() const;
|
||||
|
||||
// Lengths measured from the visual inline start and end sides
|
||||
// (i.e. left and right respectively in horizontal writing modes,
|
||||
|
|
|
@ -408,6 +408,89 @@ CompareBaseDomains(nsIURI* aTrackingURI,
|
|||
nsCaseInsensitiveCStringComparator());
|
||||
}
|
||||
|
||||
class TemporaryAccessGrantObserver final : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static void
|
||||
Create(nsIPermissionManager* aPM,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aType)
|
||||
{
|
||||
nsCOMPtr<nsITimer> timer;
|
||||
RefPtr<TemporaryAccessGrantObserver> observer =
|
||||
new TemporaryAccessGrantObserver(aPM, aPrincipal, aType);
|
||||
nsresult rv =
|
||||
NS_NewTimerWithObserver(getter_AddRefs(timer),
|
||||
observer,
|
||||
24 * 60 * 60 * 1000, // 24 hours
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
observer->SetTimer(timer);
|
||||
} else {
|
||||
timer->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void SetTimer(nsITimer* aTimer)
|
||||
{
|
||||
mTimer = aTimer;
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TemporaryAccessGrantObserver(nsIPermissionManager* aPM,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aType)
|
||||
: mPM(aPM)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mType(aType)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess(),
|
||||
"Enforcing temporary access grant lifetimes can only be done in "
|
||||
"the parent process");
|
||||
}
|
||||
|
||||
~TemporaryAccessGrantObserver() = default;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
nsCOMPtr<nsIPermissionManager> mPM;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCString mType;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(TemporaryAccessGrantObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
TemporaryAccessGrantObserver::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
if (strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0) {
|
||||
Unused << mPM->RemoveFromPrincipal(mPrincipal, mType.get());
|
||||
} else if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
}
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
|
||||
/* static */ RefPtr<AntiTrackingCommon::StorageAccessGrantPromise>
|
||||
|
@ -439,11 +522,11 @@ AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipa
|
|||
nsICookieService::BEHAVIOR_REJECT_TRACKER) {
|
||||
LOG(("Disabled by network.cookie.cookieBehavior pref (%d), bailing out early",
|
||||
StaticPrefs::network_cookie_cookieBehavior()));
|
||||
return StorageAccessGrantPromise::CreateAndResolve(true, __func__);
|
||||
return StorageAccessGrantPromise::CreateAndResolve(eAllowOnAnySite, __func__);
|
||||
}
|
||||
|
||||
if (CheckContentBlockingAllowList(aParentWindow)) {
|
||||
return StorageAccessGrantPromise::CreateAndResolve(true, __func__);
|
||||
return StorageAccessGrantPromise::CreateAndResolve(eAllowOnAnySite, __func__);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> topLevelStoragePrincipal;
|
||||
|
@ -534,7 +617,7 @@ AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipa
|
|||
auto storePermission = [pwin, parentWindow, origin, trackingOrigin,
|
||||
trackingPrincipal, trackingURI, topInnerWindow,
|
||||
topLevelStoragePrincipal, aReason]
|
||||
(bool aAnySite) -> RefPtr<StorageAccessGrantPromise> {
|
||||
(int aAllowMode) -> RefPtr<StorageAccessGrantPromise> {
|
||||
NS_ConvertUTF16toUTF8 grantedOrigin(origin);
|
||||
|
||||
nsAutoCString permissionKey;
|
||||
|
@ -562,11 +645,12 @@ AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipa
|
|||
trackingPrincipal,
|
||||
trackingOrigin,
|
||||
grantedOrigin,
|
||||
aAnySite)
|
||||
aAllowMode)
|
||||
->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
||||
[] (FirstPartyStorageAccessGrantPromise::ResolveOrRejectValue&& aValue) {
|
||||
if (aValue.IsResolve()) {
|
||||
return StorageAccessGrantPromise::CreateAndResolve(NS_SUCCEEDED(aValue.ResolveValue()), __func__);
|
||||
return StorageAccessGrantPromise::CreateAndResolve(NS_SUCCEEDED(aValue.ResolveValue()) ?
|
||||
eAllowOnAnySite : eAllow, __func__);
|
||||
}
|
||||
return StorageAccessGrantPromise::CreateAndReject(false, __func__);
|
||||
});
|
||||
|
@ -584,7 +668,7 @@ AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipa
|
|||
IPC::Principal(trackingPrincipal),
|
||||
trackingOrigin,
|
||||
grantedOrigin,
|
||||
aAnySite)
|
||||
aAllowMode)
|
||||
->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
||||
[] (const ContentChild::FirstPartyStorageAccessGrantedForOriginPromise::ResolveOrRejectValue& aValue) {
|
||||
if (aValue.IsResolve()) {
|
||||
|
@ -612,9 +696,12 @@ AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(n
|
|||
nsIPrincipal* aTrackingPrincipal,
|
||||
const nsCString& aTrackingOrigin,
|
||||
const nsCString& aGrantedOrigin,
|
||||
bool aAnySite)
|
||||
int aAllowMode)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(aAllowMode == eAllow ||
|
||||
aAllowMode == eAllowAutoGrant ||
|
||||
aAllowMode == eAllowOnAnySite);
|
||||
|
||||
nsCOMPtr<nsIURI> parentPrincipalURI;
|
||||
Unused << aParentPrincipal->GetURI(getter_AddRefs(parentPrincipalURI));
|
||||
|
@ -640,7 +727,7 @@ AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(n
|
|||
int64_t when = (PR_Now() / PR_USEC_PER_MSEC) + expirationTime;
|
||||
|
||||
nsresult rv;
|
||||
if (aAnySite) {
|
||||
if (aAllowMode == eAllowOnAnySite) {
|
||||
uint32_t privateBrowsingId = 0;
|
||||
rv = aTrackingPrincipal->GetPrivateBrowsingId(&privateBrowsingId);
|
||||
if (!NS_WARN_IF(NS_FAILED(rv)) && privateBrowsingId > 0) {
|
||||
|
@ -659,9 +746,11 @@ AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(n
|
|||
} else {
|
||||
uint32_t privateBrowsingId = 0;
|
||||
rv = aParentPrincipal->GetPrivateBrowsingId(&privateBrowsingId);
|
||||
if (!NS_WARN_IF(NS_FAILED(rv)) && privateBrowsingId > 0) {
|
||||
// If we are coming from a private window, make sure to store a session-only
|
||||
// permission which won't get persisted to disk.
|
||||
if ((!NS_WARN_IF(NS_FAILED(rv)) && privateBrowsingId > 0) ||
|
||||
(aAllowMode == eAllowAutoGrant)) {
|
||||
// If we are coming from a private window or are automatically granting a
|
||||
// permission, make sure to store a session-only permission which won't
|
||||
// get persisted to disk.
|
||||
expirationType = nsIPermissionManager::EXPIRE_SESSION;
|
||||
when = 0;
|
||||
}
|
||||
|
@ -675,6 +764,11 @@ AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(n
|
|||
rv = pm->AddFromPrincipal(aParentPrincipal, type.get(),
|
||||
nsIPermissionManager::ALLOW_ACTION,
|
||||
expirationType, when);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && (aAllowMode == eAllowAutoGrant)) {
|
||||
// Make sure temporary access grants do not survive more than 24 hours.
|
||||
TemporaryAccessGrantObserver::Create(pm, aParentPrincipal, type);
|
||||
}
|
||||
}
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
|
|
|
@ -77,6 +77,12 @@ public:
|
|||
eOpenerAfterUserInteraction,
|
||||
eOpener
|
||||
};
|
||||
enum StorageAccessPromptChoices
|
||||
{
|
||||
eAllow,
|
||||
eAllowAutoGrant,
|
||||
eAllowOnAnySite
|
||||
};
|
||||
|
||||
// Grant the permission for aOrigin to have access to the first party storage.
|
||||
// This method can handle 2 different scenarios:
|
||||
|
@ -93,9 +99,9 @@ public:
|
|||
// Ex: example.net import tracker.com/script.js which does opens a popup and
|
||||
// the user interacts with it. tracker.com is allowed when loaded by
|
||||
// example.net.
|
||||
typedef MozPromise<bool, bool, true> StorageAccessFinalCheckPromise;
|
||||
typedef MozPromise<int, bool, true> StorageAccessFinalCheckPromise;
|
||||
typedef std::function<RefPtr<StorageAccessFinalCheckPromise>()> PerformFinalChecks;
|
||||
typedef MozPromise<bool, bool, true> StorageAccessGrantPromise;
|
||||
typedef MozPromise<int, bool, true> StorageAccessGrantPromise;
|
||||
static MOZ_MUST_USE RefPtr<StorageAccessGrantPromise>
|
||||
AddFirstPartyStorageAccessGrantedFor(nsIPrincipal* aPrincipal,
|
||||
nsPIDOMWindowInner* aParentWindow,
|
||||
|
@ -120,7 +126,7 @@ public:
|
|||
nsIPrincipal* aTrackingPrinciapl,
|
||||
const nsCString& aParentOrigin,
|
||||
const nsCString& aGrantedOrigin,
|
||||
bool aAnySite);
|
||||
int aAllowMode);
|
||||
|
||||
enum ContentBlockingAllowListPurpose {
|
||||
eStorageChecks,
|
||||
|
|
|
@ -1,18 +1,30 @@
|
|||
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const CHROME_BASE = "chrome://mochitests/content/browser/browser/modules/test/browser/";
|
||||
Services.scriptloader.loadSubScript(CHROME_BASE + "head.js", this);
|
||||
/* import-globals-from ../../../../../browser/modules/test/browser/head.js */
|
||||
|
||||
async function testDoorHanger(choice) {
|
||||
info(`Running doorhanger test with choice #${choice}`);
|
||||
const BLOCK = 0;
|
||||
const ALLOW = 1;
|
||||
const ALLOW_ON_ANY_SITE = 2;
|
||||
|
||||
async function testDoorHanger(choice, showPrompt, topPage, maxConcurrent) {
|
||||
info(`Running doorhanger test with choice #${choice}, showPrompt: ${showPrompt} and ` +
|
||||
`topPage: ${topPage}, maxConcurrent: ${maxConcurrent}`);
|
||||
|
||||
if (!showPrompt) {
|
||||
is(choice, ALLOW, "When not showing a prompt, we can only auto-grant");
|
||||
}
|
||||
|
||||
await SpecialPowers.flushPrefEnv();
|
||||
await SpecialPowers.pushPrefEnv({"set": [
|
||||
["browser.contentblocking.allowlist.annotations.enabled", true],
|
||||
["browser.contentblocking.allowlist.storage.enabled", true],
|
||||
[ContentBlocking.prefIntroCount, ContentBlocking.MAX_INTROS],
|
||||
["dom.storage_access.auto_grants", true],
|
||||
["dom.storage_access.enabled", true],
|
||||
["dom.storage_access.max_concurrent_auto_grants", maxConcurrent],
|
||||
["dom.storage_access.prompt.testing", false],
|
||||
["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
|
||||
["privacy.trackingprotection.enabled", false],
|
||||
|
@ -23,13 +35,17 @@ async function testDoorHanger(choice) {
|
|||
|
||||
await UrlClassifierTestUtils.addTestTrackers();
|
||||
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, topPage);
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
async function runChecks() {
|
||||
// We need to repeat this constant here since runChecks is stringified
|
||||
// and sent to the content process.
|
||||
const BLOCK = 0;
|
||||
|
||||
await new Promise(resolve => {
|
||||
addEventListener("message", function onMessage(e) {
|
||||
if (e.data.startsWith("choice:")) {
|
||||
|
@ -61,7 +77,7 @@ async function testDoorHanger(choice) {
|
|||
/* import-globals-from storageAccessAPIHelpers.js */
|
||||
await callRequestStorageAccess();
|
||||
|
||||
if (choice == 0) {
|
||||
if (choice == BLOCK) {
|
||||
// We've said no, so cookies are still blocked
|
||||
is(document.cookie, "", "Still no cookies for me");
|
||||
document.cookie = "name=value";
|
||||
|
@ -74,23 +90,16 @@ async function testDoorHanger(choice) {
|
|||
}
|
||||
}
|
||||
|
||||
let shownPromise =
|
||||
BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
|
||||
shownPromise.then(async _ => {
|
||||
let notification =
|
||||
PopupNotifications.getNotification("storage-access", browser);
|
||||
Assert.ok(notification, "Should have gotten the notification");
|
||||
|
||||
let permChanged = TestUtils.topicObserved("perm-changed",
|
||||
(subject, data) => {
|
||||
let result;
|
||||
if (choice == 1) {
|
||||
if (choice == ALLOW) {
|
||||
result = subject &&
|
||||
subject.QueryInterface(Ci.nsIPermission)
|
||||
.type.startsWith("3rdPartyStorage^") &&
|
||||
subject.principal.origin == "http://example.net" &&
|
||||
subject.principal.origin == (new URL(topPage)).origin &&
|
||||
data == "added";
|
||||
} else if (choice == 2) {
|
||||
} else if (choice == ALLOW_ON_ANY_SITE) {
|
||||
result = subject &&
|
||||
subject.QueryInterface(Ci.nsIPermission)
|
||||
.type == "cookie" &&
|
||||
|
@ -99,24 +108,37 @@ async function testDoorHanger(choice) {
|
|||
}
|
||||
return result;
|
||||
});
|
||||
if (choice == 0) {
|
||||
let shownPromise =
|
||||
BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
|
||||
shownPromise.then(async _ => {
|
||||
if (topPage != gBrowser.currentURI.spec) {
|
||||
return;
|
||||
}
|
||||
ok(showPrompt, "We shouldn't show the prompt when we don't intend to");
|
||||
let notification = await new Promise(function poll(resolve) {
|
||||
let notification =
|
||||
PopupNotifications.getNotification("storage-access", browser);
|
||||
if (notification) {
|
||||
resolve(notification);
|
||||
return;
|
||||
}
|
||||
setTimeout(poll, 10);
|
||||
});
|
||||
Assert.ok(notification, "Should have gotten the notification");
|
||||
|
||||
if (choice == BLOCK) {
|
||||
await clickMainAction();
|
||||
} else if (choice == 1) {
|
||||
} else if (choice == ALLOW) {
|
||||
await clickSecondaryAction(choice - 1);
|
||||
} else if (choice == 2) {
|
||||
} else if (choice == ALLOW_ON_ANY_SITE) {
|
||||
await clickSecondaryAction(choice - 1);
|
||||
}
|
||||
if (choice != 0) {
|
||||
if (choice != BLOCK) {
|
||||
await permChanged;
|
||||
}
|
||||
});
|
||||
|
||||
let url;
|
||||
if (choice == 2) {
|
||||
url = TEST_3RD_PARTY_PAGE + "?disableWaitUntilPermission";
|
||||
} else {
|
||||
url = TEST_3RD_PARTY_PAGE;
|
||||
}
|
||||
let url = TEST_3RD_PARTY_PAGE + "?disableWaitUntilPermission";
|
||||
let ct = ContentTask.spawn(browser,
|
||||
{ page: url,
|
||||
callback: runChecks.toString(),
|
||||
|
@ -159,13 +181,114 @@ async function testDoorHanger(choice) {
|
|||
ifr.src = obj.page;
|
||||
});
|
||||
});
|
||||
if (showPrompt) {
|
||||
await Promise.all([ct, shownPromise]);
|
||||
} else {
|
||||
await Promise.all([ct, permChanged]);
|
||||
}
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
}
|
||||
|
||||
async function preparePermissionsFromOtherSites(topPage) {
|
||||
info("Faking permissions from other sites");
|
||||
let type = "3rdPartyStorage^https://tracking.example.org";
|
||||
let permission = Services.perms.ALLOW_ACTION;
|
||||
let expireType = Services.perms.EXPIRE_SESSION;
|
||||
if (topPage == TEST_TOP_PAGE) {
|
||||
// For the first page, don't do anything
|
||||
} else if (topPage == TEST_TOP_PAGE_2) {
|
||||
// For the second page, only add the permission from the first page
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
} else if (topPage == TEST_TOP_PAGE_3) {
|
||||
// For the third page, add the permissions from the first two pages
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN_2),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
} else if (topPage == TEST_TOP_PAGE_4) {
|
||||
// For the fourth page, add the permissions from the first three pages
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN_2),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN_3),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
} else if (topPage == TEST_TOP_PAGE_5) {
|
||||
// For the fifth page, add the permissions from the first four pages
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN_2),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN_3),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN_4),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
} else if (topPage == TEST_TOP_PAGE_6) {
|
||||
// For the sixth page, add the permissions from the first five pages
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN_2),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN_3),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN_4),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
Services.perms.add(Services.io.newURI(TEST_DOMAIN_5),
|
||||
type,
|
||||
permission,
|
||||
expireType,
|
||||
0);
|
||||
} else {
|
||||
ok(false, "Unexpected top page: " + topPage);
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanUp() {
|
||||
info("Cleaning up.");
|
||||
await new Promise(resolve => {
|
||||
|
@ -173,13 +296,30 @@ async function cleanUp() {
|
|||
});
|
||||
}
|
||||
|
||||
async function runRound(n) {
|
||||
await testDoorHanger(n);
|
||||
async function runRound(topPage, showPrompt, maxConcurrent) {
|
||||
if (showPrompt) {
|
||||
await preparePermissionsFromOtherSites(topPage);
|
||||
await testDoorHanger(BLOCK, showPrompt, topPage, maxConcurrent);
|
||||
await cleanUp();
|
||||
await preparePermissionsFromOtherSites(topPage);
|
||||
await testDoorHanger(ALLOW, showPrompt, topPage, maxConcurrent);
|
||||
await cleanUp();
|
||||
await preparePermissionsFromOtherSites(topPage);
|
||||
await testDoorHanger(ALLOW_ON_ANY_SITE, showPrompt, topPage, maxConcurrent);
|
||||
} else {
|
||||
await preparePermissionsFromOtherSites(topPage);
|
||||
await testDoorHanger(ALLOW, showPrompt, topPage, maxConcurrent);
|
||||
}
|
||||
await cleanUp();
|
||||
}
|
||||
|
||||
add_task(async function() {
|
||||
await runRound(0);
|
||||
await runRound(1);
|
||||
await runRound(2);
|
||||
await runRound(TEST_TOP_PAGE, false, 1);
|
||||
await runRound(TEST_TOP_PAGE_2, true, 1);
|
||||
await runRound(TEST_TOP_PAGE, false, 5);
|
||||
await runRound(TEST_TOP_PAGE_2, false, 5);
|
||||
await runRound(TEST_TOP_PAGE_3, false, 5);
|
||||
await runRound(TEST_TOP_PAGE_4, false, 5);
|
||||
await runRound(TEST_TOP_PAGE_5, false, 5);
|
||||
await runRound(TEST_TOP_PAGE_6, true, 5);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
const TEST_DOMAIN = "http://example.net";
|
||||
const TEST_DOMAIN_2 = "http://xn--exmple-cua.test";
|
||||
const TEST_DOMAIN_3 = "https://xn--hxajbheg2az3al.xn--jxalpdlp";
|
||||
const TEST_DOMAIN_4 = "http://prefixexample.com";
|
||||
const TEST_DOMAIN_5 = "http://test";
|
||||
const TEST_DOMAIN_6 = "http://mochi.test:8888";
|
||||
const TEST_3RD_PARTY_DOMAIN = "https://tracking.example.org";
|
||||
const TEST_3RD_PARTY_DOMAIN_TP = "https://tracking.example.com";
|
||||
const TEST_4TH_PARTY_DOMAIN = "http://not-tracking.example.com";
|
||||
|
@ -7,6 +12,11 @@ const TEST_ANOTHER_3RD_PARTY_DOMAIN = "https://another-tracking.example.net";
|
|||
const TEST_PATH = "/browser/toolkit/components/antitracking/test/browser/";
|
||||
|
||||
const TEST_TOP_PAGE = TEST_DOMAIN + TEST_PATH + "page.html";
|
||||
const TEST_TOP_PAGE_2 = TEST_DOMAIN_2 + TEST_PATH + "page.html";
|
||||
const TEST_TOP_PAGE_3 = TEST_DOMAIN_3 + TEST_PATH + "page.html";
|
||||
const TEST_TOP_PAGE_4 = TEST_DOMAIN_4 + TEST_PATH + "page.html";
|
||||
const TEST_TOP_PAGE_5 = TEST_DOMAIN_5 + TEST_PATH + "page.html";
|
||||
const TEST_TOP_PAGE_6 = TEST_DOMAIN_6 + TEST_PATH + "page.html";
|
||||
const TEST_EMBEDDER_PAGE = TEST_DOMAIN + TEST_PATH + "embedder.html";
|
||||
const TEST_POPUP_PAGE = TEST_DOMAIN + TEST_PATH + "popup.html";
|
||||
const TEST_3RD_PARTY_PAGE = TEST_3RD_PARTY_DOMAIN + TEST_PATH + "3rdParty.html";
|
||||
|
|
|
@ -172,12 +172,16 @@ ProcessMiddlemanCall(const char* aInputData, size_t aInputSize,
|
|||
CallArguments arguments;
|
||||
call->mArguments.CopyTo(&arguments);
|
||||
|
||||
bool skipCall;
|
||||
{
|
||||
MiddlemanCallContext cx(call, &arguments, MiddlemanCallPhase::MiddlemanInput);
|
||||
redirection.mMiddlemanCall(cx);
|
||||
skipCall = cx.mSkipCallInMiddleman;
|
||||
}
|
||||
|
||||
if (!skipCall) {
|
||||
RecordReplayInvokeCall(redirection.mBaseFunction, &arguments);
|
||||
}
|
||||
|
||||
{
|
||||
MiddlemanCallContext cx(call, &arguments, MiddlemanCallPhase::MiddlemanOutput);
|
||||
|
|
|
@ -172,6 +172,10 @@ struct MiddlemanCallContext
|
|||
// divergence and associated rewind will occur.
|
||||
bool mFailed;
|
||||
|
||||
// This can be set in the MiddlemanInput phase to avoid performing the call
|
||||
// in the middleman process.
|
||||
bool mSkipCallInMiddleman;
|
||||
|
||||
// During the ReplayInput phase, this can be used to fill in any middleman
|
||||
// calls whose output the current one depends on.
|
||||
InfallibleVector<MiddlemanCall*>* mDependentCalls;
|
||||
|
@ -198,7 +202,8 @@ struct MiddlemanCallContext
|
|||
|
||||
MiddlemanCallContext(MiddlemanCall* aCall, CallArguments* aArguments, MiddlemanCallPhase aPhase)
|
||||
: mCall(aCall), mArguments(aArguments), mPhase(aPhase),
|
||||
mFailed(false), mDependentCalls(nullptr), mReplayOutputIsOld(false)
|
||||
mFailed(false), mSkipCallInMiddleman(false),
|
||||
mDependentCalls(nullptr), mReplayOutputIsOld(false)
|
||||
{
|
||||
switch (mPhase) {
|
||||
case MiddlemanCallPhase::ReplayPreface:
|
||||
|
@ -429,6 +434,15 @@ MM_StackArgumentData(MiddlemanCallContext& aCx)
|
|||
}
|
||||
}
|
||||
|
||||
// Avoid calling a function in the middleman process.
|
||||
static inline void
|
||||
MM_SkipInMiddleman(MiddlemanCallContext& aCx)
|
||||
{
|
||||
if (aCx.mPhase == MiddlemanCallPhase::MiddlemanInput) {
|
||||
aCx.mSkipCallInMiddleman = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
MM_NoOp(MiddlemanCallContext& aCx)
|
||||
{
|
||||
|
|
|
@ -199,6 +199,7 @@ MM_ObjCInput(MiddlemanCallContext& aCx, id* aThingPtr)
|
|||
// List of the Objective C classes which messages might be sent to directly.
|
||||
static const char* gStaticClasses[] = {
|
||||
// Standard classes.
|
||||
"NSAutoreleasePool",
|
||||
"NSBezierPath",
|
||||
"NSButtonCell",
|
||||
"NSColor",
|
||||
|
@ -1131,31 +1132,23 @@ RR_objc_msgSend(Stream& aEvents, CallArguments* aArguments, ErrorType* aError)
|
|||
}
|
||||
}
|
||||
|
||||
static PreambleResult
|
||||
MiddlemanPreamble_objc_msgSend(CallArguments* aArguments)
|
||||
static void
|
||||
MM_Alloc(MiddlemanCallContext& aCx)
|
||||
{
|
||||
auto obj = aArguments->Arg<0, id>();
|
||||
auto message = aArguments->Arg<1, const char*>();
|
||||
|
||||
// Fake object value which allows null checks in the caller to pass.
|
||||
static const size_t FakeId = 1;
|
||||
|
||||
// Ignore uses of NSAutoreleasePool after diverging from the recording.
|
||||
// These are not performed in the middleman because the middleman has its
|
||||
// own autorelease pool, and because the middleman can process calls from
|
||||
// multiple threads which will cause these messages to behave differently.
|
||||
// release messages are also ignored, as for CFRelease.
|
||||
if ((!strcmp(message, "alloc") && obj == (id) objc_lookUpClass("NSAutoreleasePool")) ||
|
||||
(!strcmp(message, "init") && obj == (id) FakeId) ||
|
||||
!strcmp(message, "drain") ||
|
||||
!strcmp(message, "release")) {
|
||||
// Fake a return value in case the caller null checks it.
|
||||
aArguments->Rval<size_t>() = 1;
|
||||
return PreambleResult::Veto;
|
||||
if (aCx.mPhase == MiddlemanCallPhase::MiddlemanInput) {
|
||||
// Refuse to allocate NSAutoreleasePools in the middleman: the order in
|
||||
// which middleman calls happen does not guarantee the pools will be
|
||||
// created and released in LIFO order, as the pools require. Instead,
|
||||
// allocate an NSString instead so we have something to release later.
|
||||
// Messages sent to NSAutoreleasePools will all be skipped in the
|
||||
// middleman, so no one should notice this subterfuge.
|
||||
auto& obj = aCx.mArguments->Arg<0, id>();
|
||||
if (obj == (id) objc_lookUpClass("NSAutoreleasePool")) {
|
||||
obj = (id) objc_lookUpClass("NSString");
|
||||
}
|
||||
}
|
||||
|
||||
// Other messages will be handled by MM_objc_msgSend.
|
||||
return PreambleResult::Redirect;
|
||||
MM_CreateCFTypeRval(aCx);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1255,15 +1248,19 @@ struct ObjCMessageInfo
|
|||
// arguments / return values.
|
||||
static ObjCMessageInfo gObjCMiddlemanCallMessages[] = {
|
||||
// Generic
|
||||
{ "alloc", MM_CreateCFTypeRval },
|
||||
{ "alloc", MM_Alloc },
|
||||
{ "init", MM_AutoreleaseCFTypeRval },
|
||||
{ "performSelector:withObject:", MM_PerformSelector },
|
||||
{ "release", MM_SkipInMiddleman },
|
||||
{ "respondsToSelector:", MM_CString<2> },
|
||||
|
||||
// NSAppearance
|
||||
{ "_drawInRect:context:options:",
|
||||
MM_Compose<MM_StackArgumentData<sizeof(CGRect)>, MM_CFTypeArg<2>, MM_CFTypeArg<3>> },
|
||||
|
||||
// NSAutoreleasePool
|
||||
{ "drain", MM_SkipInMiddleman },
|
||||
|
||||
// NSArray
|
||||
{ "count" },
|
||||
{ "objectAtIndex:", MM_AutoreleaseCFTypeRval },
|
||||
|
@ -2088,7 +2085,7 @@ static SystemRedirection gSystemRedirections[] = {
|
|||
{ "objc_autoreleasePoolPop" },
|
||||
{ "objc_autoreleasePoolPush", RR_ScalarRval },
|
||||
{ "objc_msgSend",
|
||||
RR_objc_msgSend, Preamble_objc_msgSend, MM_objc_msgSend, MiddlemanPreamble_objc_msgSend },
|
||||
RR_objc_msgSend, Preamble_objc_msgSend, MM_objc_msgSend },
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Cocoa and CoreFoundation library functions
|
||||
|
|
Загрузка…
Ссылка в новой задаче