diff --git a/files/en-us/mozilla/add-ons/webextensions/content_scripts/cloneinto/index.md b/files/en-us/mozilla/add-ons/webextensions/content_scripts/cloneinto/index.md new file mode 100644 index 000000000000000..17702565104d016 --- /dev/null +++ b/files/en-us/mozilla/add-ons/webextensions/content_scripts/cloneinto/index.md @@ -0,0 +1,158 @@ +--- +title: cloneInto() +slug: Mozilla/Add-ons/WebExtensions/Content_scripts/cloneInto +page-type: webextension-api-function +browser-compat: webextensions.api.contentScriptGlobalScope.cloneInto +--- + +{{AddonSidebar()}} + +This function provides a safe way to take an object defined in a privileged scope and create a [structured clone](/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) of it in a less-privileged scope. It returns a reference to the clone: + +```js +var clonedObject = cloneInto(myObject, targetWindow); +``` + +You can then assign the clone to an object in the target scope as an expando property, and scripts running in that scope can access it: + +```js +targetWindow.foo = clonedObject; +``` + +This enables privileged code, such as an extension, to share an object with less-privileged code, such as a web page script. + +## Syntax + +```js-nolint +let clonedObject = cloneInto( + obj, // object + targetScope, // object + options // optional object +); +``` + +### Parameters + +- `obj` + - : `object`. The object to clone. +- `targetScope` + - : `object`. The object to attach the object to. +- `options` {{optional_inline}} + - : `object`. Options for the function. + - `cloneFunctions` {{optional_inline}} + - : `boolean`. Whether the object's functions should be cloned. Default to `false`. Cloned functions have the same semantics as functions exported using [`exportFunction`](/en-US/docs/Mozilla/Add-ons/WebExtensions/API/Content_scripts/exportFunction). See [Cloning objects that have functions](#Cloning_objects_that_have_functions). {{optional_inline}} + - `wrapReflectors` {{optional_inline}} + - : `boolean`. Whether DOM objects should be passed by reference instead of cloned. DOM objects are usually not clonable. Defaults to `false`. See [Cloning objects that contain DOM elements](#Cloning_objects_that_contain_DOM_elements). + +### Return Value + +A reference to the cloned object. + +## Examples + +This content script creates an object, clones it into the content window and makes it a property of the content window global: + +```js +// content script +var addonScriptObject = { greeting: "hello from your extension" }; +window.addonScriptObject = cloneInto(addonScriptObject, window); +``` + +Scripts running in the page can access the object: + +```js +// page script +button.addEventListener( + "click", + function () { + console.log(window.addonScriptObject.greeting); // "hello from your extension" + }, + false, +); +``` + +Of course, you don't have to assign the clone to the window itself; you can assign it to some other object in the target scope: + +```js +// Content script +window.foo.addonScriptObject = cloneInto(addonScriptObject, window); +``` + +You can also pass it into a function defined in the page script. Suppose the page script defines a function like this: + +```js +// page script +function foo(greeting) { + console.log("they said: " + greeting.message); +} +``` + +The content script can define an object, clone it, and pass it into this function: + +```js +// content script +var addonScriptObject = { message: "hello from your extension" }; +window.foo(cloneInto(addonScriptObject, window)); // "they said: hello from your extension" +``` + +### Cloning objects that have functions + +If the object to clone contains functions, you must pass the `{cloneFunctions:true}` flag, or you get an error. If you do pass this flag, then functions in the object are cloned using the same mechanism used in [`Components.utils.exportFunction`](/en-US/docs/Mozilla/Add-ons/WebExtensions/API/components/utils/exportFunction): + +```js +// content script +var addonScriptObject = { + greetme: function () { + alert("hello from your extension"); + }, +}; +window.addonScriptObject = cloneInto(addonScriptObject, window, { + cloneFunctions: true, +}); +``` + +```js +// page script +var test = document.getElementById("test"); +test.addEventListener( + "click", + function () { + window.addonScriptObject.greetme(); + }, + false, +); +``` + +### Cloning objects that contain DOM elements + +By default, if the object you clone contains objects reflected from C++, such as DOM elements, the cloning operation fails with an error. If you pass the `{wrapReflectors:true}` flag, then the object you clone contains these objects: + +```js +// content script +var addonScriptObject = { + body: window.document.body, +}; +window.addonScriptObject = cloneInto(addonScriptObject, window, { + wrapReflectors: true, +}); +``` + +```js +// page script +var test = document.getElementById("test"); +test.addEventListener( + "click", + function () { + console.log(window.addonScriptObject.body.innerHTML); + }, + false, +); +``` + +Access to these objects in the target scope is subject to the normal [script security checks](https://firefox-source-docs.mozilla.org/dom/scriptSecurity/index.html). + +{{WebExtExamples}} + +## Browser compatibility + +{{Compat}} diff --git a/files/en-us/mozilla/add-ons/webextensions/content_scripts/exportfunction/index.md b/files/en-us/mozilla/add-ons/webextensions/content_scripts/exportfunction/index.md new file mode 100644 index 000000000000000..967e7ac1f7bf8ae --- /dev/null +++ b/files/en-us/mozilla/add-ons/webextensions/content_scripts/exportfunction/index.md @@ -0,0 +1,213 @@ +--- +title: exportFunction() +slug: Mozilla/Add-ons/WebExtensions/Content_scripts/exportFunction +page-type: webextension-api-function +browser-compat: webextensions.api.contentScriptGlobalScope.exportFunction +--- + +{{AddonSidebar()}} + +This function provides a safe way to expose a function from a privileged scope to a less-privileged scope. This enables privileged code, such as an extension, to share code with less-privileged code, such as a standard web page script. A function exported from privileged to less-privileged code can be called from the less privileged code's context. + +The function has access to its surrounding closure as if called in the privileged context. + +The exported function doesn't need to be added to the less privileged code's global window object; it can be exported to any object in the target scope. + +See [Exporting functions that take arguments](#Exporting_functions_that_take_arguments) to understand what happens if the functions you export accept arguments. + +## Syntax + +```js-nolint +let exportFunctionuating = exportFunction( + func, // function + targetScope, // object + options // optional object +); +``` + +### Parameters + +- `func` + - : `function`. The function to export. +- `targetScope` + - : `object`. The object to attach the function to. This doesn't have to be the global window object; it could be an object in the target window or created by the caller. +- `options` {{optional_inline}} + + - : `object`. Options for the function. + + - `defineAs` {{optional_inline}} + - : `string`. The name of the function in `targetScope`. If omitted, you need to assign the return value of `exportFunction()` to an object in the target scope. + - `allowCrossOriginArguments` {{optional_inline}} + - : `boolean`. Whether to check that arguments to the exported function are [subsumed](https://firefox-source-docs.mozilla.org/dom/scriptSecurity/index.html#subsumes) by the caller. This allows the caller to pass objects with a different origin into the exported function, which can then use its privileged status to make cross-origin requests with the object. Defaults to `false`. + +### Return value + +The placeholder function created in the target context. + +## Exporting functions that take arguments + +Any arguments passed into the function are not cloned. Instead, they are passed through to the privileged scope as [Xrays](https://firefox-source-docs.mozilla.org/dom/scriptSecurity/xray_vision.html). + +### Modifying the argument + +An Xray for an object refers to the original. Any changes to the argument made in the exported function affect the original object passed in. For example: + +```js +// privileged scope: for example, a content script +function changeMyName(user) { + user.name = "Bill"; +} +exportFunction(changeMyName, window, { + defineAs: "changeMyName", +}); +``` + +```js +// less-privileged scope: for example, a page script +var user = { name: "Jim" }; +var test = document.getElementById("test"); +test.addEventListener( + "click", + function () { + console.log(user.name); // "Jim" + window.changeMyName(user); + console.log(user.name); // "Bill" + }, + false, +); +``` + +This behavior is subject to the normal rules of Xrays. For example, an expando property added to a DOM node isn't visible in the original object. + +### Xray filtering and waiving + +Xrays provide a filtered view of the original object. For example, functions aren't visible in the Xrays of JavaScript [`Object`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) types. If you need unfiltered access to the original, you can [waive Xrays](https://firefox-source-docs.mozilla.org/dom/scriptSecurity/xray_vision.html#waiving-xray-vision): + +```js +// privileged scope: for example, a content script +function logUser(user) { + // console.log(user.getUser()); // error + console.log(user.wrappedJSObject.getUser()); // "Bill" +} +exportFunction(logUser, window, { + defineAs: "logUser", +}); +``` + +```js +// less-privileged scope: for example, a page script +var user = { + getUser: function () { + return "Bill"; + }, +}; +var test = document.getElementById("test"); +test.addEventListener( + "click", + function () { + window.logUser(user); + }, + false, +); +``` + +See [Xray vision](https://firefox-source-docs.mozilla.org/dom/scriptSecurity/xray_vision.html) in the Firefox Source Tree documentation for more information. + +### Passing functions as arguments + +If functions are given as arguments, these are also passed as Xrays. As you can call `Function` Xrays like normal functions, this means that passing callbacks into the exported function works: + +```js +// privileged scope: for example, a content script +function logUser(getUser) { + console.log(getUser()); // "Bill" +} +exportFunction(logUser, unsafeWindow, { + defineAs: "logUser", +}); +``` + +```js +// less-privileged scope: for example, a page script +function getUser() { + return "Bill"; +} +var test = document.getElementById("test"); +test.addEventListener( + "click", + function () { + window.logUser(getUser); + }, + false, +); +``` + +### Cross-origin checking + +When the exported function is called, each argument, including `this`, is checked to ensure the caller [subsumes](https://firefox-source-docs.mozilla.org/dom/scriptSecurity/index.html#subsumes) that argument. This prevents passing cross-origin objects (such as `Window` or `Location`) to privileged functions, as the privileged code has full access to those objects and could unintentionally do something dangerous. This provision can be overridden by passing `{ allowCrossOriginArguments: true }` to `exportFunction`. + +## Examples + +### Export to global scope + +This script defines a function and then exports it to a content window: + +```js +// extension-script.js +var salutation = "hello "; +function greetme(user) { + return salutation + user; +} +exportFunction(greetme, window, { defineAs: "foo" }); +``` + +Instead of using `defineAs`, the script can assign the result of `exportFunction` to an object in the target scope: + +```js +// extension-script.js +var salutation = "hello "; +function greetme(user) { + return salutation + user; +} +window.foo = exportFunction(greetme, window); +``` + +Either way, code running in the content window's scope can call the function: + +```js +// page-script.js +var greeting = foo("alice"); +console.log(greeting); +// "hello alice" +``` + +### Export to an existing local object + +Instead of attaching the function to the target's global `window` object, the caller can attach it to any other object in the target context. Suppose the content window defines a local variable `bar`: + +```js +// page-script.js +var bar = {}; +``` + +Now the extension script can attach the function to `bar`: + +```js +// extension-script.js +exportFunction(greetme, window.bar, { + defineAs: "greetme", +}); +``` + +```js +// page-script.js +var value = bar.greetme("bob"); +console.log(value); +// "hello bob" +``` + +{{WebExtExamples}} + +## Browser compatibility + +{{Compat}} diff --git a/files/en-us/mozilla/add-ons/webextensions/content_scripts/index.md b/files/en-us/mozilla/add-ons/webextensions/content_scripts/index.md index 88096de35dc6457..a36e3db5bc59de5 100644 --- a/files/en-us/mozilla/add-ons/webextensions/content_scripts/index.md +++ b/files/en-us/mozilla/add-ons/webextensions/content_scripts/index.md @@ -163,7 +163,7 @@ If a content script needs to use a JavaScript library, then the library itself s ``` > [!NOTE] -> Firefox _does_ provide some APIs that enable content scripts to access JavaScript objects created by page scripts, and to expose their own JavaScript objects to page scripts. +> Firefox provides [cloneInto()](/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts/cloneinto) and [exportFunction()](/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts/exportFunction) to enable content scripts to access JavaScript objects created by page scripts and expose their JavaScript objects to page scripts. > > See [Sharing objects with page scripts](/en-US/docs/Mozilla/Add-ons/WebExtensions/Sharing_objects_with_page_scripts) for more details. diff --git a/files/en-us/mozilla/add-ons/webextensions/sharing_objects_with_page_scripts/index.md b/files/en-us/mozilla/add-ons/webextensions/sharing_objects_with_page_scripts/index.md index 7524a8c730ba74c..17fe5c4eca31a1b 100644 --- a/files/en-us/mozilla/add-ons/webextensions/sharing_objects_with_page_scripts/index.md +++ b/files/en-us/mozilla/add-ons/webextensions/sharing_objects_with_page_scripts/index.md @@ -93,7 +93,7 @@ Firefox also provides APIs enabling content scripts to make objects available to ### exportFunction -Given a function defined in the content script, `exportFunction()` exports it to the page script's scope, so the page script can call it. +Given a function defined in the content script, [`exportFunction()`](/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts/exportFunction) exports it to the page script's scope, so the page script can call it. For example, let's consider an extension which has a background script like this: @@ -153,7 +153,7 @@ window.notify("Message from the page script!"); ### cloneInto -Given an object defined in the content script, this creates a clone of the object in the page script's scope, thereby making the clone accessible to page scripts. By default, this uses the [structured clone algorithm](/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) to clone the object, meaning that functions in the object are not included in the clone. To include functions, pass the `cloneFunctions` option. +Given an object defined in the content script, [cloneInto()](/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts/cloneinto) creates a clone of the object in the page script's scope, thereby making the clone accessible to page scripts. By default, this uses the [structured clone algorithm](/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) to clone the object, meaning that functions in the object are not included in the clone. To include functions, pass the `cloneFunctions` option. For example, here's a content script that defines an object that contains a function, then clones it into the page script's scope: