From ed942c578e2ac742c1fff594e88f9ea3a7c0c485 Mon Sep 17 00:00:00 2001 From: Mike Taylor Date: Wed, 28 Dec 2016 13:31:22 -0600 Subject: [PATCH] Bug 1324062. Part 1 - Add a "Report Site Issue" button to the panel menu for NIGHTLY_BUILDs. r=Gijs MozReview-Commit-ID: 74T8uRuqpyf --HG-- extra : rebase_source : ae70816913cbd1510db02a47d7834248396ba78c --- .../customizableui/CustomizableUI.jsm | 6 + browser/extensions/moz.build | 6 + .../webcompat-reporter/bootstrap.js | 46 +++++ .../content/TabListener.jsm | 63 +++++++ .../content/WebCompatReporter.jsm | 162 ++++++++++++++++++ .../webcompat-reporter/content/tab-frame.js | 37 ++++ .../webcompat-reporter/content/wc-frame.js | 23 +++ .../webcompat-reporter/install.rdf.in | 29 ++++ browser/extensions/webcompat-reporter/jar.mn | 9 + .../locale/en-US/webcompat.properties | 2 + .../webcompat-reporter/locale/jar.mn | 8 + .../webcompat-reporter/locale/moz.build | 7 + .../extensions/webcompat-reporter/moz.build | 22 +++ .../webcompat-reporter/skin/lightbulb.css | 6 + .../webcompat-reporter/skin/lightbulb.svg | 9 + .../test/browser/.eslintrc.js | 7 + .../test/browser/browser.ini | 9 + .../test/browser/browser_button_state.js | 28 +++ .../test/browser/browser_disabled_cleanup.js | 9 + .../test/browser/browser_report_site_issue.js | 32 ++++ .../webcompat-reporter/test/browser/head.js | 10 ++ .../webcompat-reporter/test/browser/test.html | 5 + .../test/browser/webcompat.html | 26 +++ browser/locales/Makefile.in | 3 + browser/modules/BrowserUITelemetry.jsm | 8 + modules/libpref/init/all.js | 8 + 26 files changed, 580 insertions(+) create mode 100644 browser/extensions/webcompat-reporter/bootstrap.js create mode 100644 browser/extensions/webcompat-reporter/content/TabListener.jsm create mode 100644 browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm create mode 100644 browser/extensions/webcompat-reporter/content/tab-frame.js create mode 100644 browser/extensions/webcompat-reporter/content/wc-frame.js create mode 100644 browser/extensions/webcompat-reporter/install.rdf.in create mode 100644 browser/extensions/webcompat-reporter/jar.mn create mode 100644 browser/extensions/webcompat-reporter/locale/en-US/webcompat.properties create mode 100644 browser/extensions/webcompat-reporter/locale/jar.mn create mode 100644 browser/extensions/webcompat-reporter/locale/moz.build create mode 100644 browser/extensions/webcompat-reporter/moz.build create mode 100644 browser/extensions/webcompat-reporter/skin/lightbulb.css create mode 100644 browser/extensions/webcompat-reporter/skin/lightbulb.svg create mode 100644 browser/extensions/webcompat-reporter/test/browser/.eslintrc.js create mode 100644 browser/extensions/webcompat-reporter/test/browser/browser.ini create mode 100644 browser/extensions/webcompat-reporter/test/browser/browser_button_state.js create mode 100644 browser/extensions/webcompat-reporter/test/browser/browser_disabled_cleanup.js create mode 100644 browser/extensions/webcompat-reporter/test/browser/browser_report_site_issue.js create mode 100644 browser/extensions/webcompat-reporter/test/browser/head.js create mode 100644 browser/extensions/webcompat-reporter/test/browser/test.html create mode 100644 browser/extensions/webcompat-reporter/test/browser/webcompat.html diff --git a/browser/components/customizableui/CustomizableUI.jsm b/browser/components/customizableui/CustomizableUI.jsm index 2146afd218fa..a81c4aacbfe3 100644 --- a/browser/components/customizableui/CustomizableUI.jsm +++ b/browser/components/customizableui/CustomizableUI.jsm @@ -225,6 +225,12 @@ var CustomizableUIInternal = { panelPlacements.push("characterencoding-button"); } + if (AppConstants.NIGHTLY_BUILD) { + if (Services.prefs.getBoolPref("extensions.webcompat-reporter.enabled")) { + panelPlacements.push("webcompat-reporter-button"); + } + } + this.registerArea(CustomizableUI.AREA_PANEL, { anchor: "PanelUI-menu-button", type: CustomizableUI.TYPE_MENU_PANEL, diff --git a/browser/extensions/moz.build b/browser/extensions/moz.build index 9ce3943aac99..6606d0e13e8e 100644 --- a/browser/extensions/moz.build +++ b/browser/extensions/moz.build @@ -26,3 +26,9 @@ if CONFIG['MOZ_MORTAR']: DIRS += [ 'mortar', ] + +# Nightly-only system add-ons +if CONFIG['NIGHTLY_BUILD']: + DIRS += [ + 'webcompat-reporter', + ] diff --git a/browser/extensions/webcompat-reporter/bootstrap.js b/browser/extensions/webcompat-reporter/bootstrap.js new file mode 100644 index 000000000000..51006c8d451f --- /dev/null +++ b/browser/extensions/webcompat-reporter/bootstrap.js @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +const WEBCOMPATREPORTER_JSM = "chrome://webcompat-reporter/content/WebCompatReporter.jsm"; + +XPCOMUtils.defineLazyModuleGetter(this, "WebCompatReporter", + WEBCOMPATREPORTER_JSM); + +const PREF_WC_REPORTER_ENABLED = "extensions.webcompat-reporter.enabled"; + +let prefObserver = function(aSubject, aTopic, aData) { + let enabled = Services.prefs.getBoolPref(PREF_WC_REPORTER_ENABLED); + if (enabled) { + WebCompatReporter.init(); + } else { + WebCompatReporter.uninit(); + } +}; + +function startup(aData, aReason) { + // Observe pref changes and enable/disable as necessary. + Services.prefs.addObserver(PREF_WC_REPORTER_ENABLED, prefObserver, false); + + // Only initialize if pref is enabled. + let enabled = Services.prefs.getBoolPref(PREF_WC_REPORTER_ENABLED); + if (enabled) { + WebCompatReporter.init(); + } +} + +function shutdown(aData, aReason) { + if (aReason === APP_SHUTDOWN) { + return; + } + + Cu.unload(WEBCOMPATREPORTER_JSM); +} + +function install(aData, aReason) {} +function uninstall(aData, aReason) {} diff --git a/browser/extensions/webcompat-reporter/content/TabListener.jsm b/browser/extensions/webcompat-reporter/content/TabListener.jsm new file mode 100644 index 000000000000..16afb2568a8f --- /dev/null +++ b/browser/extensions/webcompat-reporter/content/TabListener.jsm @@ -0,0 +1,63 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +this.EXPORTED_SYMBOLS = ["TabListener"]; + +let { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI", + "resource:///modules/CustomizableUI.jsm"); + +const WIDGET_ID = "webcompat-reporter-button"; + +// Class that watches for url/location/tab changes and enables or disables +// the Report Site Issue button accordingly +class TabListener { + constructor(win) { + this.win = win; + this.browser = win.gBrowser; + this.addListeners(); + } + + addListeners() { + this.browser.addTabsProgressListener(this); + this.browser.tabContainer.addEventListener("TabSelect", this); + } + + removeListeners() { + this.browser.removeTabsProgressListener(this); + this.browser.tabContainer.removeEventListener("TabSelect", this); + } + + handleEvent(e) { + switch (e.type) { + case "TabSelect": + this.setButtonState(e.target.linkedBrowser.currentURI.scheme); + break; + } + } + + onLocationChange(browser, webProgress, request, uri, flags) { + this.setButtonState(uri.scheme); + } + + static isReportableScheme(scheme) { + return ["http", "https"].some((prefix) => scheme.startsWith(prefix)); + } + + setButtonState(scheme) { + // Bail early if the button is in the palette. + if (!CustomizableUI.getPlacementOfWidget(WIDGET_ID)) { + return; + } + + if (TabListener.isReportableScheme(scheme)) { + CustomizableUI.getWidget(WIDGET_ID).forWindow(this.win).node.removeAttribute("disabled"); + } else { + CustomizableUI.getWidget(WIDGET_ID).forWindow(this.win).node.setAttribute("disabled", true); + } + } +} diff --git a/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm b/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm new file mode 100644 index 000000000000..308bdf9be109 --- /dev/null +++ b/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm @@ -0,0 +1,162 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +this.EXPORTED_SYMBOLS = ["WebCompatReporter"]; + +let { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI", + "resource:///modules/CustomizableUI.jsm"); + +XPCOMUtils.defineLazyGetter(this, "wcStrings", function() { + return Services.strings.createBundle( + "chrome://webcompat-reporter/locale/webcompat.properties"); +}); + +const WIDGET_ID = "webcompat-reporter-button"; +const TABLISTENER_JSM = "chrome://webcompat-reporter/content/TabListener.jsm"; +const LIGHTBULB_CSS = "chrome://webcompat-reporter/skin/lightbulb.css"; + +// Weak mapping between windows and stylesheet nodes, so we can remove +// them if the button is disabled. +let styleSheetRefs = new WeakMap(); + +let WebCompatReporter = { + get endpoint() { + return Services.urlFormatter.formatURLPref( + "extensions.webcompat-reporter.newIssueEndpoint"); + }, + + init() { + Cu.import(TABLISTENER_JSM); + + CustomizableUI.createWidget({ + id: WIDGET_ID, + label: wcStrings.GetStringFromName("wc-reporter.label"), + tooltiptext: wcStrings.GetStringFromName("wc-reporter.tooltip"), + defaultArea: CustomizableUI.AREA_PANEL, + disabled: true, + onCommand: (e) => this.reportIssue(e.target.ownerDocument), + }); + + for (let win of CustomizableUI.windows) { + this.onWindowOpened(win); + } + + CustomizableUI.addListener(this); + }, + + onWindowOpened(win) { + // Attach stylesheet for the button icon. + let data = `type="text/css" href="${LIGHTBULB_CSS}"`; + let stylesheet = win.document.createProcessingInstruction( + "xml-stylesheet", data); + win.document.insertBefore(stylesheet, win.document.documentElement); + styleSheetRefs.set(win, stylesheet); + // Attach listeners to new window. + win._webcompatReporterTabListener = new TabListener(win); + }, + + onWindowClosed(win) { + if (win._webcompatReporterTabListener) { + win._webcompatReporterTabListener.removeListeners(); + delete win._webcompatReporterTabListener; + } + }, + + uninit() { + CustomizableUI.destroyWidget(WIDGET_ID); + + for (let win of CustomizableUI.windows) { + this.onWindowClosed(win); + + let stylesheet = styleSheetRefs.get(win); + if (stylesheet) { + stylesheet.remove(); + } + styleSheetRefs.delete(win); + } + + CustomizableUI.removeListener(this); + Cu.unload(TABLISTENER_JSM); + }, + + // This method injects a framescript that should send back a screenshot blob + // of the top-level window of the currently selected tab, resolved as a + // Promise. + getScreenshot(gBrowser) { + const FRAMESCRIPT = "chrome://webcompat-reporter/content/tab-frame.js"; + const TABDATA_MESSAGE = "WebCompat:SendTabData"; + + return new Promise((resolve) => { + let mm = gBrowser.selectedBrowser.messageManager; + mm.loadFrameScript(FRAMESCRIPT, false); + + mm.addMessageListener(TABDATA_MESSAGE, function receiveFn(message) { + mm.removeMessageListener(TABDATA_MESSAGE, receiveFn); + resolve([gBrowser, message.json]); + }); + }); + }, + + // This should work like so: + // 1) set up listeners for a new webcompat.com tab, and open it, passing + // along the current URI + // 2) if we successfully got a screenshot from getScreenshot, + // inject a frame script that will postMessage it to webcompat.com + // so it can show a preview to the user and include it in FormData + // Note: openWebCompatTab arguments are passed in as an array because they + // are the result of a promise resolution. + openWebCompatTab([gBrowser, tabData]) { + const SCREENSHOT_MESSAGE = "WebCompat:SendScreenshot"; + const FRAMESCRIPT = "chrome://webcompat-reporter/content/wc-frame.js"; + let win = Services.wm.getMostRecentWindow("navigator:browser"); + const WEBCOMPAT_ORIGIN = new win.URL(WebCompatReporter.endpoint).origin; + + let tab = gBrowser.loadOneTab( + `${WebCompatReporter.endpoint}?url=${encodeURIComponent(tabData.url)}&src=desktop-reporter`, + {inBackground: false}); + + // If we successfully got a screenshot blob, add a listener to know when + // the new tab is loaded before sending it over. + if (tabData && tabData.blob) { + let browser = gBrowser.getBrowserForTab(tab); + let loadedListener = { + QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener", + "nsISupportsWeakReference"]), + onStateChange(webProgress, request, flags, status) { + let isStopped = flags & Ci.nsIWebProgressListener.STATE_STOP; + let isNetwork = flags & Ci.nsIWebProgressListener.STATE_IS_NETWORK; + if (isStopped && isNetwork && webProgress.isTopLevel) { + let location; + try { + location = request.QueryInterface(Ci.nsIChannel).URI; + } catch (ex) {} + + if (location && location.prePath === WEBCOMPAT_ORIGIN) { + let mm = gBrowser.selectedBrowser.messageManager; + mm.loadFrameScript(FRAMESCRIPT, false); + mm.sendAsyncMessage(SCREENSHOT_MESSAGE, { + screenshot: tabData.blob, + origin: WEBCOMPAT_ORIGIN + }); + + browser.removeProgressListener(this); + } + } + } + }; + + browser.addProgressListener(loadedListener); + } + }, + + reportIssue(xulDoc) { + this.getScreenshot(xulDoc.defaultView.gBrowser).then(this.openWebCompatTab) + .catch(Cu.reportError); + } +}; diff --git a/browser/extensions/webcompat-reporter/content/tab-frame.js b/browser/extensions/webcompat-reporter/content/tab-frame.js new file mode 100644 index 000000000000..3ec978775eb6 --- /dev/null +++ b/browser/extensions/webcompat-reporter/content/tab-frame.js @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let { utils: Cu } = Components; + +const TABDATA_MESSAGE = "WebCompat:SendTabData"; + +let getScreenshot = function(win) { + return new Promise(resolve => { + let url = win.location.href; + try { + let dpr = win.devicePixelRatio; + let canvas = win.document.createElement("canvas"); + let ctx = canvas.getContext("2d"); + let x = win.document.documentElement.scrollLeft; + let y = win.document.documentElement.scrollTop; + let w = win.innerWidth; + let h = win.innerHeight; + canvas.width = dpr * w; + canvas.height = dpr * h; + ctx.scale(dpr, dpr); + ctx.drawWindow(win, x, y, w, h, "#fff"); + canvas.toBlob(blob => { + resolve({url, blob}); + }); + } catch (ex) { + // CanvasRenderingContext2D.drawWindow can fail depending on memory or + // surface size. Rather than reject, resolve the URL so the user can + // file an issue without a screenshot. + Cu.reportError(`WebCompatReporter: getting a screenshot failed: ${ex}`); + resolve({url}); + } + }); +}; + +getScreenshot(content).then(data => sendAsyncMessage(TABDATA_MESSAGE, data)); diff --git a/browser/extensions/webcompat-reporter/content/wc-frame.js b/browser/extensions/webcompat-reporter/content/wc-frame.js new file mode 100644 index 000000000000..f37f344d5780 --- /dev/null +++ b/browser/extensions/webcompat-reporter/content/wc-frame.js @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let { utils: Cu } = Components; + +const SCREENSHOT_MESSAGE = "WebCompat:SendScreenshot"; + +addMessageListener(SCREENSHOT_MESSAGE, function handleMessage(message) { + removeMessageListener(SCREENSHOT_MESSAGE, handleMessage); + // postMessage the screenshot blob from a content Sandbox so message event.origin + // is what we expect on the client-side (i.e., https://webcompat.com) + try { + let sb = new Cu.Sandbox(content.document.nodePrincipal); + sb.win = content; + sb.screenshotBlob = Cu.cloneInto(message.data.screenshot, content); + sb.wcOrigin = Cu.cloneInto(message.data.origin, content); + Cu.evalInSandbox("win.postMessage(screenshotBlob, wcOrigin);", sb); + Cu.nukeSandbox(sb); + } catch (ex) { + Cu.reportError(`WebCompatReporter: sending a screenshot failed: ${ex}`); + } +}); diff --git a/browser/extensions/webcompat-reporter/install.rdf.in b/browser/extensions/webcompat-reporter/install.rdf.in new file mode 100644 index 000000000000..e8cf3121f3fb --- /dev/null +++ b/browser/extensions/webcompat-reporter/install.rdf.in @@ -0,0 +1,29 @@ + + + +#filter substitution + + + + webcompat-reporter@mozilla.org + 2 + true + true + + WebCompat Reporter + Report site compatibility issues on webcompat.com. + + 1.0.0 + + + + {ec8030f7-c20a-464f-9b0e-13a3a9e97384} + @MOZ_APP_VERSION@ + @MOZ_APP_MAXVERSION@ + + + + diff --git a/browser/extensions/webcompat-reporter/jar.mn b/browser/extensions/webcompat-reporter/jar.mn new file mode 100644 index 000000000000..3f94c20b8f57 --- /dev/null +++ b/browser/extensions/webcompat-reporter/jar.mn @@ -0,0 +1,9 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[features/webcompat-reporter@mozilla.org] chrome.jar: +% content webcompat-reporter %content/ +% skin webcompat-reporter classic/1.0 %skin/ + content/ (content/*) + skin/ (skin/*) diff --git a/browser/extensions/webcompat-reporter/locale/en-US/webcompat.properties b/browser/extensions/webcompat-reporter/locale/en-US/webcompat.properties new file mode 100644 index 000000000000..b5f6935f1150 --- /dev/null +++ b/browser/extensions/webcompat-reporter/locale/en-US/webcompat.properties @@ -0,0 +1,2 @@ +wc-reporter.label=Report Site Issue +wc-reporter.tooltip=Report a site compatibility issue diff --git a/browser/extensions/webcompat-reporter/locale/jar.mn b/browser/extensions/webcompat-reporter/locale/jar.mn new file mode 100644 index 000000000000..ff2e2248f5d5 --- /dev/null +++ b/browser/extensions/webcompat-reporter/locale/jar.mn @@ -0,0 +1,8 @@ +#filter substitution +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[features/webcompat-reporter@mozilla.org] @AB_CD@.jar: +% locale webcompat-reporter @AB_CD@ %locale/@AB_CD@/ + locale/@AB_CD@/ (en-US/*) diff --git a/browser/extensions/webcompat-reporter/locale/moz.build b/browser/extensions/webcompat-reporter/locale/moz.build new file mode 100644 index 000000000000..eb4454d28f88 --- /dev/null +++ b/browser/extensions/webcompat-reporter/locale/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +JAR_MANIFESTS += ['jar.mn'] \ No newline at end of file diff --git a/browser/extensions/webcompat-reporter/moz.build b/browser/extensions/webcompat-reporter/moz.build new file mode 100644 index 000000000000..02fdb3b2e34a --- /dev/null +++ b/browser/extensions/webcompat-reporter/moz.build @@ -0,0 +1,22 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION'] +DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION'] + +DIRS += ['locale'] + +FINAL_TARGET_FILES.features['webcompat-reporter@mozilla.org'] += [ + 'bootstrap.js' +] + +FINAL_TARGET_PP_FILES.features['webcompat-reporter@mozilla.org'] += [ + 'install.rdf.in' +] + +JAR_MANIFESTS += ['jar.mn'] + +BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini'] diff --git a/browser/extensions/webcompat-reporter/skin/lightbulb.css b/browser/extensions/webcompat-reporter/skin/lightbulb.css new file mode 100644 index 000000000000..4ea51b043c2c --- /dev/null +++ b/browser/extensions/webcompat-reporter/skin/lightbulb.css @@ -0,0 +1,6 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#webcompat-reporter-button { + list-style-image: url("chrome://webcompat-reporter/skin/lightbulb.svg"); +} diff --git a/browser/extensions/webcompat-reporter/skin/lightbulb.svg b/browser/extensions/webcompat-reporter/skin/lightbulb.svg new file mode 100644 index 000000000000..a4c2a8095d03 --- /dev/null +++ b/browser/extensions/webcompat-reporter/skin/lightbulb.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/browser/extensions/webcompat-reporter/test/browser/.eslintrc.js b/browser/extensions/webcompat-reporter/test/browser/.eslintrc.js new file mode 100644 index 000000000000..7c80211924ef --- /dev/null +++ b/browser/extensions/webcompat-reporter/test/browser/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "../../../../../testing/mochitest/browser.eslintrc.js" + ] +}; diff --git a/browser/extensions/webcompat-reporter/test/browser/browser.ini b/browser/extensions/webcompat-reporter/test/browser/browser.ini new file mode 100644 index 000000000000..ce4656810c74 --- /dev/null +++ b/browser/extensions/webcompat-reporter/test/browser/browser.ini @@ -0,0 +1,9 @@ +[DEFAULT] +support-files = + head.js + test.html + webcompat.html + +[browser_disabled_cleanup.js] +[browser_button_state.js] +[browser_report_site_issue.js] diff --git a/browser/extensions/webcompat-reporter/test/browser/browser_button_state.js b/browser/extensions/webcompat-reporter/test/browser/browser_button_state.js new file mode 100644 index 000000000000..a7a977418a4e --- /dev/null +++ b/browser/extensions/webcompat-reporter/test/browser/browser_button_state.js @@ -0,0 +1,28 @@ +const REPORTABLE_PAGE = "http://example.com/"; +const REPORTABLE_PAGE2 = "https://example.com/"; +const NONREPORTABLE_PAGE = "about:blank"; + +/* Test that the Report Site Issue button is enabled for http and https tabs, + on page load, or TabSelect, and disabled for everything else. */ +add_task(function* test_button_state_disabled() { + let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, REPORTABLE_PAGE); + yield PanelUI.show(); + is(isButtonDisabled(), false, "Check that button is enabled for reportable schemes on tab load"); + + let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, NONREPORTABLE_PAGE); + is(isButtonDisabled(), true, "Check that button is disabled for non-reportable schemes on tab load"); + + let tab3 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, REPORTABLE_PAGE2); + is(isButtonDisabled(), false, "Check that button is enabled for reportable schemes on tab load"); + + + yield BrowserTestUtils.switchTab(gBrowser, tab2); + is(isButtonDisabled(), true, "Check that button is disabled for non-reportable schemes on TabSelect"); + + yield BrowserTestUtils.switchTab(gBrowser, tab1); + is(isButtonDisabled(), false, "Check that button is enabled for reportable schemes on TabSelect"); + + yield BrowserTestUtils.removeTab(tab1); + yield BrowserTestUtils.removeTab(tab2); + yield BrowserTestUtils.removeTab(tab3); +}); diff --git a/browser/extensions/webcompat-reporter/test/browser/browser_disabled_cleanup.js b/browser/extensions/webcompat-reporter/test/browser/browser_disabled_cleanup.js new file mode 100644 index 000000000000..c87460f5b601 --- /dev/null +++ b/browser/extensions/webcompat-reporter/test/browser/browser_disabled_cleanup.js @@ -0,0 +1,9 @@ +// Test the addon is cleaning up after itself when disabled. +add_task(function* test_disabled() { + yield SpecialPowers.pushPrefEnv({set: [[PREF_WC_REPORTER_ENABLED, false]]}); + + yield BrowserTestUtils.withNewTab({gBrowser, url: "about:blank"}, function() { + is(typeof window._webCompatReporterTabListener, "undefined", "TabListener expando does not exist."); + is(document.getElementById("webcompat-reporter-button"), null, "Report Site Issue button does not exist."); + }); +}); diff --git a/browser/extensions/webcompat-reporter/test/browser/browser_report_site_issue.js b/browser/extensions/webcompat-reporter/test/browser/browser_report_site_issue.js new file mode 100644 index 000000000000..6b07c263b178 --- /dev/null +++ b/browser/extensions/webcompat-reporter/test/browser/browser_report_site_issue.js @@ -0,0 +1,32 @@ +/* Test that clicking on the Report Site Issue button opens a new tab + and sends a postMessaged blob at it. + testing/profiles/prefs_general.js sets the value for + "extensions.webcompat-reporter.newIssueEndpoint" */ +add_task(function* test_screenshot() { + yield SpecialPowers.pushPrefEnv({set: [[PREF_WC_REPORTER_ENDPOINT, NEW_ISSUE_PAGE]]}); + + let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE); + yield PanelUI.show(); + + let webcompatButton = document.getElementById("webcompat-reporter-button"); + ok(webcompatButton, "Report Site Issue button exists."); + + let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser); + webcompatButton.click(); + let tab2 = yield newTabPromise; + + yield BrowserTestUtils.waitForContentEvent(tab2.linkedBrowser, "ScreenshotReceived", false, null, true); + + yield ContentTask.spawn(tab2.linkedBrowser, {TEST_PAGE}, function(args) { + let doc = content.document; + let urlParam = doc.getElementById("url").innerText; + let preview = doc.getElementById("screenshot-preview"); + is(urlParam, args.TEST_PAGE, "Reported page is correctly added to the url param"); + + is(preview.innerText, "Pass", "A Blob object was successfully transferred to the test page.") + ok(preview.style.backgroundImage.startsWith("url(\"data:image/png;base64,iVBOR"), "A green screenshot was successfully postMessaged"); + }); + + yield BrowserTestUtils.removeTab(tab2); + yield BrowserTestUtils.removeTab(tab1); +}); diff --git a/browser/extensions/webcompat-reporter/test/browser/head.js b/browser/extensions/webcompat-reporter/test/browser/head.js new file mode 100644 index 000000000000..df8bde6260a2 --- /dev/null +++ b/browser/extensions/webcompat-reporter/test/browser/head.js @@ -0,0 +1,10 @@ +const PREF_WC_REPORTER_ENABLED = "extensions.webcompat-reporter.enabled"; +const PREF_WC_REPORTER_ENDPOINT = "extensions.webcompat-reporter.newIssueEndpoint"; + +const TEST_ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com"); +const TEST_PAGE = TEST_ROOT + "test.html"; +const NEW_ISSUE_PAGE = TEST_ROOT + "webcompat.html"; + +function isButtonDisabled() { + return document.getElementById("webcompat-reporter-button").disabled; +} diff --git a/browser/extensions/webcompat-reporter/test/browser/test.html b/browser/extensions/webcompat-reporter/test/browser/test.html new file mode 100644 index 000000000000..53a2677f108d --- /dev/null +++ b/browser/extensions/webcompat-reporter/test/browser/test.html @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/browser/extensions/webcompat-reporter/test/browser/webcompat.html b/browser/extensions/webcompat-reporter/test/browser/webcompat.html new file mode 100644 index 000000000000..8f7beee28937 --- /dev/null +++ b/browser/extensions/webcompat-reporter/test/browser/webcompat.html @@ -0,0 +1,26 @@ + + + +
+
Fail
+ \ No newline at end of file diff --git a/browser/locales/Makefile.in b/browser/locales/Makefile.in index c25a956a4a4b..ff4068f0271f 100644 --- a/browser/locales/Makefile.in +++ b/browser/locales/Makefile.in @@ -104,6 +104,9 @@ libs-%: @$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$* @$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=$(PREF_DIR) @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$* +ifdef NIGHTLY_BUILD + @$(MAKE) -C ../extensions/webcompat-reporter/locale AB_CD=$* XPI_NAME=locale-$* +endif repackage-win32-installer: WIN32_INSTALLER_OUT=$(ABS_DIST)/$(PKG_INST_PATH)$(PKG_INST_BASENAME).exe repackage-win32-installer: $(call ESCAPE_WILDCARD,$(WIN32_INSTALLER_IN)) $(SUBMAKEFILES) libs-$(AB_CD) diff --git a/browser/modules/BrowserUITelemetry.jsm b/browser/modules/BrowserUITelemetry.jsm index 8f74aca423a8..2c0a4ff18d52 100644 --- a/browser/modules/BrowserUITelemetry.jsm +++ b/browser/modules/BrowserUITelemetry.jsm @@ -11,6 +11,8 @@ const {interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "AppConstants", + "resource://gre/modules/AppConstants.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry", "resource://gre/modules/UITelemetry.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", @@ -79,6 +81,12 @@ XPCOMUtils.defineLazyGetter(this, "DEFAULT_AREA_PLACEMENTS", function() { result["PanelUI-contents"].push("characterencoding-button"); } + if (AppConstants.NIGHTLY_BUILD) { + if (Services.prefs.getBoolPref("extensions.webcompat-reporter.enabled")) { + result["PanelUI-contents"].push("webcompat-reporter-button"); + } + } + return result; }); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 0425c1f39482..b65e6b6769b2 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4737,6 +4737,14 @@ pref("extensions.webextensions.keepUuidOnUninstall", false); pref("extensions.webextensions.identity.redirectDomain", "extensions.allizom.org"); pref("extensions.webextensions.remote", false); +// Report Site Issue button +pref("extensions.webcompat-reporter.newIssueEndpoint", "https://webcompat.com/issues/new"); +#ifdef NIGHTLY_BUILD +pref("extensions.webcompat-reporter.enabled", true); +#else +pref("extensions.webcompat-reporter.enabled", false); +#endif + pref("network.buffer.cache.count", 24); pref("network.buffer.cache.size", 32768);