├── .eslintignore ├── .babelrc ├── screenshots.jpg ├── shared ├── icon128.png ├── icon32.png ├── icon48.png ├── icon64.png ├── .eslintrc ├── _locales │ └── en │ │ └── messages.json ├── content.js ├── checkurl.js ├── manifest.json └── base.js ├── bin ├── webcompat-reporter.crx ├── webcompat-reporter.nex ├── webcompat-reporter.xpi └── webcompat-reporter-mobile.xpi ├── chrome ├── .eslintrc ├── addon.js └── run.js ├── firefox ├── .eslintrc └── addon.js ├── opera ├── .eslintrc └── addon.js ├── tests ├── .eslintrc ├── test-dist-output.js ├── test-manifest.js ├── test-checkurl-import.js └── lib │ └── helpers.js ├── firefox-mobile ├── .eslintrc └── addon.js ├── intern.json ├── .travis.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── .eslintrc ├── package.json ├── .github └── CONTRIBUTING.md ├── webpack.config.js └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | **/background.js 2 | **/node_modules/* 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["babel-plugin-transform-es2015-modules-commonjs"] 3 | } 4 | -------------------------------------------------------------------------------- /screenshots.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcompat/webcompat-reporter-extensions/HEAD/screenshots.jpg -------------------------------------------------------------------------------- /shared/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcompat/webcompat-reporter-extensions/HEAD/shared/icon128.png -------------------------------------------------------------------------------- /shared/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcompat/webcompat-reporter-extensions/HEAD/shared/icon32.png -------------------------------------------------------------------------------- /shared/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcompat/webcompat-reporter-extensions/HEAD/shared/icon48.png -------------------------------------------------------------------------------- /shared/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcompat/webcompat-reporter-extensions/HEAD/shared/icon64.png -------------------------------------------------------------------------------- /bin/webcompat-reporter.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcompat/webcompat-reporter-extensions/HEAD/bin/webcompat-reporter.crx -------------------------------------------------------------------------------- /bin/webcompat-reporter.nex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcompat/webcompat-reporter-extensions/HEAD/bin/webcompat-reporter.nex -------------------------------------------------------------------------------- /bin/webcompat-reporter.xpi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcompat/webcompat-reporter-extensions/HEAD/bin/webcompat-reporter.xpi -------------------------------------------------------------------------------- /bin/webcompat-reporter-mobile.xpi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcompat/webcompat-reporter-extensions/HEAD/bin/webcompat-reporter-mobile.xpi -------------------------------------------------------------------------------- /chrome/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /firefox/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /opera/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /shared/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /firefox-mobile/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /shared/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "contextMenuTitle": { 3 | "message": "Report site issue", 4 | "description": "Title for context menu item." 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /intern.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Default test suites", 3 | "debug": true, 4 | "filterErrorStack": true, 5 | "suites": "tests/**/*.js", 6 | "node": { 7 | "plugins": 8 | "node_modules/babel-register/lib/node.js" 9 | }, 10 | "instrumenterOptions": { 11 | "esModules": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /shared/content.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | // add a class to hide the download link when the add-on is installed. 5 | 6 | document.querySelector(".js-addon-link").style.display = "none"; 7 | -------------------------------------------------------------------------------- /opera/addon.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | import initAddon from "../shared/base.js"; 6 | 7 | const REPORTER_ID = "addon-reporter-opera"; 8 | 9 | initAddon(REPORTER_ID, { createContextMenu: true }); 10 | -------------------------------------------------------------------------------- /chrome/addon.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | import initAddon from "../shared/base.js"; 6 | 7 | const REPORTER_ID = "addon-reporter-chrome"; 8 | 9 | initAddon(REPORTER_ID, { createContextMenu: true }); 10 | -------------------------------------------------------------------------------- /firefox/addon.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | import initAddon from "../shared/base.js"; 6 | 7 | const REPORTER_ID = "addon-reporter-firefox"; 8 | 9 | initAddon(REPORTER_ID, { createContextMenu: true }); 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | # Only run travis on the master branch (and PRs), not all branches 4 | branches: 5 | only: 6 | - master 7 | 8 | language: node_js 9 | node_js: 10 | - "node" 11 | 12 | cache: 13 | directories: 14 | - node_modules 15 | 16 | git: 17 | depth: 5 18 | 19 | install: 20 | - travis_retry npm install 21 | 22 | script: 23 | - npm run eslint 24 | - npm run test 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.cer 3 | *.xpi 4 | bin/ 5 | **/node_modules/ 6 | **/web-ext-artifacts/ 7 | *.log 8 | **/*.log 9 | package-lock.json 10 | .eslintcache 11 | .vscode/ 12 | 13 | 14 | # ignore these, because they will be generated or moved. 15 | **/background.js 16 | **/content.js 17 | **/manifest.json 18 | **/*.png 19 | dist/ 20 | 21 | # except, we care about the source shared files. 22 | !shared/content.js 23 | !shared/*.png 24 | !shared/manifest.json 25 | !firefox-mobile/manifest.json 26 | -------------------------------------------------------------------------------- /shared/checkurl.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | export default function isReportableURL(url) { 6 | try { 7 | if (url && typeof url == "string") { 8 | return ["http:", "https:"].some(protocol => url.startsWith(protocol)); 9 | } 10 | } catch (error) { 11 | console.log(`isReportableURL error: ${error}`); 12 | } 13 | 14 | return false; 15 | } 16 | -------------------------------------------------------------------------------- /chrome/run.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 | 7 | const chromeLaunch = require("chrome-launch"); 8 | const url = "https://webcompat.com/"; 9 | const extPath = `${__dirname}/../dist/chrome`; 10 | const args = [`--load-extension=${extPath}`]; 11 | 12 | chromeLaunch(url, { args }); 13 | console.log("A new instance of Chrome should now be open in the background."); // eslint-disable-line no-console 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Community Participation Guidelines 2 | 3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines. 4 | For more details, please read the 5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 6 | 7 | ## How to Report 8 | For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page. 9 | 10 | 16 | -------------------------------------------------------------------------------- /firefox-mobile/addon.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | import initAddon from "../shared/base.js"; 6 | 7 | const REPORTER_ID = "addon-reporter-firefox-mobile"; 8 | 9 | browser.runtime.getBrowserInfo().then(function({ version }) { 10 | if (version.includes("a")) { 11 | // Uninstall, without prompts, because this addon makes no sense on Nightly 12 | // (it's built-in by default) 13 | browser.management.uninstallSelf(); 14 | } else { 15 | // Proceed if we're not running on Nightly. 16 | // Note: Fennec doesn't support context menu APIs 17 | initAddon(REPORTER_ID, { createContextMenu: false }); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /shared/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "webcompat.com reporter", 4 | "description": "Report website compatibility issues at webcompat.com", 5 | "background": { 6 | "scripts": ["background.js"] 7 | }, 8 | "content_scripts": [ 9 | { 10 | "matches": ["https://webcompat.com/"], 11 | "js": ["content.js"], 12 | "run_at": "document_end" 13 | } 14 | ], 15 | "permissions": [ 16 | "tabs", 17 | "activeTab", 18 | "contextMenus", 19 | "https://webcompat.com/", 20 | "" 21 | ], 22 | "icons": { 23 | "32": "icon32.png", 24 | "48": "icon48.png", 25 | "64": "icon64.png", 26 | "128": "icon128.png" 27 | }, 28 | "browser_action": { 29 | "default_icon": { 30 | "16": "icon32.png", 31 | "32": "icon32.png", 32 | "48": "icon48.png", 33 | "64": "icon64.png", 34 | "128": "icon128.png" 35 | }, 36 | "default_title": "Report Site Issue" 37 | }, 38 | "default_locale": "en" 39 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "es6": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:prettier/recommended" 10 | ], 11 | "globals": { 12 | "browser": true, 13 | "chrome": true, 14 | "intern": true, 15 | "module": true 16 | }, 17 | "rules": { 18 | "comma-dangle": 0, 19 | "curly": [2, "all"], 20 | "eol-last": "error", 21 | "eqeqeq": [2, "smart"], 22 | "indent": [2, 2, { "SwitchCase": 1 }], 23 | "keyword-spacing": 2, 24 | "linebreak-style": [2, "unix"], 25 | "new-cap": [2, {"newIsCap": true, "capIsNew": false}], 26 | "no-cond-assign": 0, 27 | "no-spaced-func": 2, 28 | "no-use-before-define": 2, 29 | "no-unused-vars": ["error", { "args": "none" }], 30 | "no-useless-concat": "error", 31 | "one-var": [2, "never"], 32 | "prefer-template": "error", 33 | "quotes": ["error", "double"], 34 | "semi": [2, "always"], 35 | "space-before-blocks": 2, 36 | "space-before-function-paren": [2, "never"], 37 | "space-infix-ops": 2, 38 | "space-unary-ops": 2 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/test-dist-output.js: -------------------------------------------------------------------------------- 1 | /* Any copyright is dedicated to the Public Domain. 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 | 4 | import fileExists from "file-exists"; 5 | import helpers from "./lib/helpers.js"; 6 | import shell from "shelljs"; 7 | 8 | const { registerSuite } = intern.getInterface("object"); 9 | const { assert } = intern.getPlugin("chai"); 10 | 11 | registerSuite("dist output", { 12 | before() { 13 | shell.rm("-rf", "dist/"); 14 | return helpers.compileWebpack(); 15 | }, 16 | tests: { 17 | "has shared output (single files)"() { 18 | // verify single file dist assets got added 19 | ["chrome", "opera", "firefox-mobile", "firefox"].forEach(browser => { 20 | [ 21 | "background.js", 22 | "content.js", 23 | "icon32.png", 24 | "icon48.png", 25 | "icon64.png", 26 | "icon128.png", 27 | "manifest.json" 28 | ].forEach(file => { 29 | fileExists(`dist/${browser}/${file}`, (err, exists) => 30 | assert.isTrue( 31 | exists, 32 | "all shared single-file assets are included in dist output" 33 | ) 34 | ); 35 | }); 36 | }); 37 | }, 38 | "has _locales directory with all l18n message files"() { 39 | // verify localization directory dist asset was added 40 | ["chrome", "opera", "firefox-mobile", "firefox"].forEach(browser => { 41 | ["en"].forEach(lang => { 42 | fileExists( 43 | `dist/${browser}/_locales/${lang}/messages.json`, 44 | (err, exists) => 45 | assert.isTrue( 46 | exists, 47 | "locales directory with all current translations is included in dist output" 48 | ) 49 | ); 50 | }); 51 | }); 52 | } 53 | } 54 | }); 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webcompat-reporter-addon", 3 | "version": "0.6.0", 4 | "description": "WebCompat reporter add-ons.", 5 | "author": "webcompat.com contributors", 6 | "scripts": { 7 | "eslint": "eslint --ignore-path .eslintignore -c .eslintrc . --cache", 8 | "firefox": "web-ext run --source-dir=dist/firefox", 9 | "chrome": "node ./chrome/run.js", 10 | "fennec": "web-ext run --target=firefox-android --source-dir=dist/firefox-mobile --firefox-apk=org.mozilla.firefox", 11 | "fennec-nightly": "web-ext run --target=firefox-android --source-dir=dist/firefox-mobile --firefox-apk=org.mozilla.fennec_aurora", 12 | "package:firefox": "web-ext build --source-dir=dist/firefox --artifacts-dir=bin --overwrite-dest", 13 | "package:fennec": "web-ext build --source-dir=dist/firefox-mobile --artifacts-dir=bin --overwrite-dest", 14 | "package:chrome": "npm run build:chrome && crx pack dist/chrome -o bin/webcompat-reporter-chrome.crx", 15 | "package:opera": "npm run build:opera && crx pack dist/opera -o bin/webcompat-reporter-opera.crx && shx mv bin/webcompat-reporter-opera.crx bin/webcompat-reporter-opera.nex", 16 | "build:firefox": "webpack --env=firefox", 17 | "build:fennec": "webpack --env=firefox-mobile", 18 | "build:chrome": "webpack --env=chrome", 19 | "build:opera": "webpack --env=opera", 20 | "test": "intern", 21 | "clean": "shx rm -rf dist/" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+ssh://git@github.com/webcompat/webcompat-reporter-extensions.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/webcompat/webcompat-reporter-extensions/issues" 29 | }, 30 | "homepage": "https://github.com/webcompat/webcompat-reporter-extensions#readme", 31 | "devDependencies": { 32 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", 33 | "babel-register": "^6.26.0", 34 | "chrome-launch": "^1.1.4", 35 | "copy-webpack-plugin": "^4.3.1", 36 | "crx": "^3.2.1", 37 | "eslint": "6.5.1", 38 | "eslint-config-prettier": "^2.9.0", 39 | "eslint-plugin-prettier": "^2.0.1", 40 | "file-exists": "^5.0.1", 41 | "intern": "^4.1.5", 42 | "prettier": "1.11.1", 43 | "shelljs": "^0.8.1", 44 | "shx": "^0.2.2", 45 | "web-ext": "^2.4.0", 46 | "webpack": "^4.1.1", 47 | "webpack-cli": "^3.1.1" 48 | }, 49 | "license": "MPL-2.0" 50 | } 51 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | You are welcome to contribute to this project! Here are the guidelines we try to stick to in this project. 4 | 5 | * [Questions or Problems](#questions-or-problems) 6 | * [Commit Messages](#commit-messages) 7 | * [Pull Requests](#pull-requests) 8 | * [Coding Style](#coding-style) 9 | 10 | ## Questions or Problems 11 | 12 | If you have a question about the site or about web compatibility in general, feel free to join us in the #webcompat channel on the Mozilla IRC network. [Here's how to join](https://wiki.mozilla.org/IRC#Connect_to_the_Mozilla_IRC_server). 13 | 14 | Otherwise, you can try to ping Mike Taylor on the Freenode network with the following command `/msg miketaylr Hi, I have a question about contributing to the webcompat reporter addons`. 15 | 16 | If IRC isn't your thing, send an email to Mike Taylor at miket@mozilla.com. 17 | 18 | ## Commit Messages 19 | 20 | Each commit message should include a reference to the issue being worked on. [For example](https://github.com/webcompat/webcompat-reporter-extensions/commit/3e82a09b1a609338e5c0f5e227433b3a0851ce2c), 21 | 22 | `Issue #83. Add TravisCI integration.` 23 | 24 | This creates a nice hyperlink between commits and issues automatically n GitHub. 25 | 26 | ## Pull Requests 27 | 28 | [Pull requests](https://help.github.com/articles/creating-a-pull-request/) are welcome. All PRs should correspond to an existing issue, so feel free to [file one](https://github.com/webcompat/webcompat-reporter-extensions/issues/new) if it doesn't exist already. 29 | 30 | For large changes or new features, it's a good idea to discuss the idea in an issue before sending in a PR. 31 | 32 | Pull Request titles should follow this format: 33 | 34 | `Fixes #(Number of issue). Short description of PR.` 35 | 36 | For example, 37 | 38 | `Fixes #83. Add TravisCI integration.` 39 | 40 | Be sure to request a reviewer with the following comment, @miketaylr is a good person to start with! 41 | 42 | `r? @miketaylr` 43 | 44 | ## Coding Style 45 | 46 | For JS, this project uses [Prettier](https://prettier.io/) to format everything (via eslint integration). [Why?](https://prettier.io/docs/en/why-prettier.html) 47 | 48 | Try to get your [text editor integration set up](https://prettier.io/docs/en/editors.html), it's pretty great. Otherwise, you can get a report by running the following command: 49 | 50 | `npm run eslint` 51 | -------------------------------------------------------------------------------- /tests/test-manifest.js: -------------------------------------------------------------------------------- 1 | /* Any copyright is dedicated to the Public Domain. 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 | 4 | import helpers from "./lib/helpers.js"; 5 | import shell from "shelljs"; 6 | 7 | const { registerSuite } = intern.getInterface("object"); 8 | const { assert } = intern.getPlugin("chai"); 9 | 10 | // const helpers = require("./lib/helpers"); 11 | // const shell = require("shelljs"); 12 | 13 | registerSuite("manifest.json creation", { 14 | before() { 15 | shell.rm("-rf", "dist/"); 16 | return helpers.compileWebpack(); 17 | }, 18 | tests: { 19 | "has shared keys"() { 20 | // verify that shared manifest bits ended up where they should be. 21 | ["chrome", "opera", "firefox", "firefox-mobile"].forEach(browser => { 22 | let manifest = require(`../dist/${browser}/manifest.json`); 23 | assert.containsAllKeys( 24 | manifest, 25 | [ 26 | "manifest_version", 27 | "name", 28 | "description", 29 | "background", 30 | "content_scripts", 31 | "permissions", 32 | "icons", 33 | "browser_action" 34 | ], 35 | "generated manifest.json has all expected shared keys" 36 | ); 37 | }); 38 | }, 39 | 40 | "has version number"() { 41 | // all browsers get a version key added. 42 | ["chrome", "opera", "firefox", "firefox-mobile"].forEach(browser => { 43 | let manifest = require(`../dist/${browser}/manifest.json`); 44 | assert.containsAllKeys( 45 | manifest, 46 | ["version"], 47 | "generated manifest.json has a version key" 48 | ); 49 | assert.isString(manifest["version"], "version key is a string"); 50 | assert.isNumber( 51 | parseFloat(manifest["version"]), 52 | "version key is a string that can be parsed as a number" 53 | ); 54 | }); 55 | }, 56 | 57 | "has application keys"() { 58 | // firefox browsers get an applications key added. 59 | ["firefox", "firefox-mobile"].forEach(browser => { 60 | let manifest = require(`../dist/${browser}/manifest.json`); 61 | assert.containsAllKeys( 62 | manifest, 63 | ["applications"], 64 | "generated manifest.json has an applications key" 65 | ); 66 | assert.isObject( 67 | manifest["applications"], 68 | "applications key object exists" 69 | ); 70 | }); 71 | } 72 | } 73 | }); 74 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | // webpack config file to bundle the web extensions from shared sources 6 | 7 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 8 | const version = require("./package.json").version; 9 | 10 | module.exports = function(env) { 11 | // Get extension platform from webpack env variable. (Can be set to any string.) 12 | var config = { 13 | entry: `./${env}/addon.js`, 14 | output: { 15 | filename: `../dist/${env}/background.js` 16 | }, 17 | plugins: [ 18 | new CopyWebpackPlugin([ 19 | { 20 | from: "shared/_locales/", 21 | to: `../dist/${env}/_locales/` 22 | }, 23 | { 24 | from: "shared/content.js", 25 | to: `../dist/${env}/content.js` 26 | }, 27 | { 28 | from: "shared/*.png", 29 | to: `../dist/${env}/[name].[ext]` 30 | }, 31 | { 32 | from: "shared/manifest.json", 33 | to: `../dist/${env}/[name].json`, 34 | transform: function(content, path) { 35 | let manifest = JSON.parse(content); 36 | // Derive addon versioning from package.json 37 | manifest["version"] = version; 38 | switch (env) { 39 | case "firefox": 40 | // Add Firefox-specific bits to the manifest.json 41 | manifest["applications"] = { 42 | gecko: { 43 | id: "jid1-mjpB54bRzP9Zxw@jetpack", 44 | strict_min_version: "49.0a1" 45 | } 46 | }; 47 | return JSON.stringify(manifest, null, 2); 48 | case "firefox-mobile": 49 | // Add Firefox-mobile-specific bits to the manifest.json 50 | manifest["applications"] = { 51 | gecko: { 52 | id: "webcompat-reporter-for-mobile@webcompat.com", 53 | strict_min_version: "51.0" 54 | } 55 | }; 56 | return JSON.stringify(manifest, null, 2); 57 | default: 58 | // Neither Chrome nor Opera needs the applications key 59 | return JSON.stringify(manifest, null, 2); 60 | } 61 | } 62 | } 63 | ]) 64 | ] 65 | }; 66 | return config; 67 | }; 68 | -------------------------------------------------------------------------------- /tests/test-checkurl-import.js: -------------------------------------------------------------------------------- 1 | /* Any copyright is dedicated to the Public Domain. 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 | 4 | import isReportableURL from "../shared/checkurl.js"; 5 | import helpers from "./lib/helpers.js"; 6 | import shell from "shelljs"; 7 | 8 | const { registerSuite } = intern.getInterface("object"); 9 | const { assert } = intern.getPlugin("chai"); 10 | 11 | registerSuite("isReportableURL module", { 12 | before() { 13 | shell.rm("-rf", "dist/"); 14 | return helpers.compileWebpack(); 15 | }, 16 | tests: { 17 | "isReportableURL imported successfully"() { 18 | // Verify that isReportableURL function is imported correctly. 19 | assert.exists( 20 | isReportableURL, 21 | "isReportableURL is neither 'null' nor 'undefined'" 22 | ); 23 | }, 24 | "isReportableURL is a function"() { 25 | // Verify that isReportableURL is recognized as a function. 26 | assert.isFunction(isReportableURL, "isReportableURL is a function"); 27 | }, 28 | "calling isReportableURL does not throw an error"() { 29 | // Verify that calling isReportableURL does not throw an error. 30 | assert.doesNotThrow(isReportableURL, Error); 31 | }, 32 | "calling isReportableURL with valid protocols will return true"() { 33 | // Verify that calling isReportableURL with valid protocol returns true. 34 | let httpUrl = "http://somewebsiteaddress.com"; 35 | let httpsUrl = "https://anotherwebsite.org"; 36 | assert.isTrue( 37 | isReportableURL(httpUrl), 38 | "'http:' is a reportable URL protocol" 39 | ); 40 | assert.isTrue( 41 | isReportableURL(httpsUrl), 42 | "'https:' is a reportable URL protocol" 43 | ); 44 | }, 45 | "calling isReportableURL with invalid protocols will not return true"() { 46 | // Verify that calling isReportableURL with invalid protocol does not return true. 47 | let guacUrl = "guacamole://yumavocados.com"; 48 | let ftpUrl = "ftp://downloadfreetacos.com"; 49 | let badHttpUrl = "http//sitenotloading.org"; 50 | let noProtUrl = "www.missingsomething.com"; 51 | assert.isNotTrue( 52 | isReportableURL(guacUrl), 53 | "'guacamole' is not a reportable URL protocol" 54 | ); 55 | assert.isNotTrue( 56 | isReportableURL(ftpUrl), 57 | "'ftp:' is not a reportable URL protocol" 58 | ); 59 | assert.isNotTrue( 60 | isReportableURL(badHttpUrl), 61 | "'http' without a colon is not a reportable URL protocol" 62 | ); 63 | assert.isNotTrue( 64 | isReportableURL(noProtUrl), 65 | "missing protocol means not a reportable URL" 66 | ); 67 | } 68 | } 69 | }); 70 | -------------------------------------------------------------------------------- /shared/base.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | import isReportableURL from "./checkurl.js"; 6 | 7 | const PREFIX = "https://webcompat.com/issues/new"; 8 | 9 | function createContextMenu() { 10 | chrome.contextMenus.create({ 11 | id: "webcompat-contextmenu", 12 | title: chrome.i18n.getMessage("contextMenuTitle"), 13 | contexts: ["all"] 14 | }); 15 | } 16 | 17 | function enableOrDisableOnActive(activeInfo) { 18 | const activeId = activeInfo.tabId; 19 | chrome.tabs.get(activeId, tab => { 20 | if (isReportableURL(tab.url)) { 21 | chrome.browserAction.enable(activeId); 22 | } else { 23 | chrome.browserAction.disable(activeId); 24 | } 25 | }); 26 | } 27 | 28 | function enableOrDisableOnUpdate(tabId, changeInfo, tab) { 29 | if (changeInfo.status === "loading" && isReportableURL(tab.url)) { 30 | chrome.browserAction.enable(tabId); 31 | } else if (changeInfo.status === "loading" && !isReportableURL(tab.url)) { 32 | chrome.browserAction.disable(tabId); 33 | } 34 | } 35 | 36 | function reportIssue(tab, reporterID) { 37 | chrome.tabs.captureVisibleTab({ format: "png" }, function(res) { 38 | let screenshotData = res; 39 | chrome.tabs.query({ currentWindow: true, active: true }, function(tab) { 40 | const json = JSON.stringify({ 41 | url: tab[0].url, 42 | src: reporterID, 43 | utm_source: reporterID, 44 | utm_campaign: "report-site-issue-extension" 45 | }); 46 | 47 | chrome.tabs.create({ url: PREFIX }, function(tab) { 48 | chrome.tabs.executeScript(tab.id, { 49 | runAt: "document_end", 50 | code: ` 51 | async function postMessageData(dataURI, metadata) { 52 | const res = await fetch(dataURI); 53 | const blob = await res.blob(); 54 | const data = { 55 | screenshot: blob, 56 | message: metadata 57 | }; 58 | postMessage(data, "*"); 59 | } 60 | postMessageData("${screenshotData}", ${json}); 61 | ` 62 | }); 63 | }); 64 | }); 65 | }); 66 | } 67 | 68 | function setupListeners(reporterID, options) { 69 | chrome.tabs.onCreated.addListener(tab => { 70 | // disable all new tabs until they've loaded and we know 71 | // they have reportable URLs 72 | chrome.browserAction.disable(tab.tabId); 73 | }); 74 | chrome.tabs.onUpdated.addListener(enableOrDisableOnUpdate); 75 | chrome.tabs.onActivated.addListener(enableOrDisableOnActive); 76 | chrome.browserAction.onClicked.addListener(tab => 77 | reportIssue(tab, reporterID) 78 | ); 79 | 80 | if (options && options.createContextMenu) { 81 | chrome.contextMenus.onClicked.addListener(tab => 82 | reportIssue(tab, reporterID) 83 | ); 84 | } 85 | } 86 | 87 | export default function initAddon(reporterID, options = false) { 88 | if (options && options.createContextMenu) { 89 | createContextMenu(); 90 | } 91 | setupListeners(reporterID, options); 92 | } 93 | -------------------------------------------------------------------------------- /tests/lib/helpers.js: -------------------------------------------------------------------------------- 1 | /* Any copyright is dedicated to the Public Domain. 2 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 | 4 | const webpack = require("webpack"); 5 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 6 | const version = require("../../package.json").version; 7 | 8 | /* 9 | Returns a promise that resolves once webpack has compiled all the addon 10 | configs have been built. 11 | */ 12 | 13 | function compileWebpack() { 14 | return new Promise((resolve, reject) => { 15 | const config = ["chrome", "firefox", "firefox-mobile", "opera"].map( 16 | platform => { 17 | var template = { 18 | entry: `./${platform}/addon.js`, 19 | output: { 20 | filename: `../dist/${platform}/background.js` 21 | }, 22 | plugins: [ 23 | new CopyWebpackPlugin([ 24 | { 25 | from: "shared/_locales/", 26 | to: `../dist/${platform}/_locales/` 27 | }, 28 | { 29 | from: "shared/content.js", 30 | to: `../dist/${platform}/content.js` 31 | }, 32 | { 33 | from: "shared/*.png", 34 | to: `../dist/${platform}/[name].[ext]` 35 | }, 36 | { 37 | from: "shared/manifest.json", 38 | to: `../dist/${platform}/[name].json`, 39 | transform: function(content, path) { 40 | let manifest = JSON.parse(content); 41 | // Derive addon versioning from package.json 42 | manifest["version"] = version; 43 | switch (platform) { 44 | case "firefox": 45 | // Add Firefox-specific bits to the manifest.json 46 | manifest["applications"] = { 47 | gecko: { 48 | id: "jid1-mjpB54bRzP9Zxw@jetpack", 49 | strict_min_version: "49.0a1" 50 | } 51 | }; 52 | return JSON.stringify(manifest, null, 2); 53 | case "firefox-mobile": 54 | // Add Firefox-mobile-specific bits to the manifest.json 55 | manifest["applications"] = { 56 | gecko: { 57 | id: "webcompat-reporter-for-mobile@webcompat.com", 58 | strict_min_version: "51.0" 59 | } 60 | }; 61 | return JSON.stringify(manifest, null, 2); 62 | default: 63 | // Neither Chrome nor Opera needs the applications key 64 | return JSON.stringify(manifest, null, 2); 65 | } 66 | } 67 | } 68 | ]) 69 | ] 70 | }; 71 | return template; 72 | } 73 | ); 74 | const compiler = webpack(config); 75 | compiler.run((err, stats) => { 76 | if (err) { 77 | reject(err); 78 | } 79 | resolve(); 80 | }); 81 | }); 82 | } 83 | 84 | module.exports = { 85 | compileWebpack 86 | }; 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Webcompat.com Reporter browser extensions 2 | 3 | [![Build Status](https://travis-ci.org/webcompat/webcompat-reporter-extensions.svg?branch=master)](https://travis-ci.org/webcompat/webcompat-reporter-extensions) 4 | 5 | Browser extensions for Chrome, Firefox, Opera, and Safari that allow users to click on a button in the browser chrome to report a web compatibility issue at webcompat.com. 6 | 7 | *(If someone knows how to build something similar for Internet Explorer, please file an issue) 8 | 9 | ![Screenshots of browsers with installed extension](screenshots.jpg) 10 | 11 | ### Installation 12 | 13 | Find the packaged extension in the `bin` directory, and install in your browser (usually double-clicking or dropping into a browser window does the trick). 14 | 15 | Links to extension sites coming soon. 16 | 17 | ### Bookmarklet 18 | 19 | Another way to easily report issues is via a [bookmarklet](http://en.wikipedia.org/wiki/Bookmarklet). Make one of those however you make them normally, and copy and paste the following code: 20 | 21 | ``` 22 | javascript:(function(){location.href="https://webcompat.com/?open=1&url="+encodeURIComponent(location.href)}()) 23 | ``` 24 | 25 | ### Building 26 | 27 | To build the Firefox, Chrome and Opera web extension addons, first, install npm dependencies by running the following command from the project root: 28 | 29 | `npm install` 30 | 31 | The following commands will build the addons, so they can be packaged for testing or distribution: 32 | 33 | ``` 34 | npm run build:firefox 35 | npm run build:fennec 36 | npm run build:chrome 37 | npm run build:opera 38 | ``` 39 | 40 | The following commands will run the addons in their target browser: 41 | 42 | ``` 43 | npm run firefox 44 | ``` 45 | 46 | To run the addon in Firefox for Android, use the following commands: 47 | 48 | ``` 49 | npm run fennec 50 | npm run fennec-nightly 51 | ``` 52 | 53 | Note: it's necessary to pass the device ID as an argument for these commands: 54 | 55 | ``` 56 | $ adb devices 57 | > ABCDEFGHIJKLMNOP 58 | # Note the double dashes, they're required. 59 | $ npm run fennec -- --android-device=ABCDEFGHIJKLMNOP 60 | $ npm run fennec-nightly -- --android-device=ABCDEFGHIJKLMNOP 61 | ```` 62 | 63 | ### Running tests 64 | 65 | Tests can be run with by running the following npm script: 66 | 67 | `npm run test` 68 | 69 | ### Privacy 70 | 71 | By clicking on the extension button, the user asks the browser to send the URL of a website to webcompat.com (in order to report an issue) in a new tab. No information is collected besides that which gets submitted by the user as a bug report. 72 | 73 | If you choose to upload a screenshot or other image, it will be publicly accessible. Please do not include any confidential or personal information in the screenshot. 74 | 75 | The User Agent HTTP header that your browser sends will be recorded in the bug report, if you choose to report a bug, but is hidden by default. 76 | 77 | 78 | ### License 79 | 80 | This Source Code Form is subject to the terms of the Mozilla Public 81 | License, v. 2.0. If a copy of the MPL was not distributed with this 82 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 83 | 84 | Icons derived from work licensed under Creative Commons Attribution: 85 | 86 | * Light Bulb by Jean-Philippe Cabaroc from The Noun Project 87 | --------------------------------------------------------------------------------