├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug.yml │ └── config.yml ├── PULL_REQUEST_TEMPLATE └── workflows │ ├── codeql.yml │ ├── idle.yml │ ├── lint.yml │ └── welcome-bot.yml ├── .gitignore ├── .prettierrc.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── annotate-page ├── README.md ├── icons │ └── star.png ├── manifest.json └── sidebar │ ├── panel.css │ ├── panel.html │ └── panel.js ├── apply-css ├── README.md ├── background.js ├── icons │ ├── LICENSE │ ├── off.svg │ └── on.svg └── manifest.json ├── beastify ├── README.md ├── beasts │ ├── frog.jpg │ ├── snake.jpg │ └── turtle.jpg ├── content_scripts │ └── beastify.js ├── icons │ ├── LICENSE │ ├── beasts-32-light.png │ ├── beasts-32.png │ └── beasts-48.png ├── manifest.json └── popup │ ├── choose_beast.css │ ├── choose_beast.html │ └── choose_beast.js ├── bookmark-it ├── README.md ├── background.js ├── icons │ ├── LICENSE │ ├── bookmark-it.png │ ├── bookmark-it@2x.png │ ├── star-empty-19.png │ ├── star-empty-38.png │ ├── star-filled-19.png │ └── star-filled-38.png └── manifest.json ├── borderify ├── README.md ├── borderify.js ├── icons │ ├── LICENSE │ └── border-48.png └── manifest.json ├── chill-out ├── README.md ├── background.js ├── icons │ ├── LICENSE │ ├── chillout-32.png │ └── chillout-48.png └── manifest.json ├── commands ├── README.md ├── background.js ├── manifest.json ├── options.html └── options.js ├── content-script-register ├── README.md ├── background.js ├── icons │ ├── LICENSE │ └── if_source_code_103710.svg ├── manifest.json └── popup │ ├── content-script.css │ ├── content-script.html │ └── content-script.js ├── context-menu-copy-link-with-types ├── README.md ├── background.js ├── clipboard-helper.js ├── manifest.json └── preview.html ├── contextual-identities ├── README.md ├── background.js ├── context.css ├── context.html ├── context.js ├── identity.svg └── manifest.json ├── cookie-bg-picker ├── README.md ├── background_scripts │ └── background.js ├── content_scripts │ └── updatebg.js ├── icons │ ├── bgpicker-32.png │ └── bgpicker-48.png ├── manifest.json └── popup │ ├── bgpicker.css │ ├── bgpicker.html │ ├── bgpicker.js │ └── images │ ├── bullseyes.png │ ├── starring.png │ ├── subtle.png │ ├── tactilenoise.png │ ├── triangles.png │ ├── triangles2.png │ ├── washi.png │ └── whitey.png ├── devtools-inspector-sidebar ├── README.md ├── devtools │ ├── devtools-page.html │ └── devtools.js └── manifest.json ├── devtools-panels ├── README.md ├── background_scripts │ └── background.js ├── devtools │ ├── devtools-page.html │ ├── devtools.js │ └── panel │ │ ├── devtools-panel.js │ │ └── panel.html ├── icons │ └── star.png └── manifest.json ├── discogs-search ├── README.md └── manifest.json ├── dnr-block-only ├── README.md ├── manifest.json ├── rules.json └── testpage.html ├── dnr-dynamic-with-options ├── README.md ├── manifest.json ├── options.css ├── options.html └── options.js ├── dnr-redirect-url ├── README.md ├── manifest.json ├── popup.html ├── popup.js ├── redirect-rules.json └── redirectTarget.html ├── dynamic-theme ├── README.md ├── background.js ├── manifest.json ├── moon.jpg └── sun.jpg ├── emoji-substitution ├── README.md ├── emojiMap.js ├── icons │ ├── icon.png │ └── icon@2x.png ├── manifest.json └── substitute.js ├── eslint-example ├── .eslintrc.json ├── .gitignore ├── README.md ├── file.js ├── icons │ ├── LICENSE │ ├── page-32.png │ └── page-48.png ├── main.js ├── manifest.json ├── package.json └── popup.html ├── examples.json ├── export-helpers ├── README.md ├── background.js ├── content_scripts │ └── export.js ├── icons │ ├── LICENSE │ └── arrow.svg └── manifest.json ├── favourite-colour ├── README.md ├── background.js ├── manifest.json ├── options.html └── options.js ├── find-across-tabs ├── README.md ├── background.js ├── bootstrap.min.css ├── find.html ├── find.js ├── find.svg └── manifest.json ├── firefox-code-search ├── README.md ├── background.js └── manifest.json ├── forget-it ├── README.md ├── background.js ├── icons │ ├── LICENSE │ └── trash.svg ├── manifest.json └── options │ ├── options.css │ ├── options.html │ └── options.js ├── google-userinfo ├── README.md ├── background │ ├── authorize.js │ ├── main.js │ └── userinfo.js ├── icons │ ├── LICENSE │ ├── person-32.png │ └── person-48.png ├── manifest.json └── options │ ├── options.html │ └── options.js ├── history-deleter ├── README.md ├── background.js ├── history.css ├── history.html ├── history.js ├── icons │ ├── LICENSE │ └── history.svg └── manifest.json ├── http-response ├── README.md ├── background.js ├── manifest.json └── pen.svg ├── imagify ├── README.md ├── content_scripts │ └── content.js ├── manifest.json ├── sidebar │ ├── choose_file.js │ ├── sidebar.css │ └── sidebar.html ├── viewer.css ├── viewer.html └── viewer.js ├── latest-download ├── README.md ├── icons │ ├── LICENSE │ ├── page-32.png │ └── page-48.png ├── manifest.json └── popup │ ├── latest_download.html │ └── latest_download.js ├── list-cookies ├── README.md ├── cookies.css ├── cookies.html ├── cookies.js ├── icons │ ├── cookie.png │ ├── cookie@2x.png │ ├── default19.png │ └── default38.png └── manifest.json ├── menu-accesskey-visible ├── README.md ├── _locales │ └── en │ │ └── messages.json ├── background.js └── manifest.json ├── menu-demo ├── README.md ├── _locales │ └── en │ │ └── messages.json ├── background.js ├── icons │ ├── LICENSE │ ├── page-16.png │ ├── page-32.png │ ├── page-48.png │ ├── paint-blue-16.png │ ├── paint-blue-32.png │ ├── paint-green-16.png │ └── paint-green-32.png ├── manifest.json └── sidebar │ └── sidebar.html ├── menu-labelled-open ├── README.md ├── background.js ├── icon │ ├── LICENSE │ └── label.svg └── manifest.json ├── menu-remove-element ├── README.md ├── background.js ├── contentscript.js ├── manifest.json ├── menusGetTargetElementPolyfill.js ├── popup.html └── popup.js ├── menu-search ├── README.md ├── background.js ├── icons │ ├── LICENSE │ ├── page-16.png │ ├── page-32.png │ └── page-48.png └── manifest.json ├── mocha-client-tests ├── .eslintrc.json ├── .gitignore ├── README.md ├── addon │ ├── background.js │ ├── images │ │ ├── icon-16.png │ │ ├── icon-19.png │ │ └── mocha.png │ ├── manifest.json │ ├── package.json │ ├── popup.html │ ├── scripts │ │ ├── browser-polyfill.min.js │ │ └── popup.js │ └── tests │ │ ├── lib │ │ ├── background-messaging.test.js │ │ └── test.array.js │ │ ├── mocha-run.js │ │ └── mocha-setup.js ├── karma.conf.js ├── package.json ├── screenshots │ └── addon-button.png └── tests │ └── lib │ └── background.test.js ├── native-messaging ├── README.md ├── add-on │ ├── background.js │ ├── icons │ │ ├── LICENSE │ │ └── message.svg │ └── manifest.json ├── app │ ├── ping_pong.json │ ├── ping_pong.py │ └── ping_pong_win.bat └── check_config_win.py ├── navigation-stats ├── README.md ├── background.js ├── icons │ ├── LICENSE │ └── icon-32.png ├── manifest.json ├── popup.html └── popup.js ├── notify-link-clicks-i18n ├── README.md ├── _locales │ ├── de │ │ └── messages.json │ ├── en │ │ └── messages.json │ ├── fr_FR │ │ └── messages.json │ ├── ja │ │ └── messages.json │ ├── nb_NO │ │ └── messages.json │ ├── nl │ │ └── messages.json │ └── pt_BR │ │ └── messages.json ├── background-script.js ├── content-script.js ├── icons │ ├── LICENSE │ └── link-48.png └── manifest.json ├── open-irc-links ├── README.md ├── icons │ └── icon.png └── manifest.json ├── open-my-page-button ├── README.md ├── background.js ├── icons │ ├── LICENSE │ ├── page-32.png │ └── page-48.png ├── manifest.json └── my-page.html ├── package-lock.json ├── package.json ├── page-to-extension-messaging ├── README.md ├── content-script.js ├── icons │ ├── LICENSE │ └── message-48.png └── manifest.json ├── permissions ├── .eslintrc.json ├── README.md ├── background.js ├── bootstrap.min.css ├── icon.svg ├── manifest.json ├── page.css ├── page.html └── page.js ├── private-browsing-theme ├── README.md ├── background.js └── manifest.json ├── proxy-blocker ├── README.md ├── background │ └── proxy-handler.js ├── icons │ ├── LICENSE │ └── block.svg ├── manifest.json └── options │ ├── options.css │ ├── options.html │ └── options.js ├── quicknote ├── README.md ├── icons │ ├── quicknote-32.png │ └── quicknote-48.png ├── manifest.json └── popup │ ├── quicknote.css │ ├── quicknote.html │ └── quicknote.js ├── root-cert-stats ├── README.md ├── background.js ├── icons │ ├── LICENSE │ └── icon-32.png ├── manifest.json ├── popup.css ├── popup.html └── popup.js ├── runtime-examples ├── README.md ├── background.js ├── manifest.json └── run.png ├── selection-to-clipboard ├── README.md ├── content-script.js ├── icons │ ├── LICENSE │ └── clipboard-48.png └── manifest.json ├── session-state ├── README.md ├── background.js ├── icons │ ├── LICENSE │ └── border-48.png └── manifest.json ├── store-collected-images ├── README.md ├── screenshots │ └── screenshot.png ├── webextension-plain │ ├── .eslintignore │ ├── .eslintrc │ ├── README.md │ ├── background.js │ ├── deps │ │ ├── idb-file-storage.js │ │ ├── idb-file-storage.js.map │ │ └── uuidv4.js │ ├── images │ │ ├── icon.png │ │ └── icon16.png │ ├── manifest.json │ ├── navigate-collection.css │ ├── navigate-collection.html │ ├── navigate-collection.js │ ├── popup.css │ ├── popup.html │ ├── popup.js │ ├── shared.css │ └── utils │ │ ├── handle-window-drag-and-drop.js │ │ └── image-store.js └── webextension-with-webpack │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── extension │ ├── images │ │ ├── icon.png │ │ └── icon16.png │ ├── manifest.json │ ├── navigate-collection.css │ ├── navigate-collection.html │ ├── popup.css │ ├── popup.html │ └── shared.css │ ├── package.json │ ├── src │ ├── background.js │ ├── navigate-collection.js │ ├── popup.js │ └── utils │ │ ├── handle-window-drag-and-drop.js │ │ └── image-store.js │ └── webpack.config.js ├── stored-credentials ├── README.md ├── auth.js ├── icons │ ├── LICENSE │ └── lock.svg ├── manifest.json ├── options │ ├── options.css │ ├── options.html │ └── options.js └── storage.js ├── tabs-tabs-tabs ├── README.md ├── background.js ├── manifest.json ├── tabs.css ├── tabs.html └── tabs.js ├── theme-integrated-sidebar ├── README.md ├── manifest.json ├── sidebar.html └── sidebar.js ├── theme-switcher ├── README.md ├── manifest.json ├── star-half.svg ├── switcher.css ├── switcher.html └── switcher.js ├── themes ├── animated │ ├── README.md │ ├── manifest.json │ ├── parrot.png │ └── parrot_frames │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ └── 9.png ├── temp │ └── manifest.json ├── weta_fade │ ├── README.md │ ├── manifest.json │ └── weta.png ├── weta_fade_chrome │ ├── README.md │ ├── manifest.json │ └── weta.png ├── weta_mirror │ ├── README.md │ ├── manifest.json │ ├── weta-left.png │ └── weta.png └── weta_tiled │ ├── README.md │ ├── manifest.json │ └── weta_for_tiling.png ├── top-sites ├── README.md ├── bootstrap.min.css ├── manifest.json ├── sites.html └── sites.js ├── user-agent-rewriter ├── README.md ├── background.js ├── icons │ ├── LICENSE │ ├── person-32.png │ └── person-48.png ├── manifest.json └── popup │ ├── choose_ua.css │ ├── choose_ua.html │ └── choose_ua.js ├── user-script-register ├── README.md ├── background.js ├── customUserScriptAPIs.js ├── icons │ ├── LICENSE │ └── if_source_code_103710.svg ├── manifest.json └── popup │ ├── user-script.css │ ├── user-script.html │ └── user-script.js ├── userScripts-mv3 ├── .eslintrc.json ├── README.md ├── background.js ├── manifest.json ├── options.css ├── options.html ├── options.mjs ├── userscript_api.js ├── userscript_examples │ ├── privileged.user.js │ └── unprivileged.user.js └── userscript_manager_logic.mjs ├── webpack-modules ├── .eslintrc.json ├── README.md ├── addon │ ├── icons │ │ └── leftpad-32.png │ ├── manifest.json │ └── popup │ │ ├── left-pad.html │ │ └── style.css ├── background_scripts │ └── background.js ├── package.json ├── popup │ └── left-pad.js └── webpack.config.js └── window-manipulator ├── README.md ├── icons ├── window.png ├── window19.png ├── window38.png └── window@2x.png ├── manifest.json ├── window.css ├── window.html └── window.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules/** 2 | react-es6-popup/**/dist 3 | mocha-client-tests 4 | store-collected-images/webextension-plain/deps 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parserOptions": { 4 | "ecmaVersion": "latest" 5 | }, 6 | "env": { 7 | "browser": true, 8 | "es2021": true, 9 | "node": true, 10 | "webextensions": true 11 | }, 12 | "globals": { 13 | "globalThis": false 14 | }, 15 | "extends": ["eslint:recommended"], 16 | "overrides": [], 17 | 18 | "rules": { 19 | "no-console": 0, 20 | "no-unused-vars": ["warn", { "vars": "all", "args": "after-used" }], 21 | "no-undef": ["warn"], 22 | "no-proto": ["error"], 23 | "prefer-arrow-callback": ["warn"], 24 | "prefer-spread": ["warn"] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Content or feature request 4 | url: https://github.com/mdn/mdn/issues/new/choose 5 | about: Propose new content for MDN Web Docs or submit a feature request using this link. 6 | - name: MDN GitHub Discussions 7 | url: https://github.com/orgs/mdn/discussions 8 | about: Does the issue involve a lot of changes, or is it hard to split it into actionable tasks? Start a discussion before opening an issue. 9 | - name: MDN Web Docs on Discourse 10 | url: https://discourse.mozilla.org/c/mdn/learn/250 11 | about: Need help with assessments on MDN Web Docs? We have a support community for this purpose on Discourse. 12 | - name: Help with code 13 | url: https://stackoverflow.com/ 14 | about: If you are stuck and need help with code, StackOverflow is a great resource. 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Description 4 | 5 | 6 | 7 | ### Motivation 8 | 9 | 10 | 11 | ### Additional details 12 | 13 | 14 | 15 | ### Related issues and pull requests 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.github/workflows/idle.yml: -------------------------------------------------------------------------------- 1 | # This workflow is hosted at: https://github.com/mdn/workflows/blob/main/.github/workflows/idle.yml 2 | # Docs for this workflow: https://github.com/mdn/workflows/blob/main/README.md#idle 3 | name: "Label idle issues" 4 | 5 | on: 6 | schedule: 7 | - cron: "0 8 * * *" 8 | 9 | jobs: 10 | mark-as-idle: 11 | uses: mdn/workflows/.github/workflows/idle.yml@main 12 | with: 13 | target-repo: "mdn/webextensions-examples" 14 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | on: 3 | pull_request: 4 | paths: 5 | - "**.js" 6 | 7 | jobs: 8 | eslint: 9 | name: eslint 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | pull-requests: write 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - uses: actions/setup-node@v4 19 | with: 20 | node-version: "latest" 21 | cache: "npm" 22 | 23 | - run: npm ci 24 | 25 | - uses: reviewdog/action-eslint@v1 26 | with: 27 | reporter: github-pr-review 28 | eslint_flags: "." 29 | filter_mode: "file" 30 | fail_on_error: true 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of conduct 2 | 3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines. 4 | For more details, read [Mozilla's Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 5 | 6 | ## Reporting violations 7 | 8 | For more information on how to report violations of the Community Participation Guidelines, read the [How to report](https://www.mozilla.org/about/governance/policies/participation/reporting/) page. 9 | -------------------------------------------------------------------------------- /annotate-page/README.md: -------------------------------------------------------------------------------- 1 | # annotate-page 2 | 3 | ## What it does 4 | 5 | This example adds a sidebar that lets you take notes on the current web page. The notes are saved to local storage, and the notes for each page are shown again when you open that page again. 6 | 7 | The example also uses the `commands` manifest key to add a keyboard shortcut that opens the sidebar. 8 | 9 | ## What it shows 10 | 11 | How to create a sidebar for an add-on. How to associate the sidebar with the currently active tab in that sidebar's window. How to store and restore sidebar content. 12 | -------------------------------------------------------------------------------- /annotate-page/icons/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/annotate-page/icons/star.png -------------------------------------------------------------------------------- /annotate-page/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "Web page annotator", 5 | "description": "Displays a sidebar that lets you take notes on web pages.", 6 | "version": "1.0", 7 | "browser_specific_settings": { 8 | "gecko": { 9 | "strict_min_version": "54.0a1" 10 | } 11 | }, 12 | 13 | "sidebar_action": { 14 | "default_icon": "icons/star.png", 15 | "default_title" : "Annotator", 16 | "default_panel": "sidebar/panel.html" 17 | }, 18 | 19 | "permissions": ["storage", "tabs"], 20 | 21 | "commands": { 22 | "_execute_sidebar_action": { 23 | "suggested_key": { 24 | "default": "Ctrl+Shift+Y" 25 | } 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /annotate-page/sidebar/panel.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | width: 100%; 4 | margin: 0; 5 | box-sizing: border-box; 6 | } 7 | 8 | body { 9 | height: 90%; 10 | font: caption; 11 | background-color: #F4F7F8; 12 | } 13 | 14 | p { 15 | margin: 1em 2em; 16 | } 17 | 18 | #content { 19 | height: 90%; 20 | margin: 2em 2em 0 2em; 21 | border: .5em solid #DDE4E9; 22 | transition: background-color .2s ease-out; 23 | } 24 | 25 | #content[contenteditable=true] { 26 | background-color: white; 27 | transition: background-color .2s ease-in; 28 | } 29 | 30 | [contenteditable]:focus { 31 | outline: 0px solid transparent; 32 | } 33 | -------------------------------------------------------------------------------- /annotate-page/sidebar/panel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Click inside the box to start taking notes on this page.

