зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1470651 - Support cookieStoreId option in contentScripts.register r=rpl,robwu
Differential Revision: https://phabricator.services.mozilla.com/D124537
This commit is contained in:
Родитель
ddfe25fb93
Коммит
fca992af50
|
@ -86,6 +86,12 @@ interface MozDocumentMatcher {
|
|||
[Cached, Constant, Frozen]
|
||||
readonly attribute sequence<MatchGlob>? excludeGlobs;
|
||||
|
||||
/**
|
||||
* The originAttributesPattern for which this script should be enabled for.
|
||||
*/
|
||||
[Constant, Throws]
|
||||
readonly attribute any originAttributesPatterns;
|
||||
|
||||
/**
|
||||
* The policy object for the extension that this matcher belongs to.
|
||||
*/
|
||||
|
@ -96,6 +102,8 @@ interface MozDocumentMatcher {
|
|||
dictionary MozDocumentMatcherInit {
|
||||
boolean allFrames = false;
|
||||
|
||||
sequence<OriginAttributesPatternDictionary>? originAttributesPatterns = null;
|
||||
|
||||
boolean matchAboutBlank = false;
|
||||
|
||||
unsigned long long? frameID = null;
|
||||
|
|
|
@ -144,6 +144,10 @@ class MozDocumentMatcher : public nsISupports, public nsWrapperCache {
|
|||
|
||||
Nullable<uint64_t> GetFrameID() const { return mFrameID; }
|
||||
|
||||
void GetOriginAttributesPatterns(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aVal,
|
||||
ErrorResult& aError) const;
|
||||
|
||||
WebExtensionPolicy* GetParentObject() const { return mExtension; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::HandleObject aGivenProto) override;
|
||||
|
@ -171,6 +175,7 @@ class MozDocumentMatcher : public nsISupports, public nsWrapperCache {
|
|||
bool mAllFrames;
|
||||
Nullable<uint64_t> mFrameID;
|
||||
bool mMatchAboutBlank;
|
||||
Nullable<dom::Sequence<OriginAttributesPattern>> mOriginAttributesPatterns;
|
||||
|
||||
private:
|
||||
template <typename T, typename U>
|
||||
|
|
|
@ -667,6 +667,17 @@ MozDocumentMatcher::MozDocumentMatcher(GlobalObject& aGlobal,
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!aInit.mOriginAttributesPatterns.IsNull()) {
|
||||
Sequence<OriginAttributesPattern>& arr =
|
||||
mOriginAttributesPatterns.SetValue();
|
||||
for (const auto& pattern : aInit.mOriginAttributesPatterns.Value()) {
|
||||
if (!arr.AppendElement(OriginAttributesPattern(pattern), fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebExtensionContentScript::WebExtensionContentScript(
|
||||
|
@ -698,6 +709,21 @@ bool MozDocumentMatcher::Matches(const DocInfo& aDoc) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (loadContext && !mOriginAttributesPatterns.IsNull()) {
|
||||
OriginAttributes docShellAttrs;
|
||||
loadContext->GetOriginAttributes(docShellAttrs);
|
||||
bool patternMatch = false;
|
||||
for (const auto& pattern : mOriginAttributesPatterns.Value()) {
|
||||
if (pattern.Matches(docShellAttrs)) {
|
||||
patternMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!patternMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mMatchAboutBlank && aDoc.URL().InheritsPrincipal()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -760,6 +786,14 @@ bool MozDocumentMatcher::MatchesWindowGlobal(WindowGlobalChild& aWindow) const {
|
|||
return Matches(inner->GetOuterWindow());
|
||||
}
|
||||
|
||||
void MozDocumentMatcher::GetOriginAttributesPatterns(
|
||||
JSContext* aCx, JS::MutableHandle<JS::Value> aVal,
|
||||
ErrorResult& aError) const {
|
||||
if (!ToJSValue(aCx, mOriginAttributesPatterns, aVal)) {
|
||||
aError.NoteJSContextException(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
JSObject* MozDocumentMatcher::WrapObject(JSContext* aCx,
|
||||
JS::HandleObject aGivenProto) {
|
||||
return MozDocumentMatcher_Binding::Wrap(aCx, this, aGivenProto);
|
||||
|
|
|
@ -15,6 +15,30 @@ var { ExtensionUtils } = ChromeUtils.import(
|
|||
|
||||
var { ExtensionError, getUniqueId } = ExtensionUtils;
|
||||
|
||||
function getOriginAttributesPatternForCookieStoreId(cookieStoreId) {
|
||||
if (isDefaultCookieStoreId(cookieStoreId)) {
|
||||
return {
|
||||
userContextId: Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID,
|
||||
privateBrowsingId:
|
||||
Ci.nsIScriptSecurityManager.DEFAULT_PRIVATE_BROWSING_ID,
|
||||
};
|
||||
}
|
||||
if (isPrivateCookieStoreId(cookieStoreId)) {
|
||||
return {
|
||||
userContextId: Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID,
|
||||
privateBrowsingId: 1,
|
||||
};
|
||||
}
|
||||
if (isContainerCookieStoreId(cookieStoreId)) {
|
||||
let userContextId = getContainerForCookieStoreId(cookieStoreId);
|
||||
if (userContextId !== null) {
|
||||
return { userContextId };
|
||||
}
|
||||
}
|
||||
|
||||
throw new ExtensionError("Invalid cookieStoreId");
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents (in the main browser process) a content script registered
|
||||
* programmatically (instead of being included in the addon manifest).
|
||||
|
@ -74,8 +98,18 @@ class ContentScriptParent {
|
|||
runAt: details.runAt || "document_idle",
|
||||
jsPaths: [],
|
||||
cssPaths: [],
|
||||
originAttributesPatterns: null,
|
||||
};
|
||||
|
||||
if (details.cookieStoreId != null) {
|
||||
const cookieStoreIds = Array.isArray(details.cookieStoreId)
|
||||
? details.cookieStoreId
|
||||
: [details.cookieStoreId];
|
||||
options.originAttributesPatterns = cookieStoreIds.map(cookieStoreId =>
|
||||
getOriginAttributesPatternForCookieStoreId(cookieStoreId)
|
||||
);
|
||||
}
|
||||
|
||||
const convertCodeToURL = (data, mime) => {
|
||||
const blob = new context.cloneScope.Blob(data, { type: mime });
|
||||
const blobURL = context.cloneScope.URL.createObjectURL(blob);
|
||||
|
|
|
@ -51,6 +51,20 @@
|
|||
"$ref": "extensionTypes.RunAt",
|
||||
"optional": true,
|
||||
"description": "The soonest that the JavaScript or CSS will be injected into the tab. Defaults to \"document_idle\"."
|
||||
},
|
||||
"cookieStoreId": {
|
||||
"choices": [
|
||||
{
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"optional": true,
|
||||
"description": "limit the set of matched tabs to those that belong to the given cookie store id"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -380,6 +380,12 @@ add_task(async function test_contentscripts_register_js() {
|
|||
js: [{ code: `(${textScriptCodeIdle})()` }],
|
||||
runAt: "document_idle",
|
||||
},
|
||||
{
|
||||
matches: ["http://localhost/*/file_sample.html"],
|
||||
js: [{ code: `(${textScriptCodeIdle})()` }],
|
||||
runAt: "document_idle",
|
||||
cookieStoreId: "firefox-container-1",
|
||||
},
|
||||
// Extension URLs.
|
||||
{
|
||||
matches: ["http://localhost/*/file_sample.html"],
|
||||
|
@ -401,6 +407,12 @@ add_task(async function test_contentscripts_register_js() {
|
|||
js: [{ file: "content_script.js" }],
|
||||
// "runAt" is not specified here to ensure that it defaults to document_idle when missing.
|
||||
},
|
||||
{
|
||||
matches: ["http://localhost/*/file_sample.html"],
|
||||
js: [{ file: "content_script_idle.js" }],
|
||||
runAt: "document_idle",
|
||||
cookieStoreId: "firefox-container-1",
|
||||
},
|
||||
];
|
||||
|
||||
const expectedAPIs = ["unregister"];
|
||||
|
@ -552,7 +564,14 @@ add_task(async function test_contentscripts_register_all_options() {
|
|||
);
|
||||
|
||||
const script = policy.contentScripts[0];
|
||||
let { allFrames, cssPaths, jsPaths, matchAboutBlank, runAt } = script;
|
||||
let {
|
||||
allFrames,
|
||||
cssPaths,
|
||||
jsPaths,
|
||||
matchAboutBlank,
|
||||
runAt,
|
||||
originAttributesPatterns,
|
||||
} = script;
|
||||
|
||||
deepEqual(
|
||||
{
|
||||
|
@ -561,6 +580,7 @@ add_task(async function test_contentscripts_register_all_options() {
|
|||
jsPaths,
|
||||
matchAboutBlank,
|
||||
runAt,
|
||||
originAttributesPatterns,
|
||||
},
|
||||
{
|
||||
allFrames: true,
|
||||
|
@ -568,6 +588,7 @@ add_task(async function test_contentscripts_register_all_options() {
|
|||
jsPaths: [`${baseExtURL}/content_script.js`],
|
||||
matchAboutBlank: true,
|
||||
runAt: "document_start",
|
||||
originAttributesPatterns: null,
|
||||
},
|
||||
"Got the expected content script properties"
|
||||
);
|
||||
|
@ -589,3 +610,198 @@ add_task(async function test_contentscripts_register_all_options() {
|
|||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function test_contentscripts_register_cookieStoreId() {
|
||||
async function background() {
|
||||
let cookieStoreIdCSSArray = [
|
||||
{ id: null, color: "rgb(123, 45, 67)" },
|
||||
{ id: "firefox-private", color: "rgb(255,255,0)" },
|
||||
{ id: "firefox-default", color: "red" },
|
||||
{ id: "firefox-container-1", color: "green" },
|
||||
{ id: "firefox-container-2", color: "blue" },
|
||||
{
|
||||
id: ["firefox-container-3", "firefox-container-4"],
|
||||
color: "rgb(100,100,0)",
|
||||
},
|
||||
];
|
||||
const matches = ["http://localhost/*/file_sample_registered_styles.html"];
|
||||
|
||||
for (let { id, color } of cookieStoreIdCSSArray) {
|
||||
await browser.contentScripts.register({
|
||||
css: [
|
||||
{
|
||||
code: `#registered-extension-text-style {
|
||||
background-color: ${color}}`,
|
||||
},
|
||||
],
|
||||
matches,
|
||||
runAt: "document_start",
|
||||
cookieStoreId: id,
|
||||
});
|
||||
}
|
||||
await browser.test.assertRejects(
|
||||
browser.contentScripts.register({
|
||||
css: [{ code: `body {}` }],
|
||||
matches,
|
||||
cookieStoreId: "not_a_valid_cookieStoreId",
|
||||
}),
|
||||
/Invalid cookieStoreId/,
|
||||
"contentScript.register with an invalid cookieStoreId"
|
||||
);
|
||||
|
||||
if (!navigator.userAgent.includes("Android")) {
|
||||
await browser.test.assertRejects(
|
||||
browser.contentScripts.register({
|
||||
css: [{ code: `body {}` }],
|
||||
matches,
|
||||
cookieStoreId: "firefox-container-999",
|
||||
}),
|
||||
/Invalid cookieStoreId/,
|
||||
"contentScript.register with an invalid cookieStoreId"
|
||||
);
|
||||
} else {
|
||||
// On Android, any firefox-container-... is treated as valid, so it doesn't
|
||||
// result in an error.
|
||||
// TODO bug 1743616: Fix implementation and remove this branch.
|
||||
await browser.contentScripts.register({
|
||||
css: [{ code: `body {}` }],
|
||||
matches,
|
||||
cookieStoreId: "firefox-container-999",
|
||||
});
|
||||
}
|
||||
|
||||
await browser.test.assertRejects(
|
||||
browser.contentScripts.register({
|
||||
css: [{ code: `body {}` }],
|
||||
matches,
|
||||
cookieStoreId: "",
|
||||
}),
|
||||
/Invalid cookieStoreId/,
|
||||
"contentScript.register with an invalid cookieStoreId"
|
||||
);
|
||||
|
||||
browser.test.sendMessage("background_ready");
|
||||
}
|
||||
|
||||
const extensionData = {
|
||||
manifest: {
|
||||
permissions: [
|
||||
"http://localhost/*/file_sample_registered_styles.html",
|
||||
"<all_urls>",
|
||||
],
|
||||
content_scripts: [
|
||||
{
|
||||
matches: ["http://localhost/*/file_sample_registered_styles.html"],
|
||||
run_at: "document_idle",
|
||||
js: ["check_applied_styles.js"],
|
||||
},
|
||||
],
|
||||
},
|
||||
background,
|
||||
files: {
|
||||
"check_applied_styles.js": check_applied_styles,
|
||||
},
|
||||
};
|
||||
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
...extensionData,
|
||||
incognitoOverride: "spanning",
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitMessage("background_ready");
|
||||
// Index 0 is the one from manifest.json.
|
||||
let contentScriptMatchTests = [
|
||||
{
|
||||
contentPageOptions: { userContextId: 5 },
|
||||
expectedStyles: "rgb(123, 45, 67)",
|
||||
originAttributesPatternExpected: null,
|
||||
contentScriptIndex: 1,
|
||||
},
|
||||
{
|
||||
contentPageOptions: { privateBrowsing: true },
|
||||
expectedStyles: "rgb(255, 255, 0)",
|
||||
originAttributesPatternExpected: [
|
||||
{
|
||||
privateBrowsingId: 1,
|
||||
userContextId: 0,
|
||||
},
|
||||
],
|
||||
contentScriptIndex: 2,
|
||||
},
|
||||
{
|
||||
contentPageOptions: { userContextId: 0 },
|
||||
expectedStyles: "rgb(255, 0, 0)",
|
||||
originAttributesPatternExpected: [
|
||||
{
|
||||
privateBrowsingId: 0,
|
||||
userContextId: 0,
|
||||
},
|
||||
],
|
||||
contentScriptIndex: 3,
|
||||
},
|
||||
{
|
||||
contentPageOptions: { userContextId: 1 },
|
||||
expectedStyles: "rgb(0, 128, 0)",
|
||||
originAttributesPatternExpected: [{ userContextId: 1 }],
|
||||
contentScriptIndex: 4,
|
||||
},
|
||||
{
|
||||
contentPageOptions: { userContextId: 2 },
|
||||
expectedStyles: "rgb(0, 0, 255)",
|
||||
originAttributesPatternExpected: [{ userContextId: 2 }],
|
||||
contentScriptIndex: 5,
|
||||
},
|
||||
{
|
||||
contentPageOptions: { userContextId: 3 },
|
||||
expectedStyles: "rgb(100, 100, 0)",
|
||||
originAttributesPatternExpected: [
|
||||
{ userContextId: 3 },
|
||||
{ userContextId: 4 },
|
||||
],
|
||||
contentScriptIndex: 6,
|
||||
},
|
||||
{
|
||||
contentPageOptions: { userContextId: 4 },
|
||||
expectedStyles: "rgb(100, 100, 0)",
|
||||
originAttributesPatternExpected: [
|
||||
{ userContextId: 3 },
|
||||
{ userContextId: 4 },
|
||||
],
|
||||
contentScriptIndex: 6,
|
||||
},
|
||||
];
|
||||
|
||||
const policy = WebExtensionPolicy.getByID(extension.id);
|
||||
|
||||
for (const testCase of contentScriptMatchTests) {
|
||||
const {
|
||||
contentPageOptions,
|
||||
expectedStyles,
|
||||
originAttributesPatternExpected,
|
||||
contentScriptIndex,
|
||||
} = testCase;
|
||||
const script = policy.contentScripts[contentScriptIndex];
|
||||
|
||||
deepEqual(script.originAttributesPatterns, originAttributesPatternExpected);
|
||||
let contentPage = await ExtensionTestUtils.loadContentPage(
|
||||
`about:blank`,
|
||||
contentPageOptions
|
||||
);
|
||||
await contentPage.loadURL(`${BASE_URL}/file_sample_registered_styles.html`);
|
||||
|
||||
let registeredStylesResults = await extension.awaitMessage(
|
||||
"registered-styles-results"
|
||||
);
|
||||
|
||||
equal(
|
||||
registeredStylesResults.registeredExtensionBlobStyleBG,
|
||||
expectedStyles,
|
||||
`Expected styles applied on content page loaded with options
|
||||
${JSON.stringify(contentPageOptions)}`
|
||||
);
|
||||
await contentPage.close();
|
||||
}
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче