diff options
Diffstat (limited to 'mobile/android/chrome/content/WebcompatReporter.js')
-rw-r--r-- | mobile/android/chrome/content/WebcompatReporter.js | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/mobile/android/chrome/content/WebcompatReporter.js b/mobile/android/chrome/content/WebcompatReporter.js new file mode 100644 index 000000000..66aefdda0 --- /dev/null +++ b/mobile/android/chrome/content/WebcompatReporter.js @@ -0,0 +1,144 @@ +/* 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/. */ + +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", + "resource://gre/modules/PrivateBrowsingUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm"); + +var WebcompatReporter = { + menuItem: null, + menuItemEnabled: null, + init: function() { + Services.obs.addObserver(this, "DesktopMode:Change", false); + Services.obs.addObserver(this, "chrome-document-global-created", false); + Services.obs.addObserver(this, "content-document-global-created", false); + + let visible = true; + if ("@mozilla.org/parental-controls-service;1" in Cc) { + let pc = Cc["@mozilla.org/parental-controls-service;1"].createInstance(Ci.nsIParentalControlsService); + visible = !pc.parentalControlsEnabled; + } + + this.addMenuItem(visible); + }, + + observe: function(subject, topic, data) { + if (topic == "content-document-global-created" || topic == "chrome-document-global-created") { + let win = subject; + let currentURI = win.document.documentURI; + + // Ignore non top-level documents + if (currentURI !== win.top.location.href) { + return; + } + + if (!this.menuItemEnabled && this.isReportableUrl(currentURI)) { + NativeWindow.menu.update(this.menuItem, {enabled: true}); + this.menuItemEnabled = true; + } else if (this.menuItemEnabled && !this.isReportableUrl(currentURI)) { + NativeWindow.menu.update(this.menuItem, {enabled: false}); + this.menuItemEnabled = false; + } + } else if (topic === "DesktopMode:Change") { + let args = JSON.parse(data); + let tab = BrowserApp.getTabForId(args.tabId); + let currentURI = tab.browser.currentURI.spec; + if (args.desktopMode && this.isReportableUrl(currentURI)) { + this.reportDesktopModePrompt(tab); + } + } + }, + + addMenuItem: function(visible) { + this.menuItem = NativeWindow.menu.add({ + name: this.strings.GetStringFromName("webcompat.menu.name"), + callback: () => { + Promise.resolve(BrowserApp.selectedTab).then(this.getScreenshot) + .then(this.reportIssue) + .catch(Cu.reportError); + }, + enabled: false, + visible: visible, + }); + }, + + getScreenshot: (tab) => { + return new Promise((resolve) => { + try { + let win = tab.window; + let dpr = win.devicePixelRatio; + let canvas = win.document.createElement("canvas"); + let ctx = canvas.getContext("2d"); + // Grab the visible viewport coordinates + let x = win.document.documentElement.scrollLeft; + let y = win.document.documentElement.scrollTop; + let w = win.innerWidth; + let h = win.innerHeight; + // Scale according to devicePixelRatio and coordinates + canvas.width = dpr * w; + canvas.height = dpr * h; + ctx.scale(dpr, dpr); + ctx.drawWindow(win, x, y, w, h, '#ffffff'); + let screenshot = canvas.toDataURL(); + resolve({tab: tab, data: screenshot}); + } catch (e) { + // drawWindow can fail depending on memory or surface size. Rather than reject here, + // we resolve the URL so the user can continue to file an issue without a screenshot. + Cu.reportError("WebCompatReporter: getting a screenshot failed: " + e); + resolve({tab: tab}); + } + }); + }, + + isReportableUrl: function(url) { + return url && !(url.startsWith("about") || + url.startsWith("chrome") || + url.startsWith("file") || + url.startsWith("resource")); + }, + + reportDesktopModePrompt: function(tab) { + let message = this.strings.GetStringFromName("webcompat.reportDesktopMode.message"); + let options = { + action: { + label: this.strings.GetStringFromName("webcompat.reportDesktopModeYes.label"), + callback: () => this.reportIssue({tab: tab}) + } + }; + Snackbars.show(message, Snackbars.LENGTH_LONG, options); + }, + + reportIssue: (tabData) => { + return new Promise((resolve) => { + const WEBCOMPAT_ORIGIN = "https://webcompat.com"; + let url = tabData.tab.browser.currentURI.spec + let webcompatURL = `${WEBCOMPAT_ORIGIN}/issues/new?url=${url}`; + + if (tabData.data && typeof tabData.data === "string") { + BrowserApp.deck.addEventListener("DOMContentLoaded", function sendDataToTab(event) { + BrowserApp.deck.removeEventListener("DOMContentLoaded", sendDataToTab, false); + + if (event.target.defaultView.location.origin === WEBCOMPAT_ORIGIN) { + // Waive Xray vision so event.origin is not chrome://browser on the other side. + let win = Cu.waiveXrays(event.target.defaultView); + win.postMessage(tabData.data, WEBCOMPAT_ORIGIN); + } + }, false); + } + + let isPrivateTab = PrivateBrowsingUtils.isBrowserPrivate(tabData.tab.browser); + BrowserApp.addTab(webcompatURL, {parentId: tabData.tab.id, isPrivate: isPrivateTab}); + resolve(); + }); + } +}; + +XPCOMUtils.defineLazyGetter(WebcompatReporter, "strings", function() { + return Services.strings.createBundle("chrome://browser/locale/webcompatReporter.properties"); +}); |