—PK !<à®ëL¤¹experimentalAPIs/helpMenu.jsPK !<Ôã`ûDD$¤ experimentalAPIs/aboutConfigPrefs.jsPK !<¿j¹ø33¤—chrome.manifestPK !<ÕnÊ““ ¤÷background.jsPK ! { let observer = (subject, topic, data) => { let nativeTab = subject.wrappedJSObject; let tab = tabManager.convert(nativeTab); fire.async(tab); }; Services.obs.addObserver(observer, TOPIC); return () => { Services.obs.removeObserver(observer, TOPIC); }; }, }).api(), }, }; } }; PK !<Ôã`ûDD$experimentalAPIs/aboutConfigPrefs.js/* 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/. */ "use strict"; /* global ExtensionAPI, ExtensionCommon, Services */ this.aboutConfigPrefs = class extends ExtensionAPI { getAPI(context) { const EventManager = ExtensionCommon.EventManager; const extensionIDBase = context.extension.id.split("@")[0]; const endpointPrefName = `extensions.${extensionIDBase}.newIssueEndpoint`; return { aboutConfigPrefs: { onEndpointPrefChange: new EventManager({ context, name: "aboutConfigPrefs.onEndpointPrefChange", register: fire => { const callback = () => { fire.async().catch(() => {}); // ignore Message Manager disconnects }; Services.prefs.addObserver(endpointPrefName, callback); return () => { Services.prefs.removeObserver(endpointPrefName, callback); }; }, }).api(), async getEndpointPref() { return Services.prefs.getStringPref(endpointPrefName, undefined); }, async setEndpointPref(value) { Services.prefs.setStringPref(endpointPrefName, value); }, }, }; } }; PK !<¿j¹ø33chrome.manifestlocale report-site-issue zh-CN zh-CN/locale/zh-CN/ PK !<ÕnÊ““ background.js/* 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/. */ "use strict"; /* globals browser */ const Config = { newIssueEndpoint: "https://webcompat.com/issues/new", newIssueEndpointPref: "newIssueEndpoint", screenshotFormat: { format: "jpeg", quality: 75, }, }; const FRAMEWORK_KEYS = ["hasFastClick", "hasMobify", "hasMarfeel"]; browser.helpMenu.onHelpMenuCommand.addListener(tab => { return getWebCompatInfoForTab(tab).then( info => { return openWebCompatTab(info); }, err => { console.error("WebCompat Reporter: unexpected error", err); } ); }); browser.aboutConfigPrefs.onEndpointPrefChange.addListener(checkEndpointPref); checkEndpointPref(); async function checkEndpointPref() { const value = await browser.aboutConfigPrefs.getEndpointPref(); if (value === undefined) { browser.aboutConfigPrefs.setEndpointPref(Config.newIssueEndpoint); } else { Config.newIssueEndpoint = value; } } function hasFastClickPageScript() { const win = window.wrappedJSObject; if (win.FastClick) { return true; } for (const property in win) { try { const proto = win[property].prototype; if (proto && proto.needsClick) { return true; } } catch (_) {} } return false; } function hasMobifyPageScript() { const win = window.wrappedJSObject; return !!(win.Mobify && win.Mobify.Tag); } function hasMarfeelPageScript() { const win = window.wrappedJSObject; return !!win.marfeel; } function checkForFrameworks(tabId) { return browser.tabs .executeScript(tabId, { code: ` (function() { ${hasFastClickPageScript}; ${hasMobifyPageScript}; ${hasMarfeelPageScript}; const result = { hasFastClick: hasFastClickPageScript(), hasMobify: hasMobifyPageScript(), hasMarfeel: hasMarfeelPageScript(), } return result; })(); `, }) .then(([results]) => results) .catch(() => false); } function getWebCompatInfoForTab(tab) { const { id, url } = tab; return Promise.all([ browser.browserInfo.getBlockList(), browser.browserInfo.getBuildID(), browser.browserInfo.getGraphicsPrefs(), browser.browserInfo.getUpdateChannel(), browser.browserInfo.hasTouchScreen(), browser.tabExtras.getWebcompatInfo(id), browser.browserInfo.getAdditionalData(), checkForFrameworks(id), browser.tabs.captureTab(id, Config.screenshotFormat).catch(e => { console.error("WebCompat Reporter: getting a screenshot failed", e); return Promise.resolve(undefined); }), ]).then( ([ blockList, buildID, graphicsPrefs, channel, hasTouchScreen, frameInfo, additionalData, frameworks, screenshot, ]) => { if (channel !== "linux") { delete graphicsPrefs["layers.acceleration.force-enabled"]; } const consoleLog = frameInfo.log; delete frameInfo.log; additionalData.isPB = frameInfo.isPB; additionalData.prefs = { ...additionalData.prefs, ...graphicsPrefs }; additionalData.hasMixedActiveContentBlocked = frameInfo.hasMixedActiveContentBlocked; additionalData.hasMixedDisplayContentBlocked = frameInfo.hasMixedDisplayContentBlocked; additionalData.hasTrackingContentBlocked = !!frameInfo.hasTrackingContentBlocked; return Object.assign(frameInfo, { tabId: id, blockList, details: Object.assign(graphicsPrefs, { buildID, channel, consoleLog, frameworks, additionalData, hasTouchScreen, "mixed active content blocked": frameInfo.hasMixedActiveContentBlocked, "mixed passive content blocked": frameInfo.hasMixedDisplayContentBlocked, "tracking content blocked": frameInfo.hasTrackingContentBlocked ? `true (${blockList})` : "false", }), screenshot, url, }); } ); } function stripNonASCIIChars(str) { // eslint-disable-next-line no-control-regex return str.replace(/[^\x00-\x7F]/g, ""); } async function openWebCompatTab(compatInfo) { const url = new URL(Config.newIssueEndpoint); const { details } = compatInfo; const params = { url: `${compatInfo.url}`, utm_source: "desktop-reporter", utm_campaign: "report-site-issue-button", src: "desktop-reporter", details, extra_labels: [], }; for (let framework of FRAMEWORK_KEYS) { if (details.frameworks[framework]) { params.details[framework] = true; params.extra_labels.push( framework.replace(/^has/, "type-").toLowerCase() ); } } delete details.frameworks; if (details["gfx.webrender.all"] || details["gfx.webrender.enabled"]) { params.extra_labels.push("type-webrender-enabled"); } if (compatInfo.hasTrackingContentBlocked) { params.extra_labels.push( `type-tracking-protection-${compatInfo.blockList}` ); } const json = stripNonASCIIChars(JSON.stringify(params)); const tab = await browser.tabs.create({ url: url.href }); await browser.tabs.executeScript(tab.id, { runAt: "document_end", code: `(function() { async function postMessageData(dataURI, metadata) { const res = await fetch(dataURI); const blob = await res.blob(); const data = { screenshot: blob, message: metadata }; postMessage(data, "${url.origin}"); } postMessageData("${compatInfo.screenshot}", ${json}); })()`, }); } PK ! PREVIEW_MAX_ITEMS) { break; } preview.push(getPreview(value)); } return preview; } function getObjectPreview(obj) { const preview = {}; let count = 0; for (const key of Object.keys(obj)) { if (++count > PREVIEW_MAX_ITEMS) { break; } preview[key] = getPreview(obj[key]); } return preview; } function getArgs(value) { if (typeof value === "object" && value !== null) { if (Array.isArray(value)) { return getArrayPreview(value); } return getObjectPreview(value); } return getPreview(value); } class ReportSiteIssueHelperChild extends JSWindowActorChild { _getConsoleMessages(windowId) { const ConsoleAPIStorage = Cc[ "@mozilla.org/consoleAPI-storage;1" ].getService(Ci.nsIConsoleAPIStorage); let messages = ConsoleAPIStorage.getEvents(windowId); return messages.map(evt => { const { columnNumber, filename, level, lineNumber, timeStamp } = evt; const args = evt.arguments.map(getArgs); const message = { level, log: args, uri: filename, pos: `${lineNumber}:${columnNumber}`, }; return { timeStamp, message }; }); } _getScriptErrors(windowId, includePrivate) { const messages = Services.console.getMessageArray(); return messages .filter(message => { if (message instanceof Ci.nsIScriptError) { if (!includePrivate && message.isFromPrivateWindow) { return false; } if (windowId && windowId !== message.innerWindowID) { return false; } return true; } // If this is not an nsIScriptError and we need to do window-based // filtering we skip this message. return false; }) .map(error => { const { timeStamp, errorMessage, sourceName, lineNumber, columnNumber, logLevel, } = error; const message = { level: LOG_LEVELS[logLevel], log: [errorMessage], uri: sourceName, pos: `${lineNumber}:${columnNumber}`, }; return { timeStamp, message }; }); } _getLoggedMessages(includePrivate = false) { const windowId = this.contentWindow.windowGlobalChild.innerWindowId; return this._getConsoleMessages(windowId).concat( this._getScriptErrors(windowId, includePrivate) ); } receiveMessage(msg) { switch (msg.name) { case "GetLog": return this._getLoggedMessages(); case "GetBlockingStatus": const { docShell } = this; return { hasTrackingContentBlocked: docShell.hasTrackingContentBlocked, }; } return null; } } PK !<‘y§¢¼¼experimentalAPIs/browserInfo.js/* 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/. */ "use strict"; /* global AppConstants, ExtensionAPI, Services */ function isTelemetryEnabled() { return Services.prefs.getBoolPref( "datareporting.healthreport.uploadEnabled", false ); } function getSysinfoProperty(propertyName, defaultValue) { try { return Services.sysinfo.getProperty(propertyName); } catch (e) {} return defaultValue; } function getUserAgent() { const { userAgent } = Cc[ "@mozilla.org/network/protocol;1?name=http" ].getService(Ci.nsIHttpProtocolHandler); return userAgent; } function getGfxData() { const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo); const data = {}; try { const { compositor, hwCompositing, openglCompositing, wrCompositor, wrSoftware, } = gfxInfo.getFeatures(); data.features = { compositor, hwCompositing, openglCompositing, wrCompositor, wrSoftware, }; } catch (e) {} try { if (AppConstants.platform !== "android") { data.monitors = gfxInfo.getMonitors(); } } catch (e) {} return data; } function limitStringToLength(str, maxLength) { if (typeof str !== "string") { return null; } return str.substring(0, maxLength); } function getSecurityAppData() { const maxStringLength = 256; const keys = [ ["registeredAntiVirus", "antivirus"], ["registeredAntiSpyware", "antispyware"], ["registeredFirewall", "firewall"], ]; let result = {}; for (let [inKey, outKey] of keys) { let prop = getSysinfoProperty(inKey, null); if (prop) { prop = limitStringToLength(prop, maxStringLength).split(";"); } result[outKey] = prop; } return result; } function getAdditionalPrefs() { const prefs = {}; for (const [name, dflt] of Object.entries({ "browser.opaqueResponseBlocking": false, "extensions.InstallTrigger.enabled": false, "gfx.canvas.accelerated.force-enabled": false, "gfx.webrender.compositor.force-enabled": false, "privacy.resistFingerprinting": false, })) { prefs[name] = Services.prefs.getBoolPref(name, dflt); } const cookieBehavior = "network.cookie.cookieBehavior"; prefs[cookieBehavior] = Services.prefs.getIntPref(cookieBehavior); return prefs; } function getMemoryMB() { let memoryMB = getSysinfoProperty("memsize", null); if (memoryMB) { memoryMB = Math.round(memoryMB / 1024 / 1024); } return memoryMB; } this.browserInfo = class extends ExtensionAPI { getAPI(context) { return { browserInfo: { async getGraphicsPrefs() { const prefs = {}; for (const [name, dflt] of Object.entries({ "layers.acceleration.force-enabled": false, "gfx.webrender.all": false, "gfx.webrender.blob-images": true, "gfx.webrender.enabled": false, "image.mem.shared": true, })) { prefs[name] = Services.prefs.getBoolPref(name, dflt); } return prefs; }, async getAppVersion() { return AppConstants.MOZ_APP_VERSION; }, async getBlockList() { const trackingTable = Services.prefs.getCharPref( "urlclassifier.trackingTable" ); // If content-track-digest256 is in the tracking table, // the user has enabled the strict list. return trackingTable.includes("content") ? "strict" : "basic"; }, async getBuildID() { return Services.appinfo.appBuildID; }, async getUpdateChannel() { return AppConstants.MOZ_UPDATE_CHANNEL; }, async getPlatform() { return AppConstants.platform; }, async hasTouchScreen() { const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService( Ci.nsIGfxInfo ); return gfxInfo.getInfo().ApzTouchInput == 1; }, async getAdditionalData() { const blockList = await this.getBlockList(); const userAgent = getUserAgent(); const gfxData = getGfxData(); const prefs = getAdditionalPrefs(); const memoryMb = getMemoryMB(); const data = { applicationName: Services.appinfo.name, version: Services.appinfo.version, updateChannel: AppConstants.MOZ_UPDATE_CHANNEL, osArchitecture: getSysinfoProperty("arch", null), osName: getSysinfoProperty("name", null), osVersion: getSysinfoProperty("version", null), fissionEnabled: Services.appinfo.fissionAutostart, userAgent, gfxData, blockList, prefs, memoryMb, }; if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) { data.sec = getSecurityAppData(); } if (AppConstants.platform === "android") { data.device = getSysinfoProperty("device", null); data.isTablet = getSysinfoProperty("tablet", false); } return data; }, }, }; } }; PK !<á*Æ_``!experimentalAPIs/browserInfo.json[ { "namespace": "browserInfo", "description": "experimental API extensions to get browser info not exposed via web APIs", "functions": [ { "name": "getAppVersion", "type": "function", "description": "Gets the app version", "parameters": [], "async": true }, { "name": "getBlockList", "type": "function", "description": "Gets the current blocklist", "parameters": [], "async": true }, { "name": "getBuildID", "type": "function", "description": "Gets the build ID", "parameters": [], "async": true }, { "name": "getGraphicsPrefs", "type": "function", "description": "Gets interesting about:config prefs for graphics", "parameters": [], "async": true }, { "name": "getPlatform", "type": "function", "description": "Gets the platform", "parameters": [], "async": true }, { "name": "getUpdateChannel", "type": "function", "description": "Gets the update channel", "parameters": [], "async": true }, { "name": "hasTouchScreen", "type": "function", "description": "Gets whether a touchscreen is present", "parameters": [], "async": true }, { "name": "getAdditionalData", "type": "function", "description": "Gets additional info for the new reporter experiment", "parameters": [], "async": true } ] } ] PK !<:§ŒÐÐexperimentalAPIs/helpMenu.json[ { "namespace": "helpMenu", "events": [ { "name": "onHelpMenuCommand", "type": "function", "async": "callback", "description": "Fired when the command event for the Report Site Issue menuitem in Help is fired.", "parameters": [ { "type": "function", "name": "callback", "optional": true, "parameters": [ { "name": "tab", "$ref": "tabs.Tab", "optional": true, "description": "Details about the selected tab in the window where the menuitem command fired." } ] } ] } ] } ] PK ! actor.sendQuery("GetLog")); const logs = await Promise.all(promises); const info = await actors[0].sendQuery("GetBlockingStatus"); info.hasMixedActiveContentBlocked = !!( browsingContext.secureBrowserUI.state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT ); info.hasMixedDisplayContentBlocked = !!( browsingContext.secureBrowserUI.state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT ); info.isPB = incognito; info.log = logs .flat() .sort((a, b) => a.timeStamp - b.timeStamp) .map(m => m.message); return info; }, }, }; } onShutdown(isAppShutdown) { this._unregisterActorModule(); } _registerActorModule() { resProto.setSubstitution( "report-site-issue", Services.io.newURI( "experimentalAPIs/actors/", null, this.extension.rootURI ) ); ChromeUtils.registerWindowActor("ReportSiteIssueHelper", { child: { moduleURI: "resource://report-site-issue/tabExtrasActor.jsm", }, allFrames: true, }); } _unregisterActorModule() { ChromeUtils.unregisterWindowActor("ReportSiteIssueHelper"); resProto.setSubstitution("report-site-issue", null); } }; function getActorForBrowsingContext(name, browsingContext) { const windowGlobal = browsingContext.currentWindowGlobal; return windowGlobal ? windowGlobal.getActor(name) : null; } function gatherActors(name, browsingContext) { const list = []; const actor = getActorForBrowsingContext(name, browsingContext); if (actor) { list.push(actor); } for (const child of browsingContext.children) { list.push(...gatherActors(name, child)); } return list; } PK !<þÍÚæÉÉexperimentalAPIs/tabExtras.json[ { "namespace": "tabExtras", "description": "experimental tab API extensions", "functions": [ { "name": "getWebcompatInfo", "type": "function", "description": "Gets the content blocking status and script log for a given tab", "parameters": [ { "type": "integer", "name": "tabId", "minimum": 0 } ], "async": true } ] } ] PK !<³¡;sÇÇicons/lightbulb.svg PK !< ×5G manifest.json{ "manifest_version": 2, "name": "WebCompat Reporter", "description": "Report site compatibility issues on webcompat.com", "author": "Thomas Wisniewski ", "version": "1.5.1", "homepage_url": "https://github.com/mozilla/webcompat-reporter", "browser_specific_settings": { "gecko": { "id": "webcompat-reporter@mozilla.org" } }, "experiment_apis": { "aboutConfigPrefs": { "schema": "experimentalAPIs/aboutConfigPrefs.json", "parent": { "scopes": ["addon_parent"], "script": "experimentalAPIs/aboutConfigPrefs.js", "paths": [["aboutConfigPrefs"]] } }, "browserInfo": { "schema": "experimentalAPIs/browserInfo.json", "parent": { "scopes": ["addon_parent"], "script": "experimentalAPIs/browserInfo.js", "paths": [["browserInfo"]] } }, "helpMenu": { "schema": "experimentalAPIs/helpMenu.json", "parent": { "scopes": ["addon_parent"], "script": "experimentalAPIs/helpMenu.js", "paths": [["helpMenu"]] } }, "l10n": { "schema": "experimentalAPIs/l10n.json", "parent": { "scopes": ["addon_parent"], "script": "experimentalAPIs/l10n.js", "paths": [["l10n"]] } }, "tabExtras": { "schema": "experimentalAPIs/tabExtras.json", "parent": { "scopes": ["addon_parent"], "script": "experimentalAPIs/tabExtras.js", "paths": [["tabExtras"]] } } }, "icons": { "16": "icons/lightbulb.svg", "32": "icons/lightbulb.svg", "48": "icons/lightbulb.svg", "96": "icons/lightbulb.svg", "128": "icons/lightbulb.svg" }, "permissions": ["tabs", ""], "background": { "scripts": ["background.js"] } } PKŸ