forked from mirrors/gecko-dev
Given meta/ctrl key should use browser.tabs.loadInBackground pref instead of browser.tabs.loadDivertedInBackground pref, the existing nsIBrowserDOMWindow.OPEN_NEWTAB behavior doesn't match the requirement. Add dedicate flag for "open in foreground tab" to make it controllable from nsWindowWatcher::IsWindowOpenLocationModified. Differential Revision: https://phabricator.services.mozilla.com/D201929
662 lines
20 KiB
JavaScript
662 lines
20 KiB
JavaScript
/* 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/. */
|
|
|
|
import { GeckoViewModule } from "resource://gre/modules/GeckoViewModule.sys.mjs";
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
GeckoViewUtils: "resource://gre/modules/GeckoViewUtils.sys.mjs",
|
|
E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
|
|
LoadURIDelegate: "resource://gre/modules/LoadURIDelegate.sys.mjs",
|
|
isProductURL: "chrome://global/content/shopping/ShoppingProduct.mjs",
|
|
TranslationsParent: "resource://gre/actors/TranslationsParent.sys.mjs",
|
|
});
|
|
|
|
ChromeUtils.defineLazyGetter(lazy, "ReferrerInfo", () =>
|
|
Components.Constructor(
|
|
"@mozilla.org/referrer-info;1",
|
|
"nsIReferrerInfo",
|
|
"init"
|
|
)
|
|
);
|
|
|
|
// Filter out request headers as per discussion in Bug #1567549
|
|
// CONNECTION: Used by Gecko to manage connections
|
|
// HOST: Relates to how gecko will ultimately interpret the resulting resource as that
|
|
// determines the effective request URI
|
|
const BAD_HEADERS = ["connection", "host"];
|
|
|
|
// Headers use |\r\n| as separator so these characters cannot appear
|
|
// in the header name or value
|
|
const FORBIDDEN_HEADER_CHARACTERS = ["\n", "\r"];
|
|
|
|
// Keep in sync with GeckoSession.java
|
|
const HEADER_FILTER_CORS_SAFELISTED = 1;
|
|
// eslint-disable-next-line no-unused-vars
|
|
const HEADER_FILTER_UNRESTRICTED_UNSAFE = 2;
|
|
|
|
// Create default ReferrerInfo instance for the given referrer URI string.
|
|
const createReferrerInfo = aReferrer => {
|
|
let referrerUri;
|
|
try {
|
|
referrerUri = Services.io.newURI(aReferrer);
|
|
} catch (ignored) {}
|
|
|
|
return new lazy.ReferrerInfo(Ci.nsIReferrerInfo.EMPTY, true, referrerUri);
|
|
};
|
|
|
|
function convertFlags(aFlags) {
|
|
let navFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
|
if (!aFlags) {
|
|
return navFlags;
|
|
}
|
|
// These need to match the values in GeckoSession.LOAD_FLAGS_*
|
|
if (aFlags & (1 << 0)) {
|
|
navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
|
|
}
|
|
if (aFlags & (1 << 1)) {
|
|
navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY;
|
|
}
|
|
if (aFlags & (1 << 2)) {
|
|
navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
|
|
}
|
|
if (aFlags & (1 << 3)) {
|
|
navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_POPUPS;
|
|
}
|
|
if (aFlags & (1 << 4)) {
|
|
navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER;
|
|
}
|
|
if (aFlags & (1 << 5)) {
|
|
navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
|
|
}
|
|
if (aFlags & (1 << 6)) {
|
|
navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
|
|
}
|
|
if (aFlags & (1 << 7)) {
|
|
navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE;
|
|
}
|
|
return navFlags;
|
|
}
|
|
|
|
// Handles navigation requests between Gecko and a GeckoView.
|
|
// Handles GeckoView:GoBack and :GoForward requests dispatched by
|
|
// GeckoView.goBack and .goForward.
|
|
// Dispatches GeckoView:LocationChange to the GeckoView on location change when
|
|
// active.
|
|
// Implements nsIBrowserDOMWindow.
|
|
export class GeckoViewNavigation extends GeckoViewModule {
|
|
onInitBrowser() {
|
|
this.window.browserDOMWindow = this;
|
|
|
|
debug`sessionContextId=${this.settings.sessionContextId}`;
|
|
|
|
if (this.settings.sessionContextId !== null) {
|
|
// Gecko may have issues with strings containing special characters,
|
|
// so we restrict the string format to a specific pattern.
|
|
if (!/^gvctx(-)?([a-f0-9]+)$/.test(this.settings.sessionContextId)) {
|
|
throw new Error("sessionContextId has illegal format");
|
|
}
|
|
|
|
this.browser.setAttribute(
|
|
"geckoViewSessionContextId",
|
|
this.settings.sessionContextId
|
|
);
|
|
}
|
|
|
|
// There may be a GeckoViewNavigation module in another window waiting for
|
|
// us to create a browser so it can set openWindowInfo, so allow them to do
|
|
// that now.
|
|
Services.obs.notifyObservers(this.window, "geckoview-window-created");
|
|
}
|
|
|
|
onInit() {
|
|
debug`onInit`;
|
|
|
|
this.registerListener([
|
|
"GeckoView:GoBack",
|
|
"GeckoView:GoForward",
|
|
"GeckoView:GotoHistoryIndex",
|
|
"GeckoView:LoadUri",
|
|
"GeckoView:Reload",
|
|
"GeckoView:Stop",
|
|
"GeckoView:PurgeHistory",
|
|
"GeckoView:DotPrintFinish",
|
|
]);
|
|
|
|
this._initialAboutBlank = true;
|
|
}
|
|
|
|
validateHeader(key, value, filter) {
|
|
if (!key) {
|
|
// Key cannot be empty
|
|
return false;
|
|
}
|
|
|
|
for (const c of FORBIDDEN_HEADER_CHARACTERS) {
|
|
if (key.includes(c) || value?.includes(c)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (BAD_HEADERS.includes(key.toLowerCase().trim())) {
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
filter == HEADER_FILTER_CORS_SAFELISTED &&
|
|
!this.window.windowUtils.isCORSSafelistedRequestHeader(key, value)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Bundle event handler.
|
|
async onEvent(aEvent, aData, aCallback) {
|
|
debug`onEvent: event=${aEvent}, data=${aData}`;
|
|
|
|
switch (aEvent) {
|
|
case "GeckoView:GoBack":
|
|
this.browser.goBack(aData.userInteraction);
|
|
break;
|
|
case "GeckoView:GoForward":
|
|
this.browser.goForward(aData.userInteraction);
|
|
break;
|
|
case "GeckoView:GotoHistoryIndex":
|
|
this.browser.gotoIndex(aData.index);
|
|
break;
|
|
case "GeckoView:LoadUri":
|
|
const {
|
|
uri,
|
|
referrerUri,
|
|
referrerSessionId,
|
|
flags,
|
|
headers,
|
|
headerFilter,
|
|
} = aData;
|
|
|
|
let navFlags = convertFlags(flags);
|
|
// For performance reasons we don't call the LoadUriDelegate.loadUri
|
|
// from Gecko, and instead we call it directly in the loadUri Java API.
|
|
navFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE;
|
|
|
|
let triggeringPrincipal, referrerInfo, csp;
|
|
if (referrerSessionId) {
|
|
const referrerWindow = Services.ww.getWindowByName(referrerSessionId);
|
|
triggeringPrincipal = referrerWindow.browser.contentPrincipal;
|
|
csp = referrerWindow.browser.csp;
|
|
|
|
const referrerPolicy = referrerWindow.browser.referrerInfo
|
|
? referrerWindow.browser.referrerInfo.referrerPolicy
|
|
: Ci.nsIReferrerInfo.EMPTY;
|
|
|
|
referrerInfo = new lazy.ReferrerInfo(
|
|
referrerPolicy,
|
|
true,
|
|
referrerWindow.browser.documentURI
|
|
);
|
|
} else if (referrerUri) {
|
|
referrerInfo = createReferrerInfo(referrerUri);
|
|
} else {
|
|
// External apps are treated like web pages, so they should not get
|
|
// a privileged principal.
|
|
const isExternal =
|
|
navFlags & Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
|
|
if (!isExternal || Services.io.newURI(uri).schemeIs("content")) {
|
|
// Always use the system principal as the triggering principal
|
|
// for user-initiated (ie. no referrer session and not external)
|
|
// loads. See discussion in bug 1573860.
|
|
triggeringPrincipal =
|
|
Services.scriptSecurityManager.getSystemPrincipal();
|
|
}
|
|
}
|
|
|
|
if (!triggeringPrincipal) {
|
|
triggeringPrincipal =
|
|
Services.scriptSecurityManager.createNullPrincipal({});
|
|
}
|
|
|
|
let additionalHeaders = null;
|
|
if (headers) {
|
|
additionalHeaders = "";
|
|
for (const [key, value] of Object.entries(headers)) {
|
|
if (!this.validateHeader(key, value, headerFilter)) {
|
|
console.error(`Ignoring invalid header '${key}'='${value}'.`);
|
|
continue;
|
|
}
|
|
|
|
additionalHeaders += `${key}:${value ?? ""}\r\n`;
|
|
}
|
|
|
|
if (additionalHeaders != "") {
|
|
additionalHeaders =
|
|
lazy.E10SUtils.makeInputStream(additionalHeaders);
|
|
} else {
|
|
additionalHeaders = null;
|
|
}
|
|
}
|
|
|
|
// For any navigation here, we should have an appropriate triggeringPrincipal:
|
|
//
|
|
// 1) If we have a referring session, triggeringPrincipal is the contentPrincipal from the
|
|
// referring document.
|
|
// 2) For certain URI schemes listed above, we will have a codebase principal.
|
|
// 3) In all other cases, we create a NullPrincipal.
|
|
//
|
|
// The navigation flags are driven by the app. We purposely do not propagate these from
|
|
// the referring document, but expect that the app will in most cases.
|
|
//
|
|
// The referrerInfo is derived from the referring document, if present, by propagating any
|
|
// referrer policy. If we only have the referrerUri from the app, we create a referrerInfo
|
|
// with the specified URI and no policy set. If no referrerUri is present and we have no
|
|
// referring session, the referrerInfo is null.
|
|
//
|
|
// csp is only present if we have a referring document, null otherwise.
|
|
this.browser.fixupAndLoadURIString(uri, {
|
|
flags: navFlags,
|
|
referrerInfo,
|
|
triggeringPrincipal,
|
|
headers: additionalHeaders,
|
|
csp,
|
|
});
|
|
break;
|
|
case "GeckoView:Reload":
|
|
// At the moment, GeckoView only supports one reload, which uses
|
|
// nsIWebNavigation.LOAD_FLAGS_NONE flag, and the telemetry doesn't
|
|
// do anything to differentiate reloads (i.e normal vs skip caches)
|
|
// So whenever we add more reload methods, please make sure the
|
|
// telemetry probe is adjusted
|
|
this.browser.reloadWithFlags(convertFlags(aData.flags));
|
|
break;
|
|
case "GeckoView:Stop":
|
|
this.browser.stop();
|
|
break;
|
|
case "GeckoView:PurgeHistory":
|
|
this.browser.purgeSessionHistory();
|
|
break;
|
|
case "GeckoView:DotPrintFinish":
|
|
var printActor = this.moduleManager.getActor("GeckoViewPrintDelegate");
|
|
printActor.clearStaticClone();
|
|
break;
|
|
}
|
|
}
|
|
|
|
waitAndSetupWindow(aSessionId, aOpenWindowInfo, aName) {
|
|
if (!aSessionId) {
|
|
return Promise.resolve(null);
|
|
}
|
|
|
|
return new Promise(resolve => {
|
|
const handler = {
|
|
observe(aSubject, aTopic, aData) {
|
|
if (
|
|
aTopic === "geckoview-window-created" &&
|
|
aSubject.name === aSessionId
|
|
) {
|
|
// This value will be read by nsFrameLoader while it is being initialized.
|
|
aSubject.browser.openWindowInfo = aOpenWindowInfo;
|
|
|
|
// Gecko will use this attribute to set the name of the opened window.
|
|
if (aName) {
|
|
aSubject.browser.setAttribute("name", aName);
|
|
}
|
|
|
|
if (
|
|
!aOpenWindowInfo.isRemote &&
|
|
aSubject.browser.hasAttribute("remote")
|
|
) {
|
|
// We cannot start in remote mode when we have an opener.
|
|
aSubject.browser.setAttribute("remote", "false");
|
|
aSubject.browser.removeAttribute("remoteType");
|
|
}
|
|
Services.obs.removeObserver(handler, "geckoview-window-created");
|
|
resolve(aSubject);
|
|
}
|
|
},
|
|
};
|
|
|
|
// This event is emitted from createBrowser() in geckoview.js
|
|
Services.obs.addObserver(handler, "geckoview-window-created");
|
|
});
|
|
}
|
|
|
|
handleNewSession(aUri, aOpenWindowInfo, aWhere, aFlags, aName) {
|
|
debug`handleNewSession: uri=${aUri && aUri.spec}
|
|
where=${aWhere} flags=${aFlags}`;
|
|
|
|
if (!this.enabled) {
|
|
return null;
|
|
}
|
|
|
|
const newSessionId = Services.uuid
|
|
.generateUUID()
|
|
.toString()
|
|
.slice(1, -1)
|
|
.replace(/-/g, "");
|
|
|
|
const message = {
|
|
type: "GeckoView:OnNewSession",
|
|
uri: aUri ? aUri.displaySpec : "",
|
|
newSessionId,
|
|
};
|
|
|
|
// The window might be already open by the time we get the response from
|
|
// the Java layer, so we need to start waiting before sending the message.
|
|
const setupPromise = this.waitAndSetupWindow(
|
|
newSessionId,
|
|
aOpenWindowInfo,
|
|
aName
|
|
);
|
|
|
|
let browser = undefined;
|
|
this.eventDispatcher
|
|
.sendRequestForResult(message)
|
|
.then(didOpenSession => {
|
|
if (!didOpenSession) {
|
|
return Promise.reject();
|
|
}
|
|
return setupPromise;
|
|
})
|
|
.then(
|
|
window => {
|
|
browser = window.browser;
|
|
},
|
|
() => {
|
|
browser = null;
|
|
}
|
|
);
|
|
|
|
// Wait indefinitely for app to respond with a browser or null
|
|
Services.tm.spinEventLoopUntil(
|
|
"GeckoViewNavigation.jsm:handleNewSession",
|
|
() => this.window.closed || browser !== undefined
|
|
);
|
|
return browser || null;
|
|
}
|
|
|
|
// nsIBrowserDOMWindow.
|
|
createContentWindow(
|
|
aUri,
|
|
aOpenWindowInfo,
|
|
aWhere,
|
|
aFlags,
|
|
aTriggeringPrincipal,
|
|
aCsp
|
|
) {
|
|
debug`createContentWindow: uri=${aUri && aUri.spec}
|
|
where=${aWhere} flags=${aFlags}`;
|
|
|
|
if (
|
|
lazy.LoadURIDelegate.load(
|
|
this.window,
|
|
this.eventDispatcher,
|
|
aUri,
|
|
aWhere,
|
|
aFlags,
|
|
aTriggeringPrincipal
|
|
)
|
|
) {
|
|
// The app has handled the load, abort open-window handling.
|
|
Components.returnCode = Cr.NS_ERROR_ABORT;
|
|
return null;
|
|
}
|
|
|
|
const browser = this.handleNewSession(
|
|
aUri,
|
|
aOpenWindowInfo,
|
|
aWhere,
|
|
aFlags,
|
|
null
|
|
);
|
|
if (!browser) {
|
|
Components.returnCode = Cr.NS_ERROR_ABORT;
|
|
return null;
|
|
}
|
|
|
|
return browser.browsingContext;
|
|
}
|
|
|
|
// nsIBrowserDOMWindow.
|
|
createContentWindowInFrame(aUri, aParams, aWhere, aFlags, aName) {
|
|
debug`createContentWindowInFrame: uri=${aUri && aUri.spec}
|
|
where=${aWhere} flags=${aFlags}
|
|
name=${aName}`;
|
|
|
|
if (aWhere === Ci.nsIBrowserDOMWindow.OPEN_PRINT_BROWSER) {
|
|
return this.window.moduleManager.onPrintWindow(aParams);
|
|
}
|
|
|
|
if (
|
|
lazy.LoadURIDelegate.load(
|
|
this.window,
|
|
this.eventDispatcher,
|
|
aUri,
|
|
aWhere,
|
|
aFlags,
|
|
aParams.triggeringPrincipal
|
|
)
|
|
) {
|
|
// The app has handled the load, abort open-window handling.
|
|
Components.returnCode = Cr.NS_ERROR_ABORT;
|
|
return null;
|
|
}
|
|
|
|
const browser = this.handleNewSession(
|
|
aUri,
|
|
aParams.openWindowInfo,
|
|
aWhere,
|
|
aFlags,
|
|
aName
|
|
);
|
|
if (!browser) {
|
|
Components.returnCode = Cr.NS_ERROR_ABORT;
|
|
return null;
|
|
}
|
|
|
|
return browser;
|
|
}
|
|
|
|
handleOpenUri({
|
|
uri,
|
|
openWindowInfo,
|
|
where,
|
|
flags,
|
|
triggeringPrincipal,
|
|
csp,
|
|
referrerInfo = null,
|
|
name = null,
|
|
}) {
|
|
debug`handleOpenUri: uri=${uri && uri.spec}
|
|
where=${where} flags=${flags}`;
|
|
|
|
if (
|
|
lazy.LoadURIDelegate.load(
|
|
this.window,
|
|
this.eventDispatcher,
|
|
uri,
|
|
where,
|
|
flags,
|
|
triggeringPrincipal
|
|
)
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
let browser = this.browser;
|
|
|
|
if (
|
|
where === Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW ||
|
|
where === Ci.nsIBrowserDOMWindow.OPEN_NEWTAB ||
|
|
where === Ci.nsIBrowserDOMWindow.OPEN_NEWTAB_BACKGROUND ||
|
|
where === Ci.nsIBrowserDOMWindow.OPEN_NEWTAB_FOREGROUND
|
|
) {
|
|
browser = this.handleNewSession(uri, openWindowInfo, where, flags, name);
|
|
}
|
|
|
|
if (!browser) {
|
|
// Should we throw?
|
|
return null;
|
|
}
|
|
|
|
// 3) We have a new session and a browser element, load the requested URI.
|
|
browser.loadURI(uri, {
|
|
triggeringPrincipal,
|
|
csp,
|
|
referrerInfo,
|
|
});
|
|
return browser;
|
|
}
|
|
|
|
// nsIBrowserDOMWindow.
|
|
openURI(aUri, aOpenWindowInfo, aWhere, aFlags, aTriggeringPrincipal, aCsp) {
|
|
const browser = this.handleOpenUri({
|
|
uri: aUri,
|
|
openWindowInfo: aOpenWindowInfo,
|
|
where: aWhere,
|
|
flags: aFlags,
|
|
triggeringPrincipal: aTriggeringPrincipal,
|
|
csp: aCsp,
|
|
});
|
|
return browser && browser.browsingContext;
|
|
}
|
|
|
|
// nsIBrowserDOMWindow.
|
|
openURIInFrame(aUri, aParams, aWhere, aFlags, aName) {
|
|
const browser = this.handleOpenUri({
|
|
uri: aUri,
|
|
openWindowInfo: aParams.openWindowInfo,
|
|
where: aWhere,
|
|
flags: aFlags,
|
|
triggeringPrincipal: aParams.triggeringPrincipal,
|
|
csp: aParams.csp,
|
|
referrerInfo: aParams.referrerInfo,
|
|
name: aName,
|
|
});
|
|
return browser;
|
|
}
|
|
|
|
// nsIBrowserDOMWindow.
|
|
canClose() {
|
|
debug`canClose`;
|
|
return true;
|
|
}
|
|
|
|
onEnable() {
|
|
debug`onEnable`;
|
|
|
|
const flags = Ci.nsIWebProgress.NOTIFY_LOCATION;
|
|
this.progressFilter = Cc[
|
|
"@mozilla.org/appshell/component/browser-status-filter;1"
|
|
].createInstance(Ci.nsIWebProgress);
|
|
this.progressFilter.addProgressListener(this, flags);
|
|
this.browser.addProgressListener(this.progressFilter, flags);
|
|
}
|
|
|
|
onDisable() {
|
|
debug`onDisable`;
|
|
|
|
if (!this.progressFilter) {
|
|
return;
|
|
}
|
|
this.progressFilter.removeProgressListener(this);
|
|
this.browser.removeProgressListener(this.progressFilter);
|
|
}
|
|
|
|
serializePermission({ type, capability, principal }) {
|
|
const { URI, originAttributes, privateBrowsingId } = principal;
|
|
return {
|
|
uri: Services.io.createExposableURI(URI).displaySpec,
|
|
principal: lazy.E10SUtils.serializePrincipal(principal),
|
|
perm: type,
|
|
value: capability,
|
|
contextId: originAttributes.geckoViewSessionContextId,
|
|
privateMode: privateBrowsingId != 0,
|
|
};
|
|
}
|
|
|
|
async isProductURL(aLocationURI) {
|
|
if (lazy.isProductURL(aLocationURI)) {
|
|
this.eventDispatcher.sendRequest({
|
|
type: "GeckoView:OnProductUrl",
|
|
});
|
|
}
|
|
}
|
|
|
|
// WebProgress event handler.
|
|
onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
|
|
debug`onLocationChange`;
|
|
|
|
let fixedURI = aLocationURI;
|
|
|
|
try {
|
|
fixedURI = Services.io.createExposableURI(aLocationURI);
|
|
} catch (ex) {}
|
|
|
|
// We manually fire the initial about:blank messages to make sure that we
|
|
// consistently send them so there's nothing to do here.
|
|
const ignore = this._initialAboutBlank && fixedURI.spec === "about:blank";
|
|
this._initialAboutBlank = false;
|
|
|
|
if (ignore) {
|
|
return;
|
|
}
|
|
|
|
const { contentPrincipal } = this.browser;
|
|
let permissions;
|
|
if (
|
|
contentPrincipal &&
|
|
lazy.GeckoViewUtils.isSupportedPermissionsPrincipal(contentPrincipal)
|
|
) {
|
|
let rawPerms = [];
|
|
try {
|
|
rawPerms = Services.perms.getAllForPrincipal(contentPrincipal);
|
|
} catch (ex) {
|
|
warn`Could not get permissions for principal. ${ex}`;
|
|
}
|
|
permissions = rawPerms.map(this.serializePermission);
|
|
|
|
// The only way for apps to set permissions is to get hold of an existing
|
|
// permission and change its value.
|
|
// Tracking protection exception permissions are only present when
|
|
// explicitly added by the app, so if one is not present, we need to send
|
|
// a DENY_ACTION tracking protection permission so that apps can use it
|
|
// to add tracking protection exceptions.
|
|
const trackingProtectionPermission =
|
|
contentPrincipal.privateBrowsingId == 0
|
|
? "trackingprotection"
|
|
: "trackingprotection-pb";
|
|
if (
|
|
contentPrincipal.isContentPrincipal &&
|
|
rawPerms.findIndex(p => p.type == trackingProtectionPermission) == -1
|
|
) {
|
|
permissions.push(
|
|
this.serializePermission({
|
|
type: trackingProtectionPermission,
|
|
capability: Ci.nsIPermissionManager.DENY_ACTION,
|
|
principal: contentPrincipal,
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
const message = {
|
|
type: "GeckoView:LocationChange",
|
|
uri: fixedURI.displaySpec,
|
|
canGoBack: this.browser.canGoBack,
|
|
canGoForward: this.browser.canGoForward,
|
|
isTopLevel: aWebProgress.isTopLevel,
|
|
permissions,
|
|
hasUserGesture:
|
|
this.window.document.hasValidTransientUserGestureActivation,
|
|
};
|
|
lazy.TranslationsParent.onLocationChange(this.browser);
|
|
this.eventDispatcher.sendRequest(message);
|
|
|
|
this.isProductURL(aLocationURI);
|
|
}
|
|
}
|
|
|
|
const { debug, warn } = GeckoViewNavigation.initLogging("GeckoViewNavigation");
|