-
Notifications
You must be signed in to change notification settings - Fork 22.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add documentation of component.utils exportFunction and cloneInto (#3…
…0877) * Add documentation of component.utils exportFunction and cloneInto * Resolved broken links, various edits * various edits * Various edits * Remove incorrect "based on" note * move exportFunction and cloneInto under Content_scripts * Cross-reference and slug corrections * Apply suggestions from review Co-authored-by: Rob Wu <[email protected]> * Further updates from feedback * casing fix * Apply suggestions from review Co-authored-by: Rob Wu <[email protected]> * Remove spurious ad// --------- Co-authored-by: Rob Wu <[email protected]>
- Loading branch information
Showing
4 changed files
with
374 additions
and
3 deletions.
There are no files selected for viewing
158 changes: 158 additions & 0 deletions
158
files/en-us/mozilla/add-ons/webextensions/content_scripts/cloneinto/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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}} |
213 changes: 213 additions & 0 deletions
213
files/en-us/mozilla/add-ons/webextensions/content_scripts/exportfunction/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters