From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../add-ons/sdk/guides/content_scripts/index.html | 484 +++++++++++++++++++++ files/ja/mozilla/add-ons/sdk/guides/index.html | 115 +++++ 2 files changed, 599 insertions(+) create mode 100644 files/ja/mozilla/add-ons/sdk/guides/content_scripts/index.html create mode 100644 files/ja/mozilla/add-ons/sdk/guides/index.html (limited to 'files/ja/mozilla/add-ons/sdk/guides') diff --git a/files/ja/mozilla/add-ons/sdk/guides/content_scripts/index.html b/files/ja/mozilla/add-ons/sdk/guides/content_scripts/index.html new file mode 100644 index 0000000000..071cf1fb6f --- /dev/null +++ b/files/ja/mozilla/add-ons/sdk/guides/content_scripts/index.html @@ -0,0 +1,484 @@ +--- +title: Content Scripts +slug: Mozilla/Add-ons/SDK/Guides/Content_Scripts +translation_of: Archive/Add-ons/Add-on_SDK/Guides/Content_Scripts +--- +
+

アドオンの多くはウェブページへアクセスし修正する必要があります。しかしアドオンのメインのコードは直接ウェブコンテンツにアクセスできません。 代わりにアドオン SDK は content scripts と呼ばれる別のスクリプトからウェブコンテンツにアクセスします。このページでは content scripts の開発・実装方法を記述します。

+ +

SDK を扱う上で content scripts はあなたを混乱させてしまうかもしれませんが、おそらくそれを使わなければなりません。下記は5つの基本原則です:

+ + + +

This complete add-on illustrates all of these principles. Its "main.js" attaches a content script to the current tab using the tabs module. In this case the content script is passed in as a string. The content script simply replaces the content of the page:

+ +
// main.js
+var tabs = require("sdk/tabs");
+var contentScriptString = 'document.body.innerHTML = "<h1>this page has been eaten</h1>";'
+
+tabs.activeTab.attach({
+  contentScript: contentScriptString
+});
+ +

The following high-level SDK modules can use content scripts to modify web pages:

+ + + +

Additionally, some SDK user interface components - panel, sidebar, frame - are specified using HTML, and use separate scripts to interact with this content. In many ways these are like content scripts, but they're not the focus of this article. To learn about how to interact with the content for a given user interface module, please see the module-specific documentation: panel, sidebar, frame.

+ +

Almost all the examples presented in this guide are available as complete, but minimal, add-ons in the addon-sdk-content-scripts repository on GitHub.

+ +

Loading content scripts

+ +
+

You can load a single script by assigning a string to either the contentScript or the contentScriptFile option. The contentScript option treats the string itself as a script:

+ +
// main.js
+
+var pageMod = require("sdk/page-mod");
+var contentScriptValue = 'document.body.innerHTML = ' +
+                         ' "<h1>Page matches ruleset</h1>";';
+
+pageMod.PageMod({
+  include: "*.mozilla.org",
+  contentScript: contentScriptValue
+});
+ +

The contentScriptFile option treats the string as a resource:// URL pointing to a script file stored in your add-on's data directory. jpm doesn't make a "data" directory by default, so you must add it and put your content scripts in there.

+ +

This add-on supplies a URL pointing to the file "content-script.js", located in the data subdirectory under the add-on's root directory:

+ +
// main.js
+
+var data = require("sdk/self").data;
+var pageMod = require("sdk/page-mod");
+
+pageMod.PageMod({
+  include: "*.mozilla.org",
+  contentScriptFile: data.url("content-script.js")
+});
+ +
// content-script.js
+
+document.body.innerHTML = "<h1>Page matches ruleset</h1>";
+ +
+

From Firefox 34 onwards, you can use "./content-script.js" as an alias for self.data.url("content-script.js"). So you can rewrite the above main.js code like this:

+ +
var pageMod = require("sdk/page-mod");
+
+pageMod.PageMod({
+  include: "*.mozilla.org",
+  contentScriptFile: "./content-script.js"
+});
+
+
+ +
+

Unless your content script is extremely simple and consists only of a static string, don't use contentScript: if you do, you may have problems getting your add-on approved on AMO.

+ +

Instead, keep the script in a separate file and load it using contentScriptFile. This makes your code easier to maintain, secure, debug and review.

+
+ +

You can load multiple scripts by passing an array of strings to either contentScript or contentScriptFile:

+ +
// main.js
+
+var tabs = require("sdk/tabs");
+
+tabs.on('ready', function(tab) {
+  tab.attach({
+      contentScript: ['document.body.style.border = "5px solid red";', 'window.alert("hi");']
+  });
+});
+
+ +
// main.js
+
+var data = require("sdk/self").data;
+var pageMod = require("sdk/page-mod");
+
+pageMod.PageMod({
+  include: "*.mozilla.org",
+  contentScriptFile: [data.url("jquery.min.js"), data.url("my-content-script.js")]
+});
+ +

If you do this, the scripts can interact directly with each other, just like scripts loaded by the same web page.

+ +

You can also use contentScript and contentScriptFile together. If you do this, scripts specified using contentScriptFile are loaded before those specified using contentScript. This enables you to load a JavaScript library like jQuery by URL, then pass in a simple script inline that can use jQuery:

+ +
// main.js
+
+var data = require("sdk/self").data;
+var pageMod = require("sdk/page-mod");
+
+var contentScriptString = '$("body").html("<h1>Page matches ruleset</h1>");';
+
+pageMod.PageMod({
+  include: "*.mozilla.org",
+  contentScript: contentScriptString,
+  contentScriptFile: data.url("jquery.js")
+});
+ +
+

Unless your content script is extremely simple and consists only of a static string, don't use contentScript: if you do, you may have problems getting your add-on approved on AMO.

+ +

Instead, keep the script in a separate file and load it using contentScriptFile. This makes your code easier to maintain, secure, debug and review.

+
+ +

Controlling when to attach the script

+ +

The contentScriptWhen option specifies when the content script(s) should be loaded. It takes one of:

+ +
    +
  • "start": load the scripts immediately after the document element for the page is inserted into the DOM. At this point the DOM content hasn't been loaded yet, so the script won't be able to interact with it.
  • +
  • "ready": load the scripts after the DOM for the page has been loaded: that is, at the point the DOMContentLoaded event fires. At this point, content scripts are able to interact with the DOM content, but externally-referenced stylesheets and images may not have finished loading.
  • +
  • "end": load the scripts after all content (DOM, JS, CSS, images) for the page has been loaded, at the time the window.onload event fires.
  • +
+ +

The default value is "end".

+ +

Note that tab.attach() doesn't accept contentScriptWhen, because it's generally called after the page has loaded.

+ +

Passing configuration options

+ +

The contentScriptOptions is a JSON object that is exposed to content scripts as a read-only value under the self.options property:

+ +
// main.js
+
+var tabs = require("sdk/tabs");
+
+tabs.on('ready', function(tab) {
+  tab.attach({
+      contentScript: 'window.alert(self.options.message);',
+      contentScriptOptions: {"message" : "hello world"}
+  });
+});
+ +

Any kind of jsonable value (object, array, string, etc.) can be used here.

+ +

Accessing the DOM

+ +

Content scripts can access the DOM of a page, of course, just like any scripts that the page has loaded (page scripts). But content scripts are insulated from page scripts:

+ +
    +
  • content scripts don't see any JavaScript objects added to the page by page scripts
  • +
  • if a page script has redefined the behavior of some DOM object, the content script sees the original behavior.
  • +
+ +

The same applies in reverse: page scripts can't see JavaScript objects added by content scripts.

+ +

For example, consider a page that adds a variable foo to the window object using a page script:

+ +
<!DOCTYPE html">
+<html>
+  <head>
+    <script>
+    window.foo = "hello from page script"
+    </script>
+  </head>
+</html>
+ +

Another script loaded into the page after this script will be able to access foo. But a content script will not:

+ +
// main.js
+
+var tabs = require("sdk/tabs");
+var mod = require("sdk/page-mod");
+var self = require("sdk/self");
+
+var pageUrl = self.data.url("page.html")
+
+var pageMod = mod.PageMod({
+  include: pageUrl,
+  contentScript: "console.log(window.foo);"
+})
+
+tabs.open(pageUrl);
+ +
console.log: my-addon: null
+
+ +

There are good reasons for this insulation. First, it means that content scripts don't leak objects to web pages, potentially opening up security holes. Second, it means that content scripts can create objects without worrying about whether they might clash with objects added by page scripts.

+ +

This insulation means that, for example, if a web page loads the jQuery library, then the content script won't be able to see the jQuery object added by the library - but the content script can add its own jQuery object, and it won't clash with the page script's version.

+ +

Interacting with page scripts

+ +

Usually the insulation between content scripts and page scripts is what you want. But sometimes you might want to interact with page scripts: you might want to share objects between content scripts and page scripts or to send messages between them. If you need to do this, read about interacting with page scripts.

+ +

Event listeners

+ +

You can listen for DOM events in a content script just as you can in a normal page script, but there are two important differences:

+ +

First, if you define an event listener by passing it as a string into setAttribute(), then the listener is evaluated in the page's context, so it will not have access to any variables defined in the content script.

+ +

For example, this content script will fail with the error "theMessage is not defined":

+ +
var theMessage = "Hello from content script!";
+anElement.setAttribute("onclick", "alert(theMessage);");
+ +

Second, if you define an event listener by direct assignment to a global event handler like onclick, then the assignment might be overridden by the page. For example, here's an add-on that tries to add a click handler by assignment to window.onclick:

+ +
var myScript = "window.onclick = function() {" +
+               "  console.log('unsafewindow.onclick: ' + window.document.title);" +
+               "}";
+
+require("sdk/page-mod").PageMod({
+  include: "*",
+  contentScript: myScript,
+  contentScriptWhen: "start"
+});
+ +

This will work fine on most pages, but will fail on pages which also assign to onclick:

+ +
<html>
+  <head>
+  </head>
+  <body>
+    <script>
+    window.onclick = function() {
+      window.alert("it's my click now!");
+    }
+    </script>
+  </body>
+</html>
+ +

For these reasons, it's better to add event listeners using addEventListener(), defining the listener as a function:

+ +
var theMessage = "Hello from content script!";
+
+anElement.onclick = function() {
+  alert(theMessage);
+};
+
+anotherElement.addEventListener("click", function() {
+  alert(theMessage);
+});
+ +

Communicating with the add-on

+ +

To enable add-on scripts and content scripts to communicate with each other, each end of the conversation has access to a port object.

+ +
    +
  • to send messages from one side to the other, use port.emit()
  • +
  • to receive messages sent from the other side, use port.on()
  • +
+ +

Messages are asynchronous: that is, the sender does not wait for a reply from the recipient but just emits the message and continues processing.

+ +

Here's a simple add-on that sends a message to a content script using port:

+ +
// main.js
+
+var tabs = require("sdk/tabs");
+var self = require("sdk/self");
+
+tabs.on("ready", function(tab) {
+  worker = tab.attach({
+    contentScriptFile: self.data.url("content-script.js")
+  });
+  worker.port.emit("alert", "Message from the add-on");
+});
+
+tabs.open("http://www.mozilla.org");
+ +
// content-script.js
+
+self.port.on("alert", function(message) {
+  window.alert(message);
+});
+ +
+

The context-menu module doesn't use the communication model described here. To learn about communicating with content scripts loaded using context-menu, see the context-menu documentation

+
+ +

Accessing port in the content script

+ +

In the content script the port object is available as a property of the global self object. So to emit a message from a content script:

+ +
self.port.emit("myContentScriptMessage", myContentScriptMessagePayload);
+ +

To receive a message from the add-on code:

+ +
self.port.on("myAddonMessage", function(myAddonMessagePayload) {
+  // Handle the message
+});
+ +
+

Note that the global self object is completely different from the self module, which provides an API for an add-on to access its data files and ID.

+
+ +

Accessing port in the add-on script

+ +

In the add-on code, the channel of communication between the add-on and a particular content script context is encapsulated by the worker object. So the port object for communicating with a content script is a property of the corresponding worker object.