12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /apply-css/README.md: -------------------------------------------------------------------------------- 1 | # apply-css 2 | 3 | **This add-on injects CSS into web pages. The `addons.mozilla.org` domain disallows this operation, so this add-on will not work properly when it's run on pages in the `addons.mozilla.org` domain.** 4 | 5 | ## What it does 6 | 7 | This extension includes: 8 | 9 | * a background script, "background.js" 10 | * a page action 11 | 12 | It adds the [page action](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/pageAction) 13 | to every tab the user opens. Clicking the page action 14 | for a tab applies some CSS using [tabs.insertCSS](https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/tabs/insertCSS). 15 | 16 | Clicking again removes the CSS using [tabs.removeCSS](https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/tabs/removeCSS). 17 | 18 | ## What it shows 19 | 20 | * some basic page action functions 21 | * how to apply and remove CSS. 22 | -------------------------------------------------------------------------------- /apply-css/icons/LICENSE: -------------------------------------------------------------------------------- 1 | These icons are taken from the "Material Design" iconset designed by Google: 2 | https://google.github.io/material-design-icons/. 3 | -------------------------------------------------------------------------------- /apply-css/icons/off.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /apply-css/icons/on.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /apply-css/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Adds a page action to toggle applying CSS to pages.", 4 | "manifest_version": 2, 5 | "name": "apply-css", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/apply-css", 8 | 9 | "background": { 10 | "scripts": ["background.js"] 11 | }, 12 | 13 | "page_action": { 14 | "default_icon": "icons/off.svg", 15 | "browser_style": true 16 | }, 17 | 18 | "permissions": [ 19 | "activeTab", 20 | "tabs" 21 | ] 22 | 23 | } 24 | -------------------------------------------------------------------------------- /beastify/beasts/frog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/beastify/beasts/frog.jpg -------------------------------------------------------------------------------- /beastify/beasts/snake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/beastify/beasts/snake.jpg -------------------------------------------------------------------------------- /beastify/beasts/turtle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/beastify/beasts/turtle.jpg -------------------------------------------------------------------------------- /beastify/icons/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The icon "beasts-32.png" is taken from the IconBeast Lite iconset, and used under the terms of its license (http://www.iconbeast.com/faq/), with a link back to the website: http://www.iconbeast.com/free/. 3 | 4 | The icon "beasts-48.png" is taken from Aha-Soft’s Free Retina iconset, and used under the terms of its license (http://www.aha-soft.com/free-icons/free-retina-icon-set/), with a link back to the website: http://www.aha-soft.com/. 5 | -------------------------------------------------------------------------------- /beastify/icons/beasts-32-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/beastify/icons/beasts-32-light.png -------------------------------------------------------------------------------- /beastify/icons/beasts-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/beastify/icons/beasts-32.png -------------------------------------------------------------------------------- /beastify/icons/beasts-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/beastify/icons/beasts-48.png -------------------------------------------------------------------------------- /beastify/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Adds a browser action icon to the toolbar. Click the button to choose a beast. The active tab's body content is then replaced with a picture of the chosen beast. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#beastify", 4 | "manifest_version": 2, 5 | "name": "Beastify", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/beastify", 8 | "icons": { 9 | "48": "icons/beasts-48.png" 10 | }, 11 | 12 | "permissions": [ 13 | "activeTab" 14 | ], 15 | 16 | "browser_action": { 17 | "default_icon": "icons/beasts-32.png", 18 | "theme_icons": [{ 19 | "light": "icons/beasts-32-light.png", 20 | "dark": "icons/beasts-32.png", 21 | "size": 32 22 | }], 23 | "default_title": "Beastify", 24 | "default_popup": "popup/choose_beast.html" 25 | }, 26 | 27 | "web_accessible_resources": [ 28 | "beasts/*.jpg" 29 | ] 30 | 31 | } 32 | -------------------------------------------------------------------------------- /beastify/popup/choose_beast.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 100px; 3 | } 4 | 5 | .hidden { 6 | display: none; 7 | } 8 | 9 | button { 10 | border: none; 11 | width: 100%; 12 | margin: 3% auto; 13 | padding: 4px; 14 | text-align: center; 15 | font-size: 1.5em; 16 | cursor: pointer; 17 | background-color: #E5F2F2; 18 | } 19 | 20 | button:hover { 21 | background-color: #CFF2F2; 22 | } 23 | 24 | button[type="reset"] { 25 | background-color: #FBFBC9; 26 | } 27 | 28 | button[type="reset"]:hover { 29 | background-color: #EAEA9D; 30 | } 31 | -------------------------------------------------------------------------------- /beastify/popup/choose_beast.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /bookmark-it/README.md: -------------------------------------------------------------------------------- 1 | # bookmark-it 2 | 3 | ## What it does 4 | 5 | Displays a simple button in the menu bar that toggles a bookmark for the currently active tab. 6 | 7 | To display the button, the extension registers a [browserAction](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/browserAction) in the manifest. 8 | 9 | A background script will listen for tab events and update the browserAction icon correspondingly. It also listens for `browserAction.onClicked` events to create or remove a bookmark when the user has clicked the icon. 10 | 11 | ## What it shows 12 | 13 | * how to use the various `bookmarks` functions 14 | * create a bookmark 15 | * remove a bookmark 16 | * search bookmarks by url 17 | * how to register a browserAction 18 | * how to listen for tab changes 19 | -------------------------------------------------------------------------------- /bookmark-it/icons/LICENSE: -------------------------------------------------------------------------------- 1 | All images in this folder were created by Johann Hofmann. The creator waives all rights to the images under the CC0 Public Domain Dedication. https://creativecommons.org/publicdomain/zero/1.0/ 2 | -------------------------------------------------------------------------------- /bookmark-it/icons/bookmark-it.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/bookmark-it/icons/bookmark-it.png -------------------------------------------------------------------------------- /bookmark-it/icons/bookmark-it@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/bookmark-it/icons/bookmark-it@2x.png -------------------------------------------------------------------------------- /bookmark-it/icons/star-empty-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/bookmark-it/icons/star-empty-19.png -------------------------------------------------------------------------------- /bookmark-it/icons/star-empty-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/bookmark-it/icons/star-empty-38.png -------------------------------------------------------------------------------- /bookmark-it/icons/star-filled-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/bookmark-it/icons/star-filled-19.png -------------------------------------------------------------------------------- /bookmark-it/icons/star-filled-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/bookmark-it/icons/star-filled-38.png -------------------------------------------------------------------------------- /bookmark-it/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Bookmark it!", 4 | "version": "1.1", 5 | "description": "A simple bookmark button", 6 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/bookmark-it", 7 | "icons": { 8 | "48": "icons/bookmark-it.png", 9 | "96": "icons/bookmark-it@2x.png" 10 | }, 11 | 12 | "permissions": [ 13 | "bookmarks", 14 | "tabs" 15 | ], 16 | 17 | "browser_action": { 18 | "default_icon": "icons/star-empty-38.png", 19 | "default_title": "Bookmark it!" 20 | }, 21 | 22 | "background": { 23 | "scripts": ["background.js"] 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /borderify/README.md: -------------------------------------------------------------------------------- 1 | # borderify 2 | 3 | **This add-on injects JavaScript into web pages. The `addons.mozilla.org` domain disallows this operation, so this add-on will not work properly when it's run on pages in the `addons.mozilla.org` domain.** 4 | 5 | ## What it does 6 | 7 | This extension just includes: 8 | 9 | * a content script, "borderify.js", that is injected into any pages 10 | under "mozilla.org/" or any of its subdomains 11 | 12 | The content script draws a border around the document.body. 13 | 14 | ## What it shows 15 | 16 | * how to inject content scripts declaratively using manifest.json 17 | -------------------------------------------------------------------------------- /borderify/borderify.js: -------------------------------------------------------------------------------- 1 | /* 2 | Just draw a border round the document.body. 3 | */ 4 | document.body.style.border = "5px solid red"; 5 | -------------------------------------------------------------------------------- /borderify/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The icon “border-48.png” is taken from the Google Material Design iconset, and is used under the terms of the Creative Commons Attribution-ShareAlike license: http://creativecommons.org/licenses/by-sa/3.0/. 2 | -------------------------------------------------------------------------------- /borderify/icons/border-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/borderify/icons/border-48.png -------------------------------------------------------------------------------- /borderify/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Adds a solid red border to all webpages matching mozilla.org. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#borderify", 4 | "manifest_version": 2, 5 | "name": "Borderify", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/borderify", 8 | "icons": { 9 | "48": "icons/border-48.png" 10 | }, 11 | 12 | "content_scripts": [ 13 | { 14 | "matches": ["*://*.mozilla.org/*"], 15 | "js": ["borderify.js"] 16 | } 17 | ] 18 | 19 | } 20 | -------------------------------------------------------------------------------- /chill-out/README.md: -------------------------------------------------------------------------------- 1 | # chill-out 2 | 3 | ## What it does 4 | 5 | After 6 seconds of inactivity (defined as the user not having navigated 6 | or switched away from the active tab) display a 7 | [page action](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/pageAction) 8 | for that tab. 9 | 10 | When the user clicks the page action, navigate to https://giphy.com/explore/cat. 11 | 12 | The delay of 6 seconds is to make the extension's behavior obvious, but such a short 13 | period is not recommended in practical applications. Note that in Chrome, alarms do not 14 | fire in under a minute. In Chrome: 15 | 16 | * if you install this extension "unpacked", you'll see a warning 17 | in the console, but the alarm will still go off after 6 seconds 18 | * if you package the extension and install it, then the alarm will go off after 19 | a minute. 20 | 21 | ## What it shows 22 | 23 | How to: 24 | 25 | * use various `tabs` functions. 26 | * show and hide a page action. 27 | * set alarms and handle alarms going off. 28 | -------------------------------------------------------------------------------- /chill-out/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The icon "chillout-32.png" is taken from the IconBeast Lite iconset, and used under the terms of its license (http://www.iconbeast.com/faq/), with a link back to the website: http://www.iconbeast.com/free/. 2 | 3 | The icon "chillout-48.png" is taken from Aha-Soft’s Free Retina iconset, and used under the terms of its license (http://www.aha-soft.com/free-icons/free-retina-icon-set/), with a link back to the website: http://www.aha-soft.com/. 4 | -------------------------------------------------------------------------------- /chill-out/icons/chillout-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/chill-out/icons/chillout-32.png -------------------------------------------------------------------------------- /chill-out/icons/chillout-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/chill-out/icons/chillout-48.png -------------------------------------------------------------------------------- /chill-out/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "chillout-page-action", 4 | "version": "1.0", 5 | "description": "Show a page action after a period of inactivity. Show cat gifs when the page action is clicked.", 6 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/chill-out", 7 | "icons": { 8 | "48": "icons/chillout-48.png" 9 | }, 10 | 11 | "permissions": [ 12 | "alarms", 13 | "tabs" 14 | ], 15 | 16 | "page_action": { 17 | "default_icon": "icons/chillout-32.png", 18 | "default_title": "Chill out" 19 | }, 20 | 21 | "background": { 22 | "scripts": ["background.js"] 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /commands/README.md: -------------------------------------------------------------------------------- 1 | # commands 2 | 3 | This extension shows how to use the `commands` manifest key to register keyboard shortcuts for your extension. 4 | 5 | It registers a shortcut (Ctrl+Shift+U) to send a command to the extension (Command+Shift+U on a Mac). 6 | When the user enters the shortcut, the extension opens a new browser tab and loads https://developer.mozilla.org into it. 7 | 8 | It also adds an [options page](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/user_interface/Options_pages) to the extension, which enables the user to change the registered shortcut for the extension. Just open the options page, then type a new value into the textbox (for example: "Ctrl+Shift+O") and press "Update keyboard shortcut". To reset the shortcut to its original value, press "Reset keyboard shortcut". 9 | -------------------------------------------------------------------------------- /commands/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sample Commands Extension", 3 | "description": "Press Ctrl+Shift+U to send an event (Command+Shift+U on a Mac).", 4 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/commands", 5 | "manifest_version": 2, 6 | "version": "1.0", 7 | 8 | "browser_specific_settings": { 9 | "gecko": { 10 | "id": "commands-demo@mozilla.org", 11 | "strict_min_version": "60.0b5" 12 | } 13 | }, 14 | 15 | "background": { 16 | "scripts": ["background.js"] 17 | }, 18 | 19 | "commands": { 20 | "toggle-feature": { 21 | "suggested_key": { "default": "Ctrl+Shift+U" }, 22 | "description": "Send a 'toggle-feature' event to the extension" 23 | } 24 | }, 25 | 26 | "options_ui": { 27 | "page": "options.html", 28 | "browser_style": true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /commands/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /content-script-register/background.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let registered = null; 4 | 5 | async function registerScript(message) { 6 | 7 | let hosts = message.hosts; 8 | let code = message.code; 9 | 10 | if (registered) { 11 | registered.unregister(); 12 | } 13 | 14 | registered = await browser.contentScripts.register({ 15 | matches: hosts, 16 | js: [{code}], 17 | runAt: "document_idle" 18 | }); 19 | 20 | } 21 | 22 | browser.runtime.onMessage.addListener(registerScript); 23 | -------------------------------------------------------------------------------- /content-script-register/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The icon "if_source_code_103710.svg" is from picol.org (http://www.picol.org/) and is used under the terms of the Creative Commons Attribution license: http://creativecommons.org/licenses/by/3.0/. 2 | -------------------------------------------------------------------------------- /content-script-register/icons/if_source_code_103710.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /content-script-register/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "Content script registration", 5 | "version": "1.1", 6 | 7 | "browser_specific_settings": { 8 | "gecko": { 9 | "id": "content-script-example@mozilla.org", 10 | "strict_min_version": "59.0a1" 11 | } 12 | }, 13 | 14 | "description": "Demonstration of contentScripts.register.", 15 | "icons": { 16 | "48": "icons/if_source_code_103710.svg" 17 | }, 18 | 19 | "permissions": [ 20 | "" 21 | ], 22 | 23 | "browser_action": { 24 | "default_icon": { 25 | "32" : "icons/if_source_code_103710.svg" 26 | }, 27 | "default_title": "Content script", 28 | "default_popup": "popup/content-script.html", 29 | "browser_style": true 30 | }, 31 | 32 | "background": { 33 | "scripts": ["background.js"] 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /content-script-register/popup/content-script.css: -------------------------------------------------------------------------------- 1 | 2 | #outer-wrapper { 3 | padding: 0.5em; 4 | } 5 | 6 | input { 7 | width: 100%; 8 | margin-bottom: 1em; 9 | } 10 | 11 | textarea { 12 | width: 100%; 13 | resize: none; 14 | border: 1px solid #e4e4e4; 15 | margin-bottom: 1em; 16 | } 17 | -------------------------------------------------------------------------------- /content-script-register/popup/content-script.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /content-script-register/popup/content-script.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const hostsInput = document.querySelector("#hosts"); 4 | const codeInput = document.querySelector("#code"); 5 | 6 | const defaultHosts = "*://*.org/*"; 7 | const defaultCode = "document.body.innerHTML = '

This page has been eaten

'"; 8 | 9 | hostsInput.value = defaultHosts; 10 | codeInput.value = defaultCode; 11 | 12 | function registerScript() { 13 | browser.runtime.sendMessage({ 14 | hosts: hostsInput.value.split(","), 15 | code: codeInput.value 16 | }); 17 | } 18 | 19 | document.querySelector("#register").addEventListener('click', registerScript); 20 | -------------------------------------------------------------------------------- /context-menu-copy-link-with-types/clipboard-helper.js: -------------------------------------------------------------------------------- 1 | // This function must be called in a visible page, such as a browserAction popup 2 | // or a content script. Calling it in a background page has no effect! 3 | function copyToClipboard(text, html) { 4 | function oncopy(event) { 5 | document.removeEventListener("copy", oncopy, true); 6 | // Hide the event from the page to prevent tampering. 7 | event.stopImmediatePropagation(); 8 | 9 | // Overwrite the clipboard content. 10 | event.preventDefault(); 11 | event.clipboardData.setData("text/plain", text); 12 | event.clipboardData.setData("text/html", html); 13 | } 14 | document.addEventListener("copy", oncopy, true); 15 | 16 | // Requires the clipboardWrite permission, or a user gesture: 17 | document.execCommand("copy"); 18 | } 19 | -------------------------------------------------------------------------------- /context-menu-copy-link-with-types/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Context menu: Copy link with types", 4 | "description": "Add a context menu option to links to copy the link to the clipboard, as plain text and as a link in rich HTML.", 5 | "version": "1.0", 6 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/context-menu-copy-link-with-types", 7 | 8 | "background": { 9 | "scripts": [ 10 | "background.js" 11 | ], 12 | "persistent": false 13 | }, 14 | 15 | "permissions": [ 16 | "activeTab", 17 | "contextMenus", 18 | "clipboardWrite" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /context-menu-copy-link-with-types/preview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | Use Ctrl+V (or Cmd+V) to paste plain text here: 16 | 17 | 18 | Use Ctrl+V (or Cmd+V) to paste rich text (HTML) here: 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /contextual-identities/README.md: -------------------------------------------------------------------------------- 1 | # Contextual Identities 2 | 3 | ## What it does 4 | 5 | Lists existing identities, lets you create new tabs with an identity and remove all tabs from an identity. For more information on contextual identities: https://wiki.mozilla.org/Security/Contextual_Identity_Project/Containers 6 | 7 | ## What it shows 8 | 9 | How to use the contextualIdentities API. Please note: you must have contextualIdentities enabled. You can do that by going to about:config and setting the `privacy.userContext.enabled` preference to true. If you are using web-ext you can do this by running: 10 | 11 | web-ext run --pref privacy.userContext.enabled=true 12 | 13 | Icon from: https://www.iconfinder.com/icons/290119/card_id_identification_identity_profile_icon#size=128, License: "Free for commercial use". 14 | -------------------------------------------------------------------------------- /contextual-identities/background.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/contextual-identities/background.js -------------------------------------------------------------------------------- /contextual-identities/context.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 350px; 3 | } 4 | 5 | a { 6 | margin: 10px; 7 | display: inline-block; 8 | } 9 | 10 | .panel { 11 | margin: 5px; 12 | } 13 | 14 | span.identity { 15 | width: 100px; 16 | display: inline-block; 17 | margin-left: 1em; 18 | } 19 | -------------------------------------------------------------------------------- /contextual-identities/context.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |
11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /contextual-identities/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "browser_action": { 3 | "browser_style": true, 4 | "default_title": "Contextual Identities", 5 | "default_popup": "context.html", 6 | "default_icon": { 7 | "128": "identity.svg" 8 | } 9 | }, 10 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/contextual-identities", 11 | "manifest_version": 2, 12 | "name": "Contextual Identities", 13 | "version": "1.0", 14 | "permissions": [ 15 | "contextualIdentities", 16 | "cookies" 17 | ], 18 | "icons": { 19 | "128": "identity.svg" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /cookie-bg-picker/background_scripts/background.js: -------------------------------------------------------------------------------- 1 | /* Retrieve any previously set cookie and send to content script */ 2 | 3 | function getActiveTab() { 4 | return browser.tabs.query({active: true, currentWindow: true}); 5 | } 6 | 7 | function cookieUpdate() { 8 | getActiveTab().then((tabs) => { 9 | // get any previously set cookie for the current tab 10 | let gettingCookies = browser.cookies.get({ 11 | url: tabs[0].url, 12 | name: "bgpicker" 13 | }); 14 | gettingCookies.then((cookie) => { 15 | if (cookie) { 16 | let cookieVal = JSON.parse(cookie.value); 17 | browser.tabs.sendMessage(tabs[0].id, {image: cookieVal.image}); 18 | browser.tabs.sendMessage(tabs[0].id, {color: cookieVal.color}); 19 | } 20 | }); 21 | }); 22 | } 23 | 24 | // update when the tab is updated 25 | browser.tabs.onUpdated.addListener(cookieUpdate); 26 | // update when the tab is activated 27 | browser.tabs.onActivated.addListener(cookieUpdate); 28 | -------------------------------------------------------------------------------- /cookie-bg-picker/content_scripts/updatebg.js: -------------------------------------------------------------------------------- 1 | browser.runtime.onMessage.addListener(updateBg); 2 | 3 | function updateBg(request, sender, sendResponse) { 4 | let html = document.querySelector('html'); 5 | let body = document.querySelector('body'); 6 | if (request.image) { 7 | html.style.backgroundImage = 'url(' + request.image + ')'; 8 | body.style.backgroundImage = 'url(' + request.image + ')'; 9 | } else if (request.color) { 10 | html.style.backgroundColor = request.color; 11 | body.style.backgroundColor = request.color; 12 | } else if (request.reset) { 13 | html.style.backgroundImage = ''; 14 | html.style.backgroundColor = ''; 15 | body.style.backgroundImage = ''; 16 | body.style.backgroundColor = ''; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cookie-bg-picker/icons/bgpicker-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/cookie-bg-picker/icons/bgpicker-32.png -------------------------------------------------------------------------------- /cookie-bg-picker/icons/bgpicker-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/cookie-bg-picker/icons/bgpicker-48.png -------------------------------------------------------------------------------- /cookie-bg-picker/popup/bgpicker.css: -------------------------------------------------------------------------------- 1 | /* General styling */ 2 | 3 | * { 4 | box-sizing: border-box; 5 | } 6 | 7 | html { 8 | font-family: sans-serif; 9 | font-size: 10px; 10 | background: rgb(150,150,150); 11 | height: 88px; 12 | margin: 0; 13 | } 14 | 15 | body { 16 | width: 208px; 17 | margin: 0 auto; 18 | height: inherit; 19 | } 20 | 21 | .bg-container button { 22 | display: inline-block; 23 | width: 50px; 24 | height: 30px; 25 | margin: 1px; 26 | } 27 | 28 | .color-reset { 29 | width: 208px; 30 | height: 20px; 31 | } 32 | 33 | input, .color-reset button { 34 | display: block; 35 | width: 48%; 36 | height: 110%; 37 | float: left; 38 | margin: 0 1% 1% 1%; 39 | } -------------------------------------------------------------------------------- /cookie-bg-picker/popup/bgpicker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /cookie-bg-picker/popup/images/bullseyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/cookie-bg-picker/popup/images/bullseyes.png -------------------------------------------------------------------------------- /cookie-bg-picker/popup/images/starring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/cookie-bg-picker/popup/images/starring.png -------------------------------------------------------------------------------- /cookie-bg-picker/popup/images/subtle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/cookie-bg-picker/popup/images/subtle.png -------------------------------------------------------------------------------- /cookie-bg-picker/popup/images/tactilenoise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/cookie-bg-picker/popup/images/tactilenoise.png -------------------------------------------------------------------------------- /cookie-bg-picker/popup/images/triangles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/cookie-bg-picker/popup/images/triangles.png -------------------------------------------------------------------------------- /cookie-bg-picker/popup/images/triangles2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/cookie-bg-picker/popup/images/triangles2.png -------------------------------------------------------------------------------- /cookie-bg-picker/popup/images/washi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/cookie-bg-picker/popup/images/washi.png -------------------------------------------------------------------------------- /cookie-bg-picker/popup/images/whitey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/cookie-bg-picker/popup/images/whitey.png -------------------------------------------------------------------------------- /devtools-inspector-sidebar/README.md: -------------------------------------------------------------------------------- 1 | # devtools-panels 2 | 3 | **Adds a new sidebar to the developer tools inspector.** 4 | 5 | ## What it does 6 | 7 | This extension adds a new sidebar to the inspector panel. 8 | It displays the properties of the current selected node in the markup view, using 9 | `sidebar.setExpression($0)` each time a new node is selected (listener added via 10 | `browser.devtools.panels.elements.onSelectionChanged`). 11 | 12 | To learn more about the devtools APIs, see [Extending the developer tools](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Extending_the_developer_tools). 13 | -------------------------------------------------------------------------------- /devtools-inspector-sidebar/devtools/devtools-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /devtools-inspector-sidebar/devtools/devtools.js: -------------------------------------------------------------------------------- 1 | /** 2 | This script is run whenever the devtools are open. 3 | In here, we can create our sidebar, and when the selected node in the inspector 4 | change, we evaluate it and display its properties in the sidebar. 5 | */ 6 | 7 | browser.devtools.panels.elements.createSidebarPane("DOM").then(sidebar => { 8 | browser.devtools.panels.elements.onSelectionChanged.addListener(() => { 9 | sidebar.setExpression(`$0`); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /devtools-inspector-sidebar/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Adds a new sidebar to the developer tools inspector panel. The sidebar displays the page document as an inspectable object.", 3 | "manifest_version": 2, 4 | "name": "devtools-inspector-sidebar", 5 | "version": "1.0", 6 | "author": "Nicolas Chevobbe", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/devtools-inspector-sidebar", 8 | 9 | "permissions": [""], 10 | 11 | "devtools_page": "devtools/devtools-page.html" 12 | } 13 | -------------------------------------------------------------------------------- /devtools-panels/background_scripts/background.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | When we receive the message, execute the given script in the given 4 | tab. 5 | */ 6 | function handleMessage(request, sender, sendResponse) { 7 | 8 | if (sender.url != browser.runtime.getURL("/devtools/panel/panel.html")) { 9 | return; 10 | } 11 | 12 | browser.tabs.executeScript( 13 | request.tabId, 14 | { 15 | code: request.script 16 | }); 17 | 18 | } 19 | 20 | /** 21 | Listen for messages from our devtools panel. 22 | */ 23 | browser.runtime.onMessage.addListener(handleMessage); 24 | -------------------------------------------------------------------------------- /devtools-panels/devtools/devtools-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /devtools-panels/devtools/devtools.js: -------------------------------------------------------------------------------- 1 | /** 2 | This script is run whenever the devtools are open. 3 | In here, we can create our panel. 4 | */ 5 | 6 | function handleShown() { 7 | console.log("panel is being shown"); 8 | } 9 | 10 | function handleHidden() { 11 | console.log("panel is being hidden"); 12 | } 13 | 14 | /** 15 | Create a panel, and add listeners for panel show/hide events. 16 | */ 17 | browser.devtools.panels.create( 18 | "My Panel", 19 | "/icons/star.png", 20 | "/devtools/panel/panel.html" 21 | ).then((newPanel) => { 22 | newPanel.onShown.addListener(handleShown); 23 | newPanel.onHidden.addListener(handleHidden); 24 | }); 25 | -------------------------------------------------------------------------------- /devtools-panels/devtools/panel/panel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /devtools-panels/icons/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/devtools-panels/icons/star.png -------------------------------------------------------------------------------- /devtools-panels/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Adds a new panel to the developer tools. The panel contains buttons that demonstrate various basic features of the devtools API.", 3 | "manifest_version": 2, 4 | "name": "devtools-panels", 5 | "version": "1.0", 6 | "author": "Christophe Villeneuve", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/devtools-panels", 8 | "icons": { 9 | "48": "icons/star.png" 10 | }, 11 | 12 | "background": { 13 | "scripts": ["background_scripts/background.js"] 14 | }, 15 | 16 | "permissions": [ 17 | "" 18 | ], 19 | 20 | "devtools_page": "devtools/devtools-page.html" 21 | 22 | } 23 | -------------------------------------------------------------------------------- /discogs-search/README.md: -------------------------------------------------------------------------------- 1 | # discogs-search 2 | 3 | ## What it does 4 | 5 | This add-on adds a search engine to the browser, that sends the search term to the [discogs.com](https://discogs.com) website. 6 | 7 | It also adds a keyword "disc", so you can type "disc Model 500" and get the discogs search engine without having to select it. 8 | 9 | ## What it shows 10 | 11 | How to use the [`chrome_settings_overrides`](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/chrome_settings_overrides) manifest key to define a new search engine. 12 | -------------------------------------------------------------------------------- /discogs-search/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "Discogs search engine", 5 | "description": "Adds a search engine that searches discogs.com", 6 | "version": "1.0", 7 | "browser_specific_settings": { 8 | "gecko": { 9 | "strict_min_version": "55.0" 10 | } 11 | }, 12 | "chrome_settings_overrides": { 13 | "search_provider": { 14 | "name": "Discogs", 15 | "search_url": "https://www.discogs.com/search/?q={searchTerms}", 16 | "keyword": "disc", 17 | "favicon_url": "https://www.discogs.com/favicon.ico", 18 | "is_default": false, 19 | "encoding": "UTF-8" 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /dnr-block-only/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "Block only, without host_permissions", 4 | "description": "Blocks requests to 'blocksub', 'blocktop', and 'blockall'. Uses the 'declarativeNetRequest' permission, meaning that host_permissions in manifest.json are not needed.", 5 | "version": "0.1", 6 | "permissions": [ 7 | "declarativeNetRequest" 8 | ], 9 | "declarative_net_request": { 10 | "rule_resources": [{ 11 | "id": "ruleset", 12 | "enabled": true, 13 | "path": "rules.json" 14 | }] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dnr-block-only/rules.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "condition": { 5 | "urlFilter": "blocksub" 6 | }, 7 | "action": { 8 | "type": "block" 9 | } 10 | }, 11 | { 12 | "id": 2, 13 | "condition": { 14 | "urlFilter": "blocktop", 15 | "resourceTypes": ["main_frame"] 16 | }, 17 | "action": { 18 | "type": "block" 19 | } 20 | }, 21 | { 22 | "id": 3, 23 | "condition": { 24 | "urlFilter": "blockall", 25 | "excludedResourceTypes": [] 26 | }, 27 | "action": { 28 | "type": "block" 29 | } 30 | } 31 | ] 32 | -------------------------------------------------------------------------------- /dnr-dynamic-with-options/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "DNR dynamic with options", 4 | "description": "Modify requests according to the rules specified by the user in the options page.", 5 | "version": "0.1", 6 | "permissions": ["declarativeNetRequestWithHostAccess"], 7 | "optional_host_permissions": ["*://*/"], 8 | "optional_permissions": ["*://*/"], 9 | "options_ui": { 10 | "page": "options.html" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dnr-dynamic-with-options/options.css: -------------------------------------------------------------------------------- 1 | .input-and-buttons legend { 2 | font-weight: bold; 3 | } 4 | .input-and-buttons textarea { 5 | display: block; 6 | width: 100%; 7 | min-height: 7em; 8 | } 9 | -------------------------------------------------------------------------------- /dnr-redirect-url/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "Redirect example.com requests", 4 | "description": "Redirects example.com requests. Redirects always require host_permissions.", 5 | "version": "0.1", 6 | "permissions": ["declarativeNetRequestWithHostAccess"], 7 | "host_permissions": ["*://*.example.com/"], 8 | "declarative_net_request": { 9 | "rule_resources": [ 10 | { 11 | "id": "ruleset", 12 | "enabled": true, 13 | "path": "redirect-rules.json" 14 | } 15 | ] 16 | }, 17 | "action": { 18 | "default_popup": "popup.html" 19 | }, 20 | "web_accessible_resources": [{ 21 | "resources": ["redirectTarget.html"], 22 | "matches": ["*://example.com/*"] 23 | }] 24 | } 25 | -------------------------------------------------------------------------------- /dynamic-theme/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "An example dynamic theme", 3 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/dynamic-theme", 4 | "manifest_version": 2, 5 | "name": "Dynamic theme example", 6 | "permissions": [ 7 | "alarms", 8 | "theme" 9 | ], 10 | "background": { 11 | "scripts": ["background.js"] 12 | }, 13 | "version": "1.0", 14 | "browser_specific_settings": { 15 | "gecko": { 16 | "strict_min_version": "55.0a2" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dynamic-theme/moon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/dynamic-theme/moon.jpg -------------------------------------------------------------------------------- /dynamic-theme/sun.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/dynamic-theme/sun.jpg -------------------------------------------------------------------------------- /emoji-substitution/README.md: -------------------------------------------------------------------------------- 1 | # Emoji Substitution 2 | 3 | **This add-on injects JavaScript into web pages. The `addons.mozilla.org` domain disallows this operation, so this add-on will not work properly when it's run on pages in the `addons.mozilla.org` domain.** 4 | 5 | ## What it does 6 | 7 | Replaces words that describe an emoji with the emoji itself. This runs as a content script and scans web pages, looking for text that can be replaced with emoji. As an example, after installing visit https://mozilla.org and notice that the text "firefox" should change. 8 | 9 | ## What it shows 10 | 11 | A good example for beginners that can be used as a "make your first add-on" tutorial and / or referenced to create other add-ons. 12 | -------------------------------------------------------------------------------- /emoji-substitution/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/emoji-substitution/icons/icon.png -------------------------------------------------------------------------------- /emoji-substitution/icons/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/emoji-substitution/icons/icon@2x.png -------------------------------------------------------------------------------- /emoji-substitution/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Emoji Substitution", 4 | "description": "Replaces words with emojis.", 5 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/emoji-substitution", 6 | "version": "1.0", 7 | "icons": { 8 | "48": "icons/icon.png", 9 | "96": "icons/icon@2x.png" 10 | }, 11 | 12 | "content_scripts": [ 13 | { 14 | "matches": [""], 15 | "js": ["./emojiMap.js", "./substitute.js"] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /eslint-example/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "webextensions": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /eslint-example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /eslint-example/README.md: -------------------------------------------------------------------------------- 1 | # ESLint Example 2 | 3 | ## What it shows 4 | 5 | This shows how to configure a WebExtension with 6 | [eslint](http://eslint.org/) 7 | to protect against 8 | writing JavaScript code that may be incompatible with modern versions of 9 | Firefox or Chrome. 10 | 11 | ## How to use it 12 | 13 | This requires [NodeJS](https://nodejs.org/en/) and [npm](http://npmjs.com/). 14 | 15 | * Change into the example directory and run `npm install` to install all 16 | dependencies. 17 | * Execute `npm run lint` to view a report of any coding errors. 18 | -------------------------------------------------------------------------------- /eslint-example/file.js: -------------------------------------------------------------------------------- 1 | // This special eslint comment will declare that the named 2 | // function has been "exported" into the global scope. 3 | 4 | /* exported getUsefulContents */ 5 | function getUsefulContents(callback) { 6 | callback('Hello World'); 7 | } 8 | -------------------------------------------------------------------------------- /eslint-example/icons/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The "page-32.png" and "page-48.png" icons are taken from the miu iconset created by Linh Pham Thi Dieu, and are used under the terms of its license: http://linhpham.me/miu/. 3 | -------------------------------------------------------------------------------- /eslint-example/icons/page-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/eslint-example/icons/page-32.png -------------------------------------------------------------------------------- /eslint-example/icons/page-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/eslint-example/icons/page-48.png -------------------------------------------------------------------------------- /eslint-example/main.js: -------------------------------------------------------------------------------- 1 | // This special eslint comment declares that the code below relies on 2 | // a named function in the global scope. 3 | 4 | /* global getUsefulContents */ 5 | function start() { 6 | getUsefulContents(data => { 7 | let display = document.getElementById('display'); 8 | 9 | display.innerHTML = data; 10 | }); 11 | } 12 | 13 | document.addEventListener('DOMContentLoaded', start); 14 | -------------------------------------------------------------------------------- /eslint-example/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "description": "Example using eslint", 4 | "name": "eslint-example", 5 | "version": "1.0", 6 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/eslint-example", 7 | 8 | "browser_action": { 9 | "default_icon": "icons/page-32.png", 10 | "default_popup": "popup.html" 11 | } 12 | } -------------------------------------------------------------------------------- /eslint-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "devDependencies": { 7 | "eslint": "^3.19.0" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "lint": "eslint ." 12 | }, 13 | "author": "", 14 | "license": "ISC" 15 | } 16 | -------------------------------------------------------------------------------- /eslint-example/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Pop-up 6 | 7 | 8 |

Example.com

9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /export-helpers/background.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Show a notification when we get messages from the content script. 3 | */ 4 | browser.runtime.onMessage.addListener((message) => { 5 | browser.notifications.create({ 6 | type: "basic", 7 | title: "Message from the page", 8 | message: message.content 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /export-helpers/content_scripts/export.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Define a function in the content script's scope, then export it 4 | * into the page script's scope. 5 | */ 6 | function notify(message) { 7 | browser.runtime.sendMessage({content: "Function call: " + message}); 8 | } 9 | 10 | exportFunction(notify, window, {defineAs:'notify'}); 11 | 12 | /** 13 | * Create an object that contains functions in the content script's scope, 14 | * then clone it into the page script's scope. 15 | * 16 | * Because the object contains functions, the cloneInto call must include 17 | * the `cloneFunctions` option. 18 | */ 19 | let messenger = { 20 | notify: function(message) { 21 | browser.runtime.sendMessage({content: "Object method call: " + message}); 22 | } 23 | }; 24 | 25 | window.wrappedJSObject.messenger = cloneInto(messenger, window, {cloneFunctions: true}); 26 | -------------------------------------------------------------------------------- /export-helpers/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The "arrow.svg" icon is from the Freecns Cumulus iconset by Yannick Lung 2 | and is used here under the terms of its license. 3 | 4 | https://www.iconfinder.com/iconsets/freecns-cumulus 5 | http://yannicklung.com/ 6 | -------------------------------------------------------------------------------- /export-helpers/icons/arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /export-helpers/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Demonstrates how to use export helpers in Firefox to share objects with page scripts.", 4 | "manifest_version": 2, 5 | "name": "export-helpers", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/export-helpers", 8 | "icons": { 9 | "48": "icons/arrow.svg" 10 | }, 11 | 12 | "browser_specific_settings": { 13 | "gecko": { 14 | "id": "export-helpers@mozilla.org", 15 | "strict_min_version": "49.0" 16 | } 17 | }, 18 | 19 | "background": { 20 | "scripts": ["background.js"] 21 | }, 22 | 23 | "content_scripts": [ 24 | { 25 | "matches": ["https://mdn.github.io/webextensions-examples/export-helpers.html"], 26 | "js": ["content_scripts/export.js"] 27 | } 28 | ], 29 | 30 | "permissions": [ 31 | "activeTab", 32 | "notifications" 33 | ] 34 | 35 | } 36 | -------------------------------------------------------------------------------- /favourite-colour/background.js: -------------------------------------------------------------------------------- 1 | function handleClick() { 2 | browser.runtime.openOptionsPage(); 3 | } 4 | 5 | browser.browserAction.onClicked.addListener(handleClick); 6 | -------------------------------------------------------------------------------- /favourite-colour/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "background": { 3 | "scripts": ["background.js"] 4 | }, 5 | "browser_action": { 6 | "default_title": "Favourite colour option" 7 | }, 8 | "description": "An example options ui", 9 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/favourite-colour", 10 | "manifest_version": 2, 11 | "name": "Favourite colour", 12 | "options_ui": { 13 | "page": "options.html", 14 | "browser_style": true 15 | }, 16 | "permissions": ["storage"], 17 | "version": "1.1", 18 | "browser_specific_settings": { 19 | "gecko": { 20 | "id": "favourite-colour-examples@mozilla.org", 21 | "strict_min_version": "57.0a1" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /favourite-colour/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

storage.managed colour: no value found

11 |
12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /favourite-colour/options.js: -------------------------------------------------------------------------------- 1 | async function saveOptions(e) { 2 | e.preventDefault(); 3 | await browser.storage.sync.set({ 4 | colour: document.querySelector("#colour").value 5 | }); 6 | } 7 | 8 | async function restoreOptions() { 9 | let res = await browser.storage.managed.get('colour'); 10 | document.querySelector("#managed-colour").innerText = res.colour; 11 | 12 | res = await browser.storage.sync.get('colour'); 13 | document.querySelector("#colour").value = res.colour || 'Firefox red'; 14 | } 15 | 16 | document.addEventListener('DOMContentLoaded', restoreOptions); 17 | document.querySelector("form").addEventListener("submit", saveOptions); 18 | -------------------------------------------------------------------------------- /find-across-tabs/README.md: -------------------------------------------------------------------------------- 1 | ## What it does 2 | 3 | This extension searches across multiple tabs and calls find on each of those pages. If it finds the string, the string is highlighted. 4 | 5 | ## What it shows 6 | 7 | How to use the tabs, find and sendMessage APIs. 8 | 9 | Icon is from: http://bit.ly/2xyHiZv 10 | -------------------------------------------------------------------------------- /find-across-tabs/find.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Find across tabs

11 |

Search across all the tabs and highlight the results in those tabs.

12 |

Please note: this tab is excluded from the search.

13 |
14 |
15 | 16 |
17 | 18 |
19 |
20 |

Results

21 |
    22 |
23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /find-across-tabs/find.js: -------------------------------------------------------------------------------- 1 | let backgroundPage = browser.extension.getBackgroundPage(); 2 | 3 | document.getElementById("find-form").addEventListener("submit", function(e) { 4 | // Send the query from the form to the background page. 5 | backgroundPage.find(document.getElementById("find-input").value); 6 | e.preventDefault(); 7 | }); 8 | 9 | let results = document.getElementById("result-list"); 10 | 11 | function handleMessage(request, sender, response) { 12 | // Handle responses coming back from the background page. 13 | if (request.msg === "clear-results") { 14 | results.innerHTML = ""; 15 | } 16 | if (request.msg === "found-result") { 17 | // List out responses from the background page as they come in. 18 | let li = document.createElement("li"); 19 | li.innerText = `Tab id: ${request.id} at url: ${request.url} had ${request.count} hits.`; 20 | results.appendChild(li); 21 | } 22 | } 23 | 24 | browser.runtime.onMessage.addListener(handleMessage); 25 | -------------------------------------------------------------------------------- /find-across-tabs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Find across all your tabs", 3 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/find-across-tabs", 4 | "manifest_version": 2, 5 | "name": "Find across tabs", 6 | "permissions": [ 7 | "find", 8 | "tabs" 9 | ], 10 | "background": { 11 | "scripts": ["background.js"] 12 | }, 13 | "icons": { 14 | "32": "find.svg" 15 | }, 16 | "browser_action": { 17 | "browser_style": true, 18 | "default_title": "Find across tabs", 19 | "default_icon": { 20 | "32": "find.svg" 21 | } 22 | }, 23 | "version": "1.0", 24 | "browser_specific_settings": { 25 | "gecko": { 26 | "strict_min_version": "57.0a1" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /firefox-code-search/README.md: -------------------------------------------------------------------------------- 1 | # firefox-code-search 2 | 3 | ## What it does 4 | 5 | This extension allows you to search the Firefox codebase using the awesome bar. 6 | 7 | To test it out, type 'cs' into the awesome bar followed by a search string (e.g. `cs hello world`). The results will be shown as suggestions, and clicking on a suggestion will navigate to the file where the result was found. 8 | 9 | To search a specific file, use the "path:" prefix (e.g. `cs hello path:omnibox.js`) 10 | 11 | ## What it shows 12 | 13 | How to use the omnibox API to add custom suggestions to the awesome bar. -------------------------------------------------------------------------------- /firefox-code-search/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Firefox Code Search", 3 | "description" : "To use, type 'cs' plus a search term into the url bar.", 4 | "version": "1.0", 5 | "browser_specific_settings": { 6 | "gecko": { 7 | "strict_min_version": "52.0a1" 8 | } 9 | }, 10 | "background": { 11 | "scripts": ["background.js"] 12 | }, 13 | "omnibox": { "keyword" : "cs" }, 14 | "manifest_version": 2, 15 | "permissions": [ 16 | "https://searchfox.org/" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /forget-it/README.md: -------------------------------------------------------------------------------- 1 | # Forget it! 2 | 3 | This add-on adds a button to the browser's toolbar. When the user clicks the button, the add-on clears some browsing data using the browsingData API. The details of what exactly it clears are determined by the add-on's settings, which can be accessed and modified in its options page. 4 | 5 | The example shows how to use the browsingData API, and how to use the options page to handle settings. 6 | -------------------------------------------------------------------------------- /forget-it/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The trash.svg” icon is taken from the miu iconset and is used under the terms of its license: https://www.iconfinder.com/iconsets/miu. 2 | -------------------------------------------------------------------------------- /forget-it/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Forget it!", 4 | "manifest_version": 2, 5 | "name": "forget-it", 6 | "version": "2.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/forget-it", 8 | "icons": { 9 | "48": "icons/trash.svg" 10 | }, 11 | 12 | "background": { 13 | "scripts": ["background.js"] 14 | }, 15 | 16 | "browser_action": { 17 | "default_icon": "icons/trash.svg", 18 | "default_title": "Forget it!" 19 | }, 20 | 21 | "options_ui": { 22 | "page": "options/options.html" 23 | }, 24 | 25 | "permissions": [ 26 | "browsingData", 27 | "notifications", 28 | "storage" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /forget-it/options/options.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | width: 25em; 4 | font-family: "Open Sans Light", sans-serif; 5 | font-size: 0.9em; 6 | font-weight: 300; 7 | } 8 | 9 | section.clear-options { 10 | padding: 0.5em 0; 11 | margin: 1em 0; 12 | } 13 | 14 | #clear-button { 15 | margin: 0 1.3em 1em 0; 16 | } 17 | 18 | section.clear-options input, 19 | section.clear-options>select, 20 | #clear-button { 21 | float: right; 22 | } 23 | 24 | label { 25 | display: block; 26 | padding: 0.2em 0; 27 | } 28 | 29 | label:hover { 30 | background-color: #EAEFF2; 31 | } 32 | 33 | .title { 34 | font-size: 1.2em; 35 | margin-bottom: 0.5em; 36 | } 37 | -------------------------------------------------------------------------------- /google-userinfo/background/main.js: -------------------------------------------------------------------------------- 1 | /*global getAccessToken*/ 2 | 3 | function notifyUser(user) { 4 | browser.notifications.create({ 5 | "type": "basic", 6 | "title": "Google info", 7 | "message": `Hi ${user.name}` 8 | });} 9 | 10 | function logError(error) { 11 | console.error(`Error: ${error}`); 12 | } 13 | 14 | /** 15 | When the button's clicked: 16 | - get an access token using the identity API 17 | - use it to get the user's info 18 | - show a notification containing some of it 19 | */ 20 | browser.browserAction.onClicked.addListener(() => { 21 | getAccessToken() 22 | .then(getUserInfo) 23 | .then(notifyUser) 24 | .catch(logError); 25 | }); 26 | -------------------------------------------------------------------------------- /google-userinfo/background/userinfo.js: -------------------------------------------------------------------------------- 1 | /** 2 | Fetch the user's info, passing in the access token in the Authorization 3 | HTTP request header. 4 | */ 5 | 6 | /* exported getUserInfo */ 7 | 8 | function getUserInfo(accessToken) { 9 | const requestURL = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json"; 10 | const requestHeaders = new Headers(); 11 | requestHeaders.append('Authorization', 'Bearer ' + accessToken); 12 | const driveRequest = new Request(requestURL, { 13 | method: "GET", 14 | headers: requestHeaders 15 | }); 16 | 17 | return fetch(driveRequest).then((response) => { 18 | if (response.status === 200) { 19 | return response.json(); 20 | } else { 21 | throw response.status; 22 | } 23 | }); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /google-userinfo/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The "person-32.png" "person-48.png" icons are taken from the Ionicons iconset (http://ionicons.com/), and are used here under the MIT license: http://opensource.org/licenses/MIT. 2 | -------------------------------------------------------------------------------- /google-userinfo/icons/person-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/google-userinfo/icons/person-32.png -------------------------------------------------------------------------------- /google-userinfo/icons/person-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/google-userinfo/icons/person-48.png -------------------------------------------------------------------------------- /google-userinfo/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "name": "Google User Info", 4 | "version": "1", 5 | "manifest_version": 2, 6 | "browser_specific_settings": { 7 | "gecko": { 8 | "id": "google-user-info@mozilla.org", 9 | "strict_min_version": "53a1" 10 | } 11 | }, 12 | 13 | "icons": { 14 | "48": "icons/person-48.png" 15 | }, 16 | 17 | "browser_action": { 18 | "browser_style": true, 19 | "default_icon": "icons/person-32.png" 20 | }, 21 | 22 | "permissions": [ 23 | "identity", 24 | "notifications", 25 | "*://www.googleapis.com/*", 26 | "*://accounts.google.com/*" 27 | ], 28 | 29 | "background": { 30 | "scripts": [ 31 | "background/authorize.js", 32 | "background/userinfo.js", 33 | "background/main.js" 34 | ] 35 | }, 36 | 37 | "options_ui": { 38 | "page": "options/options.html" 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /google-userinfo/options/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Redirect URL:
11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /google-userinfo/options/options.js: -------------------------------------------------------------------------------- 1 | /** 2 | Display the redirect URL. 3 | */ 4 | document.querySelector("#redirect-url").textContent = browser.identity.getRedirectURL(); 5 | -------------------------------------------------------------------------------- /history-deleter/README.md: -------------------------------------------------------------------------------- 1 | # History deleter 2 | 3 | ## What it does 4 | 5 | This extension includes a page action with a popup specified as "history.html". The page action will not appear on about:... pages. 6 | 7 | The popup shows a list of 5 history entries for the current domain. It provides a clear button to delete all entries for that domain. 8 | 9 | ## What it shows 10 | 11 | How to use the history API. 12 | -------------------------------------------------------------------------------- /history-deleter/background.js: -------------------------------------------------------------------------------- 1 | browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { 2 | if (!tab.url.match(/^about:/)) { 3 | browser.pageAction.show(tab.id); 4 | } 5 | }); 6 | -------------------------------------------------------------------------------- /history-deleter/history.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0.3em; 3 | width: auto; 4 | min-width: 250px; 5 | max-width: 500px; 6 | background-color: #f8f8f8; 7 | } 8 | 9 | div { 10 | margin-left: 0.5em; 11 | margin-right: 0.5em; 12 | border-bottom: 1px solid grey; 13 | } 14 | 15 | #history-title { 16 | font-weight: bold; 17 | margin-left: 0.5em; 18 | margin-right: 0.5em; 19 | } 20 | 21 | #history { 22 | display: block; 23 | word-wrap: break-word; 24 | margin: 0.5em; 25 | text-decoration: none; 26 | } 27 | 28 | #clear { 29 | text-decoration: none; 30 | margin-left: 0.5em; 31 | margin-right: 0.5em; 32 | } -------------------------------------------------------------------------------- /history-deleter/history.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

History for (Last 5 results) :

11 |
12 |

13 |
14 |

Clear history for

15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /history-deleter/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The history.svg” icon is taken from the Font Awesome iconset (http://fontawesome.io/icons/) and is used under the terms of its license: http://fontawesome.io/license/. 2 | -------------------------------------------------------------------------------- /history-deleter/icons/history.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /history-deleter/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "background": { 3 | "scripts": ["background.js"] 4 | }, 5 | "description": "Gives a popup to list and delete history on a domain.", 6 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/history-deleter", 7 | "page_action": { 8 | "default_title": "History deleter", 9 | "default_popup": "history.html", 10 | "default_icon": "icons/history.svg" 11 | }, 12 | "icons": { 13 | "48": "icons/history.svg", 14 | "96": "icons/history.svg" 15 | }, 16 | "permissions": [ 17 | "activeTab", 18 | "history", 19 | "tabs" 20 | ], 21 | "manifest_version": 2, 22 | "name": "History Deleter", 23 | "version": "1.0" 24 | } 25 | -------------------------------------------------------------------------------- /http-response/README.md: -------------------------------------------------------------------------------- 1 | # HTTP Response parser 2 | 3 | ## What it does 4 | 5 | Listens to HTTP Responses from example.com and changes the body of the response as it comes through. So that the word "Example" on https://example.com becomes "WebExtension Example". 6 | 7 | ## What it shows 8 | 9 | How to use the response parser on bytes. 10 | 11 | Icon is from: https://www.iconfinder.com/icons/763339/draw_edit_editor_pen_pencil_tool_write_icon#size=128 12 | -------------------------------------------------------------------------------- /http-response/background.js: -------------------------------------------------------------------------------- 1 | function listener(details) { 2 | let filter = browser.webRequest.filterResponseData(details.requestId); 3 | let decoder = new TextDecoder("utf-8"); 4 | let encoder = new TextEncoder(); 5 | 6 | filter.ondata = event => { 7 | let str = decoder.decode(event.data, {stream: true}); 8 | // Just change any instance of Example in the HTTP response 9 | // to WebExtension Example. 10 | str = str.replace(/Example/g, 'WebExtension Example'); 11 | filter.write(encoder.encode(str)); 12 | filter.disconnect(); 13 | } 14 | 15 | return {}; 16 | } 17 | 18 | browser.webRequest.onBeforeRequest.addListener( 19 | listener, 20 | {urls: ["https://example.com/*"], types: ["main_frame"]}, 21 | ["blocking"] 22 | ); 23 | -------------------------------------------------------------------------------- /http-response/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Altering HTTP responses", 4 | "manifest_version": 2, 5 | "name": "http-response-filter", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/http-response", 8 | "icons": { 9 | "48": "pen.svg" 10 | }, 11 | 12 | "permissions": [ 13 | "webRequest", "webRequestBlocking", "https://example.com/*" 14 | ], 15 | 16 | "background": { 17 | "scripts": ["background.js"] 18 | }, 19 | 20 | "browser_specific_settings": { 21 | "gecko": { 22 | "strict_min_version": "57.0a1" 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /http-response/pen.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /imagify/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Adds a sidebar offering a file picker and drap and drop zone. When an image file is chosen the active tab's body content is replaced with file selected. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#imagify", 4 | "manifest_version": 2, 5 | "name": "Imagify", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/imagify", 8 | 9 | "permissions": [ 10 | "tabs", 11 | "" 12 | ], 13 | 14 | "sidebar_action": { 15 | "default_title": "Imagify", 16 | "default_panel": "sidebar/sidebar.html" 17 | }, 18 | 19 | "web_accessible_resources": [ 20 | "/viewer.html" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /imagify/sidebar/sidebar.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | #drop_zone { 7 | border: 5px solid blue; 8 | width: 100%; 9 | height: 100%; 10 | } 11 | 12 | #drop_zone_label { 13 | margin: 1em; 14 | display: block; 15 | } -------------------------------------------------------------------------------- /imagify/sidebar/sidebar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 | Drag an image file into this Drop Zone ... 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /imagify/viewer.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | img { 7 | width: 100%; 8 | } 9 | -------------------------------------------------------------------------------- /imagify/viewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /imagify/viewer.js: -------------------------------------------------------------------------------- 1 | const params = new URLSearchParams(window.location.search); 2 | const imageBlobURL = params.get("blobURL"); 3 | document.querySelector("img").setAttribute("src", imageBlobURL); 4 | -------------------------------------------------------------------------------- /latest-download/README.md: -------------------------------------------------------------------------------- 1 | # latest-download 2 | 3 | ## What it does ## 4 | 5 | The extension includes a browser action with a popup. 6 | 7 | When the user clicks the browser action button, the popup is shown. 8 | The popup displays the most recent download, and has buttons to open the 9 | file or to remove it. 10 | 11 | If the user removes it, the file is removed from disk and from the browser's 12 | downloads history. 13 | 14 | ## What it shows ## 15 | 16 | * how to use various parts of the downloads API 17 | -------------------------------------------------------------------------------- /latest-download/icons/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The "page-32.png" and "page-48.png" icons are taken from the miu iconset created by Linh Pham Thi Dieu, and are used under the terms of its license: http://linhpham.me/miu/. 3 | -------------------------------------------------------------------------------- /latest-download/icons/page-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/latest-download/icons/page-32.png -------------------------------------------------------------------------------- /latest-download/icons/page-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/latest-download/icons/page-48.png -------------------------------------------------------------------------------- /latest-download/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Shows the last downloaded item, and lets you open or delete it", 4 | "manifest_version": 2, 5 | "name": "latest-download", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/latest-download", 8 | "icons": { 9 | "48": "icons/page-48.png" 10 | }, 11 | 12 | "permissions": [ 13 | "downloads", 14 | "downloads.open" 15 | ], 16 | 17 | "browser_action": { 18 | "browser_style": true, 19 | "default_icon": "icons/page-32.png", 20 | "default_title": "Latest download", 21 | "default_popup": "popup/latest_download.html" 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /latest-download/popup/latest_download.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /list-cookies/README.md: -------------------------------------------------------------------------------- 1 | # list-cookies 2 | 3 | ## What it does 4 | 5 | This extensions list the cookies in the active tab. 6 | 7 | # What it shows 8 | 9 | Demonstration of the getAll() function in the cookie API 10 | -------------------------------------------------------------------------------- /list-cookies/cookies.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 500px; 3 | } 4 | 5 | .panel { 6 | padding: 5px; 7 | } 8 | 9 | li { 10 | margin-bottom: 5px; 11 | } 12 | -------------------------------------------------------------------------------- /list-cookies/cookies.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 | 16 |
17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /list-cookies/icons/cookie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/list-cookies/icons/cookie.png -------------------------------------------------------------------------------- /list-cookies/icons/cookie@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/list-cookies/icons/cookie@2x.png -------------------------------------------------------------------------------- /list-cookies/icons/default19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/list-cookies/icons/default19.png -------------------------------------------------------------------------------- /list-cookies/icons/default38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/list-cookies/icons/default38.png -------------------------------------------------------------------------------- /list-cookies/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "browser_action": { 3 | "browser_style": true, 4 | "default_title": "List cookies in the active tab", 5 | "default_popup": "cookies.html", 6 | "default_icon": { 7 | "19": "icons/default19.png", 8 | "38": "icons/default38.png" 9 | } 10 | }, 11 | "description": "List cookies in the active tab.", 12 | "icons": { 13 | "48": "icons/cookie.png", 14 | "96": "icons/cookie@2x.png" 15 | }, 16 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/list-cookies", 17 | "manifest_version": 2, 18 | "name": "List cookies", 19 | "version": "1.0", 20 | "permissions": ["cookies","","tabs"] 21 | } 22 | -------------------------------------------------------------------------------- /menu-accesskey-visible/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Menu item with access key", 4 | "description": "Name of the extension." 5 | }, 6 | 7 | "extensionDescription": { 8 | "message": "Demonstrates access keys in context menu items (if supported).", 9 | "description": "Description of the add-on." 10 | }, 11 | 12 | "menuItemWithAccessKey": { 13 | "message": "Click &here && look at the extension's console", 14 | "description": "Title of context menu item that logs a message on click. The letter after the first '&' is used as an access key. To show a single '&' in the menu, use '&&'." 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /menu-accesskey-visible/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "__MSG_extensionName__", 5 | "description": "__MSG_extensionDescription__", 6 | "version": "1.0", 7 | "default_locale": "en", 8 | "browser_specific_settings": { 9 | "gecko": { 10 | "strict_min_version": "56.0a1" 11 | } 12 | }, 13 | 14 | "background": { 15 | "scripts": ["background.js"] 16 | }, 17 | 18 | "permissions": [ 19 | "menus" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /menu-demo/icons/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The "page-32.png" and "page-48.png" icons are taken from the miu iconset created by Linh Pham Thi Dieu, and are used under the terms of its license: http://linhpham.me/miu/. 3 | 4 | The "paint-blue-16", "paint-blue-32", "paint-green-16", and "paint-green-32" icons are adapted from an icon in the ["Outline icons" set](https://www.iconfinder.com/icons/1021026/paint_icon). 5 | -------------------------------------------------------------------------------- /menu-demo/icons/page-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/menu-demo/icons/page-16.png -------------------------------------------------------------------------------- /menu-demo/icons/page-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/menu-demo/icons/page-32.png -------------------------------------------------------------------------------- /menu-demo/icons/page-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/menu-demo/icons/page-48.png -------------------------------------------------------------------------------- /menu-demo/icons/paint-blue-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/menu-demo/icons/paint-blue-16.png -------------------------------------------------------------------------------- /menu-demo/icons/paint-blue-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/menu-demo/icons/paint-blue-32.png -------------------------------------------------------------------------------- /menu-demo/icons/paint-green-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/menu-demo/icons/paint-green-16.png -------------------------------------------------------------------------------- /menu-demo/icons/paint-green-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/menu-demo/icons/paint-green-32.png -------------------------------------------------------------------------------- /menu-demo/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "__MSG_extensionName__", 5 | "description": "__MSG_extensionDescription__", 6 | "version": "1.0", 7 | "default_locale": "en", 8 | "browser_specific_settings": { 9 | "gecko": { 10 | "strict_min_version": "56.0a1" 11 | } 12 | }, 13 | 14 | "background": { 15 | "scripts": ["background.js"] 16 | }, 17 | 18 | "permissions": [ 19 | "menus", 20 | "activeTab" 21 | ], 22 | 23 | "icons": { 24 | "16": "icons/page-16.png", 25 | "32": "icons/page-32.png", 26 | "48": "icons/page-48.png" 27 | }, 28 | 29 | "sidebar_action": { 30 | "default_icon": "icons/page-32.png", 31 | "default_title" : "My sidebar", 32 | "default_panel": "sidebar/sidebar.html" 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /menu-demo/sidebar/sidebar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

My panel

11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /menu-labelled-open/README.md: -------------------------------------------------------------------------------- 1 | # menu-labelled-open 2 | 3 | ## What it does 4 | 5 | This extension adds a menu item that's shown when the context menu is shown over a link. When the item is clicked, it just opens the link in the current tab. 6 | 7 | The extension also listens for the `onShown` event: when this event is fired, the extension gets the hostname for the link and displays it in the menu item's title, so the user knows the hostname for the link they are thinking of clicking. 8 | 9 | ## What it shows 10 | 11 | This extension is a demo of the [`menus.onShown` ](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/menus/onShown) and [`menus.refresh()`](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/menus/refresh) features of the `menus` API. 12 | 13 | The `onShown` event enables extensions to be notified when the menu is shown. At that point they are able to add, remove, or update their menu items, then refresh the menu using `refresh()`. 14 | -------------------------------------------------------------------------------- /menu-labelled-open/background.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const openLabelledId = "open-labelled"; 4 | 5 | browser.menus.create({ 6 | id: openLabelledId, 7 | title: "Open", 8 | contexts: ["link"] 9 | }); 10 | 11 | browser.menus.onClicked.addListener((info, tab) => { 12 | if (info.menuItemId === openLabelledId) { 13 | browser.tabs.update(tab.id, { 14 | url: info.linkUrl 15 | }); 16 | } 17 | }); 18 | 19 | function updateMenuItem(linkHostname) { 20 | browser.menus.update(openLabelledId, { 21 | title: `Open (${linkHostname})` 22 | }); 23 | browser.menus.refresh(); 24 | } 25 | 26 | browser.menus.onShown.addListener(info => { 27 | if (!info.linkUrl) { 28 | return; 29 | } 30 | let linkElement = document.createElement("a"); 31 | linkElement.href = info.linkUrl; 32 | updateMenuItem(linkElement.hostname); 33 | }); 34 | -------------------------------------------------------------------------------- /menu-labelled-open/icon/LICENSE: -------------------------------------------------------------------------------- 1 | The "label.svg" icon is taken from the PICOL iconset (http://www.picol.org/) and is used here under the terms of the Creative Commons Attribution-ShareAlike 3.0 license (https://creativecommons.org/licenses/by-sa/3.0/). 2 | -------------------------------------------------------------------------------- /menu-labelled-open/icon/label.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /menu-labelled-open/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "Labelled open", 5 | "description": "Adds a context menu item that labels links with the hostname. Demo of onShown and refresh().", 6 | "version": "1.0", 7 | "browser_specific_settings": { 8 | "gecko": { 9 | "strict_min_version": "60.0a1" 10 | } 11 | }, 12 | "icons": { 13 | "16": "icon/label.svg", 14 | "32": "icon/label.svg", 15 | "48": "icon/label.svg" 16 | }, 17 | 18 | "background": { 19 | "scripts": ["background.js"] 20 | }, 21 | 22 | "permissions": [ 23 | "menus", 24 | "" 25 | ] 26 | 27 | } 28 | -------------------------------------------------------------------------------- /menu-remove-element/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Remove element on click", 3 | "description": "Adds a context menu item that shows a panel upon click, from where you can choose to remove the clicked element.", 4 | "version": "1", 5 | "manifest_version": 2, 6 | "background": { 7 | "scripts": ["background.js"] 8 | }, 9 | "page_action": { 10 | "default_popup": "popup.html" 11 | }, 12 | "permissions": [ 13 | "menus", 14 | "activeTab" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /menu-remove-element/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | Open a menu in the current tab and right-click on the "Remove element" option. 6 |
7 | 8 | -------------------------------------------------------------------------------- /menu-search/README.md: -------------------------------------------------------------------------------- 1 | # menu-search 2 | 3 | A demo of the [search API](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/search/). 4 | 5 | ## What it does 6 | 7 | This add-on retrieves the list of installed search engines, and adds a context menu item for each one, displayed in the "selection" context. 8 | 9 | Each context menu item searches for the selected text using the corresponding search engine. 10 | 11 | This enables a user to: 12 | * select some text to search for 13 | * activate the context menu 14 | * choose which search engine to use for the search. 15 | 16 | ## What it shows 17 | 18 | * How to retrieve the set of search engines. 19 | * How to search using a specific search engine. 20 | 21 | -------------------------------------------------------------------------------- /menu-search/background.js: -------------------------------------------------------------------------------- 1 | /* 2 | Create a menu item for each installed search engine. 3 | The ID and title are both set to the search engine's name. 4 | */ 5 | function createMenuItem(engines) { 6 | for (let engine of engines) { 7 | browser.menus.create({ 8 | id: engine.name, 9 | title: engine.name, 10 | contexts: ["selection"] 11 | }); 12 | } 13 | } 14 | 15 | browser.search.get().then(createMenuItem); 16 | 17 | /* 18 | Search using the search engine whose name matches the 19 | menu item's ID. 20 | */ 21 | browser.menus.onClicked.addListener((info, tab) => { 22 | browser.search.search({ 23 | query: info.selectionText, 24 | engine: info.menuItemId 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /menu-search/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The "page-32.png" and "page-48.png" icons are taken from the miu iconset created by Linh Pham Thi Dieu, and are used under the terms of its license: http://linhpham.me/miu/. -------------------------------------------------------------------------------- /menu-search/icons/page-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/menu-search/icons/page-16.png -------------------------------------------------------------------------------- /menu-search/icons/page-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/menu-search/icons/page-32.png -------------------------------------------------------------------------------- /menu-search/icons/page-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/menu-search/icons/page-48.png -------------------------------------------------------------------------------- /menu-search/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "Search using...", 5 | "description": "Search ", 6 | "version": "1.0", 7 | "browser_specific_settings": { 8 | "gecko": { 9 | "strict_min_version": "63.0b14" 10 | } 11 | }, 12 | 13 | "background": { 14 | "scripts": ["background.js"] 15 | }, 16 | 17 | "permissions": [ 18 | "menus", 19 | "search" 20 | ], 21 | 22 | "icons": { 23 | "16": "icons/page-16.png", 24 | "32": "icons/page-32.png", 25 | "48": "icons/page-48.png" 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /mocha-client-tests/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "amd": true, 6 | "webextensions": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /mocha-client-tests/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | addon/bower_components 3 | addon/node_modules 4 | .idea -------------------------------------------------------------------------------- /mocha-client-tests/addon/background.js: -------------------------------------------------------------------------------- 1 | let Background = { 2 | receiveMessage: function(msg, sender, sendResponse) { 3 | if (msg && msg.action && Background.hasOwnProperty(msg.action)) { 4 | return Background[msg.action](msg, sender, sendResponse); 5 | } else { 6 | console.warn('No handler for message: ' + JSON.stringify(msg)); 7 | } 8 | }, 9 | ping: function(msg, sender, sendResponse) { 10 | sendResponse('pong'); 11 | return true; 12 | } 13 | }; 14 | 15 | browser.runtime.onMessage.addListener(Background.receiveMessage); 16 | -------------------------------------------------------------------------------- /mocha-client-tests/addon/images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/mocha-client-tests/addon/images/icon-16.png -------------------------------------------------------------------------------- /mocha-client-tests/addon/images/icon-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/mocha-client-tests/addon/images/icon-19.png -------------------------------------------------------------------------------- /mocha-client-tests/addon/images/mocha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/mocha-client-tests/addon/images/mocha.png -------------------------------------------------------------------------------- /mocha-client-tests/addon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mocha tests", 3 | "version": "1.0", 4 | "manifest_version": 2, 5 | "description": "Check ", 6 | "icons": { 7 | "16": "images/icon-16.png" 8 | }, 9 | "short_name": "MochaTest", 10 | "background": { 11 | "scripts": [ 12 | "background.js" 13 | ] 14 | }, 15 | "browser_action": { 16 | "default_icon": { 17 | "19": "images/icon-19.png" 18 | }, 19 | "default_title": "Mocha Test", 20 | "default_popup": "popup.html" 21 | }, 22 | "browser_specific_settings": { 23 | "gecko": { 24 | "strict_min_version": "45.0" 25 | } 26 | }, 27 | "content_security_policy": "script-src 'self'; object-src 'self'; img-src 'self'" 28 | } 29 | -------------------------------------------------------------------------------- /mocha-client-tests/addon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mocha-tests-webextension", 3 | "version": "1.0.0", 4 | "description": "Run test inside your addon", 5 | "main": "background.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "MPL-2.0", 11 | "devDependencies": { 12 | "expect.js": "^0.3.1", 13 | "mocha": "^3.1.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /mocha-client-tests/addon/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha Tests 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 |
Hello! Lets play at ping
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /mocha-client-tests/addon/scripts/popup.js: -------------------------------------------------------------------------------- 1 | setInterval(function() { 2 | let $game = document.querySelector('#game'); 3 | if($game.innerText !== 'ping'){ 4 | $game.innerText = 'ping'; 5 | } else{ 6 | browser.runtime.sendMessage({action: 'ping'}).then((response) => { 7 | $game.innerText = response; 8 | }); 9 | } 10 | }, 1000); 11 | -------------------------------------------------------------------------------- /mocha-client-tests/addon/tests/lib/background-messaging.test.js: -------------------------------------------------------------------------------- 1 | describe('Background', function() { 2 | describe('ping', function() { 3 | it('should return pong in response', function() { 4 | // Return a promise for Mocha using the Firefox browser API instead of chrome. 5 | return browser.runtime.sendMessage({action: 'ping'}) 6 | .then(function(response) { 7 | expect(response).to.equal('pong'); 8 | }); 9 | }); 10 | }); 11 | }); -------------------------------------------------------------------------------- /mocha-client-tests/addon/tests/lib/test.array.js: -------------------------------------------------------------------------------- 1 | describe('Array', function() { 2 | describe('#indexOf()', function() { 3 | it('should return -1 when the value is not present', function() { 4 | expect([1,2,3]).to.not.contain(5); 5 | expect([1,2,3]).to.not.contain(0); 6 | }); 7 | }); 8 | }); -------------------------------------------------------------------------------- /mocha-client-tests/addon/tests/mocha-run.js: -------------------------------------------------------------------------------- 1 | mocha.checkLeaks(); 2 | // Here we add initial global variables to prevent this error: 3 | // Error: global leaks detected: AppView, ExtensionOptions, ExtensionView, WebView 4 | mocha.globals(['AppView', 'ExtensionOptions', 'ExtensionView', 'WebView']); 5 | mocha.run(); -------------------------------------------------------------------------------- /mocha-client-tests/addon/tests/mocha-setup.js: -------------------------------------------------------------------------------- 1 | mocha.setup('bdd'); -------------------------------------------------------------------------------- /mocha-client-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mocha-test-webextension", 3 | "version": "1.0.0", 4 | "description": "Example how to run unit tests for WebExtension", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "karma start", 8 | "test:debug": "karma start --no-single-run --auto-watch", 9 | "web-ext": "web-ext run -s ./addon" 10 | }, 11 | "author": "", 12 | "license": "MPL-2.0", 13 | "devDependencies": { 14 | "chai": "^3.5.0", 15 | "expect.js": "^0.3.1", 16 | "karma": "^1.3.0", 17 | "karma-firefox-launcher": "^1.0.0", 18 | "karma-mocha": "^1.3.0", 19 | "mocha": "^3.1.2", 20 | "sinon-chrome": "^2.1.2", 21 | "web-ext": "^1.6.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mocha-client-tests/screenshots/addon-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/mocha-client-tests/screenshots/addon-button.png -------------------------------------------------------------------------------- /mocha-client-tests/tests/lib/background.test.js: -------------------------------------------------------------------------------- 1 | describe('Background', function() { 2 | describe('ping', function() { 3 | it('should return pong in response', function(done) { 4 | Background.ping(false, false, function(response) { 5 | expect(response).to.equal('pong'); 6 | done(); 7 | }); 8 | }); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /native-messaging/add-on/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The icon used here is taken from the "Miscellany Web icons" set by Maria & Guillem (https://www.iconfinder.com/andromina), and is used under the Creative Commons (Attribution 3.0 Unported) license. 2 | -------------------------------------------------------------------------------- /native-messaging/add-on/icons/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /native-messaging/add-on/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Native messaging example add-on", 4 | "manifest_version": 2, 5 | "name": "Native messaging example", 6 | "version": "1.0", 7 | "icons": { 8 | "48": "icons/message.svg" 9 | }, 10 | 11 | "browser_specific_settings": { 12 | "gecko": { 13 | "id": "ping_pong@example.org", 14 | "strict_min_version": "50.0" 15 | } 16 | }, 17 | 18 | "background": { 19 | "scripts": ["background.js"] 20 | }, 21 | 22 | "browser_action": { 23 | "default_icon": "icons/message.svg" 24 | }, 25 | 26 | "permissions": ["nativeMessaging"] 27 | 28 | } 29 | -------------------------------------------------------------------------------- /native-messaging/app/ping_pong.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ping_pong", 3 | "description": "Example host for native messaging", 4 | "path": "/path/to/native-messaging/app/ping_pong.py", 5 | "type": "stdio", 6 | "allowed_extensions": [ "ping_pong@example.org" ] 7 | } 8 | -------------------------------------------------------------------------------- /native-messaging/app/ping_pong_win.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call python3 C:\path\to\ping_pong.py 4 | -------------------------------------------------------------------------------- /navigation-stats/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The icon "icon-32.png" is taken from the IconBeast Lite iconset, and used under the terms of its license (http://www.iconbeast.com/faq/), with a link back to the website: http://www.iconbeast.com/free/. 2 | -------------------------------------------------------------------------------- /navigation-stats/icons/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/navigation-stats/icons/icon-32.png -------------------------------------------------------------------------------- /navigation-stats/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Navigation Stats", 4 | "version": "0.1.0", 5 | "browser_action": { 6 | "default_icon": { 7 | "32": "icons/icon-32.png" 8 | }, 9 | "default_title": "Navigation Stats", 10 | "default_popup": "popup.html" 11 | }, 12 | "permissions": ["webNavigation", "storage"], 13 | "background": { 14 | "scripts": [ "background.js" ] 15 | }, 16 | "icons": { 17 | "32": "icons/icon-32.png" 18 | }, 19 | "browser_specific_settings": { 20 | "gecko": { 21 | "strict_min_version": "50.0" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /navigation-stats/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Top navigated hosts:

5 |
    6 |
  • No data collected yet...
  • 7 |
8 |

Top navigation types:

9 |
    10 |
  • No data collected yet...
  • 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/_locales/de/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Meine Beispielerweiterung", 4 | "description": "Name of the extension." 5 | }, 6 | 7 | "extensionDescription": { 8 | "message": "Benachrichtigt den Benutzer über Linkklicks", 9 | "description": "Description of the extension." 10 | }, 11 | 12 | "notificationTitle": { 13 | "message": "Klickbenachrichtigung", 14 | "description": "Title of the click notification." 15 | }, 16 | 17 | "notificationContent": { 18 | "message": "Du hast $URL$ angeklickt", 19 | "description": "Tells the user which link they clicked.", 20 | "placeholders": { 21 | "url" : { 22 | "content" : "$1", 23 | "example" : "https://developer.mozilla.org" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Notify link clicks i18n", 4 | "description": "Name of the extension." 5 | }, 6 | 7 | "extensionDescription": { 8 | "message": "Shows a notification when the user clicks on links.", 9 | "description": "Description of the extension." 10 | }, 11 | 12 | "notificationTitle": { 13 | "message": "Click notification", 14 | "description": "Title of the click notification." 15 | }, 16 | 17 | "notificationContent": { 18 | "message": "You clicked $URL$.", 19 | "description": "Tells the user which link they clicked.", 20 | "placeholders": { 21 | "url" : { 22 | "content" : "$1", 23 | "example" : "https://developer.mozilla.org" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/_locales/fr_FR/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Notifications i18n des liens cliqués", 4 | "description": "Nom de l'extension." 5 | }, 6 | 7 | "extensionDescription": { 8 | "message": "Affiche une notification lorsqu'un utilisateur clique sur les liens.", 9 | "description": "Description de l'extension." 10 | }, 11 | 12 | "notificationTitle": { 13 | "message": "Notification de clic", 14 | "description": "Titre de la notification de clic." 15 | }, 16 | 17 | "notificationContent": { 18 | "message": "Vous avez cliqué sur $URL$.", 19 | "description": "Dit à l'utilisateur sur quel lien il a cliqué.", 20 | "placeholders": { 21 | "url" : { 22 | "content" : "$1", 23 | "example" : "https://developer.mozilla.org" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/_locales/ja/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "リンクを通知する", 4 | "description": "拡張機能の名前です。" 5 | }, 6 | 7 | "extensionDescription": { 8 | "message": "ユーザーがリンクをクリックした時通知を表示します。", 9 | "description": "拡張機能の説明です。" 10 | }, 11 | 12 | "notificationTitle": { 13 | "message": "クリック通知", 14 | "description": "pushのタイトルです。" 15 | }, 16 | 17 | "notificationContent": { 18 | "message": "$URL$がクリックされました。", 19 | "description": "リンクをクリックした時通知を表示します。:変数$1にはurlが代入されます。", 20 | "placeholders": { 21 | "url" : { 22 | "content" : "$1", 23 | "example" : "https://developer.mozilla.org" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/_locales/nb_NO/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Varsling ved trykk på lenke i18n", 4 | "description": "Navn på utvidelsen." 5 | }, 6 | 7 | "extensionDescription": { 8 | "message": "Viser en varsel når brukern trykker på en lenke", 9 | "description": "Beskrivelse av utvidelsen." 10 | }, 11 | 12 | "notificationTitle": { 13 | "message": "Varseltrykk", 14 | "description": "Tittel på varselet." 15 | }, 16 | 17 | "notificationContent": { 18 | "message": "Du trykket $URL$.", 19 | "description": "Forteller brukeren hvilken lenke som ble trykket.", 20 | "placeholders": { 21 | "url" : { 22 | "content" : "$1", 23 | "example" : "https://developer.mozilla.org" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/_locales/nl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Meld klikken op hyperlinks", 4 | "description": "Name of the extension." 5 | }, 6 | 7 | "extensionDescription": { 8 | "message": "Toon een melding wanneer een gebruiker op hyperlinks klikt.", 9 | "description": "Description of the extension." 10 | }, 11 | 12 | "notificationTitle": { 13 | "message": "Klikmelding", 14 | "description": "Title of the click notification." 15 | }, 16 | 17 | "notificationContent": { 18 | "message": "U klikte op $URL$", 19 | "description": "Tells the user which link they clicked.", 20 | "placeholders": { 21 | "url" : { 22 | "content" : "$1", 23 | "example" : "https://developer.mozilla.org" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/_locales/pt_BR/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Notificação de cliques em links i18n", 4 | "description": "Nome da extensão." 5 | }, 6 | 7 | "extensionDescription": { 8 | "message": "Mostra uma notificação quando o usuário clica nos links.", 9 | "description": "Descrição da extensão." 10 | }, 11 | 12 | "notificationTitle": { 13 | "message": "Notificação de clique", 14 | "description": "Título da notificação de clique." 15 | }, 16 | 17 | "notificationContent": { 18 | "message": "Você clicou em $URL$.", 19 | "description": "Informe aos usuários em qual link eles clicaram.", 20 | "placeholders": { 21 | "url" : { 22 | "content" : "$1", 23 | "example" : "https://developer.mozilla.org" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/background-script.js: -------------------------------------------------------------------------------- 1 | /* 2 | Log that we received the message. 3 | Then display a notification. The notification contains the URL, 4 | which we read from the message. 5 | */ 6 | function notify(message) { 7 | console.log("background script received message"); 8 | let title = browser.i18n.getMessage("notificationTitle"); 9 | let content = browser.i18n.getMessage("notificationContent", message.url); 10 | browser.notifications.create({ 11 | "type": "basic", 12 | "iconUrl": browser.extension.getURL("icons/link-48.png"), 13 | "title": title, 14 | "message": content 15 | }); 16 | } 17 | 18 | /* 19 | Assign `notify()` as a listener to messages from the content script. 20 | */ 21 | browser.runtime.onMessage.addListener(notify); 22 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/content-script.js: -------------------------------------------------------------------------------- 1 | /* 2 | If the click was on a link, send a message to the background page. 3 | The message contains the link's URL. 4 | */ 5 | function notifyExtension(e) { 6 | let target = e.target; 7 | while ((target.tagName != "A" || !target.href) && target.parentNode) { 8 | target = target.parentNode; 9 | } 10 | if (target.tagName != "A") 11 | return; 12 | 13 | console.log("content script sending message"); 14 | browser.runtime.sendMessage({"url": target.href}); 15 | } 16 | 17 | /* 18 | Add notifyExtension() as a listener to click events. 19 | */ 20 | window.addEventListener("click", notifyExtension); 21 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The "link-48.png" icon is taken from the Geomicons iconset, and is used here under the MIT license: http://opensource.org/licenses/MIT. 2 | -------------------------------------------------------------------------------- /notify-link-clicks-i18n/icons/link-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/notify-link-clicks-i18n/icons/link-48.png -------------------------------------------------------------------------------- /notify-link-clicks-i18n/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "__MSG_extensionName__", 5 | "description": "__MSG_extensionDescription__", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/notify-link-clicks-i18n", 8 | "icons": { 9 | "48": "icons/link-48.png" 10 | }, 11 | 12 | "permissions": ["notifications"], 13 | 14 | "background": { 15 | "scripts": ["background-script.js"] 16 | }, 17 | 18 | "content_scripts": [ 19 | { 20 | "matches": [""], 21 | "js": ["content-script.js"] 22 | } 23 | ], 24 | 25 | "default_locale": "en" 26 | } 27 | -------------------------------------------------------------------------------- /open-irc-links/README.md: -------------------------------------------------------------------------------- 1 | # open-irc-links 2 | 3 | 4 | ## What it does 5 | 6 | This add-on sets the default client for opening IRC links using `protocol_handlers`. 7 | 8 | Whenever a link using the IRC protocol is clicked, the link is opened in the URI defined in the add-on's manifest. 9 | In this example, all IRC protocol links are opened in mibbit.com. 10 | For example, once you've installed the extension, click this link [irc://irc.freenode.net/drupal](irc://irc.freenode.net/drupal) and the protocol handler opens the Drupal channel in mibbit.com. 11 | 12 | ## What it shows 13 | 14 | How to use protocol handlers to pass content to an application or website designed to handle that content. 15 | -------------------------------------------------------------------------------- /open-irc-links/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/open-irc-links/icons/icon.png -------------------------------------------------------------------------------- /open-irc-links/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "IRC Protocol Handler", 4 | "description": "Demos the usage of protocol_handlers", 5 | "version": "1.0.0", 6 | "icons": { 7 | "64": "icons/icon.png" 8 | }, 9 | "protocol_handlers": [ 10 | { 11 | "protocol": "irc", 12 | "name": "IRC Mibbit Extension", 13 | "uriTemplate": "https://mibbit.com/?url=%s" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /open-my-page-button/README.md: -------------------------------------------------------------------------------- 1 | # open-my-page 2 | 3 | ## What it does 4 | 5 | This extension includes: 6 | 7 | * a background script, "background.js" 8 | * a browser action 9 | * a page "my-page.html" 10 | 11 | All it does is: when the user clicks the button, open "my-page.html" in a new tab. 12 | 13 | ## What it shows 14 | 15 | * how to listen for browser action clicks in a background script 16 | * how to open a page packaged with your extension 17 | -------------------------------------------------------------------------------- /open-my-page-button/background.js: -------------------------------------------------------------------------------- 1 | /* 2 | Open a new tab, and load "my-page.html" into it. 3 | */ 4 | function openMyPage() { 5 | console.log("injecting"); 6 | browser.tabs.create({ 7 | "url": "/my-page.html" 8 | }); 9 | } 10 | 11 | 12 | /* 13 | Add openMyPage() as a listener to clicks on the browser action. 14 | */ 15 | browser.browserAction.onClicked.addListener(openMyPage); 16 | 17 | -------------------------------------------------------------------------------- /open-my-page-button/icons/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The "page-32.png" and "page-48.png" icons are taken from the miu iconset created by Linh Pham Thi Dieu, and are used under the terms of its license: http://linhpham.me/miu/. 3 | -------------------------------------------------------------------------------- /open-my-page-button/icons/page-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/open-my-page-button/icons/page-32.png -------------------------------------------------------------------------------- /open-my-page-button/icons/page-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/open-my-page-button/icons/page-48.png -------------------------------------------------------------------------------- /open-my-page-button/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Adds browser action icon to toolbar to open packaged web page. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#open-my-page-button", 4 | "manifest_version": 2, 5 | "name": "open-my-page", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/open-my-page-button", 8 | "icons": { 9 | "48": "icons/page-48.png" 10 | }, 11 | 12 | "background": { 13 | "scripts": ["background.js"] 14 | }, 15 | 16 | "browser_action": { 17 | "default_icon": "icons/page-32.png" 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /open-my-page-button/my-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

It's my page!

10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webextensions-examples", 3 | "title": "WebExtensions Examples", 4 | "version": "1.0.0", 5 | "description": "Example Firefox add-ons created using the WebExtensions API", 6 | "devDependencies": { 7 | "@babel/eslint-parser": "^7.22.11", 8 | "eslint": "^8.48.0" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/mdn/webextensions-examples.git" 13 | }, 14 | "scripts": { 15 | "test": "eslint .", 16 | "lint": "eslint .", 17 | "lint:cwd": "eslint $INIT_CWD", 18 | "lint:fix": "eslint . --fix" 19 | }, 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/mdn/webextensions-examples/issues" 23 | }, 24 | "keywords": [ 25 | "webextensions", 26 | "webextensions-apis", 27 | "mdn", 28 | "firefox", 29 | "mozilla" 30 | ], 31 | "homepage": "https://developer.mozilla.org/Add-ons/WebExtensions/Examples" 32 | } 33 | -------------------------------------------------------------------------------- /page-to-extension-messaging/README.md: -------------------------------------------------------------------------------- 1 | # page-to-extension-messaging 2 | 3 | ## What it does 4 | 5 | This extension includes a content script, which is injected only into: "https://mdn.github.io/webextensions-examples/content-script-page-script-messaging.html". 6 | 7 | The content script listens for clicks on a particular button on the page. When the button is clicked, the content script sends a message to any scripts running in the page. 8 | 9 | Conversely, the content script listens for messages from the same window posted using window.postMessage. When the content script receives such a message, it displays an alert. 10 | 11 | To test it out, visit https://mdn.github.io/webextensions-examples/content-script-page-script-messaging.html and press the buttons. One button sends a message from the page script to the content script, the other button sends a message in the other direction. 12 | 13 | ## What it shows 14 | 15 | How to exchange messages between an extension's content scripts, and scripts running in a web page. 16 | -------------------------------------------------------------------------------- /page-to-extension-messaging/content-script.js: -------------------------------------------------------------------------------- 1 | /* 2 | Listen for messages from the page. 3 | If the message was from the page script, show an alert. 4 | */ 5 | window.addEventListener("message", (event) => { 6 | if (event.source == window && 7 | event.data && 8 | event.data.direction == "from-page-script") { 9 | alert("Content script received message: \"" + event.data.message + "\""); 10 | } 11 | }); 12 | 13 | /* 14 | Send a message to the page script. 15 | */ 16 | function messagePageScript() { 17 | window.postMessage({ 18 | direction: "from-content-script", 19 | message: "Message from the content script" 20 | }, "https://mdn.github.io"); 21 | } 22 | 23 | /* 24 | Add messagePageScript() as a listener to click events on 25 | the "from-content-script" element. 26 | */ 27 | let fromContentScript = document.getElementById("from-content-script"); 28 | fromContentScript.addEventListener("click", messagePageScript); 29 | -------------------------------------------------------------------------------- /page-to-extension-messaging/icons/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The "message-48.png" icon is taken from the miu iconset created by Linh Pham Thi Dieu, and is used under the terms of its license: http://linhpham.me/miu/. 3 | -------------------------------------------------------------------------------- /page-to-extension-messaging/icons/message-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/page-to-extension-messaging/icons/message-48.png -------------------------------------------------------------------------------- /page-to-extension-messaging/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "Page to extension messaging", 5 | "description": "Visit https://mdn.github.io/webextensions-examples/content-script-page-script-messaging.html for the demo. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#page-to-extension-messaging", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/page-to-extension-messaging", 8 | "icons": { 9 | "48": "icons/message-48.png" 10 | }, 11 | 12 | "content_scripts": [ 13 | { 14 | "matches": ["https://mdn.github.io/webextensions-examples/content-script-page-script-messaging.html"], 15 | "js": ["content-script.js"] 16 | } 17 | ] 18 | 19 | } 20 | -------------------------------------------------------------------------------- /permissions/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 8 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /permissions/README.md: -------------------------------------------------------------------------------- 1 | # Permissions Example 2 | 3 | ## What it does 4 | 5 | An example using the permissions API to grant and revoke an optional permission. 6 | 7 | ## What it shows 8 | 9 | How to request a permission, catching error conditions from the request and querying the permissions API. 10 | -------------------------------------------------------------------------------- /permissions/background.js: -------------------------------------------------------------------------------- 1 | browser.browserAction.onClicked.addListener(() => { 2 | browser.tabs.create({ 3 | url: browser.runtime.getURL("page.html") 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /permissions/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /permissions/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Permissions Example", 4 | "description": "Permissions Example", 5 | "version": "1.0", 6 | 7 | "background": { 8 | "scripts": ["background.js"] 9 | }, 10 | "icons": { 11 | "48": "icon.svg" 12 | }, 13 | "browser_action": { 14 | "default_title": "Permissions Example", 15 | "default_icon": "icon.svg" 16 | }, 17 | 18 | "permissions": [ 19 | "tabs" 20 | ], 21 | "optional_permissions": [ 22 | "history" 23 | ], 24 | "browser_specific_settings": { 25 | "gecko": { 26 | "strict_min_version": "55.0a2" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /permissions/page.css: -------------------------------------------------------------------------------- 1 | p.bg-danger, p.bg-success, p.bg-warning { 2 | padding: 1em; 3 | } 4 | -------------------------------------------------------------------------------- /private-browsing-theme/README.md: -------------------------------------------------------------------------------- 1 | # Private browsing theme 2 | 3 | ## What it does 4 | 5 | An example using the theme API to apply a different theme on private windows. 6 | 7 | ## What it shows 8 | 9 | How to use the windowId argument of browser.theme.reset() and browser.theme.update(). 10 | -------------------------------------------------------------------------------- /private-browsing-theme/background.js: -------------------------------------------------------------------------------- 1 | browser.windows.onCreated.addListener(themeWindow); 2 | 3 | // Theme all currently open windows 4 | browser.windows.getAll().then(wins => wins.forEach(themeWindow)); 5 | 6 | function themeWindow(window) { 7 | // Check if the window is in private browsing 8 | if (window.incognito) { 9 | browser.theme.update(window.id, { 10 | images: { 11 | theme_frame: "", 12 | }, 13 | colors: { 14 | frame: "black", 15 | tab_background_text: "white", 16 | toolbar: "#333", 17 | toolbar_text: "white" 18 | } 19 | }); 20 | } 21 | // Reset to the default theme otherwise 22 | else { 23 | browser.theme.reset(window.id); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /private-browsing-theme/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Private browsing theme", 4 | "version": "2.0", 5 | "description": "Extension that sets a dark theme for private windows", 6 | "background": { 7 | "scripts": ["background.js"] 8 | }, 9 | "permissions": ["theme"], 10 | "browser_specific_settings": { 11 | "gecko": { 12 | "id": "private-window-theme@mozilla.org", 13 | "strict_min_version": "58.0" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /proxy-blocker/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The "lock".svg" icon is taken from the Material Core iconset and is used under the terms of its license: https://www.iconfinder.com/iconsets/material-core. 2 | -------------------------------------------------------------------------------- /proxy-blocker/icons/block.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /proxy-blocker/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "Proxy-blocker", 5 | "description": "Uses the proxy API to block requests to specific hosts.", 6 | "version": "2.0", 7 | 8 | "icons": { 9 | "48": "icons/block.svg", 10 | "96": "icons/block.svg" 11 | }, 12 | 13 | "browser_specific_settings": { 14 | "gecko": { 15 | "strict_min_version": "56.0a1" 16 | } 17 | }, 18 | 19 | "background": { 20 | "scripts": [ 21 | "background/proxy-handler.js" 22 | ] 23 | }, 24 | 25 | "options_ui": { 26 | "page": "options/options.html", 27 | "browser_style": true 28 | }, 29 | 30 | "permissions": ["proxy", "storage", ""] 31 | 32 | } 33 | -------------------------------------------------------------------------------- /proxy-blocker/options/options.css: -------------------------------------------------------------------------------- 1 | #blocked-hosts { 2 | display: block; 3 | margin-top: 1em; 4 | } 5 | -------------------------------------------------------------------------------- /proxy-blocker/options/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | Hosts to block: 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /proxy-blocker/options/options.js: -------------------------------------------------------------------------------- 1 | const blockedHostsTextArea = document.querySelector("#blocked-hosts"); 2 | 3 | // Store the currently selected settings using browser.storage.local. 4 | function storeSettings() { 5 | let blockedHosts = blockedHostsTextArea.value.split("\n"); 6 | browser.storage.local.set({ 7 | blockedHosts 8 | }); 9 | } 10 | 11 | // Update the options UI with the settings values retrieved from storage, 12 | // or the default settings if the stored settings are empty. 13 | function updateUI(restoredSettings) { 14 | blockedHostsTextArea.value = restoredSettings.blockedHosts.join("\n"); 15 | } 16 | 17 | function onError(e) { 18 | console.error(e); 19 | } 20 | 21 | // On opening the options page, fetch stored settings and update the UI with them. 22 | browser.storage.local.get().then(updateUI, onError); 23 | 24 | // Whenever the contents of the textarea changes, save the new values 25 | blockedHostsTextArea.addEventListener("change", storeSettings); 26 | -------------------------------------------------------------------------------- /quicknote/icons/quicknote-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/quicknote/icons/quicknote-32.png -------------------------------------------------------------------------------- /quicknote/icons/quicknote-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/quicknote/icons/quicknote-48.png -------------------------------------------------------------------------------- /quicknote/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "Quicknote", 5 | "version": "1.1", 6 | 7 | "description": "Allows the user to make quick notes by clicking a button and entering text into the resulting popup. The notes are saved in storage. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#quicknote", 8 | "icons": { 9 | "48": "icons/quicknote-48.png" 10 | }, 11 | 12 | "permissions": [ 13 | "storage" 14 | ], 15 | 16 | "browser_action": { 17 | "default_icon": { 18 | "32" : "icons/quicknote-32.png" 19 | }, 20 | "default_title": "Quicknote", 21 | "default_popup": "popup/quicknote.html" 22 | }, 23 | 24 | "browser_specific_settings": { 25 | "gecko": { 26 | "id": "quicknote-example@mozilla.org" 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /quicknote/popup/quicknote.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 |
20 | 21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /root-cert-stats/README.md: -------------------------------------------------------------------------------- 1 | # root-cert-stats 2 | 3 | ## What it does ## 4 | 5 | The extension includes: 6 | 7 | * a background page which collects stats about the trusted root certs used when 8 | browsing the web. It records the subject name of each root cert, and how many 9 | times that particular root cert was used to establish a TLS connection. 10 | 11 | * a browser action with a popup. The popup displays the collected stats. 12 | 13 | ## What it shows ## 14 | 15 | * how to use the webRequest.getSecurityInfo() API. 16 | -------------------------------------------------------------------------------- /root-cert-stats/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The icon "icon-32.png" is taken from the IconBeast Lite iconset, and used under the terms of its license (http://www.iconbeast.com/faq/), with a link back to the website: http://www.iconbeast.com/free/. 2 | -------------------------------------------------------------------------------- /root-cert-stats/icons/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/root-cert-stats/icons/icon-32.png -------------------------------------------------------------------------------- /root-cert-stats/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Root Certificate Stats", 4 | "description": "Track and display which CA root certificates are used and how often.", 5 | "version": "0.1.0", 6 | "browser_action": { 7 | "default_icon": { 8 | "32": "icons/icon-32.png" 9 | }, 10 | "default_title": "Root Certificate Stats", 11 | "default_popup": "popup.html" 12 | }, 13 | "permissions": ["webRequest", "webRequestBlocking", ""], 14 | "background": { 15 | "scripts": [ "background.js" ] 16 | }, 17 | "icons": { 18 | "32": "icons/icon-32.png" 19 | }, 20 | "browser_specific_settings": { 21 | "gecko": { 22 | "strict_min_version": "62.0b5" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /root-cert-stats/popup.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 1rem/2 sans-serif; 3 | } 4 | 5 | table, 6 | td { 7 | border: 1px solid #333; 8 | padding: .3rem; 9 | } 10 | 11 | .hidden { 12 | display: none; 13 | } 14 | -------------------------------------------------------------------------------- /root-cert-stats/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Root cert stats 8 | 9 | 10 | 11 |
No data to display yet
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /runtime-examples/README.md: -------------------------------------------------------------------------------- 1 | ## What it does 2 | 3 | Trigger an install notification when you install it. On the browser action text is 4 | shown showing when the background page was last loaded in minutes and seconds. 5 | 6 | Clicking the browser action triggers a reload and updates the background page load time. 7 | 8 | The notifications show the version of the extension by fetching the manifest. 9 | 10 | ## What it shows 11 | 12 | Demonstration of some runtime and notification APIs. 13 | 14 | Icon is from: https://www.iconfinder.com/icons/172151/run_icon#size=128 15 | -------------------------------------------------------------------------------- /runtime-examples/background.js: -------------------------------------------------------------------------------- 1 | let loadTime = new Date(); 2 | let manifest = browser.runtime.getManifest(); 3 | 4 | function onInstalledNotification(details) { 5 | browser.notifications.create('onInstalled', { 6 | title: `Runtime Examples version: ${manifest.version}`, 7 | message: `onInstalled has been called, background page loaded at ${loadTime.getHours()}:${loadTime.getMinutes()}`, 8 | type: 'basic' 9 | }); 10 | } 11 | 12 | function onClick() { 13 | browser.runtime.reload(); 14 | } 15 | 16 | browser.browserAction.onClicked.addListener(onClick); 17 | browser.runtime.onInstalled.addListener(onInstalledNotification); 18 | -------------------------------------------------------------------------------- /runtime-examples/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "browser_action": { 3 | "default_icon": "run.png" 4 | }, 5 | "description": "Demonstration of some runtime APIs", 6 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/runtime-examples", 7 | "manifest_version": 2, 8 | "name": "Runtime examples", 9 | "version": "1.0", 10 | "permissions": ["notifications"], 11 | "background": { 12 | "scripts": ["background.js"] 13 | }, 14 | "icons": { 15 | "128": "run.png" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /runtime-examples/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/runtime-examples/run.png -------------------------------------------------------------------------------- /selection-to-clipboard/README.md: -------------------------------------------------------------------------------- 1 | # selection-to-clipboard 2 | 3 | **This add-on injects JavaScript into web pages. The `addons.mozilla.org` domain disallows this operation, so this add-on will not work properly when it's run on pages in the `addons.mozilla.org` domain.** 4 | 5 | ## What it does 6 | 7 | This extension includes: 8 | 9 | * a content script, "content-script.js", that is injected into all pages 10 | 11 | The content script listens for text selections in the page it's attached to and copies the text to the clipboard on mouse-up. 12 | 13 | ## What it shows 14 | 15 | * how to inject content scripts declaratively using manifest.json 16 | * how to write to the [clipboard](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard) 17 | 18 | ## Note 19 | * If the `copySelection` function was in a browser event `clipboardWrite` permissions would be required e.g. 20 | ``` 21 | "permissions": ["clipboardWrite"] 22 | ``` 23 | See [Interact with the clipboard](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard). 24 | -------------------------------------------------------------------------------- /selection-to-clipboard/content-script.js: -------------------------------------------------------------------------------- 1 | /* 2 | copy the selected text to clipboard 3 | */ 4 | function copySelection() { 5 | let selectedText = window.getSelection().toString().trim(); 6 | 7 | if (selectedText) { 8 | document.execCommand("Copy"); 9 | } 10 | } 11 | 12 | /* 13 | Add copySelection() as a listener to mouseup events. 14 | */ 15 | document.addEventListener("mouseup", copySelection); -------------------------------------------------------------------------------- /selection-to-clipboard/icons/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The "page-32.png" and "page-48.png" icons are taken from the miu iconset created by Linh Pham Thi Dieu, and are used under the terms of its license: http://linhpham.me/miu/. 3 | -------------------------------------------------------------------------------- /selection-to-clipboard/icons/clipboard-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/selection-to-clipboard/icons/clipboard-48.png -------------------------------------------------------------------------------- /selection-to-clipboard/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "selection-to-clipboard", 4 | "description": "Example of WebExtensionAPI for writing to the clipboard", 5 | "version": "1.0", 6 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/selection-to-clipboard", 7 | 8 | "icons": { 9 | "48": "icons/clipboard-48.png" 10 | }, 11 | 12 | "content_scripts": [{ 13 | "matches": [""], 14 | "js": ["content-script.js"] 15 | }] 16 | } 17 | -------------------------------------------------------------------------------- /session-state/README.md: -------------------------------------------------------------------------------- 1 | # session-state 2 | 3 | ## What it does 4 | 5 | This extension adds a context menu item. When the user clicks the context menu item, the extension adds a border to the page. 6 | 7 | But the extension also uses `sessions.setTabValue` to store the fact that it has added a border to this page. If the user closes the page, then restores it, the extension will retrieve this fact using `sessions.getTabValue`, and use that to reapply the border. 8 | 9 | Note: to restore a tab, press Control+Shift+T (or Command+Shift+T on a Mac). In Firefox you can also restore the tab from your "History->Recently Closed Tabs" menu. 10 | 11 | ## What it shows 12 | 13 | This example demonstrates how you can use the [sessions](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/sessions) API to store and retrieve arbitrary state that you want to associate with a tab or window. Then if the tab/window is closed and subsequently restored, you can retrieve the state. 14 | -------------------------------------------------------------------------------- /session-state/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The icon “border-48.png” is taken from the Google Material Design iconset, and is used under the terms of the Creative Commons Attribution-ShareAlike license: http://creativecommons.org/licenses/by-sa/3.0/. 2 | -------------------------------------------------------------------------------- /session-state/icons/border-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/session-state/icons/border-48.png -------------------------------------------------------------------------------- /session-state/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "browser_specific_settings": { 3 | "gecko": { 4 | "id": "session-state@example.com", 5 | "strict_min_version": "57.0a1" 6 | } 7 | }, 8 | "manifest_version": 2, 9 | "name": "Session state", 10 | "version": "1.0", 11 | "description": "Demonstrates using the sessions API to store and restore extension-specific tab state.", 12 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/session-state", 13 | "icons": { 14 | "48": "icons/border-48.png" 15 | }, 16 | 17 | "background": { 18 | "scripts": ["background.js"] 19 | }, 20 | 21 | "permissions": ["", "menus", "sessions"] 22 | 23 | } 24 | -------------------------------------------------------------------------------- /store-collected-images/screenshots/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/store-collected-images/screenshots/screenshot.png -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/.eslintignore: -------------------------------------------------------------------------------- 1 | deps -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@babel/eslint-parser" 3 | } -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/store-collected-images/webextension-plain/images/icon.png -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/images/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/store-collected-images/webextension-plain/images/icon16.png -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "store-collected-images", 4 | "version": "1.0", 5 | 6 | "icons": { 7 | "16": "images/icon16.png", 8 | "48": "images/icon.png" 9 | }, 10 | 11 | "browser_action": { 12 | "default_icon": { 13 | "48": "images/icon.png" 14 | }, 15 | "default_title": "Collected Images" 16 | }, 17 | 18 | "background": { 19 | "scripts": ["background.js"] 20 | }, 21 | 22 | "permissions": [ 23 | "contextMenus", 24 | "" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/navigate-collection.css: -------------------------------------------------------------------------------- 1 | @import "shared.css"; -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/navigate-collection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Stored images

10 | 11 | 12 | 13 |
    14 |
    15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/popup.css: -------------------------------------------------------------------------------- 1 | @import "shared.css"; 2 | 3 | html, body { 4 | width: 250px; 5 | margin: 0; 6 | padding: 0; 7 | margin-left: 1em; 8 | } 9 | 10 | input { 11 | width: 90%; 12 | } 13 | -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
    9 |

    Collected images

    10 |

    11 | 12 | 13 |
      14 |
        15 |
    16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/shared.css: -------------------------------------------------------------------------------- 1 | ul.thumbnails { 2 | padding: 0; 3 | } 4 | 5 | ul.thumbnails li { 6 | display: inline-block; 7 | vertical-align: middle; 8 | padding: 0.4em; 9 | } 10 | 11 | .thumbnails img { 12 | max-width: 50px; 13 | max-height: 50px; 14 | } 15 | 16 | .error { 17 | background: rgba(255,0,0,0.4); 18 | } 19 | 20 | .success { 21 | background: rgba(0,255,0,0.4); 22 | } -------------------------------------------------------------------------------- /store-collected-images/webextension-plain/utils/handle-window-drag-and-drop.js: -------------------------------------------------------------------------------- 1 | /* exported preventWindowDragAndDrop */ 2 | 3 | "use strict"; 4 | 5 | function preventWindowDragAndDrop() { 6 | function preventDefault(ev) { 7 | ev.preventDefault(); 8 | return true; 9 | } 10 | 11 | window.ondragover = preventDefault; 12 | window.ondragend = preventDefault; 13 | window.ondrop = preventDefault; 14 | } 15 | -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@babel/eslint-parser", 3 | "env": { 4 | "commonjs": true 5 | } 6 | } -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build artifacts and other files. 2 | .DS_Store 3 | yarn.lock 4 | extension/dist 5 | node_modules 6 | -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/extension/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/store-collected-images/webextension-with-webpack/extension/images/icon.png -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/extension/images/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/store-collected-images/webextension-with-webpack/extension/images/icon16.png -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "store-collected-images", 4 | "version": "1.0", 5 | 6 | "icons": { 7 | "16": "images/icon16.png", 8 | "48": "images/icon.png" 9 | }, 10 | 11 | "browser_action": { 12 | "default_icon": { 13 | "48": "images/icon.png" 14 | }, 15 | "default_title": "Collected Images" 16 | }, 17 | 18 | "background": { 19 | "scripts": ["dist/background.js"] 20 | }, 21 | 22 | "permissions": [ 23 | "contextMenus", 24 | "" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/extension/navigate-collection.css: -------------------------------------------------------------------------------- 1 | @import "shared.css"; -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/extension/navigate-collection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
    9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/extension/popup.css: -------------------------------------------------------------------------------- 1 | @import "shared.css"; 2 | 3 | html, body { 4 | width: 250px; 5 | margin: 0; 6 | padding: 0; 7 | margin-left: 1em; 8 | } 9 | 10 | input { 11 | width: 90%; 12 | } 13 | -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/extension/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
    9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/extension/shared.css: -------------------------------------------------------------------------------- 1 | ul.thumbnails { 2 | padding: 0; 3 | } 4 | 5 | ul.thumbnails li { 6 | display: inline-block; 7 | vertical-align: middle; 8 | padding: 0.4em; 9 | } 10 | 11 | .thumbnails img { 12 | max-width: 50px; 13 | max-height: 50px; 14 | } 15 | 16 | .error { 17 | background: rgba(255,0,0,0.4); 18 | } 19 | 20 | .success { 21 | background: rgba(0,255,0,0.4); 22 | } -------------------------------------------------------------------------------- /store-collected-images/webextension-with-webpack/src/utils/handle-window-drag-and-drop.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function preventDefault(ev) { 4 | ev.preventDefault(); 5 | return true; 6 | } 7 | 8 | export default function preventWindowDragAndDrop() { 9 | window.ondragover = preventDefault; 10 | window.ondragend = preventDefault; 11 | window.ondrop = preventDefault; 12 | } 13 | -------------------------------------------------------------------------------- /stored-credentials/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The "lock".svg" icon is taken from the Material Core iconset and is used under the terms of its license: https://www.iconfinder.com/iconsets/material-core. 2 | -------------------------------------------------------------------------------- /stored-credentials/icons/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stored-credentials/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Performs basic authentication by supplying stored credentials.", 3 | "manifest_version": 2, 4 | "name": "stored-credentials", 5 | "version": "2.0", 6 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/stored-credentials", 7 | "icons": { 8 | "48": "icons/lock.svg" 9 | }, 10 | 11 | "browser_specific_settings": { 12 | "gecko": { 13 | "strict_min_version": "54.0a1" 14 | } 15 | }, 16 | 17 | "background": { 18 | "scripts": ["storage.js", "auth.js"] 19 | }, 20 | 21 | "options_ui": { 22 | "page": "options/options.html" 23 | }, 24 | 25 | "permissions": [ 26 | "webRequest", 27 | "webRequestBlocking", 28 | "storage", 29 | "https://httpbin.org/basic-auth/*" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /stored-credentials/options/options.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | width: 25em; 4 | font-family: "Open Sans Light", sans-serif; 5 | font-size: 0.9em; 6 | font-weight: 300; 7 | } 8 | 9 | 10 | .title { 11 | font-size: 1.2em; 12 | margin-bottom: 0.5em; 13 | } 14 | 15 | label { 16 | float: right; 17 | } 18 | 19 | input { 20 | margin: 0.5em; 21 | width: 200px; 22 | height: 2.5em; 23 | } 24 | -------------------------------------------------------------------------------- /stored-credentials/options/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
    12 |
    Username and password
    13 | 14 | 15 | 16 | 17 |
    18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /stored-credentials/storage.js: -------------------------------------------------------------------------------- 1 | /* 2 | Default settings. Initialize storage to these values. 3 | */ 4 | let authCredentials = { 5 | username: "user", 6 | password: "passwd" 7 | } 8 | 9 | /* 10 | Generic error logger. 11 | */ 12 | function onError(e) { 13 | console.error(e); 14 | } 15 | 16 | /* 17 | On startup, check whether we have stored settings. 18 | If we don't, then store the default settings. 19 | */ 20 | function checkStoredSettings(storedSettings) { 21 | if (!storedSettings.authCredentials) { 22 | browser.storage.local.set({authCredentials}); 23 | } 24 | } 25 | 26 | const gettingStoredSettings = browser.storage.local.get(); 27 | gettingStoredSettings.then(checkStoredSettings, onError); 28 | -------------------------------------------------------------------------------- /tabs-tabs-tabs/README.md: -------------------------------------------------------------------------------- 1 | # tabs, tabs, tabs 2 | 3 | ## What it does 4 | 5 | This extension includes a browser action with a popup specified as "tabs.html". 6 | 7 | The popup lets the user perform various simple operations using the tabs API. 8 | 9 | # What it shows 10 | 11 | Demonstration of various tabs API functions. 12 | -------------------------------------------------------------------------------- /tabs-tabs-tabs/background.js: -------------------------------------------------------------------------------- 1 | function updateCount(tabId, isOnRemoved) { 2 | browser.tabs.query({}) 3 | .then((tabs) => { 4 | let length = tabs.length; 5 | 6 | // onRemoved fires too early and the count is one too many. 7 | // see https://bugzilla.mozilla.org/show_bug.cgi?id=1396758 8 | if (isOnRemoved && tabId && tabs.map((t) => { return t.id; }).includes(tabId)) { 9 | length--; 10 | } 11 | 12 | browser.browserAction.setBadgeText({text: length.toString()}); 13 | if (length > 2) { 14 | browser.browserAction.setBadgeBackgroundColor({'color': 'green'}); 15 | } else { 16 | browser.browserAction.setBadgeBackgroundColor({'color': 'red'}); 17 | } 18 | }); 19 | } 20 | 21 | 22 | browser.tabs.onRemoved.addListener( 23 | (tabId) => { updateCount(tabId, true); 24 | }); 25 | browser.tabs.onCreated.addListener( 26 | (tabId) => { updateCount(tabId, false); 27 | }); 28 | updateCount(); 29 | -------------------------------------------------------------------------------- /tabs-tabs-tabs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "browser_specific_settings": { 3 | "gecko": { 4 | "strict_min_version": "58.0a1" 5 | } 6 | }, 7 | "background": { 8 | "scripts": ["background.js"] 9 | }, 10 | "browser_action": { 11 | "browser_style": true, 12 | "default_title": "Tabs, tabs, tabs", 13 | "default_popup": "tabs.html" 14 | }, 15 | "description": "A list of methods you can perform on a tab.", 16 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/tabs-tabs-tabs", 17 | "manifest_version": 2, 18 | "name": "Tabs, tabs, tabs", 19 | "permissions": [ 20 | "tabs" 21 | ], 22 | "version": "1.0" 23 | } 24 | -------------------------------------------------------------------------------- /tabs-tabs-tabs/tabs.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 350px; 3 | } 4 | 5 | a { 6 | margin: 10px; 7 | display: inline-block; 8 | } 9 | 10 | .switch-tabs { 11 | padding-left: 10px; 12 | } 13 | 14 | .switch-tabs a { 15 | display: block; 16 | } 17 | 18 | .panel { 19 | margin: 5px; 20 | } 21 | -------------------------------------------------------------------------------- /theme-integrated-sidebar/README.md: -------------------------------------------------------------------------------- 1 | # Theme integrated sidebar 2 | 3 | ## What it does 4 | 5 | An example using the theme API to integrate the interface of a sidebar_action to the current theme properties. 6 | 7 | ## What it shows 8 | 9 | How to use the browser.theme.getCurrent(), browser.theme.onUpdated and the sidebar_action API. 10 | 11 | ## Instructions to try this 12 | 13 | - Install a WebExtension theme that sets `frame`, `toolbar` or `toolbar_text` 14 | - Install this extension 15 | - The sidebar should then use colors from the WebExtension theme -------------------------------------------------------------------------------- /theme-integrated-sidebar/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "A sidebar that integrates with the current theme", 3 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/theme-integrated-sidebar", 4 | "manifest_version": 2, 5 | "name": "Theme integrated sidebar", 6 | "permissions": [ 7 | "theme" 8 | ], 9 | "sidebar_action": { 10 | "default_title": "Theme integrated sidebar", 11 | "default_panel": "sidebar.html" 12 | }, 13 | "version": "1.0", 14 | "browser_specific_settings": { 15 | "gecko": { 16 | "id": "theme-integrated-sidebar@mozilla.org", 17 | "strict_min_version": "58.0a1" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /theme-integrated-sidebar/sidebar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My theme-integrated sidebar 5 | 14 | 15 | 16 |
    This is an awesome themed element!
    17 | 18 | 19 | -------------------------------------------------------------------------------- /theme-switcher/README.md: -------------------------------------------------------------------------------- 1 | # Theme Switcher 2 | 3 | ## What it does 4 | 5 | Provides a popup that allows you to see the themes and switch between them. 6 | 7 | ## What it shows 8 | 9 | Demonstrates using the management API. 10 | 11 | The "star-half.svg" icon is taken from the Material Core iconset and is used under the terms of its license: https://www.iconfinder.com/iconsets/material-core. 12 | -------------------------------------------------------------------------------- /theme-switcher/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "browser_action": { 3 | "default_title": "Theme switcher", 4 | "default_popup": "switcher.html", 5 | "browser_style": true, 6 | "default_icon": { 7 | "128": "star-half.svg" 8 | } 9 | }, 10 | "description": "An example of how to use the management API for themes.", 11 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/theme-switcher", 12 | "manifest_version": 2, 13 | "name": "Theme Switcher", 14 | "permissions": [ 15 | "management" 16 | ], 17 | "icons": { 18 | "128": "star-half.svg" 19 | }, 20 | "version": "1.0", 21 | "browser_specific_settings": { 22 | "gecko": { 23 | "strict_min_version": "55.0a1" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /theme-switcher/star-half.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme-switcher/switcher.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 1em; 3 | } 4 | -------------------------------------------------------------------------------- /theme-switcher/switcher.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

    Choose a Firefox theme:

    10 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /theme-switcher/switcher.js: -------------------------------------------------------------------------------- 1 | let themeList = document.getElementById('theme-list'); 2 | 3 | function enableTheme(e) { 4 | browser.management.setEnabled(e.target.value, true); 5 | e.preventDefault(); 6 | window.close(); 7 | } 8 | 9 | browser.management.getAll().then((extensions) => { 10 | for (let extension of extensions) { 11 | if (extension.type !== 'theme') { 12 | continue; 13 | } 14 | let option = document.createElement('option'); 15 | option.textContent = extension.name; 16 | option.value = extension.id; 17 | if (extension.enabled) { 18 | option.selected = true; 19 | } 20 | themeList.appendChild(option); 21 | } 22 | }); 23 | 24 | themeList.addEventListener('change', enableTheme); 25 | -------------------------------------------------------------------------------- /themes/animated/README.md: -------------------------------------------------------------------------------- 1 | # Themes: Animated 2 | 3 | ## What it does 4 | 5 | Employs an animated PNG image as the theme_frame image in a theme. 6 | 7 | ## What it shows 8 | 9 | How to use an animated image in a theme. 10 | -------------------------------------------------------------------------------- /themes/animated/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Theme using an animated PNG file as the theme_frame image. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#themes", 4 | "manifest_version": 2, 5 | "name": "animated", 6 | "version": "1.1", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/themes/animated", 8 | 9 | "theme": { 10 | "images": { 11 | "theme_frame": "parrot.png" 12 | }, 13 | 14 | "colors": { 15 | "frame": "#ffffff", 16 | "tab_text": "#000" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /themes/animated/parrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot.png -------------------------------------------------------------------------------- /themes/animated/parrot_frames/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot_frames/1.png -------------------------------------------------------------------------------- /themes/animated/parrot_frames/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot_frames/10.png -------------------------------------------------------------------------------- /themes/animated/parrot_frames/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot_frames/2.png -------------------------------------------------------------------------------- /themes/animated/parrot_frames/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot_frames/3.png -------------------------------------------------------------------------------- /themes/animated/parrot_frames/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot_frames/4.png -------------------------------------------------------------------------------- /themes/animated/parrot_frames/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot_frames/5.png -------------------------------------------------------------------------------- /themes/animated/parrot_frames/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot_frames/6.png -------------------------------------------------------------------------------- /themes/animated/parrot_frames/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot_frames/7.png -------------------------------------------------------------------------------- /themes/animated/parrot_frames/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot_frames/8.png -------------------------------------------------------------------------------- /themes/animated/parrot_frames/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/animated/parrot_frames/9.png -------------------------------------------------------------------------------- /themes/temp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Theme with a single image placed centrally and then tiled. Also, illustrates the use of frame_inactive to change the header background color when the browser window isn't in focus. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#themes", 4 | "manifest_version": 2, 5 | "name": "temp", 6 | "version": "1.1", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/themes/weta_tiled", 8 | 9 | "theme": { 10 | "colors": { 11 | "frame": "blue", 12 | "frame_inactive": "#c6c6c6", 13 | "toolbar_text": "black", 14 | "tab_background_text":"black", 15 | "tab_text":"black", 16 | "sidebar_border":"black", 17 | "toolbar_field_border":"black", 18 | "tab_background_separator":"black", 19 | "toolbar_top_separator":"black", 20 | "toolbar_vertical_separator":"black", 21 | "toolbar_bottom_separator":"black", 22 | "toolbar_field_separator":"black" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /themes/weta_fade/README.md: -------------------------------------------------------------------------------- 1 | # Themes: weta_fade 2 | 3 | ## What it does 4 | 5 | Employs a PNG image as the theme_frame image in a theme. 6 | 7 | ## What it shows 8 | 9 | How to create a single image theme, using a faded edge and background color to ensure 10 | the image works well on a range of screen sizes. 11 | -------------------------------------------------------------------------------- /themes/weta_fade/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Theme using a PNG as the theme_frame image, employing a faded edge. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#themes", 4 | "manifest_version": 2, 5 | "name": "weta_fade", 6 | "version": "1.1", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/themes/weta_fade", 8 | 9 | "theme": { 10 | "images": { 11 | "theme_frame": "weta.png" 12 | }, 13 | 14 | "colors": { 15 | "frame": "#adb09f", 16 | "tab_text": "#000" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /themes/weta_fade/weta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/weta_fade/weta.png -------------------------------------------------------------------------------- /themes/weta_fade_chrome/README.md: -------------------------------------------------------------------------------- 1 | # Themes: Animated 2 | 3 | ## What it does 4 | 5 | Employs an PNG image as the Chrome compatible theme_frame image in a theme. 6 | 7 | ## What it shows 8 | 9 | How to use the Chrome compatible keys in a theme's manifest.json file to make a theme available for Firefox and Chrome. 10 | -------------------------------------------------------------------------------- /themes/weta_fade_chrome/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Version of the weta_fade theme using the Chrome compatible color definitions. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#themes", 4 | "manifest_version": 2, 5 | "name": "weta_fade_chrome", 6 | "version": "1.1", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/themes/weta_fade_chrome", 8 | 9 | 10 | "theme": { 11 | "images": { 12 | "theme_frame": "weta.png" 13 | }, 14 | 15 | "colors": { 16 | "frame": [ 173 , 176 , 159 ], 17 | "tab_text": [ 0 , 0 , 0 ] 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /themes/weta_fade_chrome/weta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/weta_fade_chrome/weta.png -------------------------------------------------------------------------------- /themes/weta_mirror/README.md: -------------------------------------------------------------------------------- 1 | # Themes: Animated 2 | 3 | ## What it does 4 | 5 | Employs two PNG images to create a mirror effect with the theme images. 6 | 7 | ## What it shows 8 | 9 | How to use "additional_backgrounds": in conjunction with "additional_backgrounds_alignment": to place multiple images within the browser header. 10 | -------------------------------------------------------------------------------- /themes/weta_mirror/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Theme using multiple additional_backgrounds images. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#themes", 4 | "manifest_version": 2, 5 | "name": "weta_mirror", 6 | "version": "1.1", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/themes/weta_mirror", 8 | 9 | "theme": { 10 | "images": { 11 | "additional_backgrounds": [ "weta.png", "weta-left.png"] 12 | }, 13 | 14 | "properties": { 15 | "additional_backgrounds_alignment": [ "right top" , "left top" ] 16 | }, 17 | 18 | "colors": { 19 | "frame": "#adb09f", 20 | "tab_text": "#000" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /themes/weta_mirror/weta-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/weta_mirror/weta-left.png -------------------------------------------------------------------------------- /themes/weta_mirror/weta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/weta_mirror/weta.png -------------------------------------------------------------------------------- /themes/weta_tiled/README.md: -------------------------------------------------------------------------------- 1 | # Themes: Animated 2 | 3 | ## What it does 4 | 5 | Tiles a PNG imagein the browser header. 6 | 7 | ## What it shows 8 | 9 | How to use "additional_backgrounds": in conjunction with "additional_backgrounds_alignment": and "additional_backgrounds_tiling": to place an image within the browser header and then tile it. 10 | -------------------------------------------------------------------------------- /themes/weta_tiled/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Theme with a single image placed centrally and then tiled. Also, illustrates the use of frame_inactive to change the header background color when the browser window isn't in focus. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#themes", 4 | "manifest_version": 2, 5 | "name": "weta_tiled", 6 | "version": "1.1", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/themes/weta_tiled", 8 | 9 | "theme": { 10 | "images": { 11 | "additional_backgrounds": "weta_for_tiling.png" 12 | }, 13 | 14 | "properties": { 15 | "additional_backgrounds_alignment": [ "top" ], 16 | "additional_backgrounds_tiling": [ "repeat" ] 17 | }, 18 | 19 | "colors": { 20 | "frame": "#adb09f", 21 | "frame_inactive": "#000", 22 | "tab_text": "#000" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /themes/weta_tiled/weta_for_tiling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/themes/weta_tiled/weta_for_tiling.png -------------------------------------------------------------------------------- /top-sites/README.md: -------------------------------------------------------------------------------- 1 | # Top Sites 2 | 3 | ## What it does 4 | 5 | This extension replaces the built-in page shown when the user opens a new tab without specifying a page to load (for example, when the user presses Ctrl+T). The replacement page is populated with links taken from the topSites API. 6 | 7 | # What it shows 8 | 9 | How to use chrome_url_overrides and the topSites API. 10 | -------------------------------------------------------------------------------- /top-sites/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Override examples", 4 | "version": "0.1", 5 | "chrome_url_overrides": { 6 | "newtab": "sites.html" 7 | }, 8 | "permissions": [ 9 | "topSites" 10 | ], 11 | "browser_specific_settings": { 12 | "gecko": { 13 | "strict_min_version": "54.0a1" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /top-sites/sites.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
    10 |

    Top Site and new tab override example

    11 |

    For more examples see this 12 | github repository 13 | or check the WebExtension 14 | documentation. 15 |

    16 |
    17 |
    18 |
    19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /top-sites/sites.js: -------------------------------------------------------------------------------- 1 | browser.topSites.get() 2 | .then((sites) => { 3 | let div = document.getElementById('site-list'); 4 | 5 | if (!sites.length) { 6 | div.innerText = 'No sites returned from the topSites API.'; 7 | return; 8 | } 9 | 10 | let ul = document.createElement('ul'); 11 | ul.className = 'list-group'; 12 | for (let site of sites) { 13 | let li = document.createElement('li'); 14 | li.className = 'list-group-item'; 15 | let a = document.createElement('a'); 16 | a.href = site.url; 17 | a.innerText = site.title || site.url; 18 | li.appendChild(a); 19 | ul.appendChild(li); 20 | } 21 | 22 | div.appendChild(ul); 23 | }); 24 | -------------------------------------------------------------------------------- /user-agent-rewriter/README.md: -------------------------------------------------------------------------------- 1 | # user-agent-rewriter 2 | 3 | ## What it does 4 | 5 | This extension uses the webRequest API to rewrite the browser's User Agent header, but only when visiting pages under "https://httpbin.org", for example: https://httpbin.org/user-agent 6 | 7 | It adds a browser action. The browser action has a popup that lets the user choose one of three browsers: Firefox 41, Chrome 41, and IE 11. When the user chooses a browser, the extension then rewrites the User Agent header so the real browser identifies itself as the chosen browser on the site https://httpbin.org/. 8 | 9 | ## What it shows 10 | 11 | * how to intercept and modify HTTP requests 12 | * how to write a browser action with a popup 13 | * how to give the popup style and behavior using CSS and JS 14 | * how to send a message from a popup script to a background script 15 | -------------------------------------------------------------------------------- /user-agent-rewriter/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The "person-32.png" "person-48.png" icons are taken from the Ionicons iconset (http://ionicons.com/), and are used here under the MIT license: http://opensource.org/licenses/MIT. 2 | -------------------------------------------------------------------------------- /user-agent-rewriter/icons/person-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/user-agent-rewriter/icons/person-32.png -------------------------------------------------------------------------------- /user-agent-rewriter/icons/person-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/user-agent-rewriter/icons/person-48.png -------------------------------------------------------------------------------- /user-agent-rewriter/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "description": "Adds browser action icon to toolbar to choose user agent string from popup menu. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#user-agent-rewriter", 4 | "manifest_version": 2, 5 | "name": "user-agent-rewriter", 6 | "version": "1.0", 7 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/user-agent-rewriter", 8 | "icons": { 9 | "48": "icons/person-48.png" 10 | }, 11 | 12 | "permissions": [ 13 | "webRequest", "webRequestBlocking", "https://httpbin.org/*" 14 | ], 15 | 16 | "background": { 17 | "scripts": ["background.js"] 18 | }, 19 | 20 | "browser_action": { 21 | "default_icon": "icons/person-32.png", 22 | "default_title": "Choose a user agent", 23 | "default_popup": "popup/choose_ua.html" 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /user-agent-rewriter/popup/choose_ua.css: -------------------------------------------------------------------------------- 1 | html, body, .ua-choices { 2 | height: 100px; 3 | width: 120px; 4 | margin: 0; 5 | } 6 | 7 | .ua-choices { 8 | display: flex; 9 | flex-direction: column; 10 | justify-content: space-around; 11 | } 12 | 13 | .ua-choice { 14 | height: 20%; 15 | margin: 0.2em; 16 | padding: 0.2em; 17 | background-color: #E5F2F2; 18 | cursor: pointer; 19 | } 20 | 21 | .ua-choice:hover { 22 | background-color: #CFF2F2; 23 | } 24 | -------------------------------------------------------------------------------- /user-agent-rewriter/popup/choose_ua.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
    12 |
    Firefox 41
    13 |
    Chrome 41
    14 |
    IE 11
    15 |
    16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /user-agent-rewriter/popup/choose_ua.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | If the user clicks on an element which has the class "ua-choice": 4 | * fetch the element's textContent: for example, "IE 11" 5 | * pass it into the background page's setUaString() function 6 | */ 7 | document.addEventListener("click", (e) => { 8 | if (!e.target.classList.contains("ua-choice")) { 9 | return; 10 | } 11 | 12 | let chosenUa = e.target.textContent; 13 | let backgroundPage = browser.extension.getBackgroundPage(); 14 | backgroundPage.setUaString(chosenUa); 15 | }); 16 | -------------------------------------------------------------------------------- /user-script-register/background.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let registered = null; 4 | 5 | async function registerScript(message) { 6 | const { 7 | hosts, 8 | code, 9 | userScriptID, 10 | } = message; 11 | 12 | if (registered) { 13 | await registered.unregister(); 14 | registered = null; 15 | } 16 | 17 | registered = await browser.userScripts.register({ 18 | matches: hosts, 19 | js: [{code}], 20 | scriptMetadata: {userScriptID}, 21 | }); 22 | } 23 | 24 | browser.runtime.onMessage.addListener(registerScript); 25 | -------------------------------------------------------------------------------- /user-script-register/customUserScriptAPIs.js: -------------------------------------------------------------------------------- 1 | 2 | browser.userScripts.onBeforeScript.addListener(script => { 3 | 4 | const scriptMetadata = script.metadata; 5 | const id = scriptMetadata.userScriptID; 6 | 7 | function getScopedName(name) { 8 | return `${id}:${name}`; 9 | } 10 | 11 | script.defineGlobals({ 12 | async GM_getValue(name) { 13 | const scopedName = getScopedName(name); 14 | const res = await browser.storage.local.get(scopedName); 15 | console.log("GM_getValue", {id, name, res, scriptMetadata}); 16 | return res[scopedName]; 17 | }, 18 | GM_setValue(name, value) { 19 | console.log("GM_setValue", {id, name, value, scriptMetadata}); 20 | return browser.storage.local.set({[getScopedName(name)]: value}); 21 | }, 22 | }); 23 | 24 | console.log("custom userScripts APIs defined"); 25 | }); 26 | 27 | console.log("apiScript executed and userScripts.onBeforeScript listener subscribed"); -------------------------------------------------------------------------------- /user-script-register/icons/LICENSE: -------------------------------------------------------------------------------- 1 | The icon "if_source_code_103710.svg" is from picol.org (http://www.picol.org/) and is used under the terms of the Creative Commons Attribution license: http://creativecommons.org/licenses/by/3.0/. 2 | -------------------------------------------------------------------------------- /user-script-register/icons/if_source_code_103710.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /user-script-register/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "manifest_version": 2, 4 | "name": "User script registration", 5 | "version": "1.0", 6 | 7 | "browser_specific_settings": { 8 | "gecko": { 9 | "id": "user-script-example@mozilla.org", 10 | "strict_min_version": "65.0" 11 | } 12 | }, 13 | 14 | "description": "Demonstration of userScripts.register.", 15 | "icons": { 16 | "48": "icons/if_source_code_103710.svg" 17 | }, 18 | 19 | "permissions": [ 20 | "", 21 | "storage" 22 | ], 23 | 24 | "sidebar_action": { 25 | "default_icon": { 26 | "32" : "icons/if_source_code_103710.svg" 27 | }, 28 | "default_title": "User script", 29 | "default_panel": "popup/user-script.html", 30 | "browser_style": true 31 | }, 32 | 33 | "background": { 34 | "scripts": ["background.js"] 35 | }, 36 | 37 | "user_scripts": { 38 | "api_script": "customUserScriptAPIs.js" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /user-script-register/popup/user-script.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 1.0em; 3 | } 4 | 5 | input { 6 | width: 100%; 7 | margin-bottom: 1em; 8 | } 9 | 10 | textarea { 11 | width: 100%; 12 | resize: none; 13 | border: 1px solid #e4e4e4; 14 | margin-bottom: 1em; 15 | min-height: 380px; 16 | } 17 | -------------------------------------------------------------------------------- /user-script-register/popup/user-script.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
    12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
    25 |
    26 |
    27 |
    28 |
    29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /userScripts-mv3/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": ["*.mjs"], 5 | "parserOptions": { 6 | "sourceType": "module" 7 | } 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /userScripts-mv3/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "User Scripts Manager extension", 4 | "description": "Demonstrates the userScripts API and optional permission, in MV3.", 5 | "version": "0.1", 6 | "host_permissions": ["*://*/"], 7 | "permissions": ["storage", "unlimitedStorage"], 8 | "optional_permissions": ["userScripts"], 9 | "background": { 10 | "scripts": ["background.js"] 11 | }, 12 | "options_ui": { 13 | "page": "options.html" 14 | }, 15 | "browser_specific_settings": { 16 | "gecko": { 17 | "id": "user-script-manager-example@mozilla.org", 18 | "strict_min_version": "136.0" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /userScripts-mv3/options.css: -------------------------------------------------------------------------------- 1 | #edit_script_dialog .source_text { 2 | display: block; 3 | width: 80vw; 4 | min-height: 10em; 5 | } 6 | -------------------------------------------------------------------------------- /userScripts-mv3/userscript_examples/unprivileged.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Demo of unprivileged user script 3 | // @description Show dialog on MDN and every URL starting with "https://example". 4 | // @match https://developer.mozilla.org/* 5 | // @include https://example* 6 | // @exclude-match https://example.com/display_userscript_result* 7 | // @grant none 8 | // @version 1.2.3 9 | // ==/UserScript== 10 | 11 | // To test: 12 | // 1. Visit https://example.com/ or https://developer.mozilla.org/ 13 | // 2. Confirm that a dialog shows up. 14 | 15 | alert(`This is a demo of a user script, running at ${document.URL}.`); 16 | 17 | // This user script should not get access to privileged APIs. 18 | if (typeof GM_info !== "undefined") { 19 | alert("Unexpectedly, GM_info is defined...?"); 20 | } 21 | -------------------------------------------------------------------------------- /webpack-modules/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "amd": true, 6 | "webextensions": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /webpack-modules/addon/icons/leftpad-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/webpack-modules/addon/icons/leftpad-32.png -------------------------------------------------------------------------------- /webpack-modules/addon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Webpack Example", 4 | "version": "1.0.0", 5 | "description": "A minimal example of how to use npm modules from within a WebExtension.", 6 | "icons": { 7 | "32": "icons/leftpad-32.png" 8 | }, 9 | "browser_action": { 10 | "default_icon": "icons/leftpad-32.png", 11 | "default_title": "Left Pad", 12 | "default_popup": "popup/left-pad.html", 13 | "browser_style": true 14 | }, 15 | "background": { 16 | "scripts": ["background_scripts/index.js"] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /webpack-modules/background_scripts/background.js: -------------------------------------------------------------------------------- 1 | const leftPad = require("left-pad"); 2 | 3 | browser.runtime.onMessage.addListener((message, sender, sendResponse) => { 4 | const result = leftPad(message.text, message.amount, message.with); 5 | sendResponse(result); 6 | }); 7 | -------------------------------------------------------------------------------- /webpack-modules/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-webextension", 3 | "version": "1.0.0", 4 | "description": "A minimal example of how to use npm modules from within a WebExtension.", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1", 7 | "build": "webpack" 8 | }, 9 | "license": "MPL-2.0", 10 | "devDependencies": { 11 | "webpack": "^5.74.0", 12 | "webpack-cli": "^4.10.0" 13 | }, 14 | "dependencies": { 15 | "left-pad": "^1.1.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /webpack-modules/popup/left-pad.js: -------------------------------------------------------------------------------- 1 | const leftPad = require("left-pad"); 2 | 3 | const resultNode = document.getElementById("result"); 4 | const textNode = document.getElementById("text"); 5 | const amountNode = document.getElementById("amount"); 6 | const withNode = document.getElementById("with"); 7 | 8 | document.getElementById("leftpad-form").addEventListener("submit", (e) => { 9 | e.preventDefault(); 10 | 11 | console.log("padding"); 12 | resultNode.value = leftPad(textNode.value, amountNode.valueAsNumber, withNode.value); 13 | }, false); 14 | 15 | document.getElementById("pad-bg").addEventListener("click", (e) => { 16 | let sendingMessage = browser.runtime.sendMessage({ 17 | text: textNode.value, 18 | amount: amountNode.valueAsNumber, 19 | with: withNode.value 20 | }); 21 | sendingMessage.then((result) => { 22 | resultNode.value = result; 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /webpack-modules/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | entry: { 5 | background_scripts: "./background_scripts/background.js", 6 | popup: "./popup/left-pad.js" 7 | }, 8 | output: { 9 | path: path.resolve(__dirname, "addon"), 10 | filename: "[name]/index.js" 11 | }, 12 | mode: 'none', 13 | }; 14 | -------------------------------------------------------------------------------- /window-manipulator/README.md: -------------------------------------------------------------------------------- 1 | # Window manipulator 2 | 3 | ## What it does 4 | 5 | This extension create a sidebar, specified as "window.html", that lets the user perform various simple operations using the windows API. 6 | 7 | # What it shows 8 | 9 | Demonstration of various windows API functions. 10 | -------------------------------------------------------------------------------- /window-manipulator/icons/window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/window-manipulator/icons/window.png -------------------------------------------------------------------------------- /window-manipulator/icons/window19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/window-manipulator/icons/window19.png -------------------------------------------------------------------------------- /window-manipulator/icons/window38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/window-manipulator/icons/window38.png -------------------------------------------------------------------------------- /window-manipulator/icons/window@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webextensions-examples/386f262076e56ae09c4d351938c09143a3917a9c/window-manipulator/icons/window@2x.png -------------------------------------------------------------------------------- /window-manipulator/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "sidebar_action": { 3 | "browser_style": true, 4 | "default_title": "Window manipulator", 5 | "default_panel": "window.html", 6 | "default_icon": { 7 | "19": "icons/window19.png", 8 | "38": "icons/window38.png" 9 | } 10 | }, 11 | "icons": { 12 | "48": "icons/window.png", 13 | "96": "icons/window@2x.png" 14 | }, 15 | "description": "A list of methods you can perform on a window.", 16 | "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/window-manipulator", 17 | "manifest_version": 2, 18 | "name": "Window manipulator", 19 | "version": "1.0" 20 | } 21 | -------------------------------------------------------------------------------- /window-manipulator/window.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 350px; 3 | } 4 | 5 | a { 6 | margin: 10px; 7 | display: inline-block; 8 | } 9 | 10 | .panel { 11 | margin: 5px; 12 | } 13 | --------------------------------------------------------------------------------