зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1345474 - Add policy flags to support incognito settings r=rpl,kmag
Differential Revision: https://phabricator.services.mozilla.com/D4100 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
d9995b9edc
Коммит
ed2e4c07aa
|
@ -94,6 +94,23 @@ interface WebExtensionPolicy {
|
|||
*/
|
||||
static readonly attribute boolean isExtensionProcess;
|
||||
|
||||
/**
|
||||
* Set based on the manifest.incognito value:
|
||||
* If "spanning" or "split" will be true.
|
||||
* If "not_allowed" will be false.
|
||||
*/
|
||||
[Pure]
|
||||
attribute boolean privateBrowsingAllowed;
|
||||
|
||||
/**
|
||||
* Returns true if the extension can access a window. Access is
|
||||
* determined by matching the windows private browsing context
|
||||
* with privateBrowsingMode. This does not, and is not meant to
|
||||
* handle specific differences between spanning and split mode.
|
||||
*/
|
||||
[Affects=Nothing]
|
||||
boolean canAccessWindow(WindowProxy window);
|
||||
|
||||
/**
|
||||
* Returns true if the extension has cross-origin access to the given URI.
|
||||
*/
|
||||
|
@ -193,4 +210,6 @@ dictionary WebExtensionInit {
|
|||
DOMString? contentSecurityPolicy = null;
|
||||
|
||||
sequence<DOMString>? backgroundScripts = null;
|
||||
|
||||
boolean privateBrowsingAllowed = true;
|
||||
};
|
||||
|
|
|
@ -636,6 +636,7 @@ class ExtensionData {
|
|||
schemaURLs: null,
|
||||
type: this.type,
|
||||
webAccessibleResources,
|
||||
privateBrowsingAllowed: manifest.incognito !== "not_allowed",
|
||||
};
|
||||
|
||||
if (this.type === "extension") {
|
||||
|
@ -1599,6 +1600,14 @@ class Extension extends ExtensionData {
|
|||
return this.manifest.optional_permissions;
|
||||
}
|
||||
|
||||
get privateBrowsingAllowed() {
|
||||
return this.policy.privateBrowsingAllowed;
|
||||
}
|
||||
|
||||
canAccessWindow(window) {
|
||||
return this.policy.canAccessWindow(window);
|
||||
}
|
||||
|
||||
// Representation of the extension to send to content
|
||||
// processes. This should include anything the content process might
|
||||
// need.
|
||||
|
@ -1615,6 +1624,7 @@ class Extension extends ExtensionData {
|
|||
whiteListedHosts: this.whiteListedHosts.patterns.map(pat => pat.pattern),
|
||||
permissions: this.permissions,
|
||||
optionalPermissions: this.optionalPermissions,
|
||||
privateBrowsingAllowed: this.privateBrowsingAllowed,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1850,6 +1860,12 @@ class Extension extends ExtensionData {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.addonData && this.addonData.incognitoOverride !== undefined) {
|
||||
this.policy.privateBrowsingAllowed = this.addonData.incognitoOverride !== "not_allowed";
|
||||
} else {
|
||||
this.policy.privateBrowsingAllowed = this.manifest.incognito !== "not_allowed";
|
||||
}
|
||||
|
||||
GlobalManager.init(this);
|
||||
|
||||
this.initSharedData();
|
||||
|
|
|
@ -712,6 +712,14 @@ class BrowserExtensionContent extends EventEmitter {
|
|||
return this._manifest;
|
||||
}
|
||||
|
||||
get privateBrowsingAllowed() {
|
||||
return this.policy.privateBrowsingAllowed;
|
||||
}
|
||||
|
||||
canAccessWindow(window) {
|
||||
return this.policy.canAccessWindow(window);
|
||||
}
|
||||
|
||||
getAPIManager() {
|
||||
let apiManagers = [ExtensionPageChild.apiManager];
|
||||
|
||||
|
|
|
@ -445,6 +445,14 @@ class BaseContext {
|
|||
return this.cloneScopePromise || this.cloneScope.Promise;
|
||||
}
|
||||
|
||||
get privateBrowsingAllowed() {
|
||||
return this.extension.privateBrowsingAllowed;
|
||||
}
|
||||
|
||||
canAccessWindow(window) {
|
||||
return this.extension.canAccessWindow(window);
|
||||
}
|
||||
|
||||
setContentWindow(contentWindow) {
|
||||
this.innerWindowID = getInnerWindowID(contentWindow);
|
||||
this.messageManager = contentWindow.docShell.messageManager;
|
||||
|
|
|
@ -368,6 +368,7 @@ var ExtensionTestCommon = class ExtensionTestCommon {
|
|||
resourceURI: jarURI,
|
||||
cleanupFile: file,
|
||||
signedState,
|
||||
incognitoOverride: data.incognitoOverride,
|
||||
temporarilyInstalled: !!data.temporarilyInstalled,
|
||||
TEST_NO_ADDON_MANAGER: true,
|
||||
});
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
class nsILoadInfo;
|
||||
|
@ -67,6 +68,18 @@ class MOZ_STACK_CLASS DocInfo final {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsILoadContext> GetLoadContext() const {
|
||||
nsCOMPtr<nsILoadContext> loadContext;
|
||||
if (nsPIDOMWindowOuter* window = GetWindow()) {
|
||||
nsIDocShell* docShell = window->GetDocShell();
|
||||
loadContext = do_QueryInterface(docShell);
|
||||
} else if (nsILoadInfo* loadInfo = GetLoadInfo()) {
|
||||
nsCOMPtr<nsISupports> requestingContext = loadInfo->GetLoadingContext();
|
||||
loadContext = do_QueryInterface(requestingContext);
|
||||
}
|
||||
return loadContext.forget();
|
||||
}
|
||||
|
||||
private:
|
||||
void SetURL(const URLInfo& aURL);
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "mozilla/AddonManagerWebAPI.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsISubstitutingProtocolHandler.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
@ -133,7 +132,8 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
|
|||
mName(aInit.mName),
|
||||
mContentSecurityPolicy(aInit.mContentSecurityPolicy),
|
||||
mLocalizeCallback(aInit.mLocalizeCallback),
|
||||
mPermissions(new AtomSet(aInit.mPermissions)) {
|
||||
mPermissions(new AtomSet(aInit.mPermissions)),
|
||||
mPrivateBrowsingAllowed(aInit.mPrivateBrowsingAllowed) {
|
||||
if (!ParseGlobs(aGlobal, aInit.mWebAccessibleResources, mWebAccessiblePaths,
|
||||
aRv)) {
|
||||
return;
|
||||
|
@ -440,6 +440,21 @@ void WebExtensionPolicy::GetContentScripts(
|
|||
aScripts.AppendElements(mContentScripts);
|
||||
}
|
||||
|
||||
bool WebExtensionPolicy::CanAccessContext(nsILoadContext* aContext) const {
|
||||
MOZ_ASSERT(aContext);
|
||||
return mPrivateBrowsingAllowed || !aContext->UsePrivateBrowsing();
|
||||
}
|
||||
|
||||
bool WebExtensionPolicy::CanAccessWindow(nsPIDOMWindowOuter* aWindow) const {
|
||||
if (mPrivateBrowsingAllowed) {
|
||||
return true;
|
||||
}
|
||||
// match browsing mode with policy
|
||||
nsIDocShell* docShell = aWindow->GetDocShell();
|
||||
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
|
||||
return !(loadContext && loadContext->UsePrivateBrowsing());
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionPolicy, mParent,
|
||||
mLocalizeCallback, mHostPermissions,
|
||||
mWebAccessiblePaths, mContentScripts)
|
||||
|
@ -545,6 +560,12 @@ bool MozDocumentMatcher::Matches(const DocInfo& aDoc) const {
|
|||
}
|
||||
}
|
||||
|
||||
// match browsing mode with policy
|
||||
nsCOMPtr<nsILoadContext> loadContext = aDoc.GetLoadContext();
|
||||
if (loadContext && mExtension && !mExtension->CanAccessContext(loadContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mMatchAboutBlank && aDoc.URL().InheritsPrincipal()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -121,6 +121,15 @@ class WebExtensionPolicy final : public nsISupports,
|
|||
bool Active() const { return mActive; }
|
||||
void SetActive(bool aActive, ErrorResult& aRv);
|
||||
|
||||
bool PrivateBrowsingAllowed() const { return mPrivateBrowsingAllowed; }
|
||||
void SetPrivateBrowsingAllowed(bool aPrivateBrowsingAllowed) {
|
||||
mPrivateBrowsingAllowed = aPrivateBrowsingAllowed;
|
||||
};
|
||||
|
||||
bool CanAccessContext(nsILoadContext* aContext) const;
|
||||
|
||||
bool CanAccessWindow(nsPIDOMWindowOuter* aWindow) const;
|
||||
|
||||
static void GetActiveExtensions(
|
||||
dom::GlobalObject& aGlobal,
|
||||
nsTArray<RefPtr<WebExtensionPolicy>>& aResults);
|
||||
|
@ -173,6 +182,8 @@ class WebExtensionPolicy final : public nsISupports,
|
|||
RefPtr<MatchPatternSet> mHostPermissions;
|
||||
MatchGlobSet mWebAccessiblePaths;
|
||||
|
||||
bool mPrivateBrowsingAllowed = false;
|
||||
|
||||
dom::Nullable<nsTArray<nsString>> mBackgroundScripts;
|
||||
|
||||
nsTArray<RefPtr<WebExtensionContentScript>> mContentScripts;
|
||||
|
|
|
@ -145,6 +145,8 @@ ExtensionManager = {
|
|||
allowedOrigins: extension.whiteListedHosts,
|
||||
webAccessibleResources: extension.webAccessibleResources,
|
||||
|
||||
privateBrowsingAllowed: extension.privateBrowsingAllowed,
|
||||
|
||||
contentSecurityPolicy: extension.contentSecurityPolicy,
|
||||
|
||||
localizeCallback,
|
||||
|
|
|
@ -9,11 +9,11 @@ this.extension = class extends ExtensionAPI {
|
|||
},
|
||||
|
||||
isAllowedIncognitoAccess() {
|
||||
return Promise.resolve(true);
|
||||
return context.privateBrowsingAllowed;
|
||||
},
|
||||
|
||||
isAllowedFileSchemeAccess() {
|
||||
return Promise.resolve(false);
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -102,8 +102,8 @@
|
|||
"incognito": {
|
||||
"type": "string",
|
||||
"enum": ["spanning"],
|
||||
"optional": true,
|
||||
"onError": "warn"
|
||||
"default": "spanning",
|
||||
"optional": true
|
||||
},
|
||||
|
||||
"background": {
|
||||
|
|
|
@ -12,7 +12,6 @@ add_task(async function test_is_allowed_incognito_access() {
|
|||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
|
@ -20,6 +19,24 @@ add_task(async function test_is_allowed_incognito_access() {
|
|||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function test_is_denied_incognito_access() {
|
||||
async function background() {
|
||||
let allowed = await browser.extension.isAllowedIncognitoAccess();
|
||||
|
||||
browser.test.assertEq(false, allowed, "isAllowedIncognitoAccess is false");
|
||||
browser.test.notifyPass("isNotAllowedIncognitoAccess");
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
incognitoOverride: "not_allowed",
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitFinish("isNotAllowedIncognitoAccess");
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function test_in_incognito_context_false() {
|
||||
function background() {
|
||||
browser.test.assertEq(false, browser.extension.inIncognitoContext, "inIncognitoContext returned false");
|
||||
|
@ -28,7 +45,6 @@ add_task(async function test_in_incognito_context_false() {
|
|||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
|
@ -46,7 +62,6 @@ add_task(async function test_is_allowed_file_scheme_access() {
|
|||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_extension_incognito_spanning() {
|
||||
let wrapper = ExtensionTestUtils.loadExtension({});
|
||||
await wrapper.startup();
|
||||
|
||||
let extension = wrapper.extension;
|
||||
let data = extension.serialize();
|
||||
equal(data.privateBrowsingAllowed, true, "Should have privateBrowsingAllowed in serialized extension");
|
||||
equal(extension.privateBrowsingAllowed, true, "Should have privateBrowsingAllowed");
|
||||
equal(extension.policy.privateBrowsingAllowed, true, "Should have privateBrowsingAllowed on policy");
|
||||
await wrapper.unload();
|
||||
});
|
||||
|
||||
// This tests setting an extension to private-only mode which is useful for testing.
|
||||
add_task(async function test_extension_incognito_test_mode() {
|
||||
let wrapper = ExtensionTestUtils.loadExtension({
|
||||
incognitoOverride: "not_allowed",
|
||||
});
|
||||
await wrapper.startup();
|
||||
|
||||
let extension = wrapper.extension;
|
||||
let data = extension.serialize();
|
||||
equal(data.privateBrowsingAllowed, false, "Should not have privateBrowsingAllowed in serialized extension");
|
||||
equal(extension.privateBrowsingAllowed, false, "Should not have privateBrowsingAllowed");
|
||||
equal(extension.policy.privateBrowsingAllowed, false, "Should not have privateBrowsingAllowed on policy");
|
||||
await wrapper.unload();
|
||||
});
|
|
@ -14,14 +14,25 @@ add_task(async function test_manifest_incognito() {
|
|||
"spanning",
|
||||
"Should have the expected incognito string");
|
||||
|
||||
normalized = await ExtensionTestUtils.normalizeManifest({
|
||||
"incognito": "not_allowed",
|
||||
});
|
||||
|
||||
equal(normalized.error,
|
||||
'Error processing incognito: Invalid enumeration value "not_allowed"',
|
||||
"Should have an error");
|
||||
Assert.deepEqual(normalized.errors, [], "Should not have a warning");
|
||||
equal(normalized.value, undefined,
|
||||
"Invalid incognito string should be undefined");
|
||||
|
||||
normalized = await ExtensionTestUtils.normalizeManifest({
|
||||
"incognito": "split",
|
||||
});
|
||||
|
||||
equal(normalized.error, undefined, "Should not have an error");
|
||||
Assert.deepEqual(normalized.errors,
|
||||
['Error processing incognito: Invalid enumeration value "split"'],
|
||||
"Should have the expected warning");
|
||||
equal(normalized.value.incognito, null,
|
||||
"Invalid incognito string should be omitted");
|
||||
equal(normalized.error,
|
||||
'Error processing incognito: Invalid enumeration value "split"',
|
||||
"Should have an error");
|
||||
Assert.deepEqual(normalized.errors, [], "Should not have a warning");
|
||||
equal(normalized.value, undefined,
|
||||
"Invalid incognito string should be undefined");
|
||||
});
|
||||
|
|
|
@ -60,6 +60,7 @@ skip-if = os == "android" # checking for telemetry needs to be updated: 1384923
|
|||
[test_ext_extension_startup_telemetry.js]
|
||||
[test_ext_geturl.js]
|
||||
[test_ext_idle.js]
|
||||
[test_ext_incognito.js]
|
||||
[test_ext_localStorage.js]
|
||||
[test_ext_management.js]
|
||||
skip-if = (os == "win" && !debug) #Bug 1419183 disable on Windows
|
||||
|
|
Загрузка…
Ссылка в новой задаче