+ +

However, the worker is not exposed to add-on code in quite the same way in all modules.

+ +

From page-worker

+ +

The page-worker object integrates the worker API directly. So to receive messages from a content script associated with a page-worker you use pageWorker.port.on():

+ +
// main.js
+
+var pageWorkers = require("sdk/page-worker");
+var self = require("sdk/self");
+
+var pageWorker = require("sdk/page-worker").Page({
+  contentScriptFile: self.data.url("content-script.js"),
+  contentURL: "http://en.wikipedia.org/wiki/Internet"
+});
+
+pageWorker.port.on("first-para", function(firstPara) {
+  console.log(firstPara);
+});
+ +

To emit user-defined messages from your add-on you can just call pageWorker.port.emit():

+ +
// main.js
+
+var pageWorkers = require("sdk/page-worker");
+var self = require("sdk/self");
+
+pageWorker = require("sdk/page-worker").Page({
+  contentScriptFile: self.data.url("content-script.js"),
+  contentURL: "http://en.wikipedia.org/wiki/Internet"
+});
+
+pageWorker.port.on("first-para", function(firstPara) {
+  console.log(firstPara);
+});
+
+pageWorker.port.emit("get-first-para");
+ +
// content-script.js
+
+self.port.on("get-first-para", getFirstPara);
+
+function getFirstPara() {
+  var paras = document.getElementsByTagName("p");
+  if (paras.length > 0) {
+    var firstPara = paras[0].textContent;
+    self.port.emit("first-para", firstPara);
+  }
+}
+ +

From page-mod

+ +

A single page-mod object might attach its scripts to multiple pages, each with its own context in which the content scripts are executing, so it needs a separate channel (worker) for each page.

+ +

So page-mod does not integrate the worker API directly. Instead, each time a content script is attached to a page, the page-mod emits an attach event, whose listener is passed the worker for that context. By supplying a listener to attach you can access the port object for content scripts attached to that page by this page-mod:

+ +
// main.js
+
+var pageMods = require("sdk/page-mod");
+var self = require("sdk/self");
+
+var pageMod = pageMods.PageMod({
+  include: ['*'],
+  contentScriptFile: self.data.url("content-script.js"),
+  onAttach: startListening
+});
+
+function startListening(worker) {
+  worker.port.on('click', function(html) {
+    worker.port.emit('warning', 'Do not click this again');
+  });
+}
+ +
// content-script.js
+
+window.addEventListener('click', function(event) {
+  self.port.emit('click', event.target.toString());
+  event.stopPropagation();
+  event.preventDefault();
+}, false);
+
+self.port.on('warning', function(message) {
+  window.alert(message);
+});
+
+ +

In the add-on above there are two messages:

+ +
    +
  • click is sent from the page-mod to the add-on, when the user clicks an element in the page
  • +
  • warning sends a silly string back to the page-mod
  • +
+ +

From Tab.attach()

+ +

The Tab.attach() method returns the worker you can use to communicate with the content script(s) you attached.

+ +

This add-on adds a button to Firefox: when the user clicks the button, the add-on attaches a content script to the active tab, sends the content script a message called "my-addon-message", and listens for a response called "my-script-response":

+ +
//main.js
+
+var tabs = require("sdk/tabs");
+var buttons = require("sdk/ui/button/action");
+var self = require("sdk/self");
+
+buttons.ActionButton({
+  id: "attach-script",
+  label: "Attach the script",
+  icon: "./icon-16.png",
+  onClick: attachScript
+});
+
+function attachScript() {
+  var worker = tabs.activeTab.attach({
+    contentScriptFile: self.data.url("content-script.js")
+  });
+  worker.port.on("my-script-response", function(response) {
+    console.log(response);
+  });
+  worker.port.emit("my-addon-message", "Message from the add-on");
+}
+
+ +
// content-script.js
+
+self.port.on("my-addon-message", handleMessage);
+
+function handleMessage(message) {
+  alert(message);
+  self.port.emit("my-script-response", "Response from content script");
+}
+ +

The port API

+ +

See the reference page for the port object.

+
+ +

The postMessage API

