summaryrefslogtreecommitdiffstats
path: root/mobile/android/chrome/content/WebcompatReporter.js
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/chrome/content/WebcompatReporter.js')
-rw-r--r--mobile/android/chrome/content/WebcompatReporter.js144
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");
+});