+ +

Before the port object was added, add-on code and content scripts communicated using a different API:

+ + + +

The API is still available and documented, but there's no reason to use it instead of the port API described here. The exception is the context-menu module, which still uses postMessage.

+ +

Content script to content script

+ +

Content scripts can only communicate with each other directly if they have been loaded into the same context. For example, if a single call to Tab.attach() attaches two content scripts, then they can see each other directly, just as page scripts loaded by the same page can. But if you call Tab.attach() twice, attaching a content script each time, then these content scripts can't communicate with each other. You must then relay messages through the main add-on code using the port API.

+ +

Cross-domain content scripts

+ +

By default, content scripts don't have any cross-domain privileges. In particular, they can't access content hosted in an iframe, if that content is served from a different domain, or make cross-domain XMLHttpRequests.

+ +

However, you can enable these features for specific domains by adding them to your add-on's package.json under the "cross-domain-content" key, which itself lives under the "permissions" key. See the article on cross-domain content scripts.

+
diff --git a/files/ja/mozilla/add-ons/sdk/guides/index.html b/files/ja/mozilla/add-ons/sdk/guides/index.html new file mode 100644 index 0000000000..0a6a4422d6 --- /dev/null +++ b/files/ja/mozilla/add-ons/sdk/guides/index.html @@ -0,0 +1,115 @@ +--- +title: ガイド +slug: Mozilla/Add-ons/SDK/Guides +translation_of: Archive/Add-ons/Add-on_SDK/Guides +--- +

This page lists more theoretical in-depth articles about the SDK.

+
+

Contributor's guide

+
+
+
+
+ Getting Started
+
+ Learn how to contribute to the SDK: getting the code, opening/taking a bug, filing a patch, getting reviews, and getting help.
+
+ Modules
+
+ Learn about the module system used by the SDK (which is based on the CommonJS specification), how sandboxes and compartments can be used to improve security, and about the built-in SDK module loader, known as Cuddlefish.
+
+ Classes and Inheritance
+
+ Learn how classes and inheritance can be implemented in JavaScript, using constructors and prototypes, and about the helper functions provided by the SDK to simplify this.
+
+
+
+
+
+ Private Properties
+
+ Learn how private properties can be implemented in JavaScript using prefixes, closures, and WeakMaps, and how the SDK supports private properties by using namespaces (which are a generalization of WeakMaps).
+
+ Content Processes
+
+ The SDK was designed to work in an environment where the code to manipulate web content runs in a different process from the main add-on code. This article highlights the main features of that design.
+
+
+
+
+

SDK infrastructure

+
+
+
+
+ Module structure of the SDK
+
+ The SDK, and add-ons built using it, are of composed from reusable JavaScript modules. This explains what these modules are, how to load modules, and how the SDK's module tree is structured.
+
+ SDK API lifecycle
+
+ Definition of the lifecycle for the SDK's APIs, including the stability ratings for APIs.
+
+
+
+
+
+ Program ID
+
+ The Program ID is a unique identifier for your add-on. This guide explains how it's created, what it's used for and how to define your own.
+
+ Firefox compatibility
+
+ Working out which Firefox releases a given SDK release is compatible with, and dealing with compatibility problems.
+
+
+
+
+

SDK idioms

+
+
+
+
+ Working With Events
+
+ Write event-driven code using the the SDK's event emitting framework.
+
+ Content scripts guide
+
+ An overview of content scripts, including: what they are, what they can do, how to load them, how to communicate with them.
+
+
+
+
+
+ Two Types of Scripts
+
+ This article explains the differences between the APIs available to your main add-on code and those available to content scripts.
+
+
+
+
+

XUL migration

+
+
+
+
+ XUL Migration Guide
+
+ Techniques to help port a XUL add-on to the SDK.
+
+ XUL versus the SDK
+
+ A comparison of the strengths and weaknesses of the SDK, compared to traditional XUL-based add-ons.
+
+
+
+
+
+ Porting Example
+
+ A walkthrough of porting a relatively simple XUL-based add-on to the SDK.
+
+
+
+

 

-- cgit v1.2.3-54-g00ecf