Bug 1589182 - remove mobile/android/extensions/ and /mobile/android/chrome/content (Fennec leftovers); r=snorp,agi

Remove mobile/android/extensions/ and /mobile/android/chrome/content from mozilla-central (Fennec leftovers)

Differential Revision: https://phabricator.services.mozilla.com/D51194

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Thomas Wisniewski 2019-11-04 20:32:10 +00:00
parent 407ce752f5
commit dea46a2eed
114 changed files with 0 additions and 20626 deletions

View File

@ -222,9 +222,6 @@ mobile/android/tests/browser/chrome/tp5/
mobile/android/app/mobile.js
mobile/android/app/geckoview-prefs.js
# Uses `#expand`
mobile/android/chrome/content/about.js
# Not much JS to lint and non-standard at that
mobile/android/installer/
mobile/android/locales/

View File

@ -1,12 +0,0 @@
"use strict";
module.exports = {
"rules": {
"complexity": ["error", 20],
// Disabled stuff
"no-console": 0, // TODO: Can we use console?
"no-cond-assign": 0,
"no-fallthrough": 0,
}
};

View File

@ -1,857 +0,0 @@
// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* 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";
ChromeUtils.defineModuleGetter(
this,
"PageActions",
"resource://gre/modules/PageActions.jsm"
);
// Define service devices. We should consider moving these to their respective
// JSM files, but we left them here to allow for better lazy JSM loading.
var rokuDevice = {
id: "roku:ecp",
target: "roku:ecp",
factory: function(aService) {
const { RokuApp } = ChromeUtils.import(
"resource://gre/modules/RokuApp.jsm"
);
return new RokuApp(aService);
},
types: ["video/mp4"],
extensions: ["mp4"],
};
var mediaPlayerDevice = {
id: "media:router",
target: "media:router",
factory: function(aService) {
const { MediaPlayerApp } = ChromeUtils.import(
"resource://gre/modules/MediaPlayerApp.jsm"
);
return new MediaPlayerApp(aService);
},
types: ["video/mp4", "video/webm", "application/x-mpegurl"],
extensions: ["mp4", "webm", "m3u", "m3u8"],
init: function() {
GlobalEventDispatcher.registerListener(this, [
"MediaPlayer:Added",
"MediaPlayer:Changed",
"MediaPlayer:Removed",
]);
},
onEvent: function(event, data, callback) {
if (event === "MediaPlayer:Added") {
let service = this.toService(data);
SimpleServiceDiscovery.addService(service);
} else if (event === "MediaPlayer:Changed") {
let service = this.toService(data);
SimpleServiceDiscovery.updateService(service);
} else if (event === "MediaPlayer:Removed") {
SimpleServiceDiscovery.removeService(data.id);
}
},
toService: function(display) {
// Convert the native data into something matching what is created in _processService()
return {
location: display.location,
target: "media:router",
friendlyName: display.friendlyName,
uuid: display.uuid,
manufacturer: display.manufacturer,
modelName: display.modelName,
};
},
};
var CastingApps = {
_castMenuId: -1,
_blocked: null,
_bound: null,
_interval: 120 * 1000, // 120 seconds
init: function ca_init() {
if (!this.isCastingEnabled()) {
return;
}
// Register targets
SimpleServiceDiscovery.registerDevice(rokuDevice);
// MediaPlayerDevice will notify us any time the native device list changes.
mediaPlayerDevice.init();
SimpleServiceDiscovery.registerDevice(mediaPlayerDevice);
// Search for devices continuously
SimpleServiceDiscovery.search(this._interval);
this._castMenuId = NativeWindow.contextmenus.add(
Strings.browser.GetStringFromName("contextmenu.sendToDevice"),
this.filterCast,
this.handleContextMenu.bind(this)
);
GlobalEventDispatcher.registerListener(this, [
"Casting:Play",
"Casting:Pause",
"Casting:Stop",
]);
Services.obs.addObserver(this, "ssdp-service-found");
Services.obs.addObserver(this, "ssdp-service-lost");
Services.obs.addObserver(this, "application-background");
Services.obs.addObserver(this, "application-foreground");
BrowserApp.deck.addEventListener("TabSelect", this, true);
BrowserApp.deck.addEventListener("pageshow", this, true);
BrowserApp.deck.addEventListener("playing", this, true);
BrowserApp.deck.addEventListener("ended", this, true);
BrowserApp.deck.addEventListener("MozAutoplayMediaBlocked", this, true);
// Note that the XBL binding is untrusted
BrowserApp.deck.addEventListener(
"MozNoControlsVideoBindingAttached",
this,
true,
true
);
},
_mirrorStarted: function(stopMirrorCallback) {
this.stopMirrorCallback = stopMirrorCallback;
NativeWindow.menu.update(this.mirrorStartMenuId, { visible: false });
NativeWindow.menu.update(this.mirrorStopMenuId, { visible: true });
},
serviceAdded: function(aService) {},
serviceLost: function(aService) {},
isCastingEnabled: function isCastingEnabled() {
return Services.prefs.getBoolPref("browser.casting.enabled");
},
onEvent: function(event, message, callback) {
switch (event) {
case "Casting:Play":
if (this.session && this.session.remoteMedia.status == "paused") {
this.session.remoteMedia.play();
}
break;
case "Casting:Pause":
if (this.session && this.session.remoteMedia.status == "started") {
this.session.remoteMedia.pause();
}
break;
case "Casting:Stop":
if (this.session) {
this.closeExternal();
}
break;
}
},
observe: function(aSubject, aTopic, aData) {
switch (aTopic) {
case "ssdp-service-found":
this.serviceAdded(SimpleServiceDiscovery.findServiceForID(aData));
break;
case "ssdp-service-lost":
this.serviceLost(SimpleServiceDiscovery.findServiceForID(aData));
break;
case "application-background":
// Turn off polling while in the background
this._interval = SimpleServiceDiscovery.search(0);
SimpleServiceDiscovery.stopSearch();
break;
case "application-foreground":
// Turn polling on when app comes back to foreground
SimpleServiceDiscovery.search(this._interval);
break;
}
},
handleEvent: function(aEvent) {
switch (aEvent.type) {
case "TabSelect": {
let tab = BrowserApp.getTabForBrowser(aEvent.target);
this._updatePageActionForTab(tab, aEvent);
break;
}
case "pageshow": {
let tab = BrowserApp.getTabForWindow(aEvent.originalTarget.defaultView);
this._updatePageActionForTab(tab, aEvent);
break;
}
case "playing":
case "ended": {
let video = aEvent.target;
if (video instanceof HTMLVideoElement) {
// If playing, send the <video>, but if ended we send nothing to shutdown the pageaction
this._updatePageActionForVideo(
aEvent.type === "playing" ? video : null
);
}
break;
}
case "MozAutoplayMediaBlocked": {
if (this._bound && this._bound.has(aEvent.target)) {
aEvent.target.dispatchEvent(
new aEvent.target.ownerGlobal.CustomEvent(
"MozNoControlsBlockedVideo"
)
);
} else {
if (!this._blocked) {
this._blocked = new WeakMap();
}
this._blocked.set(aEvent.target, true);
}
break;
}
case "MozNoControlsVideoBindingAttached": {
if (!this._bound) {
this._bound = new WeakMap();
}
let video = this._findVideoFromEventTarget(aEvent.target);
if (!video) {
return;
}
this._bound.set(video, true);
if (this._blocked && this._blocked.has(video)) {
this._blocked.delete(video);
video.dispatchEvent(
new video.ownerGlobal.CustomEvent("MozNoControlsBlockedVideo")
);
}
break;
}
}
},
_findVideoFromEventTarget(aTarget) {
if (
typeof ShadowRoot !== "undefined" &&
aTarget.parentNode instanceof ShadowRoot &&
aTarget.parentNode.host instanceof HTMLVideoElement
) {
// aTarget is <div class="videocontrols"> inside UA Widget Shadow Root
return aTarget.parentNode.host;
}
if (aTarget instanceof HTMLVideoElement) {
// aTarget is <video>.
return aTarget;
}
return null;
},
_sendEventToVideo: function _sendEventToVideo(aElement, aData) {
let event = aElement.ownerDocument.createEvent("CustomEvent");
event.initCustomEvent(
"media-videoCasting",
false,
true,
JSON.stringify(aData)
);
aElement.dispatchEvent(event);
},
handleVideoBindingAttached: function handleVideoBindingAttached(
aTab,
aEvent
) {
// Let's figure out if we have everything needed to cast a video. The binding
// defaults to |false| so we only need to send an event if |true|.
let video = this._findVideoFromEventTarget(aEvent.target);
if (!video) {
return;
}
if (SimpleServiceDiscovery.services.length == 0) {
return;
}
this.getVideo(video, 0, 0, aBundle => {
// Let the binding know casting is allowed
if (aBundle) {
this._sendEventToVideo(aBundle.element, { allow: true });
}
});
},
handleVideoBindingCast: function handleVideoBindingCast(aTab, aEvent) {
// The binding wants to start a casting session
let video = this._findVideoFromEventTarget(aEvent.target);
if (!video) {
return;
}
// Close an existing session first. closeExternal has checks for an exsting
// session and handles remote and video binding shutdown.
this.closeExternal();
// Start the new session
UITelemetry.addEvent("cast.1", "button", null);
this.openExternal(video, 0, 0);
},
makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) {
return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
},
allowableExtension: function(aURI, aExtensions) {
return (
aURI instanceof Ci.nsIURL && aExtensions.includes(aURI.fileExtension)
);
},
allowableMimeType: function(aType, aTypes) {
return aTypes.includes(aType);
},
// This method will look at the aElement (or try to find a video at aX, aY) that has
// a castable source. If found, aCallback will be called with a JSON meta bundle. If
// no castable source was found, aCallback is called with null.
getVideo: function(aElement, aX, aY, aCallback) {
let extensions = SimpleServiceDiscovery.getSupportedExtensions();
let types = SimpleServiceDiscovery.getSupportedMimeTypes();
// Fast path: Is the given element a video element?
if (aElement instanceof HTMLVideoElement) {
// If we found a video element, no need to look further, even if no
// castable video source is found.
this._getVideo(aElement, types, extensions, aCallback);
return;
}
// Maybe this is an overlay, with the video element under it.
// Use the (x, y) location to guess at a <video> element.
// The context menu system will keep walking up the DOM giving us a chance
// to find an element we match. When it hits <html> things can go BOOM.
try {
let elements = aElement.ownerDocument.querySelectorAll("video");
for (let element of elements) {
// Look for a video element contained in the overlay bounds
let rect = element.getBoundingClientRect();
if (
aY >= rect.top &&
aX >= rect.left &&
aY <= rect.bottom &&
aX <= rect.right
) {
// Once we find a <video> under the overlay, we check it and exit.
this._getVideo(element, types, extensions, aCallback);
return;
}
}
} catch (e) {}
},
_getContentTypeForURI: function(aURI, aElement, aCallback) {
let channel;
try {
let secFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
if (aElement.crossOrigin) {
secFlags = Ci.nsILoadInfo.SEC_REQUIRE_CORS_DATA_INHERITS;
if (aElement.crossOrigin === "use-credentials") {
secFlags |= Ci.nsILoadInfo.SEC_COOKIES_INCLUDE;
}
}
channel = NetUtil.newChannel({
uri: aURI,
loadingNode: aElement,
securityFlags: secFlags,
contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_VIDEO,
});
} catch (e) {
aCallback(null);
return;
}
let listener = {
onStartRequest: function(request) {
switch (channel.responseStatus) {
case 301:
case 302:
case 303:
request.cancel(0);
let location = channel.getResponseHeader("Location");
CastingApps._getContentTypeForURI(
CastingApps.makeURI(location),
aElement,
aCallback
);
break;
default:
aCallback(channel.contentType);
request.cancel(0);
break;
}
},
onStopRequest: function(request, statusCode) {},
onDataAvailable: function(request, stream, offset, count) {},
};
if (channel) {
channel.asyncOpen(listener);
} else {
aCallback(null);
}
},
// Because this method uses a callback, make sure we return ASAP if we know
// we have a castable video source.
_getVideo: function(aElement, aTypes, aExtensions, aCallback) {
// Keep a list of URIs we need for an async mimetype check
let asyncURIs = [];
// Grab the poster attribute from the <video>
let posterURL = aElement.poster;
// First, look to see if the <video> has a src attribute
let sourceURL = aElement.src;
// If empty, try the currentSrc
if (!sourceURL) {
sourceURL = aElement.currentSrc;
}
if (sourceURL) {
// Use the file extension to guess the mime type
let sourceURI = this.makeURI(
sourceURL,
null,
this.makeURI(aElement.baseURI)
);
if (this.allowableExtension(sourceURI, aExtensions)) {
aCallback({
element: aElement,
source: sourceURI.spec,
poster: posterURL,
sourceURI: sourceURI,
});
return;
}
if (aElement.type) {
// Fast sync check
if (this.allowableMimeType(aElement.type, aTypes)) {
aCallback({
element: aElement,
source: sourceURI.spec,
poster: posterURL,
sourceURI: sourceURI,
type: aElement.type,
});
return;
}
}
// Delay the async check until we sync scan all possible URIs
asyncURIs.push(sourceURI);
}
// Next, look to see if there is a <source> child element that meets
// our needs
let sourceNodes = aElement.getElementsByTagName("source");
for (let sourceNode of sourceNodes) {
let sourceURI = this.makeURI(
sourceNode.src,
null,
this.makeURI(sourceNode.baseURI)
);
// Using the type attribute is our ideal way to guess the mime type. Otherwise,
// fallback to using the file extension to guess the mime type
if (this.allowableExtension(sourceURI, aExtensions)) {
aCallback({
element: aElement,
source: sourceURI.spec,
poster: posterURL,
sourceURI: sourceURI,
type: sourceNode.type,
});
return;
}
if (sourceNode.type) {
// Fast sync check
if (this.allowableMimeType(sourceNode.type, aTypes)) {
aCallback({
element: aElement,
source: sourceURI.spec,
poster: posterURL,
sourceURI: sourceURI,
type: sourceNode.type,
});
return;
}
}
// Delay the async check until we sync scan all possible URIs
asyncURIs.push(sourceURI);
}
// Helper method that walks the array of possible URIs, fetching the mimetype as we go.
// As soon as we find a good sourceURL, avoid firing the callback any further
var _getContentTypeForURIs = aURIs => {
// Do an async fetch to figure out the mimetype of the source video
let sourceURI = aURIs.pop();
this._getContentTypeForURI(sourceURI, aElement, aType => {
if (this.allowableMimeType(aType, aTypes)) {
// We found a supported mimetype.
aCallback({
element: aElement,
source: sourceURI.spec,
poster: posterURL,
sourceURI: sourceURI,
type: aType,
});
} else if (aURIs.length > 0) {
// This URI was not a supported mimetype, so let's try the next, if we have more.
_getContentTypeForURIs(aURIs);
} else {
// We were not able to find a supported mimetype.
aCallback(null);
}
});
};
// If we didn't find a good URI directly, let's look using async methods.
if (asyncURIs.length > 0) {
_getContentTypeForURIs(asyncURIs);
}
},
// This code depends on handleVideoBindingAttached setting mozAllowCasting
// so we can quickly figure out if the video is castable
isVideoCastable: function(aElement, aX, aY) {
// Use the flag set when the <video> binding was created as the check
if (aElement instanceof HTMLVideoElement) {
return aElement.mozAllowCasting;
}
// This is called by the context menu system and the system will keep
// walking up the DOM giving us a chance to find an element we match.
// When it hits <html> things can go BOOM.
try {
// Maybe this is an overlay, with the video element under it
// Use the (x, y) location to guess at a <video> element
let elements = aElement.ownerDocument.querySelectorAll("video");
for (let element of elements) {
// Look for a video element contained in the overlay bounds
let rect = element.getBoundingClientRect();
if (
aY >= rect.top &&
aX >= rect.left &&
aY <= rect.bottom &&
aX <= rect.right
) {
// Use the flag set when the <video> binding was created as the check
return element.mozAllowCasting;
}
}
} catch (e) {}
return false;
},
filterCast: {
matches: function(aElement, aX, aY) {
// This behavior matches the pageaction: As long as a video is castable,
// we can cast it, even if it's already being cast to a device.
if (SimpleServiceDiscovery.services.length == 0) {
return false;
}
return CastingApps.isVideoCastable(aElement, aX, aY);
},
},
pageAction: {
click: function() {
// Since this is a pageaction, we use the selected browser
let browser = BrowserApp.selectedBrowser;
if (!browser) {
return;
}
// Look for a castable <video> that is playing, and start casting it
let videos = browser.contentDocument.querySelectorAll("video");
for (let video of videos) {
if (!video.paused && video.mozAllowCasting) {
UITelemetry.addEvent("cast.1", "pageaction", null);
CastingApps.openExternal(video, 0, 0);
return;
}
}
},
},
_findCastableVideo: function _findCastableVideo(aBrowser) {
if (!aBrowser) {
return null;
}
// Scan for a <video> being actively cast. Also look for a castable <video>
// on the page.
let castableVideo = null;
let videos = aBrowser.contentDocument.querySelectorAll("video");
for (let video of videos) {
if (video.mozIsCasting) {
// This <video> is cast-active. Break out of loop.
return video;
}
if (!video.paused && video.mozAllowCasting) {
// This <video> is cast-ready. Keep looking so cast-active could be found.
castableVideo = video;
}
}
// Could be null
return castableVideo;
},
_updatePageActionForTab: function _updatePageActionForTab(aTab, aEvent) {
// We only care about events on the selected tab
if (aTab != BrowserApp.selectedTab) {
return;
}
// Update the page action, scanning for a castable <video>
this._updatePageAction();
},
_updatePageActionForVideo: function _updatePageActionForVideo(aVideo) {
this._updatePageAction(aVideo);
},
_updatePageAction: function _updatePageAction(aVideo) {
// Remove any exising pageaction first, in case state changes or we don't have
// a castable video
if (this.pageAction.id) {
PageActions.remove(this.pageAction.id);
delete this.pageAction.id;
}
if (!aVideo) {
aVideo = this._findCastableVideo(BrowserApp.selectedBrowser);
if (!aVideo) {
return;
}
}
// We only show pageactions if the <video> is from the selected tab
if (
BrowserApp.selectedTab !=
BrowserApp.getTabForWindow(aVideo.ownerGlobal.top)
) {
return;
}
// We check for two state here:
// 1. The video is actively being cast
// 2. The video is allowed to be cast and is currently playing
// Both states have the same action: Show the cast page action
if (aVideo.mozIsCasting) {
this.pageAction.id = PageActions.add({
title: Strings.browser.GetStringFromName("contextmenu.sendToDevice"),
icon: "drawable://casting_active",
clickCallback: this.pageAction.click,
important: true,
useTint: false,
});
} else if (aVideo.mozAllowCasting) {
this.pageAction.id = PageActions.add({
title: Strings.browser.GetStringFromName("contextmenu.sendToDevice"),
icon: "drawable://casting",
clickCallback: this.pageAction.click,
important: true,
useTint: true,
});
}
},
prompt: function(aWindow, aCallback, aFilterFunc) {
let items = [];
let filteredServices = [];
SimpleServiceDiscovery.services.forEach(function(aService) {
let item = {
label: aService.friendlyName,
selected: false,
};
if (!aFilterFunc || aFilterFunc(aService)) {
filteredServices.push(aService);
items.push(item);
}
});
if (items.length == 0) {
return;
}
let prompt = new Prompt({
window: aWindow,
title: Strings.browser.GetStringFromName("casting.sendToDevice"),
})
.setSingleChoiceItems(items)
.show(function(data) {
let selected = data.button;
let service = selected == -1 ? null : filteredServices[selected];
if (aCallback) {
aCallback(service);
}
});
},
handleContextMenu: function(aElement, aX, aY) {
UITelemetry.addEvent("action.1", "contextmenu", null, "web_cast");
UITelemetry.addEvent("cast.1", "contextmenu", null);
this.openExternal(aElement, aX, aY);
},
openExternal: function(aElement, aX, aY) {
// Start a second screen media service
this.getVideo(aElement, aX, aY, this._openExternal.bind(this));
},
_openExternal: function(aVideo) {
if (!aVideo) {
return;
}
function filterFunc(aService) {
return (
this.allowableExtension(aVideo.sourceURI, aService.extensions) ||
this.allowableMimeType(aVideo.type, aService.types)
);
}
this.prompt(
aVideo.element.ownerGlobal,
aService => {
if (!aService) {
return;
}
// Make sure we have a player app for the given service
let app = SimpleServiceDiscovery.findAppForService(aService);
if (!app) {
return;
}
if (aVideo.element) {
aVideo.title = aVideo.element.ownerGlobal.top.document.title;
// If the video is currently playing on the device, pause it
if (!aVideo.element.paused) {
aVideo.element.pause();
}
}
app.stop(() => {
app.start(aStarted => {
if (!aStarted) {
dump("CastingApps: Unable to start app");
return;
}
app.remoteMedia(aRemoteMedia => {
if (!aRemoteMedia) {
dump("CastingApps: Failed to create remotemedia");
return;
}
this.session = {
service: aService,
app: app,
remoteMedia: aRemoteMedia,
data: {
title: aVideo.title,
source: aVideo.source,
poster: aVideo.poster,
},
videoRef: Cu.getWeakReference(aVideo.element),
};
}, this);
});
});
},
filterFunc.bind(this)
);
},
closeExternal: function() {
if (!this.session) {
return;
}
this.session.remoteMedia.shutdown();
this._shutdown();
},
_shutdown: function() {
if (!this.session) {
return;
}
this.session.app.stop();
let video = this.session.videoRef.get();
if (video) {
this._sendEventToVideo(video, { active: false });
this._updatePageAction();
}
delete this.session;
},
// RemoteMedia callback API methods
onRemoteMediaStart: function(aRemoteMedia) {
if (!this.session) {
return;
}
aRemoteMedia.load(this.session.data);
GlobalEventDispatcher.sendRequest({
type: "Casting:Started",
device: this.session.service.friendlyName,
});
let video = this.session.videoRef.get();
if (video) {
this._sendEventToVideo(video, { active: true });
this._updatePageAction(video);
}
},
onRemoteMediaStop: function(aRemoteMedia) {
GlobalEventDispatcher.sendRequest({ type: "Casting:Stopped" });
this._shutdown();
},
onRemoteMediaStatus: function(aRemoteMedia) {
if (!this.session) {
return;
}
let status = aRemoteMedia.status;
switch (status) {
case "started":
GlobalEventDispatcher.sendRequest({ type: "Casting:Playing" });
break;
case "paused":
GlobalEventDispatcher.sendRequest({ type: "Casting:Paused" });
break;
case "completed":
this.closeExternal();
break;
}
},
};

View File

@ -1,145 +0,0 @@
/* 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";
var ConsoleAPI = {
observe: function observe(aMessage, aTopic, aData) {
aMessage = aMessage.wrappedJSObject;
let mappedArguments = Array.from(
aMessage.arguments,
this.formatResult,
this
);
let joinedArguments = mappedArguments.join(" ");
if (aMessage.level == "error" || aMessage.level == "warn") {
let flag =
aMessage.level == "error"
? Ci.nsIScriptError.errorFlag
: Ci.nsIScriptError.warningFlag;
let consoleMsg = Cc["@mozilla.org/scripterror;1"].createInstance(
Ci.nsIScriptError
);
consoleMsg.init(
joinedArguments,
null,
null,
0,
0,
flag,
"content javascript"
);
Services.console.logMessage(consoleMsg);
} else if (aMessage.level == "trace") {
let bundle = Services.strings.createBundle(
"chrome://browser/locale/browser.properties"
);
let args = aMessage.arguments;
let filename = this.abbreviateSourceURL(args[0].filename);
let functionName =
args[0].functionName ||
bundle.GetStringFromName("stacktrace.anonymousFunction");
let lineNumber = args[0].lineNumber;
let body = bundle.formatStringFromName("stacktrace.outputMessage", [
filename,
functionName,
lineNumber,
]);
body += "\n";
args.forEach(function(aFrame) {
let functionName =
aFrame.functionName ||
bundle.GetStringFromName("stacktrace.anonymousFunction");
body +=
" " +
aFrame.filename +
" :: " +
functionName +
" :: " +
aFrame.lineNumber +
"\n";
});
Services.console.logStringMessage(body);
} else if (aMessage.level == "time" && aMessage.arguments) {
let bundle = Services.strings.createBundle(
"chrome://browser/locale/browser.properties"
);
let body = bundle.formatStringFromName("timer.start", [
aMessage.arguments.name,
]);
Services.console.logStringMessage(body);
} else if (aMessage.level == "timeEnd" && aMessage.arguments) {
let bundle = Services.strings.createBundle(
"chrome://browser/locale/browser.properties"
);
let body = bundle.formatStringFromName("timer.end", [
aMessage.arguments.name,
aMessage.arguments.duration,
]);
Services.console.logStringMessage(body);
} else if (
["group", "groupCollapsed", "groupEnd"].includes(aMessage.level)
) {
// Do nothing yet
} else {
Services.console.logStringMessage(joinedArguments);
}
},
getResultType: function getResultType(aResult) {
let type = aResult === null ? "null" : typeof aResult;
if (type == "object" && aResult.constructor && aResult.constructor.name) {
type = aResult.constructor.name;
}
return type.toLowerCase();
},
formatResult: function formatResult(aResult) {
let output = "";
let type = this.getResultType(aResult);
switch (type) {
case "string":
case "boolean":
case "date":
case "error":
case "number":
case "regexp":
output = aResult.toString();
break;
case "null":
case "undefined":
output = type;
break;
default:
output = aResult.toString();
break;
}
return output;
},
abbreviateSourceURL: function abbreviateSourceURL(aSourceURL) {
// Remove any query parameters.
let hookIndex = aSourceURL.indexOf("?");
if (hookIndex > -1) {
aSourceURL = aSourceURL.substring(0, hookIndex);
}
// Remove a trailing "/".
if (aSourceURL[aSourceURL.length - 1] == "/") {
aSourceURL = aSourceURL.substring(0, aSourceURL.length - 1);
}
// Remove all but the last path component.
let slashIndex = aSourceURL.lastIndexOf("/");
if (slashIndex > -1) {
aSourceURL = aSourceURL.substring(slashIndex + 1);
}
return aSourceURL;
},
};

View File

@ -1,73 +0,0 @@
/* 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";
ChromeUtils.defineModuleGetter(
this,
"ConsoleAPI",
"resource://gre/modules/Console.jsm"
);
/*
* Collection of methods and features specific to using a GeckoView instance.
* The code is isolated from browser.js for code size and performance reasons.
*/
var EmbedRT = {
_scopes: {},
onEvent: function(event, data, callback) {
switch (event) {
case "GeckoView:ImportScript":
this.importScript(data.scriptURL);
break;
}
},
/*
* Loads a script file into a sandbox and calls an optional load function
*/
importScript: function(scriptURL) {
if (scriptURL in this._scopes) {
return;
}
let principal = Cc["@mozilla.org/systemprincipal;1"].createInstance(
Ci.nsIPrincipal
);
let sandbox = new Cu.Sandbox(principal, {
sandboxName: scriptURL,
wantGlobalProperties: ["indexedDB"],
});
sandbox.console = new ConsoleAPI({ consoleID: "script/" + scriptURL });
// As we don't want our caller to control the JS version used for the
// script file, we run loadSubScript within the context of the
// sandbox with the latest JS version set explicitly.
sandbox.__SCRIPT_URI_SPEC__ = scriptURL;
Cu.evalInSandbox(
"Components.classes['@mozilla.org/moz/jssubscript-loader;1'].createInstance(Components.interfaces.mozIJSSubScriptLoader).loadSubScript(__SCRIPT_URI_SPEC__);",
sandbox,
"ECMAv5"
);
this._scopes[scriptURL] = sandbox;
if ("load" in sandbox) {
let params = {
window: window,
resourceURI: scriptURL,
};
try {
sandbox.load(params);
} catch (e) {
dump(
"Exception calling 'load' method in script: " + scriptURL + "\n" + e
);
}
}
},
};

View File

@ -1,158 +0,0 @@
"use strict";
ChromeUtils.defineModuleGetter(
this,
"ExtensionData",
"resource://gre/modules/Extension.jsm"
);
var ExtensionPermissions = {
// id -> object containing update details (see applyUpdate() )
updates: new Map(),
// Prepare the strings needed for a permission notification.
_prepareStrings(info) {
let appName = Strings.brand.GetStringFromName("brandShortName");
let info2 = Object.assign({ appName }, info);
let strings = ExtensionData.formatPermissionStrings(info2, Strings.browser);
// We dump the main body of the dialog into a big android
// TextView. Build a big string with the full contents here.
let message = "";
if (strings.msgs.length > 0) {
message = [
strings.listIntro,
...strings.msgs.map(s => `\u2022 ${s}`),
].join("\n");
}
return {
header: strings.header.replace("<>", info.addonName),
message,
acceptText: strings.acceptText,
cancelText: strings.cancelText,
};
},
// Prepare an icon for a permission notification
_prepareIcon(iconURL) {
// We can render pngs with ResourceDrawableUtils
if (iconURL.endsWith(".png")) {
return iconURL;
}
// If we can't render an icon, show the default
return "DEFAULT";
},
async observe(subject, topic, data) {
switch (topic) {
case "webextension-permission-prompt": {
let { target, info } = subject.wrappedJSObject;
let stringInfo = Object.assign({ addonName: info.addon.name }, info);
let details = this._prepareStrings(stringInfo);
details.icon = this._prepareIcon(info.icon);
details.type = "Extension:PermissionPrompt";
let accepted = await EventDispatcher.instance.sendRequestForResult(
details
);
if (accepted) {
info.resolve();
} else {
info.reject();
}
break;
}
case "webextension-update-permissions":
let info = subject.wrappedJSObject;
let { addon, resolve, reject } = info;
let stringInfo = Object.assign(
{
type: "update",
addonName: addon.name,
},
info
);
let details = this._prepareStrings(stringInfo);
// If there are no promptable permissions, just apply the update
if (details.message.length == 0) {
resolve();
return;
}
// Store all the details about the update until the user chooses to
// look at update, at which point we will pick up in this.applyUpdate()
details.icon = this._prepareIcon(addon.iconURL || "dummy.svg");
let first = this.updates.size == 0;
this.updates.set(addon.id, { details, resolve, reject });
if (first) {
EventDispatcher.instance.sendRequest({
type: "Extension:ShowUpdateIcon",
value: true,
});
}
break;
case "webextension-optional-permission-prompt": {
let info = subject.wrappedJSObject;
let { name, resolve } = info;
let stringInfo = Object.assign(
{
type: "optional",
addonName: name,
},
info
);
let details = this._prepareStrings(stringInfo);
// If there are no promptable permissions, just apply the update
if (details.message.length == 0) {
resolve(true);
return;
}
// Store all the details about the update until the user chooses to
// look at update, at which point we will pick up in this.applyUpdate()
details.icon = this._prepareIcon(info.icon || "dummy.svg");
details.type = "Extension:PermissionPrompt";
let accepted = await EventDispatcher.instance.sendRequestForResult(
details
);
resolve(accepted);
}
}
},
async applyUpdate(id) {
if (!this.updates.has(id)) {
return;
}
let update = this.updates.get(id);
this.updates.delete(id);
if (this.updates.size == 0) {
EventDispatcher.instance.sendRequest({
type: "Extension:ShowUpdateIcon",
value: false,
});
}
let { details } = update;
details.type = "Extension:PermissionPrompt";
let accepted = await EventDispatcher.instance.sendRequestForResult(details);
if (accepted) {
update.resolve();
} else {
update.reject();
}
},
};

View File

@ -1,147 +0,0 @@
/* 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";
var FeedHandler = {
PREF_CONTENTHANDLERS_BRANCH: "browser.contentHandlers.types.",
TYPE_MAYBE_FEED: "application/vnd.mozilla.maybe.feed",
_contentTypes: null,
getContentHandlers: function fh_getContentHandlers(contentType) {
if (!this._contentTypes) {
this.loadContentHandlers();
}
if (!(contentType in this._contentTypes)) {
return [];
}
return this._contentTypes[contentType];
},
loadContentHandlers: function fh_loadContentHandlers() {
this._contentTypes = {};
let kids = Services.prefs
.getBranch(this.PREF_CONTENTHANDLERS_BRANCH)
.getChildList("");
// First get the numbers of the providers by getting all ###.uri prefs
let nums = [];
for (let i = 0; i < kids.length; i++) {
let match = /^(\d+)\.uri$/.exec(kids[i]);
if (!match) {
continue;
} else {
nums.push(match[1]);
}
}
// Sort them, to get them back in order
nums.sort(function(a, b) {
return a - b;
});
// Now register them
for (let i = 0; i < nums.length; i++) {
let branch = Services.prefs.getBranch(
this.PREF_CONTENTHANDLERS_BRANCH + nums[i] + "."
);
let vals = branch.getChildList("");
if (vals.length == 0) {
return;
}
try {
let type = branch.getCharPref("type");
let uri = branch.getComplexValue("uri", Ci.nsIPrefLocalizedString).data;
let title = branch.getComplexValue("title", Ci.nsIPrefLocalizedString)
.data;
if (!(type in this._contentTypes)) {
this._contentTypes[type] = [];
}
this._contentTypes[type].push({
contentType: type,
uri: uri,
name: title,
});
} catch (ex) {}
}
},
onEvent: function fh_onEvent(event, args, callback) {
if (event === "Feeds:Subscribe") {
let tab = BrowserApp.getTabForId(args.tabId);
if (!tab) {
return;
}
let browser = tab.browser;
let feeds = browser.feeds;
if (feeds == null) {
return;
}
// First, let's decide on which feed to subscribe
let feedIndex = -1;
if (feeds.length > 1) {
let p = new Prompt({
window: browser.contentWindow,
title: Strings.browser.GetStringFromName("feedHandler.chooseFeed"),
})
.setSingleChoiceItems(
feeds.map(function(feed) {
return { label: feed.title || feed.href };
})
)
.show(data => {
feedIndex = data.button;
if (feedIndex == -1) {
return;
}
this.loadFeed(feeds[feedIndex], browser);
});
return;
}
this.loadFeed(feeds[0], browser);
}
},
loadFeed: function fh_loadFeed(aFeed, aBrowser) {
let feedURL = aFeed.href;
// Next, we decide on which service to send the feed
let handlers = this.getContentHandlers(this.TYPE_MAYBE_FEED);
if (handlers.length == 0) {
return;
}
// JSON for Prompt
let p = new Prompt({
window: aBrowser.contentWindow,
title: Strings.browser.GetStringFromName("feedHandler.subscribeWith"),
})
.setSingleChoiceItems(
handlers.map(function(handler) {
return { label: handler.name };
})
)
.show(function(data) {
if (data.button == -1) {
return;
}
// Merge the handler URL and the feed URL
let readerURL = handlers[data.button].uri;
readerURL = readerURL.replace(/%s/gi, encodeURIComponent(feedURL));
// Open the resultant URL in a new tab
BrowserApp.addTab(readerURL, { parentId: BrowserApp.selectedTab.id });
});
},
};

View File

@ -1,69 +0,0 @@
/* 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";
var Feedback = {
get _feedbackURL() {
delete this._feedbackURL;
return (this._feedbackURL = Services.urlFormatter.formatURLPref(
"app.feedbackURL"
));
},
onEvent: function(event, data, callback) {
if (event !== "Feedback:Show") {
return;
}
// Don't prompt for feedback in distribution builds.
try {
Services.prefs.getCharPref("distribution.id");
return;
} catch (e) {}
let url = this._feedbackURL;
let browser = BrowserApp.selectOrAddTab(url, {
parentId: BrowserApp.selectedTab.id,
}).browser;
browser.addEventListener("FeedbackClose", this, false, true);
browser.addEventListener("FeedbackMaybeLater", this, false, true);
// Dispatch a custom event to the page content when feedback is prompted by the browser.
// This will be used by the page to determine it's being loaded directly by the browser,
// instead of by the user visiting the page, e.g. through browser history.
function loadListener(event) {
browser.removeEventListener("DOMContentLoaded", loadListener);
browser.contentDocument.dispatchEvent(
new CustomEvent("FeedbackPrompted")
);
}
browser.addEventListener("DOMContentLoaded", loadListener);
},
handleEvent: function(event) {
if (!this._isAllowed(event.target)) {
return;
}
switch (event.type) {
case "FeedbackClose":
// Do nothing.
break;
case "FeedbackMaybeLater":
GlobalEventDispatcher.sendRequest({ type: "Feedback:MaybeLater" });
break;
}
let win = event.target.ownerGlobal.top;
BrowserApp.closeTab(BrowserApp.getTabForWindow(win));
},
_isAllowed: function(node) {
let uri = node.ownerDocument.documentURIObject;
let feedbackURI = Services.io.newURI(this._feedbackURL);
return uri.prePath === feedbackURI.prePath;
},
};

View File

@ -1,231 +0,0 @@
/* 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";
var FindHelper = {
_finder: null,
_targetTab: null,
_initialViewport: null,
_viewportChanged: false,
_result: null,
// Start of nsIObserver implementation.
onEvent: function(event, data, callback) {
switch (event) {
case "FindInPage:Opened": {
this._findOpened();
break;
}
case "FindInPage:Closed": {
this._uninit();
this._findClosed();
break;
}
case "Tab:Selected": {
// Allow for page switching.
this._uninit();
break;
}
case "FindInPage:Find": {
this.doFind(data.searchString);
break;
}
case "FindInPage:Next": {
this.findAgain(data.searchString, false);
break;
}
case "FindInPage:Prev": {
this.findAgain(data.searchString, true);
break;
}
}
},
/**
* When the FindInPageBar opens/ becomes visible, it's time to:
* 1. Add listeners for other message types sent from the FindInPageBar
* 2. initialize the Finder instance, if necessary.
*/
_findOpened: function() {
GlobalEventDispatcher.registerListener(this, [
"FindInPage:Find",
"FindInPage:Next",
"FindInPage:Prev",
]);
// Initialize the finder component for the current page by performing a fake find.
this._init();
this._finder.requestMatchesCount("");
},
/**
* Fetch the Finder instance from the active tabs' browser and start tracking
* the active viewport.
*/
_init: function() {
// If there's no find in progress, start one.
if (this._finder) {
return;
}
this._targetTab = BrowserApp.selectedTab;
try {
this._finder = this._targetTab.browser.finder;
} catch (e) {
throw new Error(
"FindHelper: " +
e +
"\n" +
"JS stack: \n" +
(e.stack || Components.stack.formattedStack)
);
}
this._finder.addResultListener(this);
this._initialViewport = JSON.stringify(this._targetTab.getViewport());
this._viewportChanged = false;
WindowEventDispatcher.registerListener(this, ["Tab:Selected"]);
},
/**
* Detach from the Finder instance (so stop listening for messages) and stop
* tracking the active viewport.
*/
_uninit: function() {
// If there's no find in progress, there's nothing to clean up.
if (!this._finder) {
return;
}
this._finder.removeSelection();
this._finder.removeResultListener(this);
this._finder = null;
this._targetTab = null;
this._initialViewport = null;
this._viewportChanged = false;
WindowEventDispatcher.unregisterListener(this, ["Tab:Selected"]);
},
/**
* When the FindInPageBar closes, it's time to stop listening for its messages.
*/
_findClosed: function() {
GlobalEventDispatcher.unregisterListener(this, [
"FindInPage:Find",
"FindInPage:Next",
"FindInPage:Prev",
]);
},
/**
* Start an asynchronous find-in-page operation, using the current Finder
* instance and request to count the amount of matches.
* If no Finder instance is currently active, we'll lazily initialize it here.
*
* @param {String} searchString Word to search for in the current document
* @return {Object} Echo of the current find action
*/
doFind: function(searchString) {
if (!this._finder) {
this._init();
}
this._finder.fastFind(searchString, false);
return { searchString, findBackwards: false };
},
/**
* Restart the same find-in-page operation as before via `doFind()`. If we
* haven't called `doFind()`, we simply kick off a regular find.
*
* @param {String} searchString Word to search for in the current document
* @param {Boolean} findBackwards Direction to search in
* @return {Object} Echo of the current find action
*/
findAgain: function(searchString, findBackwards) {
// This always happens if the user taps next/previous after re-opening the
// search bar, and not only forces _init() but also an initial fastFind(STRING)
// before any findAgain(DIRECTION).
if (!this._finder) {
return this.doFind(searchString);
}
this._finder.findAgain(searchString, findBackwards, false, false);
return { searchString, findBackwards };
},
// Start of Finder.jsm listener implementation.
/**
* Pass along the count results to FindInPageBar for display. The result that
* is sent to the FindInPageBar is augmented with the current find-in-page count
* limit.
*
* @param {Object} result Result coming from the Finder instance that contains
* the following properties:
* - {Number} total The total amount of matches found
* - {Number} current The index of current found range
* in the document
*/
onMatchesCountResult: function(result) {
this._result = result;
GlobalEventDispatcher.sendRequest(
Object.assign(
{
type: "FindInPage:MatchesCountResult",
},
this._result
)
);
},
/**
* When a find-in-page action finishes, this method is invoked. This is mainly
* used at the moment to detect if the current viewport has changed, which might
* be indicated by not finding a string in the current page.
*
* @param {Object} aData A dictionary, representing the find result, which
* contains the following properties:
* - {String} searchString Word that was searched for
* in the current document
* - {Number} result One of the following
* Ci.nsITypeAheadFind.* result
* indicators: FIND_FOUND,
* FIND_NOTFOUND, FIND_WRAPPED,
* FIND_PENDING
* - {Boolean} findBackwards Whether the search direction
* was backwards
* - {Boolean} findAgain Whether the previous search
* was repeated
* - {Boolean} drawOutline Whether we may (re-)draw the
* outline of a hyperlink
* - {Boolean} linksOnly Whether links-only mode was
* active
*/
onFindResult: function(aData) {
if (aData.result == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
if (this._viewportChanged) {
if (this._targetTab != BrowserApp.selectedTab) {
// this should never happen
Cu.reportError("Warning: selected tab changed during find!");
// fall through and restore viewport on the initial tab anyway
}
this._targetTab.sendViewportUpdate();
}
} else {
// Disabled until bug 1014113 is fixed
// ZoomHelper.zoomToRect(aData.rect);
this._viewportChanged = true;
}
},
};

View File

@ -1,127 +0,0 @@
/* 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/. */
const LINKIFY_TIMEOUT = 0;
function Linkifier() {
this._linkifyTimer = null;
this._phoneRegex = /(?:\s|^)[\+]?(\(?\d{1,8}\)?)?([- ]+\(?\d{1,8}\)?)+( ?(x|ext) ?\d{1,3})?(?:\s|$)/g;
}
Linkifier.prototype = {
_buildAnchor: function(aDoc, aNumberText) {
let anchorNode = aDoc.createElement("a");
let cleanedText = "";
for (let i = 0; i < aNumberText.length; i++) {
let c = aNumberText.charAt(i);
if ((c >= "0" && c <= "9") || c == "+") {
// assuming there is only the leading '+'.
cleanedText += c;
}
}
anchorNode.setAttribute("href", "tel:" + cleanedText);
let nodeText = aDoc.createTextNode(aNumberText);
anchorNode.appendChild(nodeText);
return anchorNode;
},
_linkifyNodeNumbers: function(aNodeToProcess, aDoc) {
let parent = aNodeToProcess.parentNode;
let nodeText = aNodeToProcess.nodeValue;
// Replacing the original text node with a sequence of
// |text before number|anchor with number|text after number nodes.
// Each step a couple of (optional) text node and anchor node are appended.
let anchorNode = null;
let m = null;
let startIndex = 0;
let prevNode = null;
while ((m = this._phoneRegex.exec(nodeText))) {
anchorNode = this._buildAnchor(
aDoc,
nodeText.substr(m.index, m[0].length)
);
let textExistsBeforeNumber = m.index > startIndex;
let nodeToAdd = null;
if (textExistsBeforeNumber) {
nodeToAdd = aDoc.createTextNode(
nodeText.substr(startIndex, m.index - startIndex)
);
} else {
nodeToAdd = anchorNode;
}
if (!prevNode) {
// first time, need to replace the whole node with the first new one.
parent.replaceChild(nodeToAdd, aNodeToProcess);
} else {
parent.insertBefore(nodeToAdd, prevNode.nextSibling);
} // inserts after.
if (textExistsBeforeNumber) {
// if we added the text node before the anchor, we still need to add the anchor node.
parent.insertBefore(anchorNode, nodeToAdd.nextSibling);
}
// next nodes need to be appended to this node.
prevNode = anchorNode;
startIndex = m.index + m[0].length;
}
// if some text is remaining after the last anchor.
if (startIndex > 0 && startIndex < nodeText.length) {
let lastNode = aDoc.createTextNode(nodeText.substr(startIndex));
parent.insertBefore(lastNode, prevNode.nextSibling);
return lastNode;
}
return anchorNode;
},
linkifyNumbers: function(aDoc) {
// Removing any installed timer in case the page has changed and a previous timer is still running.
if (this._linkifyTimer) {
clearTimeout(this._linkifyTimer);
this._linkifyTimer = null;
}
let filterNode = function(node) {
if (
node.parentNode.tagName != "A" &&
node.parentNode.tagName != "SCRIPT" &&
node.parentNode.tagName != "NOSCRIPT" &&
node.parentNode.tagName != "STYLE" &&
node.parentNode.tagName != "APPLET" &&
node.parentNode.tagName != "TEXTAREA"
) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_REJECT;
};
let nodeWalker = aDoc.createTreeWalker(
aDoc.body,
NodeFilter.SHOW_TEXT,
filterNode,
false
);
let parseNode = () => {
let node = nodeWalker.nextNode();
if (!node) {
this._linkifyTimer = null;
return;
}
let lastAddedNode = this._linkifyNodeNumbers(node, aDoc);
// we assign a different timeout whether the node was processed or not.
if (lastAddedNode) {
nodeWalker.currentNode = lastAddedNode;
this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
} else {
this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
}
};
this._linkifyTimer = setTimeout(parseNode, LINKIFY_TIMEOUT);
},
};

View File

@ -1,62 +0,0 @@
/* 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";
ChromeUtils.defineModuleGetter(
this,
"Snackbars",
"resource://gre/modules/Snackbars.jsm"
);
var MasterPassword = {
pref: "privacy.masterpassword.enabled",
get _pk11DB() {
delete this._pk11DB;
return (this._pk11DB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
Ci.nsIPK11TokenDB
));
},
get enabled() {
let token = this._pk11DB.getInternalKeyToken();
if (token) {
return token.hasPassword;
}
return false;
},
setPassword: function setPassword(aPassword) {
try {
let token = this._pk11DB.getInternalKeyToken();
if (token.needsUserInit) {
token.initPassword(aPassword);
} else if (!token.needsLogin()) {
token.changePassword("", aPassword);
}
return true;
} catch (e) {
dump("MasterPassword.setPassword: " + e);
}
return false;
},
removePassword: function removePassword(aOldPassword) {
try {
let token = this._pk11DB.getInternalKeyToken();
if (token.checkPassword(aOldPassword)) {
token.changePassword(aOldPassword, "");
return true;
}
} catch (e) {
dump("MasterPassword.removePassword: " + e + "\n");
}
Snackbars.show(
Strings.browser.GetStringFromName("masterPassword.incorrect"),
Snackbars.LENGTH_LONG
);
return false;
},
};

View File

@ -1,89 +0,0 @@
/* 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";
const MAX_CONTENT_VIEWERS_PREF = "browser.sessionhistory.max_total_viewers";
var MemoryObserver = {
// When we turn off the bfcache by overwriting the old default value, we want
// to be able to restore it later on if memory pressure decreases again.
_defaultMaxContentViewers: -1,
observe: function mo_observe(aSubject, aTopic, aData) {
if (aTopic == "memory-pressure") {
if (aData != "heap-minimize") {
this.handleLowMemory();
}
// The JS engine would normally GC on this notification, but since we
// disabled that in favor of this method (bug 669346), we should gc here.
// See bug 784040 for when this code was ported from XUL to native Fennec.
this.gc();
} else if (aTopic == "memory-pressure-stop") {
this.handleEnoughMemory();
} else if (aTopic == "Memory:Dump") {
this.dumpMemoryStats(aData);
}
},
handleLowMemory: function() {
// do things to reduce memory usage here
if (
!Services.prefs.getBoolPref("browser.tabs.disableBackgroundZombification")
) {
let tabs = BrowserApp.tabs;
let selected = BrowserApp.selectedTab;
for (let i = 0; i < tabs.length; i++) {
if (tabs[i] != selected && !tabs[i].playingAudio) {
tabs[i].zombify();
}
}
}
// Change some preferences temporarily for only this session
let defaults = Services.prefs.getDefaultBranch(null);
// Stop using the bfcache
if (
!Services.prefs.getBoolPref(
"browser.sessionhistory.bfcacheIgnoreMemoryPressure"
)
) {
this._defaultMaxContentViewers = defaults.getIntPref(
MAX_CONTENT_VIEWERS_PREF
);
defaults.setIntPref(MAX_CONTENT_VIEWERS_PREF, 0);
}
},
handleEnoughMemory: function() {
// Re-enable the bfcache
let defaults = Services.prefs.getDefaultBranch(null);
if (
!Services.prefs.getBoolPref(
"browser.sessionhistory.bfcacheIgnoreMemoryPressure"
)
) {
defaults.setIntPref(
MAX_CONTENT_VIEWERS_PREF,
this._defaultMaxContentViewers
);
}
},
gc: function() {
window.windowUtils.garbageCollect();
Cu.forceGC();
},
dumpMemoryStats: function(aLabel) {
let memDumper = Cc["@mozilla.org/memory-info-dumper;1"].getService(
Ci.nsIMemoryInfoDumper
);
memDumper.dumpMemoryInfoToTempDir(
aLabel,
/* anonymize = */ false,
/* minimize = */ false
);
},
};

View File

@ -1,53 +0,0 @@
/* 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";
var OfflineApps = {
allowSite: function(aDocument) {
Services.perms.addFromPrincipal(
aDocument.nodePrincipal,
"offline-app",
Services.perms.ALLOW_ACTION
);
// When a site is enabled while loading, manifest resources will
// start fetching immediately. This one time we need to do it
// ourselves.
this._startFetching(aDocument);
},
disallowSite: function(aDocument) {
Services.perms.addFromPrincipal(
aDocument.nodePrincipal,
"offline-app",
Services.perms.DENY_ACTION
);
},
_startFetching: function(aDocument) {
if (!aDocument.documentElement) {
return;
}
let manifest = aDocument.documentElement.getAttribute("manifest");
if (!manifest) {
return;
}
let manifestURI = Services.io.newURI(
manifest,
aDocument.characterSet,
aDocument.documentURIObject
);
let updateService = Cc[
"@mozilla.org/offlinecacheupdate-service;1"
].getService(Ci.nsIOfflineCacheUpdateService);
updateService.scheduleUpdate(
manifestURI,
aDocument.documentURIObject,
aDocument.nodePrincipal,
window
);
},
};

View File

@ -1,204 +0,0 @@
/* 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";
var PermissionsHelper = {
_permissonTypes: [
"password",
"geolocation",
"popup",
"indexedDB",
"offline-app",
"desktop-notification",
"plugins",
"native-intent",
],
_permissionStrings: {
password: {
label: "password.logins",
allowed: "password.save",
denied: "password.dontSave",
},
geolocation: {
label: "geolocation.location",
allowed: "geolocation.allow",
denied: "geolocation.dontAllow",
},
popup: {
label: "blockPopups.label2",
allowed: "popup.show",
denied: "popup.dontShow",
},
indexedDB: {
label: "offlineApps.offlineData",
allowed: "offlineApps.allow",
denied: "offlineApps.dontAllow2",
},
"offline-app": {
label: "offlineApps.offlineData",
allowed: "offlineApps.allow",
denied: "offlineApps.dontAllow2",
},
"desktop-notification": {
label: "desktopNotification.notifications",
allowed: "desktopNotification2.allow",
denied: "desktopNotification2.dontAllow",
},
plugins: {
label: "clickToPlayPlugins.plugins",
allowed: "clickToPlayPlugins.activate",
denied: "clickToPlayPlugins.dontActivate",
},
"native-intent": {
label: "helperapps.openWithList2",
allowed: "helperapps.always",
denied: "helperapps.never",
},
},
onEvent: function onEvent(event, data, callback) {
let principal = BrowserApp.selectedBrowser.contentPrincipal;
let check = false;
switch (event) {
case "Permissions:Check":
check = true;
// fall-through
case "Permissions:Get":
let permissions = [];
for (let i = 0; i < this._permissonTypes.length; i++) {
let type = this._permissonTypes[i];
let value = this.getPermission(principal, type);
// Only add the permission if it was set by the user
if (value == Services.perms.UNKNOWN_ACTION) {
continue;
}
if (check) {
GlobalEventDispatcher.sendRequest({
type: "Permissions:CheckResult",
hasPermissions: true,
});
return;
}
// Get the strings that correspond to the permission type
let typeStrings = this._permissionStrings[type];
let label = Strings.browser.GetStringFromName(typeStrings.label);
// Get the key to look up the appropriate string entity
let valueKey =
value == Services.perms.ALLOW_ACTION ? "allowed" : "denied";
let valueString = Strings.browser.GetStringFromName(
typeStrings[valueKey]
);
permissions.push({
type: type,
setting: label,
value: valueString,
});
}
if (check) {
GlobalEventDispatcher.sendRequest({
type: "Permissions:CheckResult",
hasPermissions: false,
});
return;
}
// Keep track of permissions, so we know which ones to clear
this._currentPermissions = permissions;
WindowEventDispatcher.sendRequest({
type: "Permissions:Data",
permissions: permissions,
});
break;
case "Permissions:Clear":
// An array of the indices of the permissions we want to clear
let permissionsToClear = data.permissions;
let privacyContext = BrowserApp.selectedBrowser.docShell.QueryInterface(
Ci.nsILoadContext
);
for (let i = 0; i < permissionsToClear.length; i++) {
let indexToClear = permissionsToClear[i];
let permissionType = this._currentPermissions[indexToClear].type;
this.clearPermission(uri, permissionType, privacyContext);
}
break;
}
},
/**
* Gets the permission value stored for a specified permission type.
*
* @param aType
* The permission type string stored in permission manager.
* e.g. "geolocation", "indexedDB", "popup"
*
* @return A permission value defined in nsIPermissionManager.
*/
getPermission: function getPermission(aPrincipal, aType) {
let aURI = BrowserApp.selectedBrowser.lastURI;
// Password saving isn't a nsIPermissionManager permission type, so handle
// it seperately.
if (aType == "password") {
// By default, login saving is enabled, so if it is disabled, the
// user selected the never remember option
if (!Services.logins.getLoginSavingEnabled(aURI.displayPrePath)) {
return Services.perms.DENY_ACTION;
}
// Check to see if the user ever actually saved a login
if (Services.logins.countLogins(aURI.displayPrePath, "", "")) {
return Services.perms.ALLOW_ACTION;
}
return Services.perms.UNKNOWN_ACTION;
}
// Geolocation consumers use testExactPermissionForPrincipal
if (aType == "geolocation") {
return Services.perms.testExactPermissionFromPrincipal(aPrincipal, aType);
}
return Services.perms.testPermissionFromPrincipal(aPrincipal, aType);
},
/**
* Clears a user-set permission value for the site given a permission type.
*
* @param aType
* The permission type string stored in permission manager.
* e.g. "geolocation", "indexedDB", "popup"
*/
clearPermission: function clearPermission(aPrincipal, aType, aContext) {
// Password saving isn't a nsIPermissionManager permission type, so handle
// it seperately.
if (aType == "password") {
// Get rid of exisiting stored logings
let logins = Services.logins.findLogins(aURI.displayPrePath, "", "");
for (let i = 0; i < logins.length; i++) {
Services.logins.removeLogin(logins[i]);
}
// Re-set login saving to enabled
Services.logins.setLoginSavingEnabled(aURI.displayPrePath, true);
} else {
Services.perms.removeFromPrincipal(aPrincipal, aType);
// Clear content prefs set in ContentPermissionPrompt.js
Cc["@mozilla.org/content-pref/service;1"]
.getService(Ci.nsIContentPrefService2)
.removeByDomainAndName(
aURI.spec,
aType + ".request.remember",
aContext
);
}
},
};

View File

@ -1,64 +0,0 @@
/* -*- Mode: tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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";
const TOPIC_PRESENTATION_VIEW_READY = "presentation-view-ready";
const TOPIC_PRESENTATION_RECEIVER_LAUNCH = "presentation-receiver:launch";
const TOPIC_PRESENTATION_RECEIVER_LAUNCH_RESPONSE =
"presentation-receiver:launch:response";
// globals Services
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
function log(str) {
// dump("-*- PresentationView.js -*-: " + str + "\n");
}
let PresentationView = {
_id: null,
startup: function startup() {
// use hash as the ID of this top level window
this._id = window.location.hash.substr(1);
// Listen "presentation-receiver:launch" sent from
// PresentationRequestUIGlue.
Services.obs.addObserver(this, TOPIC_PRESENTATION_RECEIVER_LAUNCH);
// Notify PresentationView is ready.
Services.obs.notifyObservers(null, TOPIC_PRESENTATION_VIEW_READY, this._id);
},
stop: function stop() {
Services.obs.removeObserver(this, TOPIC_PRESENTATION_RECEIVER_LAUNCH);
},
observe: function observe(aSubject, aTopic, aData) {
log("Got observe: aTopic=" + aTopic);
let requestData = JSON.parse(aData);
if (this._id != requestData.windowId) {
return;
}
let browser = document.getElementById("content");
browser.setAttribute("mozpresentation", requestData.url);
try {
browser.loadURI(requestData.url);
Services.obs.notifyObservers(
browser,
TOPIC_PRESENTATION_RECEIVER_LAUNCH_RESPONSE,
JSON.stringify({ result: "success", requestId: requestData.requestId })
);
} catch (e) {
Services.obs.notifyObservers(
null,
TOPIC_PRESENTATION_RECEIVER_LAUNCH_RESPONSE,
JSON.stringify({ result: "error", reason: e.message })
);
}
},
};

View File

@ -1,15 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
<window id="presentation-window"
onload="PresentationView.startup();"
onunload="PresentationView.stop();"
windowtype="navigator:browser"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<browser id="content" type="content" src="about:blank" flex="1"/>
<script type="application/javascript" src="chrome://browser/content/PresentationView.js"/>
</window>

View File

@ -1,92 +0,0 @@
// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* 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";
ChromeUtils.defineModuleGetter(
this,
"Snackbars",
"resource://gre/modules/Snackbars.jsm"
);
var PrintHelper = {
onEvent: function(event, data, callback) {
let browser = BrowserApp.selectedBrowser;
switch (event) {
case "Print:PDF":
this.generatePDF(browser).then(
data => callback.onSuccess(data),
error => callback.onError(error)
);
break;
}
},
generatePDF: function(aBrowser) {
// Create the final destination file location
let fileName = ContentAreaUtils.getDefaultFileName(
aBrowser.contentTitle,
aBrowser.currentURI,
null,
null
);
fileName = fileName.trim() + ".pdf";
let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
file.append(fileName);
file.createUnique(file.NORMAL_FILE_TYPE, parseInt("666", 8));
let printSettings = Cc[
"@mozilla.org/gfx/printsettings-service;1"
].getService(Ci.nsIPrintSettingsService).newPrintSettings;
printSettings.printSilent = true;
printSettings.showPrintProgress = false;
printSettings.printBGImages = false;
printSettings.printBGColors = false;
printSettings.printToFile = true;
printSettings.toFileName = file.path;
printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
let webBrowserPrint = aBrowser.contentWindow.getInterface(
Ci.nsIWebBrowserPrint
);
return new Promise((resolve, reject) => {
webBrowserPrint.print(printSettings, {
onStateChange: function(webProgress, request, stateFlags, status) {
// We get two STATE_START calls, one for STATE_IS_DOCUMENT and one for STATE_IS_NETWORK
if (
stateFlags & Ci.nsIWebProgressListener.STATE_START &&
stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK
) {
// Let the user know something is happening. Generating the PDF can take some time.
Snackbars.show(
Strings.browser.GetStringFromName("alertPrintjobToast"),
Snackbars.LENGTH_LONG
);
}
// We get two STATE_STOP calls, one for STATE_IS_DOCUMENT and one for STATE_IS_NETWORK
if (
stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK
) {
if (Components.isSuccessCode(status)) {
// Send the details to Java
resolve({ file: file.path, title: fileName });
} else {
reject();
}
}
},
onProgressChange: function() {},
onLocationChange: function() {},
onStatusChange: function() {},
onSecurityChange: function() {},
onContentBlockingEvent: function() {},
});
});
},
};

View File

@ -1,342 +0,0 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* 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";
ChromeUtils.defineModuleGetter(
this,
"Snackbars",
"resource://gre/modules/Snackbars.jsm"
);
/* globals MAX_URI_LENGTH, MAX_TITLE_LENGTH */
var Reader = {
// These values should match those defined in BrowserContract.java.
STATUS_UNFETCHED: 0,
STATUS_FETCH_FAILED_TEMPORARY: 1,
STATUS_FETCH_FAILED_PERMANENT: 2,
STATUS_FETCH_FAILED_UNSUPPORTED_FORMAT: 3,
STATUS_FETCHED_ARTICLE: 4,
get _hasUsedToolbar() {
delete this._hasUsedToolbar;
return (this._hasUsedToolbar = Services.prefs.getBoolPref(
"reader.has_used_toolbar"
));
},
/**
* BackPressListener (listeners / ReaderView Ids).
*/
_backPressListeners: [],
_backPressViewIds: [],
/**
* Set a backPressListener for this tabId / ReaderView Id pair.
*/
_addBackPressListener: function(tabId, viewId, listener) {
this._backPressListeners[tabId] = listener;
this._backPressViewIds[viewId] = tabId;
},
/**
* Remove a backPressListener for this ReaderView Id.
*/
_removeBackPressListener: function(viewId) {
let tabId = this._backPressViewIds[viewId];
if (tabId != undefined) {
this._backPressListeners[tabId] = null;
delete this._backPressViewIds[viewId];
}
},
/**
* If the requested tab has a backPress listener, return its results, else false.
*/
onBackPress: function(tabId) {
let listener = this._backPressListeners[tabId];
return { handled: listener ? listener() : false };
},
onEvent: function Reader_onEvent(event, data, callback) {
switch (event) {
case "Reader:RemoveFromCache": {
ReaderMode.removeArticleFromCache(data.url).catch(e =>
Cu.reportError("Error removing article from cache: " + e)
);
break;
}
case "Reader:AddToCache": {
let tab = BrowserApp.getTabForId(data.tabID);
if (!tab) {
throw new Error(
"No tab for tabID = " +
data.tabID +
" when trying to save reader view article"
);
}
// If the article is coming from reader mode, we must have fetched it already.
this._getArticleData(tab.browser)
.then(article => {
ReaderMode.storeArticleInCache(article);
})
.catch(e => Cu.reportError("Error storing article in cache: " + e));
break;
}
}
},
receiveMessage: function(message) {
switch (message.name) {
case "Reader:ArticleGet":
this._getArticle(message.data.url).then(
article => {
// Make sure the target browser is still alive before trying to send data back.
if (message.target.messageManager) {
message.target.messageManager.sendAsyncMessage(
"Reader:ArticleData",
{ article: article }
);
}
},
e => {
if (e && e.newURL) {
message.target.loadURI(
"about:reader?url=" + encodeURIComponent(e.newURL)
);
}
}
);
break;
// On DropdownClosed in ReaderView, we cleanup / clear existing BackPressListener.
case "Reader:DropdownClosed": {
this._removeBackPressListener(message.data);
break;
}
// On DropdownOpened in ReaderView, we add BackPressListener to handle a subsequent BACK request.
case "Reader:DropdownOpened": {
let tabId = BrowserApp.selectedTab.id;
this._addBackPressListener(tabId, message.data, () => {
// User hit BACK key while ReaderView has the banner font-dropdown opened.
// Close it and return prevent-default.
if (message.target.messageManager) {
message.target.messageManager.sendAsyncMessage(
"Reader:CloseDropdown"
);
return true;
}
// We can assume ReaderView banner's font-dropdown doesn't need to be closed.
return false;
});
break;
}
case "Reader:FaviconRequest": {
GlobalEventDispatcher.sendRequestForResult({
type: "Reader:FaviconRequest",
url: message.data.url,
}).then(data => {
message.target.messageManager.sendAsyncMessage(
"Reader:FaviconReturn",
data
);
});
break;
}
case "Reader:SystemUIVisibility":
this._showSystemUI(message.data.visible);
break;
case "Reader:ToolbarHidden":
if (!this._hasUsedToolbar) {
Snackbars.show(
Strings.browser.GetStringFromName("readerMode.toolbarTip"),
Snackbars.LENGTH_LONG
);
Services.prefs.setBoolPref("reader.has_used_toolbar", true);
this._hasUsedToolbar = true;
}
break;
case "Reader:UpdateReaderButton": {
let tab = BrowserApp.getTabForBrowser(message.target);
tab.browser.isArticle = message.data.isArticle;
this.updatePageAction(tab);
break;
}
}
},
pageAction: {
readerModeCallback: function(browser) {
let url = browser.currentURI.spec;
if (url.startsWith("about:reader")) {
UITelemetry.addEvent("action.1", "button", null, "reader_exit");
} else {
UITelemetry.addEvent("action.1", "button", null, "reader_enter");
}
browser.messageManager.sendAsyncMessage("Reader:ToggleReaderMode");
},
},
updatePageAction: function(tab) {
if (!tab.getActive()) {
return;
}
if (this.pageAction.id) {
PageActions.remove(this.pageAction.id);
delete this.pageAction.id;
}
let showPageAction = (icon, title, useTint) => {
this.pageAction.id = PageActions.add({
icon: icon,
title: title,
clickCallback: () => this.pageAction.readerModeCallback(browser),
important: true,
useTint: useTint,
});
};
let browser = tab.browser;
if (browser.currentURI.spec.startsWith("about:reader")) {
showPageAction(
"drawable://ic_readermode_on",
Strings.reader.GetStringFromName("readerView.close"),
false
);
// Only start a reader session if the viewer is in the foreground. We do
// not track background reader viewers.
UITelemetry.startSession("reader.1", null);
return;
}
// not in ReaderMode, to make sure System UI is visible, not dimmed.
this._showSystemUI(true);
// Only stop a reader session if the foreground viewer is not visible.
UITelemetry.stopSession("reader.1", "", null);
if (browser.isArticle) {
showPageAction(
"drawable://ic_readermode",
Strings.reader.GetStringFromName("readerView.enter"),
true
);
UITelemetry.addEvent("show.1", "button", null, "reader_available");
this._sendMmaEvent("reader_available");
} else {
UITelemetry.addEvent("show.1", "button", null, "reader_unavailable");
}
},
_sendMmaEvent: function(event) {
WindowEventDispatcher.sendRequest({
type: "Mma:" + event,
});
},
_showSystemUI: function(visibility) {
WindowEventDispatcher.sendRequest({
type: "SystemUI:Visibility",
visible: visibility,
});
},
/**
* Gets an article for a given URL. This method will download and parse a document
* if it does not find the article in the cache.
*
* @param url The article URL.
* @return {Promise}
* @resolves JS object representing the article, or null if no article is found.
*/
async _getArticle(url) {
// First try to find a parsed article in the cache.
let article = await ReaderMode.getArticleFromCache(url);
if (article) {
return article;
}
// Article hasn't been found in the cache, we need to
// download the page and parse the article out of it.
return ReaderMode.downloadAndParseDocument(url).catch(e => {
if (e && e.newURL) {
// Pass up the error so we can navigate the browser in question to the new URL:
throw e;
}
Cu.reportError("Error downloading and parsing document: " + e);
return null;
});
},
_getArticleData: function(browser) {
return new Promise((resolve, reject) => {
if (browser == null) {
reject("_getArticleData needs valid browser");
}
let mm = browser.messageManager;
let listener = message => {
mm.removeMessageListener("Reader:StoredArticleData", listener);
resolve(message.data.article);
};
mm.addMessageListener("Reader:StoredArticleData", listener);
mm.sendAsyncMessage("Reader:GetStoredArticleData");
});
},
/**
* Migrates old indexedDB reader mode cache to new JSON cache.
*/
async migrateCache() {
let cacheDB = await new Promise((resolve, reject) => {
let request = window.indexedDB.open("about:reader", 1);
request.onsuccess = event => resolve(event.target.result);
request.onerror = event => reject(request.error);
// If there is no DB to migrate, don't do anything.
request.onupgradeneeded = event => resolve(null);
});
if (!cacheDB) {
return;
}
let articles = await new Promise((resolve, reject) => {
let articles = [];
let transaction = cacheDB.transaction(cacheDB.objectStoreNames);
let store = transaction.objectStore(cacheDB.objectStoreNames[0]);
let request = store.openCursor();
request.onsuccess = event => {
let cursor = event.target.result;
if (!cursor) {
resolve(articles);
} else {
articles.push(cursor.value);
cursor.continue();
}
};
request.onerror = event => reject(request.error);
});
for (let article of articles) {
await ReaderMode.storeArticleInCache(article);
}
// Delete the database.
window.indexedDB.deleteDatabase("about:reader");
},
};

View File

@ -1,406 +0,0 @@
// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* 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/. */
/* globals DebuggerServer */
"use strict";
XPCOMUtils.defineLazyGetter(this, "require", () => {
let { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
return require;
});
XPCOMUtils.defineLazyGetter(this, "DebuggerServer", () => {
let { DebuggerServer } = require("devtools/server/debugger-server");
return DebuggerServer;
});
XPCOMUtils.defineLazyGetter(this, "SocketListener", () => {
let { SocketListener } = require("devtools/shared/security/socket");
return SocketListener;
});
var RemoteDebugger = {
init(aWindow) {
this._windowType = "navigator:browser";
USBRemoteDebugger.init();
WiFiRemoteDebugger.init();
const listener = event => {
if (event.target !== aWindow) {
return;
}
const newType =
event.type === "activate" ? "navigator:browser" : "navigator:geckoview";
if (this._windowType === newType) {
return;
}
this._windowType = newType;
if (this.isAnyEnabled) {
this.initServer();
}
};
aWindow.addEventListener("activate", listener, { mozSystemGroup: true });
aWindow.addEventListener("deactivate", listener, { mozSystemGroup: true });
},
get isAnyEnabled() {
return USBRemoteDebugger.isEnabled || WiFiRemoteDebugger.isEnabled;
},
/**
* Prompt the user to accept or decline the incoming connection.
*
* @param session object
* The session object will contain at least the following fields:
* {
* authentication,
* client: {
* host,
* port
* },
* server: {
* host,
* port
* }
* }
* Specific authentication modes may include additional fields. Check
* the different |allowConnection| methods in
* devtools/shared/security/auth.js.
* @return An AuthenticationResult value.
* A promise that will be resolved to the above is also allowed.
*/
allowConnection(session) {
if (this._promptingForAllow) {
// Don't stack connection prompts if one is already open
return DebuggerServer.AuthenticationResult.DENY;
}
if (!session.server.port) {
this._promptingForAllow = this._promptForUSB(session);
} else {
this._promptingForAllow = this._promptForTCP(session);
}
this._promptingForAllow.then(() => (this._promptingForAllow = null));
return this._promptingForAllow;
},
_promptForUSB(session) {
if (session.authentication !== "PROMPT") {
// This dialog is not prepared for any other authentication method at
// this time.
return DebuggerServer.AuthenticationResult.DENY;
}
return new Promise(resolve => {
let title = Strings.browser.GetStringFromName(
"remoteIncomingPromptTitle"
);
let msg = Strings.browser.GetStringFromName("remoteIncomingPromptUSB");
let allow = Strings.browser.GetStringFromName(
"remoteIncomingPromptAllow"
);
let deny = Strings.browser.GetStringFromName("remoteIncomingPromptDeny");
// Make prompt. Note: button order is in reverse.
let prompt = new Prompt({
window: null,
hint: "remotedebug",
title: title,
message: msg,
buttons: [allow, deny],
priority: 1,
});
prompt.show(data => {
let result = data.button;
if (result === 0) {
resolve(DebuggerServer.AuthenticationResult.ALLOW);
} else {
resolve(DebuggerServer.AuthenticationResult.DENY);
}
});
});
},
_promptForTCP(session) {
if (session.authentication !== "OOB_CERT" || !session.client.cert) {
// This dialog is not prepared for any other authentication method at
// this time.
return DebuggerServer.AuthenticationResult.DENY;
}
return new Promise(resolve => {
let title = Strings.browser.GetStringFromName(
"remoteIncomingPromptTitle"
);
let msg = Strings.browser.formatStringFromName(
"remoteIncomingPromptTCP",
[session.client.host, session.client.port]
);
let scan = Strings.browser.GetStringFromName("remoteIncomingPromptScan");
let scanAndRemember = Strings.browser.GetStringFromName(
"remoteIncomingPromptScanAndRemember"
);
let deny = Strings.browser.GetStringFromName("remoteIncomingPromptDeny");
// Make prompt. Note: button order is in reverse.
let prompt = new Prompt({
window: null,
hint: "remotedebug",
title: title,
message: msg,
buttons: [scan, scanAndRemember, deny],
priority: 1,
});
prompt.show(data => {
let result = data.button;
if (result === 0) {
resolve(DebuggerServer.AuthenticationResult.ALLOW);
} else if (result === 1) {
resolve(DebuggerServer.AuthenticationResult.ALLOW_PERSIST);
} else {
resolve(DebuggerServer.AuthenticationResult.DENY);
}
});
});
},
/**
* During OOB_CERT authentication, the user must transfer some data through
* some out of band mechanism from the client to the server to authenticate
* the devices.
*
* This implementation instructs Fennec to invoke a QR decoder and return the
* the data it contains back here.
*
* @return An object containing:
* * sha256: hash(ClientCert)
* * k : K(random 128-bit number)
* A promise that will be resolved to the above is also allowed.
*/
receiveOOB() {
if (this._receivingOOB) {
return this._receivingOOB;
}
this._receivingOOB = WindowEventDispatcher.sendRequestForResult({
type: "DevToolsAuth:Scan",
}).then(
data => {
return JSON.parse(data);
},
() => {
let title = Strings.browser.GetStringFromName(
"remoteQRScanFailedPromptTitle"
);
let msg = Strings.browser.GetStringFromName(
"remoteQRScanFailedPromptMessage"
);
let ok = Strings.browser.GetStringFromName(
"remoteQRScanFailedPromptOK"
);
let prompt = new Prompt({
window: null,
hint: "remotedebug",
title: title,
message: msg,
buttons: [ok],
priority: 1,
});
prompt.show();
}
);
this._receivingOOB.then(() => (this._receivingOOB = null));
return this._receivingOOB;
},
initServer: function() {
DebuggerServer.init();
// Add browser and Fennec specific actors
DebuggerServer.registerAllActors();
const {
createRootActor,
} = require("resource://gre/modules/dbg-browser-actors.js");
DebuggerServer.setRootActor(createRootActor);
// Allow debugging of chrome for any process
DebuggerServer.allowChromeProcess = true;
DebuggerServer.chromeWindowType = this._windowType;
// Force the Server to stay alive even if there are no connections at the moment.
DebuggerServer.keepAlive = true;
},
};
RemoteDebugger.allowConnection = RemoteDebugger.allowConnection.bind(
RemoteDebugger
);
RemoteDebugger.receiveOOB = RemoteDebugger.receiveOOB.bind(RemoteDebugger);
var USBRemoteDebugger = {
init() {
Services.prefs.addObserver("devtools.", this);
if (this.isEnabled) {
this.start();
}
},
observe(subject, topic, data) {
if (topic != "nsPref:changed") {
return;
}
switch (data) {
case "devtools.remote.usb.enabled":
Services.prefs.setBoolPref(
"devtools.debugger.remote-enabled",
RemoteDebugger.isAnyEnabled
);
if (this.isEnabled) {
this.start();
} else {
this.stop();
}
break;
case "devtools.debugger.remote-port":
case "devtools.debugger.unix-domain-socket":
if (this.isEnabled) {
this.stop();
this.start();
}
break;
}
},
get isEnabled() {
return Services.prefs.getBoolPref("devtools.remote.usb.enabled");
},
start: function() {
if (this._listener) {
return;
}
RemoteDebugger.initServer();
const portOrPath =
Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
Services.prefs.getIntPref("devtools.debugger.remote-port");
try {
dump("Starting USB debugger on " + portOrPath);
const AuthenticatorType = DebuggerServer.Authenticators.get("PROMPT");
const authenticator = new AuthenticatorType.Server();
authenticator.allowConnection = RemoteDebugger.allowConnection;
const socketOptions = { authenticator, portOrPath };
this._listener = new SocketListener(DebuggerServer, socketOptions);
this._listener.open();
} catch (e) {
dump("Unable to start USB debugger server: " + e);
}
},
stop: function() {
if (!this._listener) {
return;
}
try {
this._listener.close();
this._listener = null;
} catch (e) {
dump("Unable to stop USB debugger server: " + e);
}
},
};
var WiFiRemoteDebugger = {
init() {
Services.prefs.addObserver("devtools.", this);
if (this.isEnabled) {
this.start();
}
},
observe(subject, topic, data) {
if (topic != "nsPref:changed") {
return;
}
switch (data) {
case "devtools.remote.wifi.enabled":
Services.prefs.setBoolPref(
"devtools.debugger.remote-enabled",
RemoteDebugger.isAnyEnabled
);
// Allow remote debugging on non-local interfaces when WiFi debug is
// enabled
// TODO: Bug 1034411: Lock down to WiFi interface only
Services.prefs.setBoolPref(
"devtools.debugger.force-local",
!this.isEnabled
);
if (this.isEnabled) {
this.start();
} else {
this.stop();
}
break;
}
},
get isEnabled() {
return Services.prefs.getBoolPref("devtools.remote.wifi.enabled");
},
start: function() {
if (this._listener) {
return;
}
RemoteDebugger.initServer();
try {
dump("Starting WiFi debugger");
const AuthenticatorType = DebuggerServer.Authenticators.get("OOB_CERT");
const authenticator = new AuthenticatorType.Server();
authenticator.allowConnection = RemoteDebugger.allowConnection;
authenticator.receiveOOB = RemoteDebugger.receiveOOB;
const socketOptions = {
authenticator,
discoverable: true,
encryption: true,
portOrPath: -1,
};
this._listener = new SocketListener(DebuggerServer, socketOptions);
this._listener.open();
let port = this._listener.port;
dump("Started WiFi debugger on " + port);
} catch (e) {
dump("Unable to start WiFi debugger server: " + e);
}
},
stop: function() {
if (!this._listener) {
return;
}
try {
this._listener.close();
this._listener = null;
} catch (e) {
dump("Unable to stop WiFi debugger server: " + e);
}
},
};

View File

@ -1,147 +0,0 @@
/* 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/. */
const {EventDispatcher} = ChromeUtils.import("resource://gre/modules/Messaging.jsm");
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
function init() {
// Include the build date and a warning about Telemetry
// if this is an "a#" (nightly or aurora) build
#expand const version = "__MOZ_APP_VERSION_DISPLAY__";
if (/a\d+$/.test(version)) {
let buildID = Services.appinfo.appBuildID;
let buildDate = buildID.slice(0, 4) + "-" + buildID.slice(4, 6) + "-" + buildID.slice(6, 8);
let br = document.createElement("br");
let versionPara = document.getElementById("version");
versionPara.appendChild(br);
let date = document.createTextNode("(" + buildDate + ")");
versionPara.appendChild(date);
document.getElementById("telemetry").hidden = false;
}
// Include the Distribution information if available
try {
let distroId = Services.prefs.getCharPref("distribution.id");
if (distroId) {
let distroVersion = Services.prefs.getCharPref("distribution.version");
let distroIdField = document.getElementById("distributionID");
distroIdField.textContent = distroId + " - " + distroVersion;
distroIdField.hidden = false;
let distroAbout = Services.prefs.getStringPref("distribution.about");
let distroField = document.getElementById("distributionAbout");
distroField.textContent = distroAbout;
distroField.hidden = false;
}
} catch (e) {
// Pref is unset
}
// get URLs from prefs
try {
let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
let links = [
{id: "releaseNotesURL", pref: "app.releaseNotesURL"},
{id: "supportURL", pref: "app.supportURL"},
{id: "faqURL", pref: "app.faqURL"},
{id: "privacyURL", pref: "app.privacyURL"},
{id: "creditsURL", pref: "app.creditsURL"},
];
links.forEach(function(link) {
let url = formatter.formatURLPref(link.pref);
let element = document.getElementById(link.id);
if (element) {
element.setAttribute("href", url);
}
});
} catch (ex) {}
#ifdef MOZ_UPDATER
function expectUpdateResult() {
EventDispatcher.instance.registerListener(function listener(event, data, callback) {
EventDispatcher.instance.unregisterListener(listener, event);
showUpdateMessage(data.result);
}, "Update:CheckResult");
}
function checkForUpdates() {
showCheckingMessage();
expectUpdateResult();
EventDispatcher.instance.sendRequest({ type: "Update:Check" });
}
function downloadUpdate() {
expectUpdateResult();
EventDispatcher.instance.sendRequest({ type: "Update:Download" });
}
function installUpdate() {
showCheckAction();
EventDispatcher.instance.sendRequest({ type: "Update:Install" });
}
let updateLink = document.getElementById("updateLink");
let checkingSpan = document.getElementById("update-message-checking");
let noneSpan = document.getElementById("update-message-none");
let foundSpan = document.getElementById("update-message-found");
let downloadingSpan = document.getElementById("update-message-downloading");
let downloadedSpan = document.getElementById("update-message-downloaded");
updateLink.onclick = checkForUpdates;
foundSpan.onclick = downloadUpdate;
downloadedSpan.onclick = installUpdate;
function showCheckAction() {
checkingSpan.style.display = "none";
noneSpan.style.display = "none";
foundSpan.style.display = "none";
downloadingSpan.style.display = "none";
downloadedSpan.style.display = "none";
updateLink.style.display = "block";
}
function showCheckingMessage() {
updateLink.style.display = "none";
noneSpan.style.display = "none";
foundSpan.style.display = "none";
downloadingSpan.style.display = "none";
downloadedSpan.style.display = "none";
checkingSpan.style.display = "block";
}
function showUpdateMessage(aResult) {
updateLink.style.display = "none";
checkingSpan.style.display = "none";
noneSpan.style.display = "none";
foundSpan.style.display = "none";
downloadingSpan.style.display = "none";
downloadedSpan.style.display = "none";
// the aResult values come from mobile/android/base/UpdateServiceHelper.java
switch (aResult) {
case "NOT_AVAILABLE":
noneSpan.style.display = "block";
setTimeout(showCheckAction, 2000);
break;
case "AVAILABLE":
foundSpan.style.display = "block";
break;
case "DOWNLOADING":
downloadingSpan.style.display = "block";
expectUpdateResult();
break;
case "DOWNLOADED":
downloadedSpan.style.display = "block";
break;
}
}
#endif
}
document.addEventListener("DOMContentLoaded", init);

View File

@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % fennecDTD SYSTEM "chrome://browser/locale/about.dtd">
%fennecDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=480; initial-scale=.6667; user-scalable=no"/>
<title>&aboutPage.title;</title>
<link rel="stylesheet" href="chrome://browser/skin/aboutPage.css" type="text/css"/>
<link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
</head>
<body dir="&locale.dir;">
<div id="header" dir="ltr">
<div id="wordmark"></div>
#expand <p id="version">__MOZ_APP_VERSION_DISPLAY__</p>
</div>
<div id="banner">
<div id="logo"/>
#ifdef MOZ_UPDATER
<div id="updateBox">
<a id="updateLink" href="">&aboutPage.checkForUpdates.link;</a>
<span id="update-message-checking">&aboutPage.checkForUpdates.checking;</span>
<span id="update-message-none">&aboutPage.checkForUpdates.none;</span>
<span id="update-message-found">&aboutPage.checkForUpdates.available2;</span>
<span id="update-message-downloading">&aboutPage.checkForUpdates.downloading;</span>
<span id="update-message-downloaded">&aboutPage.checkForUpdates.downloaded2;</span>
</div>
#endif
<div id="messages">
<p id="distributionAbout" hidden="true"/>
<p id="distributionID" hidden="true"/>
<p id="telemetry" hidden="true">
&aboutPage.warningVersion;
</p>
</div>
</div>
<ul id="aboutLinks">
<div class="top-border"></div>
<li><a id="faqURL">&aboutPage.faq.label;</a></li>
<li><a id="supportURL">&aboutPage.support.label;</a></li>
<li><a id="privacyURL">&aboutPage.privacyPolicy.label;</a></li>
<li><a href="about:rights">&aboutPage.rights.label;</a></li>
#ifndef NIGHTLY_BUILD
#ifndef FENNEC_NIGHTLY
<li><a id="releaseNotesURL">&aboutPage.relNotes.label;</a></li>
#endif
#endif
<li><a id="creditsURL">&aboutPage.credits.label;</a></li>
<li><a href="about:license">&aboutPage.license.label;</a></li>
<div class="bottom-border"></div>
</ul>
#ifdef RELEASE_OR_BETA
#ifndef FENNEC_NIGHTLY
<div id="aboutDetails">
<p>&aboutPage.logoTrademark;</p>
</div>
#endif
#endif
<script type="application/javascript" src="chrome://browser/content/about.js" />
</body>
</html>

View File

@ -1,377 +0,0 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* 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/. */
/**
* Wrap a remote fxa-content-server.
*
* An about:accounts tab loads and displays an fxa-content-server page,
* depending on the current Android Account status and an optional 'action'
* parameter.
*
* We show a spinner while the remote iframe is loading. We expect the
* WebChannel message listening to the fxa-content-server to send this tab's
* <browser>'s messageManager a LOADED message when the remote iframe provides
* the WebChannel LOADED message. See the messageManager registration and the
* |loadedDeferred| promise. This loosely couples the WebChannel implementation
* and about:accounts! (We need this coupling in order to distinguish
* WebChannel LOADED messages produced by multiple about:accounts tabs.)
*
* We capture error conditions by accessing the inner nsIWebNavigation of the
* iframe directly.
*/
"use strict";
const { Accounts } = ChromeUtils.import("resource://gre/modules/Accounts.jsm");
const { PromiseUtils } = ChromeUtils.import(
"resource://gre/modules/PromiseUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const ACTION_URL_PARAM = "action";
const COMMAND_LOADED = "fxaccounts:loaded";
const log = ChromeUtils.import(
"resource://gre/modules/AndroidLog.jsm",
{}
).AndroidLog.bind("FxAccounts");
XPCOMUtils.defineLazyServiceGetter(
this,
"ParentalControls",
"@mozilla.org/parental-controls-service;1",
"nsIParentalControlsService"
);
// Shows the toplevel element with |id| to be shown - all other top-level
// elements are hidden.
// If |id| is 'spinner', then 'remote' is also shown, with opacity 0.
function show(id) {
let allTop = document.querySelectorAll(".toplevel");
for (let elt of allTop) {
if (elt.getAttribute("id") == id) {
elt.style.display = "block";
} else {
elt.style.display = "none";
}
}
if (id == "spinner") {
document.getElementById("remote").style.display = "block";
document.getElementById("remote").style.opacity = 0;
}
}
// Each time we try to load the remote <iframe>, loadedDeferred is replaced. It
// is resolved by a LOADED message, and rejected by a failure to load.
var loadedDeferred = null;
// We have a new load starting. Replace the existing promise with a new one,
// and queue up the transition to remote content.
function deferTransitionToRemoteAfterLoaded() {
log.d("Waiting for LOADED message.");
loadedDeferred = PromiseUtils.defer();
loadedDeferred.promise
.then(() => {
log.d("Got LOADED message!");
document.getElementById("remote").style.opacity = 0;
show("remote");
document.getElementById("remote").style.opacity = 1;
})
.catch(e => {
log.w("Did not get LOADED message: " + e.toString());
});
}
function handleLoadedMessage(message) {
loadedDeferred.resolve();
}
var wrapper = {
iframe: null,
url: null,
init: function(url) {
this.url = url;
deferTransitionToRemoteAfterLoaded();
let iframe = document.getElementById("remote");
this.iframe = iframe;
let docShell = this.iframe.frameLoader.docShell;
docShell.QueryInterface(Ci.nsIWebProgress);
docShell.addProgressListener(
this.iframeListener,
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT |
Ci.nsIWebProgress.NOTIFY_LOCATION
);
// Set the iframe's location with loadURI/LOAD_FLAGS_BYPASS_HISTORY to
// avoid having a new history entry being added.
let webNav = iframe.frameLoader.docShell.QueryInterface(
Ci.nsIWebNavigation
);
let loadURIOptions = {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
};
webNav.loadURI(url, loadURIOptions);
},
retry: function() {
deferTransitionToRemoteAfterLoaded();
let webNav = this.iframe.frameLoader.docShell.QueryInterface(
Ci.nsIWebNavigation
);
let loadURIOptions = {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
};
webNav.loadURI(this.url, loadURIOptions);
},
iframeListener: {
QueryInterface: ChromeUtils.generateQI([
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference,
]),
onStateChange: function(aWebProgress, aRequest, aState, aStatus) {
let failure = false;
// Captive portals sometimes redirect users
if (aState & Ci.nsIWebProgressListener.STATE_REDIRECTING) {
failure = true;
} else if (aState & Ci.nsIWebProgressListener.STATE_STOP) {
if (aRequest instanceof Ci.nsIHttpChannel) {
try {
failure = aRequest.responseStatus != 200;
} catch (e) {
failure = aStatus != Cr.NS_OK;
}
}
}
// Calling cancel() will raise some OnStateChange notifications by itself,
// so avoid doing that more than once
if (failure && aStatus != Cr.NS_BINDING_ABORTED) {
aRequest.cancel(Cr.NS_BINDING_ABORTED);
// Since after a promise is fulfilled, subsequent fulfillments are
// treated as no-ops, we don't care that we might see multiple failures
// due to multiple listener callbacks. (It's not easy to extract this
// from the Promises spec, but it is widely quoted. Start with
// http://stackoverflow.com/a/18218542.)
loadedDeferred.reject(new Error("Failed in onStateChange!"));
show("networkError");
}
},
onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
if (
aRequest &&
aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE
) {
aRequest.cancel(Cr.NS_BINDING_ABORTED);
// As above, we're not concerned by multiple listener callbacks.
loadedDeferred.reject(new Error("Failed in onLocationChange!"));
show("networkError");
}
},
},
};
function retry() {
log.i("Retrying.");
show("spinner");
wrapper.retry();
}
function openPrefs() {
log.i("Opening Sync preferences.");
// If an Android Account exists, this will open the Status Activity.
// Otherwise, it will begin the Get Started flow. This should only be shown
// when an Account actually exists.
Accounts.launchSetup();
}
function getURLForAction(action, urlParams) {
let url = Services.urlFormatter.formatURLPref(
"identity.fxaccounts.remote.webchannel.uri"
);
url = url + (url.endsWith("/") ? "" : "/") + action;
const CONTEXT = "fx_fennec_v1";
// The only service managed by Fennec, to date, is Firefox Sync.
const SERVICE = "sync";
urlParams = urlParams || new URLSearchParams("");
urlParams.set("service", SERVICE);
urlParams.set("context", CONTEXT);
// Ideally we'd just merge urlParams with new URL(url).searchParams, but our
// URLSearchParams implementation doesn't support iteration (bug 1085284).
let urlParamStr = urlParams.toString();
if (urlParamStr) {
url += (url.includes("?") ? "&" : "?") + urlParamStr;
}
return url;
}
function updateDisplayedEmail(user) {
let emailDiv = document.getElementById("email");
if (emailDiv && user) {
emailDiv.textContent = user.email;
}
}
function init() {
// Test for restrictions before getFirefoxAccount(), since that will fail if
// we are restricted.
if (!ParentalControls.isAllowed(ParentalControls.MODIFY_ACCOUNTS)) {
// It's better to log and show an error message than to invite user
// confusion by removing about:accounts entirely. That is, if the user is
// restricted, this way they'll discover as much and may be able to get
// out of their restricted profile. If we remove about:accounts entirely,
// it will look like Fennec is buggy, and the user will be very confused.
log.e(
"This profile cannot connect to Firefox Accounts: showing restricted error."
);
show("restrictedError");
return;
}
Accounts.getFirefoxAccount()
.then(user => {
// It's possible for the window to start closing before getting the user
// completes. Tests in particular can cause this.
if (window.closed) {
return;
}
updateDisplayedEmail(user);
// Ideally we'd use new URL(document.URL).searchParams, but for about: URIs,
// searchParams is empty.
let urlParams = new URLSearchParams(document.URL.split("?")[1] || "");
let action = urlParams.get(ACTION_URL_PARAM);
urlParams.delete(ACTION_URL_PARAM);
switch (action) {
case "signup":
if (user) {
// Asking to sign-up when already signed in just shows prefs.
show("prefs");
} else {
show("spinner");
wrapper.init(getURLForAction("signup", urlParams));
}
break;
case "signin":
if (user) {
// Asking to sign-in when already signed in just shows prefs.
show("prefs");
} else {
show("spinner");
wrapper.init(getURLForAction("signin", urlParams));
}
break;
case "force_auth":
if (user) {
show("spinner");
urlParams.set("email", user.email); // In future, pin using the UID.
wrapper.init(getURLForAction("force_auth", urlParams));
} else {
show("spinner");
wrapper.init(getURLForAction("signup", urlParams));
}
break;
case "manage":
if (user) {
show("spinner");
urlParams.set("email", user.email); // In future, pin using the UID.
wrapper.init(getURLForAction("settings", urlParams));
} else {
show("spinner");
wrapper.init(getURLForAction("signup", urlParams));
}
break;
case "avatar":
if (user) {
show("spinner");
urlParams.set("email", user.email); // In future, pin using the UID.
wrapper.init(getURLForAction("settings/avatar/change", urlParams));
} else {
show("spinner");
wrapper.init(getURLForAction("signup", urlParams));
}
break;
default:
// Unrecognized or no action specified.
if (action) {
log.w("Ignoring unrecognized action: " + action);
}
if (user) {
show("prefs");
} else {
show("spinner");
wrapper.init(getURLForAction("signup", urlParams));
}
break;
}
})
.catch(e => {
log.e("Failed to get the signed in user: " + e.toString());
});
}
document.addEventListener(
"DOMContentLoaded",
function() {
init();
var buttonRetry = document.getElementById("buttonRetry");
buttonRetry.addEventListener("click", retry);
var buttonOpenPrefs = document.getElementById("buttonOpenPrefs");
buttonOpenPrefs.addEventListener("click", openPrefs);
},
{ capture: true, once: true }
);
// This window is contained in a XUL <browser> element. Return the
// messageManager of that <browser> element, or null.
function getBrowserMessageManager() {
let browser = window.docShell.rootTreeItem.domWindow.BrowserApp.getBrowserForDocument(
document
);
if (browser) {
return browser.messageManager;
}
return null;
}
// Add a single listener for 'loaded' messages from the iframe in this
// <browser>. These 'loaded' messages are ferried from the WebChannel to just
// this <browser>.
var mm = getBrowserMessageManager();
if (mm) {
mm.addMessageListener(COMMAND_LOADED, handleLoadedMessage);
} else {
log.e("No messageManager, not listening for LOADED message!");
}
window.addEventListener("unload", function(event) {
try {
let mm = getBrowserMessageManager();
if (mm) {
mm.removeMessageListener(COMMAND_LOADED, handleLoadedMessage);
}
} catch (e) {
// This could fail if the page is being torn down, the tab is being
// destroyed, etc.
log.w("Not removing listener for LOADED message: " + e.toString());
}
});

View File

@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
%globalDTD;
<!ENTITY % aboutDTD SYSTEM "chrome://browser/locale/aboutAccounts.dtd">
%aboutDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml" dir="&locale.dir;">
<head>
<title>Firefox Sync</title>
<meta name="viewport" content="width=device-width; user-scalable=0" />
<link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
<link rel="stylesheet" href="chrome://browser/skin/spinner.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/aboutAccounts.css" type="text/css"/>
</head>
<body>
<div id="spinner" class="toplevel">
<div class="container flex-column">
<!-- Empty text-container for spacing. -->
<div class="text-container flex-column" />
<div class="mui-refresh-main">
<div class="mui-refresh-wrapper">
<div class="mui-spinner-wrapper">
<div class="mui-spinner-main">
<div class="mui-spinner-left">
<div class="mui-half-circle-left" />
</div>
<div class="mui-spinner-right">
<div class="mui-half-circle-right" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<iframe mozframetype="content" id="remote" class="toplevel" />
<div id="prefs" class="toplevel">
<div class="container flex-column">
<div class="text-container flex-column">
<div class="text">&aboutAccounts.connected.title;</div>
<div class="hint">&aboutAccounts.connected.description;</div>
<div id="email" class="hint"></div>
</div>
<a id="buttonOpenPrefs" tabindex="0" href="#">&aboutAccounts.syncPreferences.label;</a>
</div>
</div>
<div id="networkError" class="toplevel">
<div class="container flex-column">
<div class="text-container flex-column">
<div class="text">&aboutAccounts.noConnection.title;</div>
</div>
<div class="button-row">
<button id="buttonRetry" class="button" tabindex="1">&aboutAccounts.retry.label;</button>
</div>
</div>
</div>
<div id="restrictedError" class="toplevel">
<div class="container flex-column">
<div class="text-container flex-column">
<div class="text">&aboutAccounts.restrictedError.title;</div>
<div class="hint">&aboutAccounts.restrictedError.description;</div>
</div>
</div>
</div>
<script type="application/javascript" src="chrome://browser/content/aboutAccounts.js"></script>
</body>
</html>

View File

@ -1,897 +0,0 @@
/* 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 gChromeWin */
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { AddonManager } = ChromeUtils.import(
"resource://gre/modules/AddonManager.jsm"
);
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { EventDispatcher } = ChromeUtils.import(
"resource://gre/modules/Messaging.jsm"
);
const AMO_ICON = "chrome://browser/skin/images/amo-logo.png";
const UPDATE_INDICATOR = "chrome://browser/skin/images/extension-update.svg";
var gStringBundle = Services.strings.createBundle(
"chrome://browser/locale/aboutAddons.properties"
);
XPCOMUtils.defineLazyGetter(window, "gChromeWin", function() {
return window.docShell.rootTreeItem.domWindow;
});
ChromeUtils.defineModuleGetter(
window,
"Preferences",
"resource://gre/modules/Preferences.jsm"
);
var ContextMenus = {
target: null,
init: function() {
document.addEventListener("contextmenu", this);
document
.getElementById("contextmenu-enable")
.addEventListener("click", ContextMenus.enable.bind(this));
document
.getElementById("contextmenu-disable")
.addEventListener("click", ContextMenus.disable.bind(this));
document
.getElementById("contextmenu-uninstall")
.addEventListener("click", ContextMenus.uninstall.bind(this));
// XXX - Hack to fix bug 985867 for now
document.addEventListener("touchstart", function() {});
},
handleEvent: function(event) {
// store the target of context menu events so that we know which app to act on
this.target = event.target;
while (!this.target.hasAttribute("contextmenu")) {
this.target = this.target.parentNode;
}
if (!this.target) {
document
.getElementById("contextmenu-enable")
.setAttribute("hidden", "true");
document
.getElementById("contextmenu-disable")
.setAttribute("hidden", "true");
document
.getElementById("contextmenu-uninstall")
.setAttribute("hidden", "true");
return;
}
let addon = this.target.addon;
if (addon.scope == AddonManager.SCOPE_APPLICATION) {
document
.getElementById("contextmenu-uninstall")
.setAttribute("hidden", "true");
} else {
document
.getElementById("contextmenu-uninstall")
.removeAttribute("hidden");
}
// Hide the enable/disable context menu items if the add-on was disabled by
// Firefox (e.g. unsigned or blocklisted add-on).
if (addon.appDisabled) {
document
.getElementById("contextmenu-enable")
.setAttribute("hidden", "true");
document
.getElementById("contextmenu-disable")
.setAttribute("hidden", "true");
return;
}
let enabled = this.target.getAttribute("isDisabled") != "true";
if (enabled) {
document
.getElementById("contextmenu-enable")
.setAttribute("hidden", "true");
document.getElementById("contextmenu-disable").removeAttribute("hidden");
} else {
document.getElementById("contextmenu-enable").removeAttribute("hidden");
document
.getElementById("contextmenu-disable")
.setAttribute("hidden", "true");
}
},
enable: function(event) {
Addons.setEnabled(true, this.target.addon);
this.target = null;
},
disable: function(event) {
Addons.setEnabled(false, this.target.addon);
this.target = null;
},
uninstall: function(event) {
Addons.uninstall(this.target.addon);
this.target = null;
},
};
function sendEMPong() {
Services.obs.notifyObservers(window, "EM-pong");
}
async function init() {
window.addEventListener("popstate", onPopState);
AddonManager.addInstallListener(Addons);
AddonManager.addAddonListener(Addons);
await Addons.init();
showAddons();
ContextMenus.init();
Services.obs.addObserver(sendEMPong, "EM-ping");
// The addons list has been loaded and rendered, send a notification
// if the openOptionsPage is waiting to be able to select an addon details page.
Services.obs.notifyObservers(window, "EM-loaded");
}
function uninit() {
AddonManager.removeInstallListener(Addons);
AddonManager.removeAddonListener(Addons);
Services.obs.removeObserver(sendEMPong, "EM-ping");
}
function openLink(url) {
let BrowserApp = gChromeWin.BrowserApp;
BrowserApp.addTab(url, {
selected: true,
parentId: BrowserApp.selectedTab.id,
});
}
function openOptionsInTab(url) {
let BrowserApp = gChromeWin.BrowserApp;
BrowserApp.selectOrAddTab(url, {
startsWith: true,
selected: true,
parentId: BrowserApp.selectedTab.id,
});
}
function onPopState(aEvent) {
// Called when back/forward is used to change the state of the page
if (aEvent.state) {
// Show the detail page for an addon
const listItem = Addons._getElementForAddon(aEvent.state.id);
if (listItem) {
Addons.showDetails(listItem);
} else {
// If the addon doesn't exist anymore, go back in the history.
history.back();
}
} else {
// Clear any previous detail addon
let detailItem = document.querySelector("#addons-details > .addon-item");
detailItem.addon = null;
showAddons();
}
}
function showAddonDetails(addonId) {
const listItem = Addons._getElementForAddon(addonId);
if (listItem) {
Addons.showDetails(listItem);
history.pushState({ id: addonId }, document.title);
} else {
throw new Error(`Addon not found: ${addonId}`);
}
}
function showAddons() {
// Hide the addon options and show the addons list
let details = document.querySelector("#addons-details");
details.classList.add("hidden");
let list = document.querySelector("#addons-list");
list.classList.remove("hidden");
document.documentElement.removeAttribute("details");
// Clean the optionsBox content when switching to the add-ons list view.
let optionsBox = document.querySelector(
"#addons-details > .addon-item .options-box"
);
optionsBox.innerHTML = "";
}
function showAddonOptions() {
// Hide the addon list and show the addon options
let list = document.querySelector("#addons-list");
list.classList.add("hidden");
let details = document.querySelector("#addons-details");
details.classList.remove("hidden");
document.documentElement.setAttribute("details", "true");
}
var Addons = {
_restartCount: 0,
_createItem: function _createItem(aAddon) {
let outer = document.createElement("div");
outer.setAttribute("addonID", aAddon.id);
outer.className = "addon-item list-item";
outer.setAttribute("role", "button");
outer.setAttribute("contextmenu", "addonmenu");
outer.addEventListener(
"click",
() => {
this.showDetails(outer);
history.pushState({ id: aAddon.id }, document.title);
},
true
);
let img = document.createElement("img");
img.className = "icon";
img.setAttribute("src", aAddon.iconURL || AMO_ICON);
outer.appendChild(img);
let inner = document.createElement("div");
inner.className = "inner";
let details = document.createElement("div");
details.className = "details";
inner.appendChild(details);
let titlePart = document.createElement("div");
titlePart.textContent = aAddon.name;
titlePart.className = "title";
details.appendChild(titlePart);
let versionPart = document.createElement("div");
versionPart.textContent = aAddon.version;
versionPart.className = "version";
details.appendChild(versionPart);
if ("description" in aAddon) {
let descPart = document.createElement("div");
descPart.textContent = aAddon.description;
descPart.className = "description";
inner.appendChild(descPart);
}
outer.appendChild(inner);
let update = document.createElement("img");
update.className = "update-indicator";
update.setAttribute("src", UPDATE_INDICATOR);
outer.appendChild(update);
return outer;
},
_createBrowseItem: function _createBrowseItem() {
let outer = document.createElement("div");
outer.className = "addon-item list-item";
outer.setAttribute("role", "button");
outer.addEventListener(
"click",
function(event) {
try {
openLink(
Services.urlFormatter.formatURLPref(
"extensions.getAddons.browseAddons"
)
);
} catch (e) {
Cu.reportError(e);
}
},
true
);
let img = document.createElement("img");
img.className = "icon";
img.setAttribute("src", AMO_ICON);
outer.appendChild(img);
let inner = document.createElement("div");
inner.className = "inner";
let title = document.createElement("div");
title.id = "browse-title";
title.className = "title";
title.textContent = this._getAmoTitle();
inner.appendChild(title);
outer.appendChild(inner);
return outer;
},
// Ensure we get a localized string by using the previous title as a fallback
// if the new one has not yet been translated.
_getAmoTitle: function _getAmoTitle() {
const initialTitleUS = "Browse all Firefox Add-ons";
const updatedTitleUS = "Browse Firefoxs Recommended Extensions";
const initialTitleLocalized = gStringBundle.GetStringFromName(
"addons.browseAll"
);
const updatedTitleLocalized = gStringBundle.GetStringFromName(
"addons.browseRecommended"
);
let title = initialTitleLocalized;
const titleWasLocalized = updatedTitleLocalized !== updatedTitleUS;
const localeIsDefaultUS =
updatedTitleLocalized === updatedTitleUS &&
initialTitleLocalized === initialTitleUS;
if (titleWasLocalized || localeIsDefaultUS) {
title = updatedTitleLocalized;
}
EventDispatcher.instance.dispatch("about:addons", { amoTitle: title });
return title;
},
_createItemForAddon: function _createItemForAddon(aAddon) {
let opType = this._getOpTypeForOperations(aAddon.pendingOperations);
let hasUpdate = this._addonHasUpdate(aAddon);
let optionsURL = aAddon.optionsURL || "";
let blocked = "";
switch (aAddon.blocklistState) {
case Ci.nsIBlocklistService.STATE_BLOCKED:
blocked = "blocked";
break;
case Ci.nsIBlocklistService.STATE_SOFTBLOCKED:
blocked = "softBlocked";
break;
case Ci.nsIBlocklistService.STATE_OUTDATED:
blocked = "outdated";
break;
}
let item = this._createItem(aAddon);
item.setAttribute("isDisabled", !aAddon.isActive);
item.setAttribute(
"isUnsigned",
aAddon.signedState <= AddonManager.SIGNEDSTATE_MISSING
);
item.setAttribute("opType", opType);
if (blocked) {
item.setAttribute("blockedStatus", blocked);
}
item.setAttribute("optionsURL", optionsURL);
item.setAttribute("hasUpdate", hasUpdate);
item.addon = aAddon;
return item;
},
_getElementForAddon: function(aKey) {
let list = document.getElementById("addons-list");
let element = list.querySelector('div[addonID="' + CSS.escape(aKey) + '"]');
return element;
},
_addonHasUpdate(addon) {
return gChromeWin.ExtensionPermissions.updates.has(addon.id);
},
init: async function init() {
const aAddons = await AddonManager.getAllAddons();
// Clear all content before filling the addons
let list = document.getElementById("addons-list");
list.innerHTML = "";
aAddons.sort(function(a, b) {
return a.name.localeCompare(b.name);
});
for (let i = 0; i < aAddons.length; i++) {
// Don't create item for system add-ons.
if (aAddons[i].isSystem) {
continue;
}
let item = this._createItemForAddon(aAddons[i]);
list.appendChild(item);
}
// Add a "Browse all Firefox Add-ons" item to the bottom of the list.
let browseItem = this._createBrowseItem();
list.appendChild(browseItem);
document
.getElementById("update-btn")
.addEventListener("click", Addons.updateCurrent.bind(this));
document
.getElementById("uninstall-btn")
.addEventListener("click", Addons.uninstallCurrent.bind(this));
document
.getElementById("cancel-btn")
.addEventListener("click", Addons.cancelUninstall.bind(this));
document
.getElementById("disable-btn")
.addEventListener("click", Addons.disable.bind(this));
document
.getElementById("enable-btn")
.addEventListener("click", Addons.enable.bind(this));
document
.getElementById("unsigned-learn-more")
.addEventListener("click", function() {
openLink(
Services.urlFormatter.formatURLPref("app.support.baseURL") +
"unsigned-addons"
);
});
},
_getOpTypeForOperations: function _getOpTypeForOperations(aOperations) {
if (aOperations & AddonManager.PENDING_UNINSTALL) {
return "needs-uninstall";
}
if (aOperations & AddonManager.PENDING_ENABLE) {
return "needs-enable";
}
if (aOperations & AddonManager.PENDING_DISABLE) {
return "needs-disable";
}
return "";
},
showDetails: function showDetails(aListItem) {
let detailItem = document.querySelector("#addons-details > .addon-item");
detailItem.setAttribute("isDisabled", aListItem.getAttribute("isDisabled"));
detailItem.setAttribute("isUnsigned", aListItem.getAttribute("isUnsigned"));
detailItem.setAttribute("opType", aListItem.getAttribute("opType"));
detailItem.setAttribute("optionsURL", aListItem.getAttribute("optionsURL"));
let addon = (detailItem.addon = aListItem.addon);
let favicon = document.querySelector("#addons-details > .addon-item .icon");
favicon.setAttribute("src", addon.iconURL || AMO_ICON);
detailItem.querySelector(".title").textContent = addon.name;
detailItem.querySelector(".version").textContent = addon.version;
detailItem.querySelector(".description-full").textContent =
addon.description;
detailItem.querySelector(
".status-uninstalled"
).textContent = gStringBundle.formatStringFromName(
"addonStatus.uninstalled",
[addon.name]
);
let updateBtn = document.getElementById("update-btn");
if (this._addonHasUpdate(addon)) {
updateBtn.removeAttribute("hidden");
} else {
updateBtn.setAttribute("hidden", true);
}
let enableBtn = document.getElementById("enable-btn");
if (addon.appDisabled) {
enableBtn.setAttribute("disabled", "true");
} else {
enableBtn.removeAttribute("disabled");
}
let uninstallBtn = document.getElementById("uninstall-btn");
if (addon.scope == AddonManager.SCOPE_APPLICATION) {
uninstallBtn.setAttribute("disabled", "true");
} else {
uninstallBtn.removeAttribute("disabled");
}
let addonItem = document.querySelector("#addons-details > .addon-item");
let optionsBox = addonItem.querySelector(".options-box");
let optionsURL = aListItem.getAttribute("optionsURL");
// Always clean the options content before rendering the options of the
// newly selected extension.
optionsBox.innerHTML = "";
switch (parseInt(addon.optionsType)) {
case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:
// Allow the options to use all the available width space.
optionsBox.classList.remove("inner");
this.createWebExtensionOptions(optionsBox, addon, addonItem);
break;
case AddonManager.OPTIONS_TYPE_TAB:
// Keep the usual layout for any options related the legacy (or system) add-ons
// when the options are opened in a new tab from a single button in the addon
// details page.
optionsBox.classList.add("inner");
this.createOptionsInTabButton(optionsBox, addon, addonItem);
break;
}
showAddonOptions();
},
createOptionsInTabButton: function(destination, addon, detailItem) {
let frame = destination.querySelector("iframe#addon-options");
let button = destination.querySelector("button#open-addon-options");
if (frame) {
// Remove any existent options frame (e.g. when the addon updates
// contains the open_in_tab options for the first time).
frame.remove();
}
if (!button) {
button = document.createElement("button");
button.setAttribute("id", "open-addon-options");
button.textContent = gStringBundle.GetStringFromName("addon.options");
destination.appendChild(button);
}
button.onclick = async () => {
if (addon.isWebExtension) {
// WebExtensions are loaded asynchronously and the optionsURL
// may not be available until the addon has been started.
await addon.startupPromise;
}
const { optionsURL } = addon;
openOptionsInTab(optionsURL);
};
// Ensure that the Addon Options are visible (the options box will be hidden if the optionsURL
// attribute is an empty string, which happens when a WebExtensions is still loading).
detailItem.removeAttribute("optionsURL");
},
createWebExtensionOptions: async function(destination, addon, detailItem) {
// WebExtensions are loaded asynchronously and the optionsURL
// may not be available until the addon has been started.
await addon.startupPromise;
const { optionsURL, optionsBrowserStyle } = addon;
let frame = destination.querySelector("iframe#addon-options");
if (!frame) {
let originalHeight;
frame = document.createElement("iframe");
frame.setAttribute("id", "addon-options");
frame.setAttribute("mozbrowser", "true");
frame.setAttribute("style", "width: 100%; overflow: hidden;");
// Adjust iframe height to the iframe content (also between navigation of multiple options
// files).
frame.onload = evt => {
if (evt.target !== frame) {
return;
}
const { document } = frame.contentWindow;
const bodyScrollHeight = document.body && document.body.scrollHeight;
const documentScrollHeight = document.documentElement.scrollHeight;
// Set the iframe height to the maximum between the body and the document
// scrollHeight values.
frame.style.height =
Math.max(bodyScrollHeight, documentScrollHeight) + "px";
// Restore the original iframe height between option page loads,
// so that we don't force the new document to have the same size
// of the previosuly loaded option page.
frame.contentWindow.addEventListener(
"unload",
() => {
frame.style.height = originalHeight + "px";
},
{ once: true }
);
};
destination.appendChild(frame);
originalHeight = frame.getBoundingClientRect().height;
}
// Loading the URL this way prevents the native back
// button from applying to the iframe.
frame.contentWindow.location.replace(optionsURL);
// Ensure that the Addon Options are visible (the options box will be hidden if the optionsURL
// attribute is an empty string, which happens when a WebExtensions is still loading).
detailItem.removeAttribute("optionsURL");
},
setEnabled: function setEnabled(aValue, aAddon) {
let detailItem = document.querySelector("#addons-details > .addon-item");
let addon = aAddon || detailItem.addon;
if (!addon) {
return;
}
let listItem = this._getElementForAddon(addon.id);
function setDisabled(addon, value) {
if (value) {
return addon.disable();
}
return addon.enable();
}
function updateOtherThemeStateInUI(item) {
if (aValue) {
// Mark the previously enabled theme as disabled.
if (item.addon.isActive) {
item.setAttribute("isDisabled", true);
return true;
}
// The current theme is being disabled - enable the default theme.
} else if (item.addon.id == "default-theme@mozilla.org") {
item.removeAttribute("isDisabled");
return true;
}
return false;
}
let opType;
if (addon.type == "theme") {
// Themes take care of themselves to make sure only one is active at the
// same time, but we need to fix up the state of other themes in the UI.
let list = document.getElementById("addons-list");
let item = list.firstElementChild;
while (item) {
if (
item.addon &&
item.addon.type == "theme" &&
updateOtherThemeStateInUI(item)
) {
break;
}
item = item.nextSibling;
}
setDisabled(addon, !aValue);
} else if (addon.type == "locale") {
setDisabled(addon, !aValue);
} else {
setDisabled(addon, !aValue);
opType = this._getOpTypeForOperations(addon.pendingOperations);
if (
addon.pendingOperations & AddonManager.PENDING_ENABLE ||
addon.pendingOperations & AddonManager.PENDING_DISABLE
) {
this.showRestart();
} else if (
listItem &&
/needs-(enable|disable)/.test(listItem.getAttribute("opType"))
) {
this.hideRestart();
}
}
if (addon == detailItem.addon) {
detailItem.setAttribute("isDisabled", !aValue);
if (opType) {
detailItem.setAttribute("opType", opType);
} else {
detailItem.removeAttribute("opType");
}
// Remove any addon options iframe if the currently selected addon has been disabled.
if (!aValue) {
const addonOptionsIframe = document.querySelector("#addon-options");
if (addonOptionsIframe) {
addonOptionsIframe.remove();
}
}
}
// Sync to the list item
if (listItem) {
listItem.setAttribute("isDisabled", !aValue);
if (opType) {
listItem.setAttribute("opType", opType);
} else {
listItem.removeAttribute("opType");
}
}
},
enable: function enable() {
this.setEnabled(true);
},
disable: function disable() {
this.setEnabled(false);
},
updateCurrent() {
let detailItem = document.querySelector("#addons-details > .addon-item");
let addon = detailItem.addon;
if (!addon) {
return;
}
gChromeWin.ExtensionPermissions.applyUpdate(addon.id);
},
uninstallCurrent: function uninstallCurrent() {
let detailItem = document.querySelector("#addons-details > .addon-item");
let addon = detailItem.addon;
if (!addon) {
return;
}
this.uninstall(addon);
},
uninstall: function uninstall(aAddon) {
if (!aAddon) {
return;
}
let listItem = this._getElementForAddon(aAddon.id);
aAddon.uninstall();
if (aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
this.showRestart();
// A disabled addon doesn't need a restart so it has no pending ops and
// can't be cancelled
let opType = this._getOpTypeForOperations(aAddon.pendingOperations);
if (!aAddon.isActive && opType == "") {
opType = "needs-uninstall";
}
detailItem.setAttribute("opType", opType);
listItem.setAttribute("opType", opType);
}
},
cancelUninstall: function ev_cancelUninstall() {
let detailItem = document.querySelector("#addons-details > .addon-item");
let addon = detailItem.addon;
if (!addon) {
return;
}
addon.cancelUninstall();
this.hideRestart();
let opType = this._getOpTypeForOperations(addon.pendingOperations);
detailItem.setAttribute("opType", opType);
let listItem = this._getElementForAddon(addon.id);
listItem.setAttribute("opType", opType);
},
showRestart: function showRestart() {
this._restartCount++;
gChromeWin.XPInstallObserver.showRestartPrompt();
},
hideRestart: function hideRestart() {
this._restartCount--;
if (this._restartCount == 0) {
gChromeWin.XPInstallObserver.hideRestartPrompt();
}
},
onEnabled: function(aAddon) {
let listItem = this._getElementForAddon(aAddon.id);
if (!listItem) {
return;
}
// Reload the details to pick up any options now that it's enabled.
listItem.setAttribute("optionsURL", aAddon.optionsURL || "");
let detailItem = document.querySelector("#addons-details > .addon-item");
if (aAddon == detailItem.addon) {
this.showDetails(listItem);
}
},
onInstallEnded: function(aInstall, aAddon) {
let needsRestart = false;
if (
aInstall.existingAddon &&
aInstall.existingAddon.pendingOperations & AddonManager.PENDING_UPGRADE
) {
needsRestart = true;
} else if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL) {
needsRestart = true;
}
let list = document.getElementById("addons-list");
let element = this._getElementForAddon(aAddon.id);
if (!element) {
element = this._createItemForAddon(aAddon);
list.insertBefore(element, list.firstElementChild);
}
if (needsRestart) {
element.setAttribute("opType", "needs-restart");
}
},
onInstalled: function(aAddon) {
let list = document.getElementById("addons-list");
let element = this._getElementForAddon(aAddon.id);
if (element) {
// Upgrade of an existing addon, update version and description in
// list item and detail view, plus indicators about a pending update.
element.querySelector(".version").textContent = aAddon.version;
let desc = element.querySelector(".description");
if (desc) {
desc.textContent = aAddon.description;
}
element.setAttribute("hasUpdate", false);
document.getElementById("update-btn").setAttribute("hidden", true);
element = document.querySelector("#addons-details > .addon-item");
if (element.addon && element.addon.id == aAddon.id) {
element.querySelector(".version").textContent = aAddon.version;
element.querySelector(".description-full").textContent =
aAddon.description;
}
} else {
element = this._createItemForAddon(aAddon);
// Themes aren't considered active on install, so set existing as disabled, and new one enabled.
if (aAddon.type == "theme") {
let item = list.firstElementChild;
while (item) {
if (item.addon && item.addon.type == "theme") {
item.setAttribute("isDisabled", true);
}
item = item.nextSibling;
}
element.setAttribute("isDisabled", false);
}
list.insertBefore(element, list.firstElementChild);
}
},
onUninstalled: function(aAddon) {
let list = document.getElementById("addons-list");
let element = this._getElementForAddon(aAddon.id);
list.removeChild(element);
// Go back if we're in the detail view of the add-on that was uninstalled.
let detailItem = document.querySelector("#addons-details > .addon-item");
if (detailItem.addon.id == aAddon.id) {
history.back();
}
},
onInstallFailed: function(aInstall) {},
onDownloadProgress: function xpidm_onDownloadProgress(aInstall) {},
onDownloadFailed: function(aInstall) {},
onDownloadCancelled: function(aInstall) {},
};
window.addEventListener("load", init);
window.addEventListener("unload", uninit);

View File

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
%globalDTD;
<!ENTITY % aboutDTD SYSTEM "chrome://browser/locale/aboutAddons.dtd" >
%aboutDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&aboutAddons.title2;</title>
<meta name="viewport" content="width=device-width; user-scalable=0" />
<link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
<link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/aboutAddons.css" type="text/css"/>
</head>
<body dir="&locale.dir;">
<menu type="context" id="addonmenu">
<menuitem id="contextmenu-enable" label="&addonAction.enable;"></menuitem>
<menuitem id="contextmenu-disable" label="&addonAction.disable;" ></menuitem>
<menuitem id="contextmenu-uninstall" label="&addonAction.uninstall;" ></menuitem>
</menu>
<div id="addons-header" class="header">
<div>&aboutAddons.header2;</div>
</div>
<div id="addons-list" class="list hidden">
</div>
<div id="addons-details" class="list hidden">
<div class="addon-item list-item">
<img class="icon"/>
<div class="inner">
<div class="details">
<div class="title"></div><div class="version"></div>
</div>
<div class="description-full"></div>
</div>
<div class="warn-unsigned">&addonUnsigned.message; <a id="unsigned-learn-more">&addonUnsigned.learnMore;</a></div>
<div class="options-box"></div>
<div class="status status-uninstalled show-on-uninstall"></div>
<div class="buttons">
<button id="update-btn" class="show-on-update">&addonAction.update;</button>
<button id="enable-btn" class="show-on-disable hide-on-enable hide-on-uninstall" >&addonAction.enable;</button>
<button id="disable-btn" class="show-on-enable hide-on-disable hide-on-uninstall" >&addonAction.disable;</button>
<button id="uninstall-btn" class="hide-on-uninstall" >&addonAction.uninstall;</button>
<button id="cancel-btn" class="show-on-uninstall" >&addonAction.undo;</button>
</div>
</div>
</div>
<script type="application/javascript" src="chrome://browser/content/aboutAddons.js"></script>
</body>
</html>

View File

@ -1,152 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % certerrorDTD
SYSTEM "chrome://browser/locale/aboutCertError.dtd">
%certerrorDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&certerror.pagetitle;</title>
<meta name="viewport" content="width=device-width; user-scalable=false" />
<link rel="stylesheet" href="chrome://browser/skin/netError.css" media="all" />
<!-- This page currently uses the same favicon as neterror.xhtml.
If the location of the favicon is changed for both pages, the
FAVICON_ERRORPAGE_URL symbol in toolkit/components/places/src/nsFaviconService.h
should be updated. If this page starts using a different favicon
than neterrorm nsFaviconService->SetAndLoadFaviconForPage
should be updated to ignore this one as well. -->
<link rel="icon" type="image/png" id="favicon" sizes="64x64" href="chrome://browser/skin/images/certerror-warning.png"/>
<script type="application/javascript"><![CDATA[
// Error url MUST be formatted like this:
// about:certerror?e=error&u=url&d=desc
// Note that this file uses document.documentURI to get
// the URL (with the format from above). This is because
// document.location.href gets the current URI off the docshell,
// which is the URL displayed in the location bar, i.e.
// the URI that the user attempted to load.
function getCSSClass() {
var url = document.documentURI;
var matches = url.match(/s\=([^&]+)\&/);
// s is optional, if no match just return nothing
if (!matches || matches.length < 2)
return "";
// parenthetical match is the second entry
return decodeURIComponent(matches[1]);
}
function initPage() {
// Replace the "#1" string in the intro with the hostname. Trickier
// than it might seem since we want to preserve the <b> tags, but
// not allow for any injection by just using innerHTML. Instead,
// just find the right target text node.
var intro = document.getElementById("introContentP1");
function replaceWithHost(node) {
if (node.textContent == "#1")
node.textContent = location.host;
else
for (var i = 0; i < node.childNodes.length; i++)
replaceWithHost(node.childNodes[i]);
}
replaceWithHost(intro);
if (getCSSClass() == "expertBadCert") {
toggle("technicalContent");
toggle("expertContent");
}
// Disallow overrides if this is a Strict-Transport-Security
// host and the cert is bad (STS Spec section 7.3) or if the
// certerror is in a frame (bug 633691).
if (getCSSClass() == "badStsCert" || window != top)
document.getElementById("expertContent").setAttribute("hidden", "true");
var event = new CustomEvent("AboutCertErrorLoad", {bubbles: true});
document.dispatchEvent(event);
}
function createLink(el, id, text) {
var anchorEl = document.createElement("a");
anchorEl.setAttribute("id", id);
anchorEl.setAttribute("title", text);
anchorEl.appendChild(document.createTextNode(text));
el.appendChild(anchorEl);
}
function toggle(id) {
var el = document.getElementById(id);
if (el.hasAttribute("collapsed"))
el.removeAttribute("collapsed");
else
el.setAttribute("collapsed", true);
}
]]></script>
</head>
<body id="errorPage" class="certerror" dir="&locale.dir;">
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer">
<!-- Error Title -->
<div id="errorTitle">
<h1 class="errorTitleText">&certerror.longpagetitle;</h1>
</div>
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<div id="introContent">
<p id="introContentP1">&certerror.introPara1;</p>
</div>
<div id="whatShouldIDoContent">
<h2>&certerror.whatShouldIDo.heading;</h2>
<div>
<p id="whatShouldIDoContentText">&certerror.whatShouldIDo.content;</p>
<button id="getMeOutOfHereButton">&certerror.getMeOutOfHere.label;</button>
</div>
</div>
<!-- The following sections can be unhidden by default by setting the
"browser.xul.error_pages.expert_bad_cert" pref to true -->
<div id="technicalContent" collapsed="true">
<h2 class="expander" onclick="toggle('technicalContent');" id="technicalContentHeading">&certerror.technical.heading;</h2>
<p id="technicalContentText"/>
</div>
<div id="expertContent" collapsed="true">
<h2 class="expander" onclick="toggle('expertContent');" id="expertContentHeading">&certerror.expert.heading;</h2>
<div>
<p>&certerror.expert.content;</p>
<p>&certerror.expert.contentPara2;</p>
<button id="temporaryExceptionButton">&certerror.addTemporaryException.label;</button>
<button id="permanentExceptionButton">&certerror.addPermanentException.label;</button>
</div>
</div>
</div>
</div>
<!--
- Note: It is important to run the script this way, instead of using
- an onload handler. This is because error pages are loaded as
- LOAD_BACKGROUND, which means that onload handlers will not be executed.
-->
<script type="application/javascript">initPage();</script>
</body>
</html>

View File

@ -1,427 +0,0 @@
/* 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";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
ChromeUtils.defineModuleGetter(
this,
"Downloads",
"resource://gre/modules/Downloads.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"DownloadUtils",
"resource://gre/modules/DownloadUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"EventDispatcher",
"resource://gre/modules/Messaging.jsm"
);
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(
this,
"PluralForm",
"resource://gre/modules/PluralForm.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
var gStrings = Services.strings.createBundle(
"chrome://browser/locale/aboutDownloads.properties"
);
XPCOMUtils.defineLazyGetter(this, "strings", () =>
Services.strings.createBundle(
"chrome://browser/locale/aboutDownloads.properties"
)
);
function deleteDownload(download) {
download.finalize(true).catch(Cu.reportError);
OS.File.remove(download.target.path).catch(ex => {
if (!(ex instanceof OS.File.Error && ex.becauseNoSuchFile)) {
Cu.reportError(ex);
}
});
}
var contextMenu = {
_items: [],
_targetDownload: null,
init: function() {
let element = document.getElementById("downloadmenu");
element.addEventListener(
"click",
event => (event.download = this._targetDownload),
true
);
this._items = [
new ContextMenuItem(
"open",
download => download.succeeded,
download => download.launch().catch(Cu.reportError)
),
new ContextMenuItem(
"retry",
download =>
download.error || (download.canceled && !download.hasPartialData),
download => download.start().catch(Cu.reportError)
),
new ContextMenuItem(
"remove",
download => download.stopped,
download => {
Downloads.getList(Downloads.ALL)
.then(list => list.remove(download))
.catch(Cu.reportError);
deleteDownload(download);
}
),
new ContextMenuItem(
"pause",
download => !download.stopped && download.hasPartialData,
download => download.cancel().catch(Cu.reportError)
),
new ContextMenuItem(
"resume",
download => download.canceled && download.hasPartialData,
download => download.start().catch(Cu.reportError)
),
new ContextMenuItem(
"cancel",
download =>
!download.stopped || (download.canceled && download.hasPartialData),
download => {
download.cancel().catch(Cu.reportError);
download.removePartialData().catch(Cu.reportError);
}
),
// following menu item is a global action
new ContextMenuItem(
"removeall",
() => downloadLists.finished.length > 0,
() => downloadLists.removeFinished()
),
];
},
addContextMenuEventListener: function(element) {
element.addEventListener("contextmenu", this.onContextMenu.bind(this));
},
onContextMenu: function(event) {
let target = event.target;
while (target && !target.download) {
target = target.parentNode;
}
if (!target) {
Cu.reportError("No download found for context menu target");
event.preventDefault();
return;
}
// capture the target download for menu items to use in a click event
this._targetDownload = target.download;
for (let item of this._items) {
item.updateVisibility(target.download);
}
},
};
function ContextMenuItem(name, isVisible, action) {
this.element = document.getElementById("contextmenu-" + name);
this.isVisible = isVisible;
this.element.addEventListener("click", event => action(event.download));
}
ContextMenuItem.prototype = {
updateVisibility: function(download) {
this.element.hidden = !this.isVisible(download);
},
};
function DownloadListView(type, listElementId) {
this.listElement = document.getElementById(listElementId);
contextMenu.addContextMenuEventListener(this.listElement);
this.items = new Map();
Downloads.getList(type)
.then(list => list.addView(this))
.catch(Cu.reportError);
window.addEventListener("unload", event => {
Downloads.getList(type)
.then(list => list.removeView(this))
.catch(Cu.reportError);
});
}
DownloadListView.prototype = {
get finished() {
let finished = [];
for (let download of this.items.keys()) {
if (download.stopped && (!download.hasPartialData || download.error)) {
finished.push(download);
}
}
return finished;
},
insertOrMoveItem: function(item) {
var compare = (a, b) => {
// active downloads always before stopped downloads
if (a.stopped != b.stopped) {
return b.stopped ? -1 : 1;
}
// most recent downloads first
return b.startTime - a.startTime;
};
let insertLocation = this.listElement.firstChild;
while (
insertLocation &&
compare(item.download, insertLocation.download) > 0
) {
insertLocation = insertLocation.nextElementSibling;
}
this.listElement.insertBefore(item.element, insertLocation);
},
onDownloadAdded: function(download) {
let item = new DownloadItem(download);
this.items.set(download, item);
this.insertOrMoveItem(item);
},
onDownloadChanged: function(download) {
let item = this.items.get(download);
if (!item) {
Cu.reportError("No DownloadItem found for download");
return;
}
if (item.stateChanged) {
this.insertOrMoveItem(item);
}
item.onDownloadChanged();
},
onDownloadRemoved: function(download) {
let item = this.items.get(download);
if (!item) {
Cu.reportError("No DownloadItem found for download");
return;
}
this.items.delete(download);
this.listElement.removeChild(item.element);
EventDispatcher.instance.sendRequest({
type: "Download:Remove",
path: download.target.path,
});
},
};
var downloadLists = {
init: function() {
this.publicDownloads = new DownloadListView(
Downloads.PUBLIC,
"public-downloads-list"
);
this.privateDownloads = new DownloadListView(
Downloads.PRIVATE,
"private-downloads-list"
);
},
get finished() {
return this.publicDownloads.finished.concat(this.privateDownloads.finished);
},
removeFinished: function() {
let finished = this.finished;
if (finished.length == 0) {
return;
}
let title = strings.GetStringFromName("downloadAction.deleteAll");
let messageForm = strings.GetStringFromName("downloadMessage.deleteAll");
let message = PluralForm.get(finished.length, messageForm).replace(
"#1",
finished.length
);
if (Services.prompt.confirm(null, title, message)) {
Downloads.getList(Downloads.ALL).then(list => {
for (let download of finished) {
list.remove(download).catch(Cu.reportError);
deleteDownload(download);
}
}, Cu.reportError);
}
},
};
function DownloadItem(download) {
this._download = download;
this._updateFromDownload();
this._domain = DownloadUtils.getURIHost(download.source.url)[0];
this._fileName = this._htmlEscape(OS.Path.basename(download.target.path));
this._iconUrl = "moz-icon://" + this._fileName + "?size=64";
this._startDate = this._htmlEscape(
DownloadUtils.getReadableDates(download.startTime)[0]
);
this._element = this.createElement();
}
const kDownloadStatePropertyNames = [
"stopped",
"succeeded",
"canceled",
"error",
"startTime",
];
DownloadItem.prototype = {
_htmlEscape: function(s) {
s = s.replace(/&/g, "&amp;");
s = s.replace(/>/g, "&gt;");
s = s.replace(/</g, "&lt;");
s = s.replace(/"/g, "&quot;");
s = s.replace(/'/g, "&apos;");
return s;
},
_updateFromDownload: function() {
this._state = {};
kDownloadStatePropertyNames.forEach(
name => (this._state[name] = this._download[name]),
this
);
},
get stateChanged() {
return kDownloadStatePropertyNames.some(
name => this._state[name] != this._download[name],
this
);
},
get download() {
return this._download;
},
get element() {
return this._element;
},
createElement: function() {
let template = document.getElementById("download-item");
// TODO: use this once <template> is working
// let element = document.importNode(template.content, true);
// simulate a <template> node...
let element = template.cloneNode(true);
element.removeAttribute("id");
element.removeAttribute("style");
// launch the download if clicked
element.addEventListener("click", this.onClick.bind(this));
// set download as an expando property for the context menu
element.download = this.download;
// fill in template placeholders
this.updateElement(element);
return element;
},
updateElement: function(element) {
element.querySelector(".date").textContent = this.startDate;
element.querySelector(".domain").textContent = this.domain;
element.querySelector(".icon").src = this.iconUrl;
element.querySelector(".size").textContent = this.size;
element.querySelector(".state").textContent = this.stateDescription;
element.querySelector(".title").setAttribute("value", this.fileName);
},
onClick: function(event) {
if (this.download.succeeded) {
this.download.launch().catch(Cu.reportError);
}
},
onDownloadChanged: function() {
this._updateFromDownload();
this.updateElement(this.element);
},
// template properties below
get domain() {
return this._domain;
},
get fileName() {
return this._fileName;
},
get id() {
return this._id;
},
get iconUrl() {
return this._iconUrl;
},
get size() {
if (this.download.succeeded && this.download.target.exists) {
return DownloadUtils.convertByteUnits(this.download.target.size).join("");
} else if (this.download.hasProgress) {
return DownloadUtils.convertByteUnits(this.download.totalBytes).join("");
}
return strings.GetStringFromName("downloadState.unknownSize");
},
get startDate() {
return this._startDate;
},
get stateDescription() {
let name;
if (this.download.error) {
name = "downloadState.failed";
} else if (this.download.canceled) {
if (this.download.hasPartialData) {
name = "downloadState.paused";
} else {
name = "downloadState.canceled";
}
} else if (!this.download.stopped) {
if (this.download.currentBytes > 0) {
name = "downloadState.downloading";
} else {
name = "downloadState.starting";
}
}
if (name) {
return strings.GetStringFromName(name);
}
return "";
},
};
window.addEventListener("DOMContentLoaded", event => {
contextMenu.init();
downloadLists.init();
});

View File

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
%globalDTD;
<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/aboutDownloads.dtd" >
%downloadsDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head>
<title>&aboutDownloads.title;</title>
<meta name="viewport" content="width=device-width; user-scalable=0" />
<link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
<link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/aboutDownloads.css" type="text/css"/>
</head>
<body dir="&locale.dir;">
<menu type="context" id="downloadmenu">
<menuitem id="contextmenu-open" label="&aboutDownloads.open;"></menuitem>
<menuitem id="contextmenu-retry" label="&aboutDownloads.retry;"></menuitem>
<menuitem id="contextmenu-remove" label="&aboutDownloads.remove;"></menuitem>
<menuitem id="contextmenu-pause" label="&aboutDownloads.pause;"></menuitem>
<menuitem id="contextmenu-resume" label="&aboutDownloads.resume;"></menuitem>
<menuitem id="contextmenu-cancel" label="&aboutDownloads.cancel;"></menuitem>
<menuitem id="contextmenu-removeall" label="&aboutDownloads.removeAll;"></menuitem>
</menu>
<!--template id="download-item"-->
<li id="download-item" class="list-item" role="button" contextmenu="downloadmenu" style="display: none">
<img class="icon" src=""/>
<div class="details">
<div class="row">
<!-- This is a hack so that we can crop this label in its center -->
<xul:label class="title" crop="center" value=""/>
<div class="date"></div>
</div>
<div class="size"></div>
<div class="domain"></div>
<div class="state"></div>
</div>
</li>
<!--/template-->
<div class="header">
<div>&aboutDownloads.header;</div>
</div>
<ul id="private-downloads-list" class="list"></ul>
<ul id="public-downloads-list" class="list"></ul>
<span id="no-downloads-indicator">&aboutDownloads.empty;</span>
<script type="application/javascript" src="chrome://browser/content/aboutDownloads.js"/>
</body>
</html>

View File

@ -1,127 +0,0 @@
/* 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";
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
AndroidLog: "resource://gre/modules/AndroidLog.jsm",
EventDispatcher: "resource://gre/modules/Messaging.jsm",
});
const LOGTAG = "Experiments";
const EXPERIMENTS_CONFIGURATION =
"https://firefox.settings.services.mozilla.com/v1/buckets/fennec/collections/experiments/records";
const Experiments = Services.wm.getMostRecentWindow("navigator:browser")
.Experiments;
document.addEventListener("DOMContentLoaded", initList);
function log(msg) {
AndroidLog.d(LOGTAG, msg);
}
function initList() {
const list = document.getElementById("list");
list.addEventListener("click", toggleOverride);
Promise.all([
promiseEnabledExperiments(),
promiseExperimentsConfiguration(),
]).then(values => {
const enabledExperiments = values[0];
const serverConfiguration = values[1];
serverConfiguration.data.forEach(function(experiment) {
try {
let item = document.createElement("li");
item.textContent = experiment.name;
item.setAttribute("name", experiment.name);
item.setAttribute(
"isEnabled",
enabledExperiments.includes(experiment.name)
);
list.appendChild(item);
} catch (e) {
log(`Error while setting experiments list: ${e.error}`);
}
});
});
}
function toggleOverride(experiment) {
const item = experiment.originalTarget;
const name = item.getAttribute("name");
const isEnabled = item.getAttribute("isEnabled") === "true";
log(`toggleOverride: ${name}`);
Experiments.setOverride(name, !isEnabled);
item.setAttribute("isEnabled", !isEnabled);
}
/**
* Get the list of locally enabled experiments.
*/
function promiseEnabledExperiments() {
log("Getting the locally enabled experiments");
return EventDispatcher.instance
.sendRequestForResult({
type: "Experiments:GetActive",
})
.then(experiments => {
log("List of locally enabled experiments ready");
return experiments;
});
}
/**
* Fetch the list of experiments from server configuration.
*/
function promiseExperimentsConfiguration() {
log("Fetching server experiments");
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
try {
xhr.open("GET", EXPERIMENTS_CONFIGURATION, true);
} catch (e) {
reject(`Error opening request: ${e}`);
return;
}
xhr.onerror = function(e) {
reject(`Error making request: ${e.error}`);
};
xhr.onload = function(event) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
resolve(JSON.parse(xhr.responseText));
} catch (e) {
const errorMessage = `Error while parsing request: ${e}`;
log(errorMessage);
reject(errorMessage);
}
} else {
const errorMessage = `Request to ${url} returned status ${
xhr.status
}`;
log(errorMessage);
reject(errorMessage);
}
}
log("Finished fetching server experiments");
};
xhr.send(null);
});
}

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Switchboard Experiments</title>
<meta name="viewport" content="width=device-width; user-scalable=0" />
<link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/aboutExperiments.css" type="text/css"/>
<script type="application/javascript" src="chrome://browser/content/aboutExperiments.js"></script>
</head>
<body>
<ul id="list"/>
</body>
</html>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % abouthomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
%abouthomeDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&abouthome.title;</title>
</head>
<body>
</body>
</html>

View File

@ -1,628 +0,0 @@
/* 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/. */
ChromeUtils.import(
"resource://services-common/utils.js"
); /* global: CommonUtils */
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { Accounts } = ChromeUtils.import("resource://gre/modules/Accounts.jsm");
XPCOMUtils.defineLazyGetter(
window,
"gChromeWin",
() => window.docShell.rootTreeItem.domWindow
);
ChromeUtils.defineModuleGetter(
this,
"EventDispatcher",
"resource://gre/modules/Messaging.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Snackbars",
"resource://gre/modules/Snackbars.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Prompt",
"resource://gre/modules/Prompt.jsm"
);
var debug = ChromeUtils.import(
"resource://gre/modules/AndroidLog.jsm",
{}
).AndroidLog.d.bind(null, "AboutLogins");
var gStringBundle = Services.strings.createBundle(
"chrome://browser/locale/aboutLogins.properties"
);
function copyStringShowSnackbar(string, notifyString) {
try {
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
Ci.nsIClipboardHelper
);
clipboard.copyString(string);
Snackbars.show(notifyString, Snackbars.LENGTH_LONG);
} catch (e) {
debug("Error copying from about:logins");
Snackbars.show(
gStringBundle.GetStringFromName("loginsDetails.copyFailed"),
Snackbars.LENGTH_LONG
);
}
}
// Delay filtering while typing in MS
const FILTER_DELAY = 500;
var Logins = {
_logins: [],
_filterTimer: null,
_selectedLogin: null,
// Load the logins list, displaying interstitial UI (see
// #logins-list-loading-body) while loading. There are careful
// jank-avoiding measures taken in this function; be careful when
// modifying it!
//
// Returns a Promise that resolves to the list of logins, ordered by
// origin.
_promiseLogins: function() {
let contentBody = document.getElementById("content-body");
let emptyBody = document.getElementById("empty-body");
let filterIcon = document.getElementById("filter-button");
let showSpinner = () => {
this._toggleListBody(true);
emptyBody.classList.add("hidden");
};
let getAllLogins = () => {
let logins = [];
try {
logins = Services.logins.getAllLogins();
} catch (e) {
// It's likely that the Master Password was not entered; give
// a hint to the next person.
throw new Error(
"Possible Master Password permissions error: " + e.toString()
);
}
logins.sort((a, b) => a.origin.localeCompare(b.origin));
return logins;
};
let hideSpinner = logins => {
this._toggleListBody(false);
if (!logins.length) {
contentBody.classList.add("hidden");
filterIcon.classList.add("hidden");
emptyBody.classList.remove("hidden");
} else {
contentBody.classList.remove("hidden");
emptyBody.classList.add("hidden");
}
return logins;
};
// Return a promise that is resolved after a paint.
let waitForPaint = () => {
// We're changing 'display'. We need to wait for the new value to take
// effect; otherwise, we'll block and never paint a change. Since
// requestAnimationFrame callback is generally triggered *before* any
// style flush and layout, we wait for two animation frames. This
// approach was cribbed from
// https://dxr.mozilla.org/mozilla-central/rev/5abe3c4deab94270440422c850bbeaf512b1f38d/browser/base/content/browser-fullScreen.js?offset=0#469.
return new Promise(function(resolve, reject) {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
resolve();
});
});
});
};
// getAllLogins janks the main-thread. We need to paint before that jank;
// by throwing the janky load onto the next tick, we paint the spinner; the
// spinner is CSS animated off-main-thread.
return Promise.resolve()
.then(showSpinner)
.then(waitForPaint)
.then(getAllLogins)
.then(hideSpinner);
},
// Reload the logins list, displaying interstitial UI while loading.
// Update the stored and displayed list upon completion.
_reloadList: function() {
this._promiseLogins()
.then(logins => {
this._logins = logins;
this._loadList(logins);
})
.catch(e => {
// There's no way to recover from errors, sadly. Log and make
// it obvious that something is up.
this._logins = [];
debug("Failed to _reloadList!");
Cu.reportError(e);
});
},
_toggleListBody: function(isLoading) {
let contentBody = document.getElementById("content-body");
let loadingBody = document.getElementById("logins-list-loading-body");
if (isLoading) {
contentBody.classList.add("hidden");
loadingBody.classList.remove("hidden");
} else {
loadingBody.classList.add("hidden");
contentBody.classList.remove("hidden");
}
},
init: function() {
window.addEventListener("popstate", this);
Services.obs.addObserver(this, "passwordmgr-storage-changed");
document
.getElementById("update-btn")
.addEventListener("click", this._onSaveEditLogin.bind(this));
document
.getElementById("password-btn")
.addEventListener("click", this._onPasswordBtn.bind(this));
let filterInput = document.getElementById("filter-input");
let filterContainer = document.getElementById("filter-input-container");
filterInput.addEventListener("input", event => {
// Stop any in-progress filter timer
if (this._filterTimer) {
clearTimeout(this._filterTimer);
this._filterTimer = null;
}
// Start a new timer
this._filterTimer = setTimeout(() => {
this._filter(event);
}, FILTER_DELAY);
});
filterInput.addEventListener("blur", event => {
filterContainer.setAttribute("hidden", true);
});
document
.getElementById("filter-button")
.addEventListener("click", event => {
filterContainer.removeAttribute("hidden");
filterInput.focus();
});
document.getElementById("filter-clear").addEventListener("click", event => {
// Stop any in-progress filter timer
if (this._filterTimer) {
clearTimeout(this._filterTimer);
this._filterTimer = null;
}
filterInput.blur();
filterInput.value = "";
this._loadList(this._logins);
});
this._showList();
this._updatePasswordBtn(true);
this._reloadList();
},
uninit: function() {
Services.obs.removeObserver(this, "passwordmgr-storage-changed");
window.removeEventListener("popstate", this);
},
_loadList: function(logins) {
let list = document.getElementById("logins-list");
let newList = list.cloneNode(false);
logins.forEach(login => {
let item = this._createItemForLogin(login);
newList.appendChild(item);
});
list.parentNode.replaceChild(newList, list);
},
_showList: function() {
let loginsListPage = document.getElementById("logins-list-page");
loginsListPage.classList.remove("hidden");
let editLoginPage = document.getElementById("edit-login-page");
editLoginPage.classList.add("hidden");
// If the Show/Hide password button has been flipped, reset it
if (this._isPasswordBtnInHideMode()) {
this._updatePasswordBtn(true);
}
},
_onPopState: function(event) {
// Called when back/forward is used to change the state of the page
if (event.state) {
this._showEditLoginDialog(event.state.id);
} else {
this._selectedLogin = null;
this._showList();
}
},
_showEditLoginDialog: function(login) {
let listPage = document.getElementById("logins-list-page");
listPage.classList.add("hidden");
let editLoginPage = document.getElementById("edit-login-page");
editLoginPage.classList.remove("hidden");
let usernameField = document.getElementById("username");
usernameField.value = login.username;
let passwordField = document.getElementById("password");
passwordField.value = login.password;
let domainField = document.getElementById("origin");
domainField.value = login.origin;
let img = document.getElementById("favicon");
this._loadFavicon(img, login.origin);
let headerText = document.getElementById("edit-login-header-text");
if (login.origin && login.origin != "") {
headerText.textContent = login.origin;
} else {
headerText.textContent = gStringBundle.GetStringFromName(
"editLogin.fallbackTitle"
);
}
passwordField.addEventListener("input", event => {
let newPassword = passwordField.value;
let updateBtn = document.getElementById("update-btn");
if (newPassword === "") {
updateBtn.disabled = true;
updateBtn.classList.add("disabled-btn");
} else if (newPassword !== "" && updateBtn.disabled === true) {
updateBtn.disabled = false;
updateBtn.classList.remove("disabled-btn");
}
});
},
_onSaveEditLogin: function() {
let newUsername = document.getElementById("username").value;
let newPassword = document.getElementById("password").value;
let origUsername = this._selectedLogin.username;
let origPassword = this._selectedLogin.password;
try {
if (newUsername === origUsername && newPassword === origPassword) {
Snackbars.show(
gStringBundle.GetStringFromName("editLogin.saved1"),
Snackbars.LENGTH_LONG
);
this._showList();
return;
}
let logins = Services.logins.findLogins(
this._selectedLogin.origin,
this._selectedLogin.formActionOrigin,
this._selectedLogin.httpRealm
);
for (let i = 0; i < logins.length; i++) {
if (logins[i].username == origUsername) {
let propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
Ci.nsIWritablePropertyBag
);
if (newUsername !== origUsername) {
propBag.setProperty("username", newUsername);
}
if (newPassword !== origPassword) {
propBag.setProperty("password", newPassword);
}
// Sync relies on timePasswordChanged to decide whether
// or not to sync a login, so touch it.
propBag.setProperty("timePasswordChanged", Date.now());
Services.logins.modifyLogin(logins[i], propBag);
break;
}
}
} catch (e) {
Snackbars.show(
gStringBundle.GetStringFromName("editLogin.couldNotSave"),
Snackbars.LENGTH_LONG
);
return;
}
Snackbars.show(
gStringBundle.GetStringFromName("editLogin.saved1"),
Snackbars.LENGTH_LONG
);
this._showList();
},
_onPasswordBtn: function() {
this._updatePasswordBtn(this._isPasswordBtnInHideMode());
},
_updatePasswordBtn: function(aShouldShow) {
let passwordField = document.getElementById("password");
let button = document.getElementById("password-btn");
let show = gStringBundle.GetStringFromName("password-btn.show");
let hide = gStringBundle.GetStringFromName("password-btn.hide");
if (aShouldShow) {
passwordField.type = "password";
button.textContent = show;
button.classList.remove("password-btn-hide");
} else {
passwordField.type = "text";
button.textContent = hide;
button.classList.add("password-btn-hide");
}
},
_isPasswordBtnInHideMode: function() {
let button = document.getElementById("password-btn");
return button.classList.contains("password-btn-hide");
},
_showPassword: function(password) {
let passwordPrompt = new Prompt({
window: window,
message: password,
buttons: [
gStringBundle.GetStringFromName("loginsDialog.copy"),
gStringBundle.GetStringFromName("loginsDialog.cancel"),
],
}).show(data => {
switch (data.button) {
case 0:
// Corresponds to "Copy password" button.
copyStringShowSnackbar(
password,
gStringBundle.GetStringFromName("loginsDetails.passwordCopied")
);
}
});
},
_onLoginClick: function(event) {
let loginItem = event.currentTarget;
let login = loginItem.login;
if (!login) {
debug("No login!");
return;
}
let prompt = new Prompt({
window: window,
});
let menuItems = [
{ label: gStringBundle.GetStringFromName("loginsMenu.showPassword") },
{ label: gStringBundle.GetStringFromName("loginsMenu.copyPassword") },
{ label: gStringBundle.GetStringFromName("loginsMenu.copyUsername") },
{ label: gStringBundle.GetStringFromName("loginsMenu.editLogin") },
{ label: gStringBundle.GetStringFromName("loginsMenu.delete") },
{ label: gStringBundle.GetStringFromName("loginsMenu.deleteAll") },
];
prompt.setSingleChoiceItems(menuItems);
prompt.show(data => {
// Switch on indices of buttons, as they were added when creating login item.
switch (data.button) {
case 0:
this._showPassword(login.password);
break;
case 1:
copyStringShowSnackbar(
login.password,
gStringBundle.GetStringFromName("loginsDetails.passwordCopied")
);
break;
case 2:
copyStringShowSnackbar(
login.username,
gStringBundle.GetStringFromName("loginsDetails.usernameCopied")
);
break;
case 3:
this._selectedLogin = login;
this._showEditLoginDialog(login);
history.pushState({ id: login.guid }, document.title);
break;
case 4:
Accounts.getFirefoxAccount().then(user => {
const promptMessage = user
? gStringBundle.GetStringFromName(
"loginsDialog.confirmDeleteForFxaUser"
)
: gStringBundle.GetStringFromName("loginsDialog.confirmDelete");
const confirmationMessage = gStringBundle.GetStringFromName(
"loginsDetails.deleted"
);
this._showConfirmationPrompt(
promptMessage,
confirmationMessage,
() => Services.logins.removeLogin(login)
);
});
break;
case 5:
Accounts.getFirefoxAccount().then(user => {
const promptMessage = user
? gStringBundle.GetStringFromName(
"loginsDialog.confirmDeleteAllForFxaUser"
)
: gStringBundle.GetStringFromName(
"loginsDialog.confirmDeleteAll"
);
const confirmationMessage = gStringBundle.GetStringFromName(
"loginsDetails.deletedAll"
);
this._showConfirmationPrompt(
promptMessage,
confirmationMessage,
() => Services.logins.removeAllLogins()
);
});
break;
}
});
},
_showConfirmationPrompt: function(
promptMessage,
confirmationMessage,
actionToPerform
) {
new Prompt({
window: window,
message: promptMessage,
buttons: [
// Use default, generic values
gStringBundle.GetStringFromName("loginsDialog.confirm"),
gStringBundle.GetStringFromName("loginsDialog.cancel"),
],
}).show(data => {
switch (data.button) {
case 0:
// Corresponds to "confirm" button.
actionToPerform();
Snackbars.show(confirmationMessage, Snackbars.LENGTH_LONG);
}
});
},
_loadFavicon: function(aImg, aOrigin) {
// Load favicon from cache.
EventDispatcher.instance
.sendRequestForResult({
type: "Favicon:Request",
url: aOrigin,
skipNetwork: true,
})
.then(
function(faviconUrl) {
aImg.style.backgroundImage = "url('" + faviconUrl + "')";
aImg.style.visibility = "visible";
},
function(data) {
debug("Favicon cache failure : " + data);
aImg.style.visibility = "visible";
}
);
},
_createItemForLogin: function(login) {
let loginItem = document.createElement("div");
loginItem.setAttribute("loginID", login.guid);
loginItem.className = "login-item list-item";
loginItem.addEventListener("click", this, true);
loginItem.addEventListener("contextmenu", this, true);
// Create item icon.
let img = document.createElement("div");
img.className = "icon";
this._loadFavicon(img, login.origin);
loginItem.appendChild(img);
// Create item details.
let inner = document.createElement("div");
inner.className = "inner";
let details = document.createElement("div");
details.className = "details";
inner.appendChild(details);
let titlePart = document.createElement("div");
titlePart.className = "origin";
titlePart.textContent = login.origin;
details.appendChild(titlePart);
let versionPart = document.createElement("div");
versionPart.textContent = login.httpRealm;
versionPart.className = "realm";
details.appendChild(versionPart);
let descPart = document.createElement("div");
descPart.textContent = login.username;
descPart.className = "username";
inner.appendChild(descPart);
loginItem.appendChild(inner);
loginItem.login = login;
return loginItem;
},
handleEvent: function(event) {
switch (event.type) {
case "popstate": {
this._onPopState(event);
break;
}
case "contextmenu":
case "click": {
this._onLoginClick(event);
break;
}
}
},
observe: function(subject, topic, data) {
switch (topic) {
case "passwordmgr-storage-changed": {
this._reloadList();
break;
}
}
},
_filter: function(event) {
let value = event.target.value.toLowerCase();
let logins = this._logins.filter(login => {
if (login.origin.toLowerCase().includes(value)) {
return true;
}
if (login.username && login.username.toLowerCase().includes(value)) {
return true;
}
if (login.httpRealm && login.httpRealm.toLowerCase().includes(value)) {
return true;
}
return false;
});
this._loadList(logins);
},
};
window.addEventListener("load", Logins.init.bind(Logins));
window.addEventListener("unload", Logins.uninit.bind(Logins));

View File

@ -1,90 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
%globalDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % aboutDTD SYSTEM "chrome://browser/locale/aboutLogins.dtd" >
%aboutDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&aboutLogins.title;</title>
<meta name="viewport" content="width=device-width; user-scalable=0" />
<link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
<link rel="stylesheet" href="chrome://browser/skin/spinner.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/aboutLogins.css" type="text/css"/>
<script type="application/javascript" src="chrome://browser/content/aboutLogins.js"></script>
</head>
<body dir="&locale.dir;">
<div id="logins-list-page">
<div id="logins-header" class="header">
<div>&aboutLogins.title;</div>
<ul class="toolbar-buttons">
<li id="filter-button"></li>
</ul>
</div>
<div id="content-body">
<div id="logins-list" class="list"/>
<div id="filter-input-container" hidden="true">
<input id="filter-input" type="search"/>
<div id="filter-clear"/>
</div>
</div>
<div id="logins-list-loading-body" class="hidden">
<div id="loading-img-container">
<div id="spinner" class="mui-refresh-main">
<div class="mui-refresh-wrapper">
<div class="mui-spinner-wrapper">
<div class="mui-spinner-main">
<div class="mui-spinner-left">
<div class="mui-half-circle-left" />
</div>
<div class="mui-spinner-right">
<div class="mui-half-circle-right" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="empty-body" class="hidden">
<div id="empty-obj-text-container">
<object type="image/svg+xml" id="empty-icon" data="chrome://browser/skin/images/icon_key_emptypage.svg"/>
<div class="empty-text">&aboutLogins.emptyLoginText;</div>
<div class="empty-hint">&aboutLogins.emptyLoginHint;</div>
</div>
</div>
</div>
<div id="edit-login-page" class="hidden">
<div id="edit-login-header" class="header">
<div id="edit-login-header-text"/>
</div>
<div class="edit-login-div">
<div id="favicon" class="edit-login-icon"/>
<input type="text" name="origin" id="origin" class="edit-login-input" disabled="disabled"/>
</div>
<div class="edit-login-div">
<input type="text" name="username" id="username" class="edit-login-input" autocomplete="off"/>
</div>
<div class="edit-login-div">
<input type="password" id="password" name="password" value="password" class="edit-login-input" />
<button id="password-btn"></button>
</div>
<div class="edit-login-div">
<button id="update-btn" class="update-button">&aboutLogins.update;</button>
</div>
</div>
</body>
</html>

View File

@ -1,36 +0,0 @@
/* 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";
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
ChromeUtils.import("resource://gre/modules/Services.jsm");
const { PrivateBrowsingUtils } = ChromeUtils.import(
"resource://gre/modules/PrivateBrowsingUtils.jsm"
);
XPCOMUtils.defineLazyGetter(
window,
"gChromeWin",
() => window.docShell.rootTreeItem.domWindow
);
document.addEventListener("DOMContentLoaded", function() {
let BrowserApp = window.gChromeWin.BrowserApp;
if (!PrivateBrowsingUtils.isContentWindowPrivate(window)) {
document.body.setAttribute("class", "normal");
document
.getElementById("newPrivateTabLink")
.addEventListener("click", function() {
BrowserApp.addTab("about:privatebrowsing", {
selected: true,
parentId: BrowserApp.selectedTab.id,
isPrivate: true,
});
});
}
});

View File

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
# 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/.
-->
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % privatebrowsingpageDTD SYSTEM "chrome://browser/locale/aboutPrivateBrowsing.dtd">
%privatebrowsingpageDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&privatebrowsingpage.title;</title>
<meta name="viewport" content="width=device-width, initial-scale=1; user-scalable=no"/>
<link rel="stylesheet" href="chrome://browser/skin/aboutPrivateBrowsing.css" type="text/css" media="all"/>
<link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
<script type="application/javascript" src="chrome://browser/content/aboutPrivateBrowsing.js"></script>
</head>
<body class="private" dir="&locale.dir;">
<img class="showPrivate masq" src="chrome://browser/skin/images/privatebrowsing-mask-and-shield.svg" />
<img class="showNormal masq" src="chrome://browser/skin/images/privatebrowsing-mask.png" />
<h1 class="showPrivate">&privatebrowsingpage.title;<br />&privatebrowsingpage.title.private;</h1>
<h1 class="showNormal">&privatebrowsingpage.title.normal1;</h1>
<div class="contentSection">
<p class="showPrivate">&privatebrowsingpage.description.trackingProtection;<br /><br />&privatebrowsingpage.description.privateDetails;</p>
<p class="showNormal">&privatebrowsingpage.description.normal2;</p>
<p class="showPrivate"><a href="https://support.mozilla.org/kb/private-browsing-firefox-android">&privatebrowsingpage.link.private;</a></p>
<p class="showNormal"><a href="#" id="newPrivateTabLink">&privatebrowsingpage.link.normal;</a></p>
</div>
</body>
</html>

View File

@ -1,95 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title data-l10n-id="rights-title"></title>
<link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css" type="text/css"/>
<link rel="localization" href="branding/brand.ftl"/>
<link rel="localization" href="toolkit/about/aboutRights.ftl"/>
</head>
<body id="your-rights">
<div class="container">
<h1 data-l10n-id="rights-title"></h1>
<p data-l10n-id="rights-intro"></p>
<ul>
<li data-l10n-id="rights-intro-point-1"><a href="http://www.mozilla.org/MPL/" data-l10n-name="mozilla-public-license-link"></a></li>
<!-- Point 2 discusses Mozilla trademarks, and isn't needed when the build is unbranded.
- Point 4 discusses privacy policy, unbranded builds get a placeholder (for the vendor to replace)
- Point 5 discusses web service terms, unbranded builds gets a placeholder (for the vendor to replace) -->
<li data-l10n-id="rights-intro-point-2"><a href="http://www.mozilla.org/foundation/trademarks/policy.html" data-l10n-name="mozilla-trademarks-link"></a></li>
<li data-l10n-id="rights-intro-point-3"></li>
<li data-l10n-id="rights-intro-point-4"><a href="https://www.mozilla.org/legal/privacy/firefox.html" data-l10n-name="mozilla-privacy-policy-link"></a></li>
<li data-l10n-id="rights-intro-point-5"><a href="about:rights#webservices" onclick="showServices();" data-l10n-name="mozilla-service-terms-link"></a></li>
<li data-l10n-id="rights-intro-point-6"></li>
</ul>
<div id="webservices-container">
<a name="webservices"/>
<h3 data-l10n-id="rights-webservices-header"></h3>
<p data-l10n-id="rights-webservices"><a href="about:rights#disabling-webservices" onclick="showDisablingServices();" id="showDisablingWebServices" data-l10n-name="mozilla-disable-service-link"></a></p>
<div id="disabling-webservices-container" style="margin-left:40px;">
<a name="disabling-webservices"/>
<!-- Safe Browsing cannot be disabled in Firefox Mobile; these instructions show how to do it on desktop
<p><strong>&rights.safebrowsing-a;</strong>&rights.safebrowsing-b;</p>
<ul>
<li>&rights.safebrowsing-term1;</li>
<li>&rights.safebrowsing-term2;</li>
<li>&rights2.safebrowsing-term3;</li>
<li>&rights.safebrowsing-term4;</li>
</ul>
-->
<p data-l10n-id="rights-locationawarebrowsing"></p>
<ul>
<li data-l10n-id="rights-locationawarebrowsing-term-1"></li>
<li data-l10n-id="rights-locationawarebrowsing-term-2"></li>
<li data-l10n-id="rights-locationawarebrowsing-term-3"></li>
<li data-l10n-id="rights-locationawarebrowsing-term-4"></li>
</ul>
</div>
<ol>
<!-- Terms only apply to official builds, unbranded builds get a placeholder. -->
<li data-l10n-id="rights-webservices-term-1"></li>
<li data-l10n-id="rights-webservices-term-2"></li>
<li data-l10n-id="rights-webservices-term-3"></li>
<li data-l10n-id="rights-webservices-term-4"></li>
<li data-l10n-id="rights-webservices-term-5"></li>
<li data-l10n-id="rights-webservices-term-6"></li>
<li data-l10n-id="rights-webservices-term-7"></li>
</ol>
</div>
</div>
<script type="application/javascript"><![CDATA[
var servicesDiv = document.getElementById("webservices-container");
servicesDiv.style.display = "none";
function showServices() {
servicesDiv.style.display = "";
}
var disablingServicesDiv = document.getElementById("disabling-webservices-container");
disablingServicesDiv.style.display = "none";
function showDisablingServices() {
disablingServicesDiv.style.display = "";
}
]]></script>
</body>
</html>

View File

@ -1,211 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % blockedSiteDTD SYSTEM "chrome://browser/locale/phishing.dtd">
%blockedSiteDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width; user-scalable=false" />
<link rel="stylesheet" href="chrome://browser/skin/netError.css" media="all" />
<link rel="icon" type="image/png" id="favicon" sizes="64x64" href="chrome://browser/skin/images/blocked-warning.png"/>
<script type="application/javascript"><![CDATA[
// Error url MUST be formatted like this:
// about:blocked?e=error_code&u=url(&o=1)?
// (o=1 when user overrides are allowed)
// Note that this file uses document.documentURI to get
// the URL (with the format from above). This is because
// document.location.href gets the current URI off the docshell,
// which is the URL displayed in the location bar, i.e.
// the URI that the user attempted to load.
function getErrorCode() {
var url = document.documentURI;
var error = url.search(/e\=/);
var duffUrl = url.search(/\&u\=/);
return decodeURIComponent(url.slice(error + 2, duffUrl));
}
function getURL() {
var url = document.documentURI;
var match = url.match(/&u=([^&]+)&/);
// match == null if not found; if so, return an empty string
// instead of what would turn out to be portions of the URI
if (!match)
return "";
url = decodeURIComponent(match[1]);
// If this is a view-source page, then get then real URI of the page
if (/^view-source\:/.test(url))
url = url.slice(12);
return url;
}
/**
* Check whether this warning page should be overridable or whether
* the "ignore warning" button should be hidden.
*/
function getOverride() {
var url = document.documentURI;
var match = url.match(/&o=1&/);
return !!match;
}
/**
* Attempt to get the hostname via document.location. Fail back
* to getURL so that we always return something meaningful.
*/
function getHostString() {
try {
return document.location.hostname;
} catch (e) {
return getURL();
}
}
function initPage() {
var error = "";
switch (getErrorCode()) {
case "malwareBlocked" :
error = "malware";
break;
case "deceptiveBlocked" :
error = "phishing";
break;
case "unwantedBlocked" :
error = "unwanted";
break;
case "harmfulBlocked" :
error = "harmful";
break;
default:
return;
}
var el;
if (error !== "malware") {
el = document.getElementById("errorTitleText_malware");
el.remove();
el = document.getElementById("errorShortDescText_malware");
el.remove();
el = document.getElementById("errorLongDescText_malware");
el.remove();
}
if (error !== "phishing") {
el = document.getElementById("errorTitleText_phishing");
el.remove();
el = document.getElementById("errorShortDescText_phishing");
el.remove();
el = document.getElementById("errorLongDescText_phishing");
el.remove();
}
if (error !== "unwanted") {
el = document.getElementById("errorTitleText_unwanted");
el.remove();
el = document.getElementById("errorShortDescText_unwanted");
el.remove();
el = document.getElementById("errorLongDescText_unwanted");
el.remove();
}
if (error !== "harmful") {
el = document.getElementById("errorTitleText_harmful");
el.remove();
el = document.getElementById("errorShortDescText_harmful");
el.remove();
}
if (!getOverride()) {
var btn = document.getElementById("ignoreWarningButton");
if (btn) {
btn.remove();
}
}
// Set sitename
let siteElem = document.getElementById(error + "_sitename");
if (siteElem) {
siteElem.textContent = getHostString();
}
document.title = document.getElementById("errorTitleText_" + error)
.innerHTML;
// Inform the test harness that we're done loading the page
var event = new CustomEvent("AboutBlockedLoaded", { bubbles: true });
document.dispatchEvent(event);
}
]]></script>
</head>
<body id="errorPage" class="blockedsite" dir="&locale.dir;">
<div id="errorPageContainer">
<!-- Error Title -->
<div id="errorTitle">
<h1 id="errorTitleText_phishing" class="errorTitleText">&safeb.blocked.phishingPage.title3;</h1>
<h1 id="errorTitleText_malware" class="errorTitleText">&safeb.blocked.malwarePage.title;</h1>
<h1 id="errorTitleText_unwanted" class="errorTitleText">&safeb.blocked.unwantedPage.title;</h1>
<h1 id="errorTitleText_harmful" class="errorTitleText">&safeb.blocked.harmfulPage.title;</h1>
</div>
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc3;</p>
<p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc;</p>
<p id="errorShortDescText_unwanted">&safeb.blocked.unwantedPage.shortDesc;</p>
<p id="errorShortDescText_harmful">&safeb.blocked.harmfulPage.shortDesc;</p>
</div>
<!-- Long Description -->
<div id="errorLongDesc">
<p id="errorLongDescText_phishing">&safeb.blocked.phishingPage.longDesc3;</p>
<p id="errorLongDescText_malware">&safeb.blocked.malwarePage.longDesc;</p>
<p id="errorLongDescText_unwanted">&safeb.blocked.unwantedPage.longDesc;</p>
</div>
<!-- Advisory -->
<div id="advisoryDesc">
<p id="advisoryDescText">&safeb.palm.advisory.desc;</p>
</div>
<!-- Action buttons -->
<div id="buttons">
<!-- Commands handled in browser.js -->
<button id="getMeOutButton">&safeb.palm.accept.label;</button>
<button id="reportButton">&safeb.palm.reportPage.label;</button>
</div>
</div>
<div id="ignoreWarning">
<button id="ignoreWarningButton">&safeb.palm.decline.label;</button>
</div>
</div>
<!--
- Note: It is important to run the script this way, instead of using
- an onload handler. This is because error pages are loaded as
- LOAD_BACKGROUND, which means that onload handlers will not be executed.
-->
<script type="application/javascript">initPage();</script>
</body>
</html>

View File

@ -1,4 +0,0 @@
/* 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/. */

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<window id="main-window"
onload="BrowserApp.startup();"
windowtype="navigator:browser"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://browser/content/browser.js"/>
<deck id="browsers" flex="1"/>
</window>

View File

@ -1,637 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AboutReader",
"resource://gre/modules/AboutReader.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"ReaderMode",
"resource://gre/modules/ReaderMode.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Readerable",
"resource://gre/modules/Readerable.jsm"
);
XPCOMUtils.defineLazyGetter(this, "gPipNSSBundle", function() {
return Services.strings.createBundle(
"chrome://pipnss/locale/pipnss.properties"
);
});
XPCOMUtils.defineLazyGetter(this, "gNSSErrorsBundle", function() {
return Services.strings.createBundle(
"chrome://pipnss/locale/nsserrors.properties"
);
});
var dump = ChromeUtils.import(
"resource://gre/modules/AndroidLog.jsm",
{}
).AndroidLog.d.bind(null, "Content");
var global = this;
var AboutBlockedSiteListener = {
init(chromeGlobal) {
addEventListener("AboutBlockedLoaded", this, false, true);
},
get isBlockedSite() {
return content.document.documentURI.startsWith("about:blocked");
},
handleEvent(aEvent) {
if (!this.isBlockedSite) {
return;
}
if (aEvent.type != "AboutBlockedLoaded") {
return;
}
let provider = "";
if (docShell.failedChannel) {
let classifiedChannel = docShell.failedChannel.QueryInterface(
Ci.nsIClassifiedChannel
);
if (classifiedChannel) {
provider = classifiedChannel.matchedProvider;
}
}
let advisoryUrl = Services.prefs.getCharPref(
"browser.safebrowsing.provider." + provider + ".advisoryURL",
""
);
if (!advisoryUrl) {
let el = content.document.getElementById("advisoryDesc");
el.remove();
return;
}
let advisoryLinkText = Services.prefs.getCharPref(
"browser.safebrowsing.provider." + provider + ".advisoryName",
""
);
if (!advisoryLinkText) {
let el = content.document.getElementById("advisoryDesc");
el.remove();
return;
}
let anchorEl = content.document.getElementById("advisory_provider");
anchorEl.setAttribute("href", advisoryUrl);
anchorEl.textContent = advisoryLinkText;
},
};
AboutBlockedSiteListener.init();
/* The following code, in particular AboutCertErrorListener and
* AboutNetErrorListener, is mostly copied from content browser.js and content.js.
* Certificate error handling should be unified to remove this duplicated code.
*/
const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
const SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11;
const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13;
const SEC_ERROR_UNTRUSTED_ISSUER = SEC_ERROR_BASE + 20;
const SEC_ERROR_UNTRUSTED_CERT = SEC_ERROR_BASE + 21;
const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = SEC_ERROR_BASE + 30;
const SEC_ERROR_CA_CERT_INVALID = SEC_ERROR_BASE + 36;
const SEC_ERROR_OCSP_FUTURE_RESPONSE = SEC_ERROR_BASE + 131;
const SEC_ERROR_OCSP_OLD_RESPONSE = SEC_ERROR_BASE + 132;
const SEC_ERROR_REUSED_ISSUER_AND_SERIAL = SEC_ERROR_BASE + 138;
const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176;
const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE =
MOZILLA_PKIX_ERROR_BASE + 5;
const MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE =
MOZILLA_PKIX_ERROR_BASE + 6;
const MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED =
MOZILLA_PKIX_ERROR_BASE + 13;
const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
const SSL_ERROR_SSL_DISABLED = SSL_ERROR_BASE + 20;
const SSL_ERROR_SSL2_DISABLED = SSL_ERROR_BASE + 14;
var AboutNetErrorListener = {
init(chromeGlobal) {
addEventListener("AboutNetErrorLoad", this, false, true);
},
get isNetErrorSite() {
return content.document.documentURI.startsWith("about:neterror");
},
_getErrorMessageFromCode(securityInfo, doc) {
let uri = Services.io.newURI(doc.location);
let hostString = uri.host;
if (uri.port != 443 && uri.port != -1) {
hostString += ":" + uri.port;
}
let id_str = "";
switch (securityInfo.errorCode) {
case SSL_ERROR_SSL_DISABLED:
id_str = "PSMERR_SSL_Disabled";
break;
case SSL_ERROR_SSL2_DISABLED:
id_str = "PSMERR_SSL2_Disabled";
break;
case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
id_str = "PSMERR_HostReusedIssuerSerial";
break;
}
let nss_error_id_str = securityInfo.errorCodeString;
let msg2 = "";
if (id_str) {
msg2 = gPipNSSBundle.GetStringFromName(id_str) + "\n";
} else if (nss_error_id_str) {
msg2 = gNSSErrorsBundle.GetStringFromName(nss_error_id_str) + "\n";
}
if (!msg2) {
// We couldn't get an error message. Use the error string.
// Note that this is different from before where we used PR_ErrorToString.
msg2 = nss_error_id_str;
}
let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2", [
hostString,
msg2,
]);
if (nss_error_id_str) {
msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3", [
nss_error_id_str,
]);
}
return msg;
},
handleEvent(aEvent) {
if (!this.isNetErrorSite) {
return;
}
if (aEvent.type != "AboutNetErrorLoad") {
return;
}
let { securityInfo } = docShell.failedChannel;
// We don't have a securityInfo when this is for example a DNS error.
if (securityInfo) {
securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
let msg = this._getErrorMessageFromCode(
securityInfo,
aEvent.originalTarget.ownerGlobal
);
let id = content.document.getElementById("errorShortDescText");
id.textContent = msg;
}
},
};
AboutNetErrorListener.init();
var AboutCertErrorListener = {
init(chromeGlobal) {
addEventListener("AboutCertErrorLoad", this, false, true);
},
get isCertErrorSite() {
return content.document.documentURI.startsWith("about:certerror");
},
_setTechDetailsMsgPart1(hostString, securityInfo, technicalInfo, doc) {
let msg = gPipNSSBundle.formatStringFromName("certErrorIntro", [
hostString,
]);
msg += "\n\n";
if (securityInfo.isUntrusted && !securityInfo.serverCert.isSelfSigned) {
switch (securityInfo.errorCode) {
case SEC_ERROR_UNKNOWN_ISSUER:
msg +=
gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer") +
"\n";
msg +=
gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer2") +
"\n";
msg +=
gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer3") +
"\n";
break;
case SEC_ERROR_CA_CERT_INVALID:
msg +=
gPipNSSBundle.GetStringFromName("certErrorTrust_CaInvalid") + "\n";
break;
case SEC_ERROR_UNTRUSTED_ISSUER:
msg +=
gPipNSSBundle.GetStringFromName("certErrorTrust_Issuer") + "\n";
break;
case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
msg +=
gPipNSSBundle.GetStringFromName(
"certErrorTrust_SignatureAlgorithmDisabled"
) + "\n";
break;
case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
msg +=
gPipNSSBundle.GetStringFromName("certErrorTrust_ExpiredIssuer") +
"\n";
break;
// This error code currently only exists for the Symantec distrust, we may need to adjust
// it to fit other distrusts later.
case MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED:
msg +=
gPipNSSBundle.formatStringFromName("certErrorTrust_Symantec", [
hostString,
]) + "\n";
break;
case SEC_ERROR_UNTRUSTED_CERT:
default:
msg +=
gPipNSSBundle.GetStringFromName("certErrorTrust_Untrusted") + "\n";
}
}
if (securityInfo.isUntrusted && securityInfo.serverCert.isSelfSigned) {
msg +=
gPipNSSBundle.GetStringFromName("certErrorTrust_SelfSigned") + "\n";
}
technicalInfo.appendChild(doc.createTextNode(msg));
},
_setTechDetails(securityInfo, location) {
if (!securityInfo || !location) {
return;
}
let validity = securityInfo.serverCert.validity;
let doc = content.document;
// CSS class and error code are set from nsDocShell.
let searchParams = new URLSearchParams(doc.documentURI.split("?")[1]);
let cssClass = searchParams.get("s");
let error = searchParams.get("e");
let technicalInfo = doc.getElementById("technicalContentText");
let uri = Services.io.newURI(location);
let hostString = uri.host;
if (uri.port != 443 && uri.port != -1) {
hostString += ":" + uri.port;
}
// This error code currently only exists for the Symantec distrust
// in Firefox 63, so we add copy explaining that to the user.
// In case of future distrusts of that scale we might need to add
// additional parameters that allow us to identify the affected party
// without replicating the complex logic from certverifier code.
if (
securityInfo.errorCode ==
MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED
) {
let introContent = doc.getElementById("introContent");
let description = doc.createElement("p");
description.textContent = gPipNSSBundle.formatStringFromName(
"certErrorSymantecDistrustDescription",
[hostString]
);
introContent.append(description);
// The regular "what should I do" message does not make sense in this case.
doc.getElementById(
"whatShouldIDoContentText"
).textContent = gPipNSSBundle.GetStringFromName(
"certErrorSymantecDistrustAdministrator"
);
}
this._setTechDetailsMsgPart1(hostString, securityInfo, technicalInfo, doc);
if (securityInfo.isDomainMismatch) {
let subjectAltNamesList = securityInfo.serverCert.subjectAltNames;
let subjectAltNames = subjectAltNamesList.split(",");
let numSubjectAltNames = subjectAltNames.length;
let msgPrefix = "";
if (numSubjectAltNames != 0) {
if (numSubjectAltNames == 1) {
msgPrefix = gPipNSSBundle.GetStringFromName(
"certErrorMismatchSinglePrefix"
);
// Let's check if we want to make this a link.
let okHost = subjectAltNamesList;
let href = "";
let thisHost = doc.location.hostname;
let proto = doc.location.protocol + "//";
// If okHost is a wildcard domain ("*.example.com") let's
// use "www" instead. "*.example.com" isn't going to
// get anyone anywhere useful. bug 432491
okHost = okHost.replace(/^\*\./, "www.");
/* case #1:
* example.com uses an invalid security certificate.
*
* The certificate is only valid for www.example.com
*
* Make sure to include the "." ahead of thisHost so that
* a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
*
* We'd normally just use a RegExp here except that we lack a
* library function to escape them properly (bug 248062), and
* domain names are famous for having '.' characters in them,
* which would allow spurious and possibly hostile matches.
*/
if (okHost.endsWith("." + thisHost)) {
href = proto + okHost;
}
/* case #2:
* browser.garage.maemo.org uses an invalid security certificate.
*
* The certificate is only valid for garage.maemo.org
*/
if (thisHost.endsWith("." + okHost)) {
href = proto + okHost;
}
// If we set a link, meaning there's something helpful for
// the user here, expand the section by default
if (href && cssClass != "expertBadCert") {
doc.getElementById("technicalContentText").style.display = "block";
}
// Set the link if we want it.
if (href) {
let referrerlink = doc.createElement("a");
referrerlink.append(subjectAltNamesList + "\n");
referrerlink.title = subjectAltNamesList;
referrerlink.id = "cert_domain_link";
referrerlink.href = href;
let fragment = BrowserUtils.getLocalizedFragment(
doc,
msgPrefix,
referrerlink
);
technicalInfo.appendChild(fragment);
} else {
let fragment = BrowserUtils.getLocalizedFragment(
doc,
msgPrefix,
subjectAltNamesList
);
technicalInfo.appendChild(fragment);
}
technicalInfo.append("\n");
} else {
let msg =
gPipNSSBundle.GetStringFromName("certErrorMismatchMultiple") + "\n";
for (let i = 0; i < numSubjectAltNames; i++) {
msg += subjectAltNames[i];
if (i != numSubjectAltNames - 1) {
msg += ", ";
}
}
technicalInfo.append(msg + "\n");
}
} else {
let msg = gPipNSSBundle.formatStringFromName("certErrorMismatch", [
hostString,
]);
technicalInfo.append(msg + "\n");
}
}
if (securityInfo.isNotValidAtThisTime) {
let nowTime = new Date().getTime() * 1000;
let dateOptions = {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
};
let now = new Services.intl.DateTimeFormat(undefined, dateOptions).format(
new Date()
);
let msg = "";
if (validity.notBefore) {
if (nowTime > validity.notAfter) {
msg +=
gPipNSSBundle.formatStringFromName("certErrorExpiredNow", [
validity.notAfterLocalTime,
now,
]) + "\n";
} else {
msg +=
gPipNSSBundle.formatStringFromName("certErrorNotYetValidNow", [
validity.notBeforeLocalTime,
now,
]) + "\n";
}
} else {
// If something goes wrong, we assume the cert expired.
msg +=
gPipNSSBundle.formatStringFromName("certErrorExpiredNow", ["", now]) +
"\n";
}
technicalInfo.append(msg);
}
technicalInfo.append("\n");
// Add link to certificate and error message.
let errorCodeMsg = gPipNSSBundle.formatStringFromName(
"certErrorCodePrefix3",
[securityInfo.errorCodeString]
);
technicalInfo.append(errorCodeMsg);
},
handleEvent(aEvent) {
if (!this.isCertErrorSite) {
return;
}
if (aEvent.type != "AboutCertErrorLoad") {
return;
}
let ownerDoc = aEvent.originalTarget.ownerGlobal;
let securityInfo =
docShell.failedChannel && docShell.failedChannel.securityInfo;
securityInfo
.QueryInterface(Ci.nsITransportSecurityInfo)
.QueryInterface(Ci.nsISerializable);
this._setTechDetails(securityInfo, ownerDoc.location.href);
},
};
AboutCertErrorListener.init();
// This is copied from desktop's tab-content.js. See bug 1153485 about sharing this code somehow.
var AboutReaderListener = {
_articlePromise: null,
_isLeavingReaderableReaderMode: false,
init: function() {
addEventListener("AboutReaderContentLoaded", this, false, true);
addEventListener("DOMContentLoaded", this, false);
addEventListener("pageshow", this, false);
addEventListener("pagehide", this, false);
addMessageListener("Reader:ToggleReaderMode", this);
addMessageListener("Reader:PushState", this);
},
receiveMessage: function(message) {
switch (message.name) {
case "Reader:ToggleReaderMode":
let url = content.document.location.href;
if (!this.isAboutReader) {
this._articlePromise = ReaderMode.parseDocument(
content.document
).catch(Cu.reportError);
ReaderMode.enterReaderMode(docShell, content);
} else {
this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
ReaderMode.leaveReaderMode(docShell, content);
}
break;
case "Reader:PushState":
this.updateReaderButton(!!(message.data && message.data.isArticle));
break;
}
},
get isAboutReader() {
return content.document.documentURI.startsWith("about:reader");
},
get isReaderableAboutReader() {
return (
this.isAboutReader && !content.document.documentElement.dataset.isError
);
},
get isErrorPage() {
return (
content.document.documentURI.startsWith("about:neterror") ||
content.document.documentURI.startsWith("about:certerror") ||
content.document.documentURI.startsWith("about:blocked")
);
},
handleEvent: function(aEvent) {
if (aEvent.originalTarget.defaultView != content) {
return;
}
switch (aEvent.type) {
case "AboutReaderContentLoaded":
if (!this.isAboutReader) {
return;
}
// If we are restoring multiple reader mode tabs during session restore, duplicate "DOMContentLoaded"
// events may be fired for the visible tab. The inital "DOMContentLoaded" may be received before the
// document body is available, so we avoid instantiating an AboutReader object, expecting that a
// valid message will follow. See bug 925983.
if (content.document.body) {
new AboutReader(global, content, this._articlePromise);
this._articlePromise = null;
}
break;
case "pagehide":
// this._isLeavingReaderableReaderMode is used here to keep the Reader Mode icon
// visible in the location bar when transitioning from reader-mode page
// back to the source page.
sendAsyncMessage("Reader:UpdateReaderButton", {
isArticle: this._isLeavingReaderableReaderMode,
});
if (this._isLeavingReaderableReaderMode) {
this._isLeavingReaderableReaderMode = false;
}
break;
case "pageshow":
// If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
// event, so we need to rely on "pageshow" in this case.
if (aEvent.persisted) {
this.updateReaderButton();
}
break;
case "DOMContentLoaded":
this.updateReaderButton();
break;
}
},
updateReaderButton: function(forceNonArticle) {
// Do not show Reader View icon on error pages (bug 1320900)
if (this.isErrorPage) {
sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
} else if (
!Readerable.isEnabledForParseOnLoad ||
this.isAboutReader ||
!(content.document instanceof content.HTMLDocument) ||
content.document.mozSyntheticDocument
) {
} else {
this.scheduleReadabilityCheckPostPaint(forceNonArticle);
}
},
cancelPotentialPendingReadabilityCheck: function() {
if (this._pendingReadabilityCheck) {
removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
delete this._pendingReadabilityCheck;
}
},
scheduleReadabilityCheckPostPaint: function(forceNonArticle) {
if (this._pendingReadabilityCheck) {
// We need to stop this check before we re-add one because we don't know
// if forceNonArticle was true or false last time.
this.cancelPotentialPendingReadabilityCheck();
}
this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(
this,
forceNonArticle
);
addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
},
onPaintWhenWaitedFor: function(forceNonArticle, event) {
// In non-e10s, we'll get called for paints other than ours, and so it's
// possible that this page hasn't been laid out yet, in which case we
// should wait until we get an event that does relate to our layout. We
// determine whether any of our content got painted by checking if there
// are any painted rects.
if (!event.clientRects.length) {
return;
}
Services.console.logStringMessage(`ON PAINT WHEN WAITED FOR\n`);
this.cancelPotentialPendingReadabilityCheck();
// Only send updates when there are articles; there's no point updating with
// |false| all the time.
if (Readerable.isProbablyReaderable(content.document)) {
sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
} else if (forceNonArticle) {
sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
}
},
};
AboutReaderListener.init();
Services.obs.notifyObservers(this, "tab-content-frameloader-created");

View File

@ -1,114 +0,0 @@
# 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/.
# LOCALIZATION NOTE: do not localize
af=Afrikaans
ak=Akan
ar=عربي
as=অসমীয়া
ast-ES=Asturianu
be=Беларуская
bg=Български
bn-BD=বাংলা (বাংলাদেশ)
bn-IN=বাংলা (ভারত)
br-FR=Brezhoneg
ca=català
ca-valencia=català (valencià)
cs=Čeština
cy=Cymraeg
da=Dansk
de=Deutsch
de-AT=Deutsch (Österreich)
de-CH=Deutsch (Schweiz)
de-DE=Deutsch (Deutschland)
el=Ελληνικά
en-AU=English (Australian)
en-CA=English (Canadian)
en-GB=English (British)
en-NZ=English (New Zealand)
en-US=English (US)
en-ZA=English (South African)
eo=Esperanto
es-AR=Español (de Argentina)
es-CL=Español (de Chile)
es-ES=Español (de España)
es-MX=Español (de México)
et=Eesti keel
eu=Euskara
fa=فارسی
fi=suomi
fr=Français
fur-IT=Furlan
fy-NL=Frysk
ga-IE=Gaeilge
gl=Galego
gu-IN=ગુજરાતી
he=עברית
hi=हिन्दी
hi-IN=हिन्दी (भारत)
hr=Hrvatski
hsb=Hornjoserbsce
hu=Magyar
hy-AM=Հայերեն
id=Bahasa Indonesia
is=íslenska
it=Italiano
ja=日本語
ka=ქართული
kk=Қазақ
kn=ಕನ್ನಡ
ko=한국어
ku=Kurdî
la=Latina
lt=lietuvių
lv=Latviešu
mg=Malagasy
mi=Māori (Aotearoa)
mk=Македонски
ml=മലയാളം
mn=Монгол
mr=मराठी
nb-NO=Norsk bokmål
ne-NP=नेपाली
nl=Nederlands
nn-NO=Norsk nynorsk
nr=isiNdebele Sepumalanga
nso=Sepedi
oc=occitan (lengadocian)
or=ଓଡ଼ିଆ
pa-IN=ਪੰਜਾਬੀ
pl=Polski
pt-BR=Português (do Brasil)
pt-PT=Português (Europeu)
rm=rumantsch
ro=română
ru=Русский
rw=Ikinyarwanda
si=සිංහල
sk=slovenčina
sl=slovensko
sq=Shqip
sr=Српски
sr-Latn=Srpski
ss=Siswati
st=Sesotho
sv-SE=Svenska
ta=தமிழ்
ta-IN=தமிழ் (இந்தியா)
ta-LK=தமிழ் (இலங்கை)
te=తెలుగు
th=ไทย
tn=Setswana
tr=Türkçe
ts=Mutsonga
tt-RU=Tatarça
uk=Українська
ur=اُردو
ve=Tshivenḓa
vi=Tiếng Việt
wo=Wolof
xh=isiXhosa
zh-CN=中文 (简体)
zh-TW=正體中文 (繁體)
zu=isiZulu

View File

@ -1,315 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % netErrorDTD
SYSTEM "chrome://global/locale/netError.dtd">
%netErrorDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width; user-scalable=false;" />
<title>&loadError.label;</title>
<link rel="stylesheet" href="chrome://browser/skin/netError.css" media="all" />
<!-- If the location of the favicon is changed here, the FAVICON_ERRORPAGE_URL symbol in
toolkit/components/places/src/nsFaviconService.h should be updated. -->
<link rel="icon" type="image/png" id="favicon" sizes="64x64" href="chrome://browser/skin/images/errorpage-warning.png"/>
<script type="application/javascript"><![CDATA[
// Error url MUST be formatted like this:
// moz-neterror:page?e=error&u=url&d=desc
//
// or optionally, to specify an alternate CSS class to allow for
// custom styling and favicon:
//
// moz-neterror:page?e=error&u=url&s=classname&d=desc
// Note that this file uses document.documentURI to get
// the URL (with the format from above). This is because
// document.location.href gets the current URI off the docshell,
// which is the URL displayed in the location bar, i.e.
// the URI that the user attempted to load.
function getErrorCode() {
var url = document.documentURI;
var error = url.search(/e\=/);
var duffUrl = url.search(/\&u\=/);
return decodeURIComponent(url.slice(error + 2, duffUrl));
}
function getCSSClass() {
var url = document.documentURI;
var matches = url.match(/s\=([^&]+)\&/);
// s is optional, if no match just return nothing
if (!matches || matches.length < 2)
return "";
// parenthetical match is the second entry
return decodeURIComponent(matches[1]);
}
function getDescription() {
var url = document.documentURI;
var desc = url.search(/d\=/);
// desc == -1 if not found; if so, return an empty string
// instead of what would turn out to be portions of the URI
if (desc == -1)
return "";
return decodeURIComponent(url.slice(desc + 2));
}
function retryThis(buttonEl) {
// Note: The application may wish to handle switching off "offline mode"
// before this event handler runs, but using a capturing event handler.
// Session history has the URL of the page that failed
// to load, not the one of the error page. So, just call
// reload(), which will also repost POST data correctly.
try {
location.reload();
} catch (e) {
// We probably tried to reload a URI that caused an exception to
// occur; e.g. a nonexistent file.
}
}
function initPage() {
var err = getErrorCode();
// if it's an unknown error or there's no title or description
// defined, get the generic message
var errTitle = document.getElementById("et_" + err);
var errDesc = document.getElementById("ed_" + err);
if (!errTitle || !errDesc) {
errTitle = document.getElementById("et_generic");
errDesc = document.getElementById("ed_generic");
}
var title = document.getElementsByClassName("errorTitleText")[0];
if (title) {
title.parentNode.replaceChild(errTitle, title);
// change id to the replaced child's id so styling works
errTitle.classList.add("errorTitleText");
}
var ld = document.getElementById("errorLongDesc");
if (ld) {
ld.parentNode.replaceChild(errDesc, ld);
// change id to the replaced child's id so styling works
errDesc.id = "errorLongDesc";
}
// remove undisplayed errors to avoid bug 39098
var errContainer = document.getElementById("errorContainer");
errContainer.remove();
var className = getCSSClass();
if (className && className != "expertBadCert") {
// Associate a CSS class with the root of the page, if one was passed in,
// to allow custom styling.
// Not "expertBadCert" though, don't want to deal with the favicon
document.documentElement.className = className;
// Also, if they specified a CSS class, they must supply their own
// favicon. In order to trigger the browser to repaint though, we
// need to remove/add the link element.
var favicon = document.getElementById("favicon");
var faviconParent = favicon.parentNode;
faviconParent.removeChild(favicon);
favicon.setAttribute("href", "chrome://global/skin/icons/" + className + "_favicon.png");
faviconParent.appendChild(favicon);
}
if (className == "expertBadCert") {
showSecuritySection();
}
if (err == "remoteXUL") {
// Remove the "Try again" button for remote XUL errors given that
// it is useless.
document.getElementById("errorTryAgain").style.display = "none";
}
if (err == "cspBlocked") {
// Remove the "Try again" button for CSP violations, since it's
// almost certainly useless. (Bug 553180)
document.getElementById("errorTryAgain").style.display = "none";
}
if (err == "nssBadCert") {
// Remove the "Try again" button for security exceptions, since it's
// almost certainly useless.
document.getElementById("errorTryAgain").style.display = "none";
document.getElementById("errorPage").setAttribute("class", "certerror");
} else {
// Remove the override block for non-certificate errors. CSS-hiding
// isn't good enough here, because of bug 39098
var secOverride = document.getElementById("securityOverrideDiv");
secOverride.remove();
}
if (err != "nssBadCert" && err != "nssFailure2") {
var sd = document.getElementById("errorShortDescText");
if (sd) {
sd.textContent = getDescription();
}
}
if (err == "inadequateSecurityError") {
// Remove the "Try again" button for HTTP/2 inadequate security as it
// is useless.
document.getElementById("errorTryAgain").style.display = "none";
var container = document.getElementById("errorLongDesc");
for (var span of container.querySelectorAll("span.hostname")) {
span.textContent = document.location.hostname;
}
}
var event = new CustomEvent("AboutNetErrorLoad", {bubbles: true});
document.dispatchEvent(event);
}
function showSecuritySection() {
// Swap link out, content in
document.getElementById("securityOverrideContent").style.display = "";
document.getElementById("securityOverrideLink").style.display = "none";
}
function createLink(el, id, text) {
var anchorEl = document.createElement("a");
anchorEl.setAttribute("id", id);
anchorEl.setAttribute("title", text);
anchorEl.appendChild(document.createTextNode(text));
el.appendChild(anchorEl);
}
]]></script>
</head>
<body id="errorPage" dir="&locale.dir;">
<!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
<div id="errorContainer">
<div id="errorTitlesContainer">
<h1 id="et_generic">&generic.title;</h1>
<h1 id="et_dnsNotFound">&dnsNotFound.title;</h1>
<h1 id="et_fileNotFound">&fileNotFound.title;</h1>
<h1 id="et_fileAccessDenied">&fileAccessDenied.title;</h1>
<h1 id="et_malformedURI">&malformedURI.title;</h1>
<h1 id="et_unknownProtocolFound">&unknownProtocolFound.title;</h1>
<h1 id="et_connectionFailure">&connectionFailure.title;</h1>
<h1 id="et_netTimeout">&netTimeout.title;</h1>
<h1 id="et_redirectLoop">&redirectLoop.title;</h1>
<h1 id="et_unknownSocketType">&unknownSocketType.title;</h1>
<h1 id="et_netReset">&netReset.title;</h1>
<h1 id="et_notCached">&notCached.title;</h1>
<!-- Since Fennec not yet have offline mode, change the title to
connectionFailure to prevent confusion -->
<h1 id="et_netOffline">&connectionFailure.title;</h1>
<h1 id="et_netInterrupt">&netInterrupt.title;</h1>
<h1 id="et_deniedPortAccess">&deniedPortAccess.title;</h1>
<h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
<h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
<h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
<h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
<h1 id="et_nssFailure2">&nssFailure2.title;</h1>
<h1 id="et_nssBadCert">&nssBadCert.title;</h1>
<h1 id="et_cspBlocked">&cspBlocked.title;</h1>
<h1 id="et_remoteXUL">&remoteXUL.title;</h1>
<h1 id="et_corruptedContentErrorv2">&corruptedContentErrorv2.title;</h1>
<h1 id="et_sslv3Used">&sslv3Used.title;</h1>
<h1 id="et_weakCryptoUsed">&weakCryptoUsed.title;</h1>
<h1 id="et_inadequateSecurityError">&inadequateSecurityError.title;</h1>
<h1 id="et_networkProtocolError">&networkProtocolError.title;</h1>
</div>
<div id="errorDescriptionsContainer">
<div id="ed_generic">&generic.longDesc;</div>
<div id="ed_dnsNotFound">&dnsNotFound.longDesc4;</div>
<div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
<div id="ed_fileAccessDenied">&fileAccessDenied.longDesc;</div>
<div id="ed_malformedURI">&malformedURI.longDesc2;</div>
<div id="ed_unknownProtocolFound">&unknownProtocolFound.longDesc;</div>
<div id="ed_connectionFailure">&connectionFailure.longDesc2;</div>
<div id="ed_netTimeout">&netTimeout.longDesc2;</div>
<div id="ed_redirectLoop">&redirectLoop.longDesc;</div>
<div id="ed_unknownSocketType">&unknownSocketType.longDesc;</div>
<div id="ed_netReset">&netReset.longDesc2;</div>
<div id="ed_notCached">&notCached.longDesc;</div>
<!-- Change longDesc from netOffline to connectionFailure,
suggesting user to check their wifi/cell_data connection -->
<div id="ed_netOffline">&connectionFailure.longDesc2;</div>
<div id="ed_netInterrupt">&netInterrupt.longDesc2;</div>
<div id="ed_deniedPortAccess">&deniedPortAccess.longDesc;</div>
<div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc3;</div>
<div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
<div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
<div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
<div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
<div id="ed_nssBadCert">&nssBadCert.longDesc2;</div>
<div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
<div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
<div id="ed_corruptedContentErrorv2">&corruptedContentErrorv2.longDesc;</div>
<div id="ed_sslv3Used">&sslv3Used.longDesc;</div>
<div id="ed_weakCryptoUsed">&weakCryptoUsed.longDesc;</div>
<div id="ed_inadequateSecurityError">&inadequateSecurityError.longDesc;</div>
<div id="ed_networkProtocolError">&networkProtocolError.longDesc;</div>
</div>
</div>
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer">
<!-- Error Title -->
<div id="errorTitle">
<h1 class="errorTitleText" />
</div>
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p id="errorShortDescText" />
</div>
<!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
<div id="errorLongDesc" />
<!-- Override section - For ssl errors only. Removed on init for other
error types. -->
<div id="securityOverrideDiv">
<a id="securityOverrideLink" href="javascript:showSecuritySection();" >&securityOverride.linkText;</a>
<div id="securityOverrideContent" style="display: none;">&securityOverride.warningContent;</div>
</div>
</div>
<!-- Retry Button -->
<button id="errorTryAgain" onclick="retryThis(this);">&retry.label;</button>
</div>
<!--
- Note: It is important to run the script this way, instead of using
- an onload handler. This is because error pages are loaded as
- LOAD_BACKGROUND, which means that onload handlers will not be executed.
-->
<script type="application/javascript">initPage();</script>
</body>
</html>

View File

@ -1,74 +0,0 @@
#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/.
chrome.jar:
% content browser %content/ contentaccessible=yes
* content/about.xhtml (content/about.xhtml)
* content/about.js (content/about.js)
content/content.js (content/content.js)
content/aboutAddons.xhtml (content/aboutAddons.xhtml)
content/aboutAddons.js (content/aboutAddons.js)
content/aboutCertError.xhtml (content/aboutCertError.xhtml)
content/aboutDownloads.xhtml (content/aboutDownloads.xhtml)
content/aboutDownloads.js (content/aboutDownloads.js)
content/aboutPrivateBrowsing.xhtml (content/aboutPrivateBrowsing.xhtml)
content/aboutPrivateBrowsing.js (content/aboutPrivateBrowsing.js)
content/Reader.js (content/Reader.js)
content/aboutHome.xhtml (content/aboutHome.xhtml)
content/aboutRights.xhtml (content/aboutRights.xhtml)
content/blockedSite.xhtml (content/blockedSite.xhtml)
content/languages.properties (content/languages.properties)
content/browser.xul (content/browser.xul)
content/browser.css (content/browser.css)
content/browser.js (content/browser.js)
content/PresentationView.xul (content/PresentationView.xul)
content/PresentationView.js (content/PresentationView.js)
content/netError.xhtml (content/netError.xhtml)
content/EmbedRT.js (content/EmbedRT.js)
content/MemoryObserver.js (content/MemoryObserver.js)
content/ConsoleAPI.js (content/ConsoleAPI.js)
content/PrintHelper.js (content/PrintHelper.js)
content/OfflineApps.js (content/OfflineApps.js)
content/MasterPassword.js (content/MasterPassword.js)
content/FindHelper.js (content/FindHelper.js)
content/PermissionsHelper.js (content/PermissionsHelper.js)
content/FeedHandler.js (content/FeedHandler.js)
content/Feedback.js (content/Feedback.js)
content/Linkify.js (content/Linkify.js)
content/CastingApps.js (content/CastingApps.js)
content/RemoteDebugger.js (content/RemoteDebugger.js)
content/aboutAccounts.xhtml (content/aboutAccounts.xhtml)
content/aboutAccounts.js (content/aboutAccounts.js)
content/aboutExperiments.xhtml (content/aboutExperiments.xhtml)
content/aboutExperiments.js (content/aboutExperiments.js)
content/aboutLogins.xhtml (content/aboutLogins.xhtml)
content/aboutLogins.js (content/aboutLogins.js)
content/ExtensionPermissions.js (content/ExtensionPermissions.js)
% override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml
% override chrome://mozapps/content/extensions/extensions.xul chrome://browser/content/aboutAddons.xhtml
# L10n resource overrides.
% override chrome://global/locale/aboutReader.properties chrome://browser/locale/overrides/aboutReader.properties
% override chrome://global/locale/charsetMenu.properties chrome://browser/locale/overrides/charsetMenu.properties
% override chrome://global/locale/commonDialogs.properties chrome://browser/locale/overrides/commonDialogs.properties
% override chrome://global/locale/intl.properties chrome://browser/locale/overrides/intl.properties
% override chrome://global/locale/intl.css chrome://browser/locale/overrides/intl.css
% override chrome://global/locale/search/search.properties chrome://browser/locale/overrides/search/search.properties
% override chrome://pluginproblem/locale/pluginproblem.dtd chrome://browser/locale/overrides/plugins/pluginproblem.dtd
% override chrome://global/locale/mozilla.dtd chrome://browser/locale/overrides/global/mozilla.dtd
% override chrome://global/locale/aboutWebrtc.properties chrome://browser/locale/overrides/global/aboutWebrtc.properties
# overrides for dom l10n, also for en-US
# keep this file list in sync with filter.py
% override chrome://global/locale/global.dtd chrome://browser/locale/overrides/global.dtd
% override chrome://global/locale/AccessFu.properties chrome://browser/locale/overrides/AccessFu.properties
% override chrome://global/locale/dom/dom.properties chrome://browser/locale/overrides/dom/dom.properties
% override chrome://global/locale/plugins.properties chrome://browser/locale/overrides/plugins.properties
# mobile/locales/jar.mn resources and overrides
% override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
% override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties

View File

@ -18,5 +18,3 @@ DEFINES['PACKAGE'] = 'browser'
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME']
JAR_MANIFESTS += ['jar.mn']

View File

@ -1,16 +0,0 @@
# -*- 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/.
with Files('**'):
BUG_COMPONENT = ('Firefox for Android', 'General')
DIRS += [
'webcompat'
]
if not CONFIG['MOZ_UPDATE_CHANNEL'] in ('release', 'esr'):
DIRS += [
'report-site-issue'
]

View File

@ -1,69 +0,0 @@
"use strict";
module.exports = {
"rules": {
// Rules from the mozilla plugin
"mozilla/balanced-listeners": "error",
"mozilla/no-aArgs": "error",
"mozilla/var-only-at-top-level": "error",
"valid-jsdoc": ["error", {
"prefer": {
"return": "returns",
},
"preferType": {
"Boolean": "boolean",
"Number": "number",
"String": "string",
"bool": "boolean",
},
"requireParamDescription": false,
"requireReturn": false,
"requireReturnDescription": false,
}],
// No expressions where a statement is expected
"no-unused-expressions": "error",
// No declaring variables that are never used
"no-unused-vars": "error",
// Disallow using variables outside the blocks they are defined (especially
// since only let and const are used, see "no-var").
"block-scoped-var": "error",
// Warn about cyclomatic complexity in functions.
"complexity": ["error", {"max": 26}],
// Maximum depth callbacks can be nested.
"max-nested-callbacks": ["error", 4],
// Allow the console API aside from console.log.
"no-console": ["error", {allow: ["error", "info", "trace", "warn"]}],
// Disallow fallthrough of case statements, except if there is a comment.
"no-fallthrough": "error",
// Disallow use of multiline strings (use template strings instead).
"no-multi-str": "error",
// Disallow usage of __proto__ property.
"no-proto": "error",
// Disallow use of assignment in return statement. It is preferable for a
// single line of code to have only one easily predictable effect.
"no-return-assign": "error",
// Require use of the second argument for parseInt().
"radix": "error",
// Require "use strict" to be defined globally in the script.
"strict": ["error", "global"],
// Disallow Yoda conditions (where literal value comes first).
"yoda": "error",
// Disallow function or variable declarations in nested blocks
"no-inner-declarations": "error",
},
};

View File

@ -1,306 +0,0 @@
/* 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"];
// If parental controls are on, we don't activate (that is, we don't show our
// menu item or prompt the user when they use "request desktop site".
browser.browserInfo.getParentalControlsEnabled().then(enabled => {
if (enabled) {
return;
}
browser.aboutConfigPrefs.onEndpointPrefChange.addListener(checkEndpointPref);
checkEndpointPref();
activateMenuItem();
activateDesktopViewPrompts();
});
function activateDesktopViewPrompts() {
Promise.all([
browser.l10n.getMessage("webcompat.reportDesktopMode.message"),
browser.l10n.getMessage("webcompat.reportDesktopModeYes.label"),
])
.then(([message, button]) => {
browser.tabExtras.onDesktopSiteRequested.addListener(async tabId => {
browser.tabs
.get(tabId)
.then(tab => {
browser.snackbars
.show(message, button)
.then(() => {
reportForTab(tab);
})
.catch(() => {});
})
.catch(() => {});
});
})
.catch(() => {});
}
function activateMenuItem() {
browser.nativeMenu.show();
browser.l10n
.getMessage("webcompat.menu.name")
.then(label => {
// We use Fennec NativeMenus because its BrowserAction implementation
// lacks support for enabling/disabling its items.
browser.nativeMenu.setLabel(label);
})
.catch(() => {});
browser.nativeMenu.onClicked.addListener(async () => {
const tabs = await browser.tabs.query({ active: true });
reportForTab(tabs[0]);
});
async function updateMenuItem(url) {
if (isReportableUrl(url)) {
await browser.nativeMenu.enable();
} else {
await browser.nativeMenu.disable();
}
}
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if ("url" in changeInfo && tab.active) {
updateMenuItem(tab.url);
}
});
browser.tabs.onActivated.addListener(({ tabId }) => {
browser.tabs
.get(tabId)
.then(({ url }) => {
updateMenuItem(url);
})
.catch(() => {
updateMenuItem("about"); // So the action is disabled
});
});
browser.tabs
.query({ active: true })
.then(tabs => {
updateMenuItem(tabs[0].url);
})
.catch(() => {});
}
function isReportableUrl(url) {
return (
url &&
!(
url.startsWith("about") ||
url.startsWith("chrome") ||
url.startsWith("file") ||
url.startsWith("resource")
)
);
}
async function reportForTab(tab) {
return getWebCompatInfoForTab(tab)
.then(async info => {
return openWebCompatTab(info, tab.incognito);
})
.catch(err => {
console.error("Report Site Issue: unexpected error", err);
});
}
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, windiwId, url } = tab;
return Promise.all([
browser.browserInfo.getBlockList(),
browser.browserInfo.getBuildID(),
browser.browserInfo.getGraphicsPrefs(),
browser.browserInfo.getUpdateChannel(),
browser.browserInfo.hasTouchScreen(),
browser.tabExtras.getWebcompatInfo(id),
checkForFrameworks(id),
browser.tabs
.captureVisibleTab(windiwId, Config.screenshotFormat)
.catch(e => {
console.error("Report Site Issue: getting a screenshot failed", e);
return Promise.resolve(undefined);
}),
]).then(
([
blockList,
buildID,
graphicsPrefs,
channel,
hasTouchScreen,
frameInfo,
frameworks,
screenshot,
]) => {
if (channel !== "linux") {
delete graphicsPrefs["layers.acceleration.force-enabled"];
}
const consoleLog = frameInfo.log;
delete frameInfo.log;
return Object.assign(frameInfo, {
tabId: id,
blockList,
details: Object.assign(graphicsPrefs, {
buildID,
channel,
consoleLog,
frameworks,
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, usePrivateTab) {
const url = new URL(Config.newIssueEndpoint);
const { details } = compatInfo;
const params = {
url: `${compatInfo.url}`,
utm_source: "mobile-reporter",
utm_campaign: "report-site-issue-button",
src: "mobile-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}`
);
}
// Need custom API for private tabs until https://bugzil.la/1372178 is fixed
const tab = usePrivateTab
? await browser.tabExtras.createPrivateTab()
: await browser.tabs.create({ url: "about:blank" });
const json = stripNonASCIIChars(JSON.stringify(params));
await browser.tabExtras.loadURIWithPostData(
tab.id,
url.href,
json,
"application/json"
);
await browser.tabs.executeScript(tab.id, {
runAt: "document_end",
code: `(function() {
async function sendScreenshot(dataURI) {
const res = await fetch(dataURI);
const blob = await res.blob();
postMessage(blob, "${url.origin}");
}
sendScreenshot("${compatInfo.screenshot}");
})()`,
});
}

View File

@ -1,41 +0,0 @@
/* 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 */
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
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);
},
},
};
}
};

View File

@ -1,35 +0,0 @@
[
{
"namespace": "aboutConfigPrefs",
"description": "experimental API extension to allow access to about:config preferences",
"events": [
{
"name": "onEndpointPrefChange",
"type": "function",
"parameters": []
}
],
"functions": [
{
"name": "getEndpointPref",
"type": "function",
"description": "Get the endpoint preference's value",
"parameters": [],
"async": true
},
{
"name": "setEndpointPref",
"type": "function",
"description": "Set the endpoint preference's value",
"parameters": [
{
"name": "value",
"type": "string",
"description": "The new value"
}
],
"async": true
}
]
}
]

View File

@ -1,72 +0,0 @@
/* 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 */
var { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const gParentalControls = (function() {
if ("@mozilla.org/parental-controls-service;1" in Cc) {
return Cc["@mozilla.org/parental-controls-service;1"].createInstance(
Ci.nsIParentalControlsService
);
}
return { parentalControlsEnabled: false };
})();
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 getParentalControlsEnabled() {
return gParentalControls.parentalControlsEnabled;
},
async getPlatform() {
return AppConstants.platform;
},
async hasTouchScreen() {
const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(
Ci.nsIGfxInfo
);
return gfxInfo.getInfo().ApzTouchInput == 1;
},
},
};
}
};

View File

@ -1,64 +0,0 @@
[
{
"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": "getParentalControlsEnabled",
"type": "function",
"description": "Gets whether the Parent Controls are enabled",
"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
}
]
}
]

View File

@ -1,33 +0,0 @@
/* 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, XPCOMUtils */
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "l10nStrings", function() {
return Services.strings.createBundle(
"chrome://browser/locale/webcompatReporter.properties"
);
});
let l10nManifest;
this.l10n = class extends ExtensionAPI {
getAPI(context) {
return {
l10n: {
getMessage(name) {
try {
return Promise.resolve(l10nStrings.GetStringFromName(name));
} catch (e) {
return Promise.reject(e);
}
},
},
};
}
};

View File

@ -1,19 +0,0 @@
[
{
"namespace": "l10n",
"description": "A stop-gap L10N API only meant to be used until a Fluent-based API is added in bug 1425104",
"functions": [
{
"name": "getMessage",
"type": "function",
"description": "Gets the message with the given name",
"parameters": [{
"name": "name",
"type": "string",
"description": "The name of the message"
}],
"async": true
}
]
}
]

View File

@ -1,65 +0,0 @@
/* 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 */
const {
Management: {
global: { windowTracker },
},
} = ChromeUtils.import("resource://gre/modules/Extension.jsm", null);
function getNativeWindow() {
return windowTracker.topWindow.NativeWindow;
}
const clickHandlers = new ExtensionCommon.EventEmitter();
const menuItem = getNativeWindow().menu.add({
name: "Report site issue",
callback: () => {
clickHandlers.emit("click");
},
enabled: false,
visible: false,
});
this.nativeMenu = class extends ExtensionAPI {
getAPI(context) {
return {
nativeMenu: {
onClicked: new ExtensionCommon.EventManager({
context,
name: "nativeMenu.onClicked",
register: fire => {
const callback = () => {
fire.async().catch(() => {}); // ignore Message Manager disconnects
};
clickHandlers.on("click", callback);
return () => {
clickHandlers.off("click", callback);
};
},
}).api(),
async disable() {
getNativeWindow().menu.update(menuItem, { enabled: false });
},
async enable() {
getNativeWindow().menu.update(menuItem, { enabled: true });
},
async hide() {
getNativeWindow().menu.update(menuItem, { visible: false });
},
async show() {
getNativeWindow().menu.update(menuItem, { visible: true });
},
async setLabel(label) {
getNativeWindow().menu.update(menuItem, { name: label });
},
},
};
}
};

View File

@ -1,53 +0,0 @@
[
{
"namespace": "nativeMenu",
"description": "experimental extension wrapping around a Fennec NativeMenu",
"events": [
{
"name": "onClicked",
"type": "function",
"parameters": []
}
],
"functions": [
{
"name": "disable",
"type": "function",
"async": true,
"description": "Disable the addon's menu item",
"parameters": []
},
{
"name": "enable",
"type": "function",
"async": true,
"description": "Enable the addon's menu item",
"parameters": []
},
{
"name": "hide",
"type": "function",
"async": true,
"description": "Hide the addon's menu item",
"parameters": []
},
{
"name": "show",
"type": "function",
"async": true,
"description": "Show the addon's menu item",
"parameters": []
},
{
"name": "setLabel",
"type": "function",
"async": true,
"description": "Set the label of the addon's menu item",
"parameters": [{
"name": "label",
"type": "string"
}]
}
]
}
]

View File

@ -1,43 +0,0 @@
/* 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 */
ChromeUtils.defineModuleGetter(
this,
"clearTimeout",
"resource://gre/modules/Timer.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"setTimeout",
"resource://gre/modules/Timer.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Snackbars",
"resource://gre/modules/Snackbars.jsm"
);
this.snackbars = class extends ExtensionAPI {
getAPI(context) {
return {
snackbars: {
show(message, button) {
return new Promise((callback, rejection) => {
Snackbars.show(message, Snackbars.LENGTH_LONG, {
action: {
label: button,
callback,
rejection,
},
});
});
},
},
};
}
};

View File

@ -1,21 +0,0 @@
[
{
"namespace": "snackbars",
"description": "experimental API extensions for prompting the user via Android Snackbar notifications",
"functions": [
{
"name": "show",
"type": "function",
"description": "Shows a Snackbar with the given message and button",
"parameters": [{
"name": "message",
"type": "string"
},{
"name": "button",
"type": "string"
}],
"async": true
}
]
}
]

View File

@ -1,327 +0,0 @@
/* 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 ChromeUtils, ExtensionAPI, ExtensionCommon, XPCOMUtils */
var Services;
ChromeUtils.defineModuleGetter(
this,
"EventDispatcher",
"resource://gre/modules/Messaging.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm"
);
XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
XPCOMUtils.defineLazyGetter(
this,
"GlobalEventDispatcher",
() => EventDispatcher.instance
);
function getInfoFrameScript(messageName) {
/* eslint-env mozilla/frame-script */
({ Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"));
const PREVIEW_MAX_ITEMS = 10;
const LOG_LEVEL_MAP = {
0: "debug",
1: "info",
2: "warn",
3: "error",
};
function getInnerWindowId(window) {
return window.windowUtils.currentInnerWindowID;
}
function getInnerWindowIDsForAllFrames(window) {
const innerWindowID = getInnerWindowId(window);
let ids = [innerWindowID];
if (window.frames) {
for (let i = 0; i < window.frames.length; i++) {
ids = ids.concat(getInnerWindowIDsForAllFrames(window.frames[i]));
}
}
return ids;
}
function getLoggedMessages(window, includePrivate = false) {
const ids = getInnerWindowIDsForAllFrames(window);
return getConsoleMessages(ids)
.concat(getScriptErrors(ids, includePrivate))
.sort((a, b) => a.timeStamp - b.timeStamp)
.map(m => m.message);
}
function getPreview(value) {
switch (typeof value) {
case "function":
return "function ()";
case "object":
if (value === null) {
return null;
}
if (Array.isArray(value)) {
return `(${value.length})[...]`;
}
return "{...}";
case "undefined":
return "undefined";
default:
return value;
}
}
function getArrayPreview(arr) {
const preview = [];
for (const value of arr) {
preview.push(getPreview(value));
if (preview.length === PREVIEW_MAX_ITEMS) {
break;
}
}
return preview;
}
function getObjectPreview(obj) {
const preview = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
preview[key] = getPreview(obj[key]);
}
if (Object.keys(preview).length === PREVIEW_MAX_ITEMS) {
break;
}
}
return preview;
}
function getArgs(value) {
if (typeof value === "object" && value !== null) {
if (Array.isArray(value)) {
return getArrayPreview(value);
}
return getObjectPreview(value);
}
return getPreview(value);
}
function getConsoleMessages(windowIds) {
const ConsoleAPIStorage = Cc[
"@mozilla.org/consoleAPI-storage;1"
].getService(Ci.nsIConsoleAPIStorage);
let messages = [];
for (const id of windowIds) {
messages = messages.concat(ConsoleAPIStorage.getEvents(id) || []);
}
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 };
});
}
function getScriptErrors(windowIds, includePrivate = false) {
const messages = Services.console.getMessageArray() || [];
return messages
.filter(message => {
if (message instanceof Ci.nsIScriptError) {
if (!includePrivate && message.isFromPrivateWindow) {
return false;
}
if (windowIds && !windowIds.includes(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_LEVEL_MAP[logLevel],
log: [errorMessage],
uri: sourceName,
pos: `${lineNumber}:${columnNumber}`,
};
return { timeStamp, message };
});
}
sendAsyncMessage(messageName, {
hasMixedActiveContentBlocked: docShell.hasMixedActiveContentBlocked,
hasMixedDisplayContentBlocked: docShell.hasMixedDisplayContentBlocked,
hasTrackingContentBlocked: docShell.hasTrackingContentBlocked,
log: getLoggedMessages(content, true), // also on private tabs
});
}
this.tabExtras = class extends ExtensionAPI {
getAPI(context) {
const EventManager = ExtensionCommon.EventManager;
const { tabManager } = context.extension;
const {
Management: {
global: { windowTracker },
},
} = ChromeUtils.import("resource://gre/modules/Extension.jsm", null);
return {
tabExtras: {
onDesktopSiteRequested: new EventManager({
context,
name: "tabExtras.onDesktopSiteRequested",
register: fire => {
const callback = tab => {
fire.async(tab).catch(() => {}); // ignore Message Manager disconnects
};
const listener = {
onEvent: (event, data, _callback) => {
if (event === "DesktopMode:Change" && data.desktopMode) {
callback(data.tabId);
}
},
};
GlobalEventDispatcher.registerListener(
listener,
"DesktopMode:Change"
);
return () => {
GlobalEventDispatcher.unregisterListener(
listener,
"DesktopMode:Change"
);
};
},
}).api(),
async createPrivateTab() {
const { BrowserApp } = windowTracker.topWindow;
const nativeTab = BrowserApp.addTab("about:blank", {
selected: true,
isPrivate: true,
});
return Promise.resolve(tabManager.convert(nativeTab));
},
async loadURIWithPostData(
tabId,
url,
postDataString,
postDataContentType
) {
const tab = tabManager.get(tabId);
if (!tab || !tab.browser) {
return Promise.reject("Invalid tab");
}
try {
new URL(url);
} catch (_) {
return Promise.reject("Invalid url");
}
if (
typeof postDataString !== "string" &&
!(postDataString instanceof String)
) {
return Promise.reject("postDataString must be a string");
}
const stringStream = Cc[
"@mozilla.org/io/string-input-stream;1"
].createInstance(Ci.nsIStringInputStream);
stringStream.data = postData;
const postData = Cc[
"@mozilla.org/network/mime-input-stream;1"
].createInstance(Ci.nsIMIMEInputStream);
postData.addHeader(
"Content-Type",
postDataContentType || "application/x-www-form-urlencoded"
);
postData.setData(stringStream);
return new Promise(resolve => {
const listener = {
onLocationChange(
browser,
webProgress,
request,
locationURI,
flags
) {
if (
webProgress.isTopLevel &&
browser === tab.browser &&
locationURI.spec === url
) {
windowTracker.removeListener("progress", listener);
resolve();
}
},
};
windowTracker.addListener("progress", listener);
let loadURIOptions = {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
postData,
};
tab.browser.webNavigation.loadURI(url, loadURIOptions);
});
},
async getWebcompatInfo(tabId) {
return new Promise(resolve => {
const messageName = "WebExtension:GetWebcompatInfo";
const code = `${getInfoFrameScript.toString()};getInfoFrameScript("${messageName}")`;
const mm = tabManager.get(tabId).browser.messageManager;
mm.loadFrameScript(`data:,${encodeURI(code)}`, false);
mm.addMessageListener(messageName, function receiveFn(message) {
mm.removeMessageListener(messageName, receiveFn);
resolve(message.json);
});
});
},
},
};
}
};

View File

@ -1,57 +0,0 @@
[
{
"namespace": "tabExtras",
"description": "experimental tab API extensions",
"events": [{
"name": "onDesktopSiteRequested",
"type": "function",
"parameters": [
{
"name": "tabId",
"type": "integer",
"description": "The related tab's id"
}
]
}],
"functions": [
{
"name": "createPrivateTab",
"type": "function",
"description": "Create and select a new private about:blank tab",
"parameters": [],
"async": true
},
{
"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
},
{
"name": "loadURIWithPostData",
"type": "function",
"description": "Loads a URI on the given tab using a POST request",
"parameters": [{
"type": "integer",
"name": "tabId",
"minimum": 0
}, {
"type": "string",
"name": "url"
}, {
"type": "string",
"name": "postData"
}, {
"type": "string",
"name": "postDataContentType"
}],
"async": true
}
]
}
]

View File

@ -1,72 +0,0 @@
{
"manifest_version": 2,
"name": "WebCompat Reporter",
"description": "Report site compatibility issues on webcompat.com",
"author": "Thomas Wisniewski <twisniewski@mozilla.com>",
"version": "1.1.0",
"homepage_url": "https://github.com/mozilla/webcompat-reporter",
"applications": {
"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"]]
}
},
"l10n": {
"schema": "experimentalAPIs/l10n.json",
"parent": {
"scopes": ["addon_parent"],
"script": "experimentalAPIs/l10n.js",
"paths": [["l10n"]]
}
},
"nativeMenu": {
"schema": "experimentalAPIs/nativeMenu.json",
"parent": {
"scopes": ["addon_parent"],
"script": "experimentalAPIs/nativeMenu.js",
"paths": [["nativeMenu"]]
}
},
"snackbars": {
"schema": "experimentalAPIs/snackbars.json",
"parent": {
"scopes": ["addon_parent"],
"script": "experimentalAPIs/snackbars.js",
"paths": [["snackbars"]]
}
},
"tabExtras": {
"schema": "experimentalAPIs/tabExtras.json",
"parent": {
"scopes": ["addon_parent"],
"script": "experimentalAPIs/tabExtras.js",
"paths": [["tabExtras"]]
}
}
},
"permissions": [
"tabs",
"<all_urls>"
],
"background": {
"scripts": [
"background.js"
]
}
}

View File

@ -1,31 +0,0 @@
# -*- 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']
FINAL_TARGET_FILES.features['webcompat-reporter@mozilla.org'] += [
'background.js',
'manifest.json'
]
FINAL_TARGET_FILES.features['webcompat-reporter@mozilla.org'].experimentalAPIs += [
'experimentalAPIs/aboutConfigPrefs.js',
'experimentalAPIs/aboutConfigPrefs.json',
'experimentalAPIs/browserInfo.js',
'experimentalAPIs/browserInfo.json',
'experimentalAPIs/l10n.js',
'experimentalAPIs/l10n.json',
'experimentalAPIs/nativeMenu.js',
'experimentalAPIs/nativeMenu.json',
'experimentalAPIs/snackbars.js',
'experimentalAPIs/snackbars.json',
'experimentalAPIs/tabExtras.js',
'experimentalAPIs/tabExtras.json'
]
with Files('**'):
BUG_COMPONENT = ('Web Compatibility Tools', 'General')

View File

@ -1,37 +0,0 @@
/* 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";
var EXPORTED_SYMBOLS = ["AboutCompat"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const addonID = "webcompat@mozilla.org";
const addonPageRelativeURL = "/about-compat/aboutCompat.html";
function AboutCompat() {
this.chromeURL = WebExtensionPolicy.getByID(addonID).getURL(
addonPageRelativeURL
);
}
AboutCompat.prototype = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule]),
getURIFlags() {
return Ci.nsIAboutModule.URI_MUST_LOAD_IN_EXTENSION_PROCESS;
},
newChannel(aURI, aLoadInfo) {
const uri = Services.io.newURI(this.chromeURL);
const channel = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
channel.originalURI = aURI;
channel.owner = (Services.scriptSecurityManager.createContentPrincipal ||
Services.scriptSecurityManager.createCodebasePrincipal)(
uri,
aLoadInfo.originAttributes
);
return channel;
},
};

View File

@ -1,198 +0,0 @@
@media (any-pointer: fine) {
:root {
font-family: sans-serif;
margin: 40px auto;
min-width: 30em;
max-width: 60em;
}
table {
width: 100%;
padding-bottom: 2em;
}
.hidden {
display: none;
}
.table-title-container {
align-items: center;
display: flex;
justify-content: space-between;
}
.wide-button {
display: block;
min-height: 32px;
padding-left: 30px;
padding-right: 30px;
}
.submitting {
background-image: url(chrome://global/skin/icons/loading.png);
background-position: center;
background-repeat: no-repeat;
background-size: 16px;
}
.submitting .submit-crash-button-label {
display: none;
}
.failed-to-submit {
color: #ca8695;
}
a.button-as-link {
-moz-appearance: none;
min-height: 30px;
color: var(--in-content-text-color) !important;
border: 1px solid var(--in-content-box-border-color) !important;
border-radius: 2px;
background-color: var(--in-content-page-background);
line-height: 30px;
margin: 4px 8px;
/* Ensure font-size isn't overridden by widget styling (e.g. in forms.css) */
font-size: 1em;
}
a.button-as-link:hover {
background-color: var(--in-content-box-background-hover) !important;
text-decoration: none;
}
h2.lighter-font-weight {
font-weight: lighter;
}
html[dir="ltr"] th {
text-align: left;
}
html[dir="rtl"] th {
text-align: right;
}
}
@media (any-pointer: coarse), (any-pointer: none) {
* {
margin: 0;
padding: 0;
}
html {
font-family: sans-serif;
font-size: 14px;
-moz-text-size-adjust: none;
background-color: #f5f5f5;
}
table,
tr,
p {
display: block;
background: #fff;
}
table {
border-top: 2px solid #0a84ff;
margin-top: -2px;
position: absolute;
width: 100%;
z-index: 1;
display: none;
}
tr {
position: relative;
border-bottom: 1px solid #d7d9db;
padding: 1em;
}
a {
color: #000;
font-size: 94%;
}
.tab {
cursor: pointer;
position: relative;
z-index: 2;
display: inline-block;
text-align: left;
padding: 1em;
font-weight: bold;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border: 1px solid #d7d9db;
border-bottom: 0;
margin-bottom: 2px;
background: #f5f5f5;
color: #363b40;
font-size: 1em;
font-weight: bold;
padding: 1em;
}
.tab.active {
border-bottom-color: #fff;
background: #fff;
margin-bottom: 0;
padding-bottom: calc(1em + 2px);
}
.tab.active + table {
display: block;
}
td {
display: block;
position: relative;
}
td:dir(ltr) {
padding-right: 6.5em;
}
td:dir(rtl) {
padding-left: 6.5em;
}
td[colspan="4"] {
padding: 1em;
font-style: italic;
text-align: center;
}
td:not([colspan]):nth-child(1) {
font-weight: bold;
}
td:not([colspan]):nth-child(1) {
padding-bottom: 0.25em;
}
td:nth-child(3) {
display: contents;
}
button {
background: #e8e8e7;
position: absolute;
top: 0;
bottom: 0;
width: 6em;
border: 0;
border-left: 1px solid #d7d9db;
-moz-appearance: none;
color: #000;
}
button:dir(ltr) {
right: 0;
}
button:dir(rtl) {
left: 0;
}
button::-moz-focus-inner {
border: 0;
}
}

View File

@ -1,33 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<base/>
<!-- If you change this script tag you must update the hash in the extension's
`content_security_policy` 'sha256-MmZkN2QaIHhfRWPZ8TVRjijTn5Ci1iEabtTEWrt9CCo=' -->
<script>/* globals browser */ document.head.firstElementChild.href = browser.runtime.getURL("");</script>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="about-compat/aboutCompat.css" />
<link rel="stylesheet" media="screen and (pointer:fine), projection" type="text/css"
href="chrome://global/skin/in-content/common.css"/>
<link rel="localization" href="toolkit/about/aboutCompat.ftl"/>
<title data-l10n-id="text-title"></title>
<script src="about-compat/aboutCompat.js"></script>
</head>
<body>
<h2 class="tab active" data-l10n-id="label-overrides"></h2>
<table id="overrides">
<col/>
<col/>
<col/>
</table>
<h2 class="tab" data-l10n-id="label-interventions"></h2>
<table id="interventions">
<col/>
<col/>
<col/>
</table>
</body>
</html>

View File

@ -1,171 +0,0 @@
/* 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 */
let availablePatches;
const portToAddon = (function() {
let port;
function connect() {
port = browser.runtime.connect({ name: "AboutCompatTab" });
port.onMessage.addListener(onMessageFromAddon);
port.onDisconnect.addListener(e => {
port = undefined;
});
}
connect();
async function send(message) {
if (port) {
return port.postMessage(message);
}
return Promise.reject("background script port disconnected");
}
return { send };
})();
const $ = function(sel) {
return document.querySelector(sel);
};
const DOMContentLoadedPromise = new Promise(resolve => {
document.addEventListener(
"DOMContentLoaded",
() => {
resolve();
},
{ once: true }
);
});
Promise.all([
browser.runtime.sendMessage("getOverridesAndInterventions"),
DOMContentLoadedPromise,
]).then(([info]) => {
document.body.addEventListener("click", async evt => {
const ele = evt.target;
if (ele.nodeName === "BUTTON") {
const row = ele.closest("[data-id]");
if (row) {
evt.preventDefault();
ele.disabled = true;
const id = row.getAttribute("data-id");
try {
await browser.runtime.sendMessage({ command: "toggle", id });
} catch (_) {
ele.disabled = false;
}
}
} else if (ele.classList.contains("tab")) {
document.querySelectorAll(".tab").forEach(tab => {
tab.classList.remove("active");
});
ele.classList.add("active");
}
});
availablePatches = info;
redraw();
});
function onMessageFromAddon(msg) {
if ("interventionsChanged" in msg) {
redrawTable($("#interventions"), msg.interventionsChanged);
}
if ("overridesChanged" in msg) {
redrawTable($("#overrides"), msg.overridesChanged);
}
const id = msg.toggling || msg.toggled;
const button = $(`[data-id="${id}"] button`);
if (!button) {
return;
}
const active = msg.active;
document.l10n.setAttributes(
button,
active ? "label-disable" : "label-enable"
);
button.disabled = !!msg.toggling;
}
function redraw() {
if (!availablePatches) {
return;
}
const { overrides, interventions } = availablePatches;
const showHidden = location.hash === "#all";
redrawTable($("#overrides"), overrides, showHidden);
redrawTable($("#interventions"), interventions, showHidden);
}
function redrawTable(table, data, showHidden = false) {
const df = document.createDocumentFragment();
table.querySelectorAll("tr").forEach(tr => {
tr.remove();
});
let noEntriesMessage;
if (data === false) {
noEntriesMessage = "text-disabled-in-about-config";
} else if (data.length === 0) {
noEntriesMessage =
table.id === "overrides" ? "text-no-overrides" : "text-no-interventions";
}
if (noEntriesMessage) {
const tr = document.createElement("tr");
df.appendChild(tr);
const td = document.createElement("td");
td.setAttribute("colspan", "3");
document.l10n.setAttributes(td, noEntriesMessage);
tr.appendChild(td);
table.appendChild(df);
return;
}
for (const row of data) {
if (row.hidden && !showHidden) {
continue;
}
const tr = document.createElement("tr");
tr.setAttribute("data-id", row.id);
df.appendChild(tr);
let td = document.createElement("td");
td.innerText = row.domain;
tr.appendChild(td);
td = document.createElement("td");
const a = document.createElement("a");
const bug = row.bug;
a.href = `https://bugzilla.mozilla.org/show_bug.cgi?id=${bug}`;
document.l10n.setAttributes(a, "label-more-information", { bug });
a.target = "_blank";
td.appendChild(a);
tr.appendChild(td);
td = document.createElement("td");
tr.appendChild(td);
const button = document.createElement("button");
document.l10n.setAttributes(
button,
row.active ? "label-disable" : "label-enable"
);
td.appendChild(button);
}
table.appendChild(df);
}
window.onhashchange = redraw;

View File

@ -1,42 +0,0 @@
/* 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, Services, XPCOMUtils */
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"resProto",
"@mozilla.org/network/protocol;1?name=resource",
"nsISubstitutingProtocolHandler"
);
const ResourceSubstitution = "webcompat";
const ProcessScriptURL = "resource://webcompat/aboutPageProcessScript.js";
this.aboutPage = class extends ExtensionAPI {
onStartup() {
const { rootURI } = this.extension;
resProto.setSubstitution(
ResourceSubstitution,
Services.io.newURI("about-compat/", null, rootURI)
);
Services.ppmm.loadProcessScript(ProcessScriptURL, true);
}
onShutdown() {
resProto.setSubstitution(ResourceSubstitution, null);
Services.ppmm.removeDelayedProcessScript(ProcessScriptURL);
}
};

View File

@ -1,4 +0,0 @@
[{
"namespace": "aboutCompat",
"description": "Enables the about:compat page"
}]

View File

@ -1,29 +0,0 @@
/* 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";
const Cm = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
const classID = Components.ID("{97bf9550-2a7b-11e9-b56e-0800200c9a66}");
if (!Cm.isCIDRegistered(classID)) {
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const factory = XPCOMUtils.generateSingletonFactory(function() {
const { AboutCompat } = ChromeUtils.import(
"resource://webcompat/AboutCompat.jsm"
);
return new AboutCompat();
});
Cm.registerFactory(
classID,
"about:compat",
"@mozilla.org/network/protocol/about;1?what=compat",
factory
);
}

View File

@ -1,426 +0,0 @@
/* 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 module */
/**
* For detailed information on our policies, and a documention on this format
* and its possibilites, please check the Mozilla-Wiki at
*
* https://wiki.mozilla.org/Compatibility/Go_Faster_Addon/Override_Policies_and_Workflows#User_Agent_overrides
*/
const AVAILABLE_INJECTIONS = [
{
id: "testbed-injection",
platform: "all",
domain: "webcompat-addon-testbed.herokuapp.com",
bug: "0000000",
hidden: true,
contentScripts: {
matches: ["*://webcompat-addon-testbed.herokuapp.com/*"],
css: [
{
file: "injections/css/bug0000000-testbed-css-injection.css",
},
],
js: [
{
file: "injections/js/bug0000000-testbed-js-injection.js",
},
],
},
},
{
id: "bug1452707",
platform: "desktop",
domain: "ib.absa.co.za",
bug: "1452707",
contentScripts: {
matches: ["https://ib.absa.co.za/*"],
js: [
{
file:
"injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js",
},
],
},
},
{
id: "bug1457335",
platform: "desktop",
domain: "histography.io",
bug: "1457335",
contentScripts: {
matches: ["*://histography.io/*"],
js: [
{
file: "injections/js/bug1457335-histography.io-ua-change.js",
},
],
},
},
{
id: "bug1472075",
platform: "desktop",
domain: "bankofamerica.com",
bug: "1472075",
contentScripts: {
matches: ["*://*.bankofamerica.com/*"],
js: [
{
file: "injections/js/bug1472075-bankofamerica.com-ua-change.js",
},
],
},
},
{
id: "bug1472081",
platform: "desktop",
domain: "election.gov.np",
bug: "1472081",
contentScripts: {
matches: ["http://202.166.205.141/bbvrs/*"],
allFrames: true,
js: [
{
file:
"injections/js/bug1472081-election.gov.np-window.sidebar-shim.js",
},
],
},
},
{
id: "bug1482066",
platform: "desktop",
domain: "portalminasnet.com",
bug: "1482066",
contentScripts: {
matches: ["*://portalminasnet.com/*"],
allFrames: true,
js: [
{
file:
"injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js",
},
],
},
},
{
id: "bug1570856",
platform: "android",
domain: "medium.com",
bug: "1570856",
contentScripts: {
matches: ["*://medium.com/*"],
js: [
{
file: "injections/js/bug1570856-medium.com-menu-isTier1.js",
},
],
allFrames: true,
},
},
{
id: "bug1579159",
platform: "android",
domain: "m.tailieu.vn",
bug: "1579159",
contentScripts: {
matches: ["*://m.tailieu.vn/*", "*://m.elib.vn/*"],
js: [
{
file: "injections/js/bug1579159-m.tailieu.vn-pdfjs-worker-disable.js",
},
],
allFrames: true,
},
},
{
id: "bug1577245",
platform: "android",
domain: "help.pandora.com",
bug: "1577245",
contentScripts: {
matches: [
"https://faq.usps.com/*",
"https://help.duo.com/*",
"https://help.hulu.com/*",
"https://help.pandora.com/*",
"https://my211.force.com/*",
"https://support.paypay.ne.jp/*",
"https://usps.force.com/*",
],
js: [
{
file:
"injections/js/bug1577245-salesforce-communities-hide-unsupported.js",
},
],
},
},
{
id: "bug1526977",
platform: "desktop",
domain: "sreedharscce.in",
bug: "1526977",
contentScripts: {
matches: ["*://*.sreedharscce.in/authenticate"],
css: [
{
file: "injections/css/bug1526977-sreedharscce.in-login-fix.css",
},
],
},
},
{
id: "bug1518781",
platform: "desktop",
domain: "twitch.tv",
bug: "1518781",
contentScripts: {
matches: ["*://*.twitch.tv/*"],
css: [
{
file: "injections/css/bug1518781-twitch.tv-webkit-scrollbar.css",
},
],
},
},
{
id: "bug1551672",
platform: "android",
domain: "Sites using PDK 5 video",
bug: "1551672",
data: {
urls: ["https://*/*/tpPdk.js", "https://*/*/pdk/js/*/*.js"],
types: ["script"],
},
customFunc: "pdk5fix",
},
{
id: "bug1577870",
platform: "desktop",
domain: "Download prompt for files with no content-type",
bug: "1577870",
data: {
urls: [
"https://*.linkedin.com/tscp-serving/dtag*",
"https://ads-us.rd.linksynergy.com/as.php*",
"https://www.office.com/logout?sid*",
],
contentType: {
name: "content-type",
value: "text/html; charset=utf-8",
},
},
customFunc: "noSniffFix",
},
{
id: "bug1305028",
platform: "desktop",
domain: "gaming.youtube.com",
bug: "1305028",
contentScripts: {
matches: ["*://gaming.youtube.com/*"],
css: [
{
file:
"injections/css/bug1305028-gaming.youtube.com-webkit-scrollbar.css",
},
],
},
},
{
id: "bug1432935-discord",
platform: "desktop",
domain: "discordapp.com",
bug: "1432935",
contentScripts: {
matches: ["*://discordapp.com/*"],
css: [
{
file:
"injections/css/bug1432935-discordapp.com-webkit-scorllbar-white-line.css",
},
],
},
},
{
id: "bug1561371",
platform: "android",
domain: "mail.google.com",
bug: "1561371",
contentScripts: {
matches: ["*://mail.google.com/*"],
css: [
{
file:
"injections/css/bug1561371-mail.google.com-allow-horizontal-scrolling.css",
},
],
},
},
{
id: "bug1567610",
platform: "all",
domain: "dns.google.com",
bug: "1567610",
contentScripts: {
matches: ["*://dns.google.com/*", "*://dns.google/*"],
css: [
{
file: "injections/css/bug1567610-dns.google.com-moz-fit-content.css",
},
],
},
},
{
id: "bug1568256",
platform: "android",
domain: "zertifikate.commerzbank.de",
bug: "1568256",
contentScripts: {
matches: ["*://*.zertifikate.commerzbank.de/webforms/mobile/*"],
css: [
{
file: "injections/css/bug1568256-zertifikate.commerzbank.de-flex.css",
},
],
},
},
{
id: "bug1568908",
platform: "desktop",
domain: "console.cloud.google.com",
bug: "1568908",
contentScripts: {
matches: ["*://*.console.cloud.google.com/*"],
css: [
{
file:
"injections/css/bug1568908-console.cloud.google.com-scrollbar-fix.css",
},
],
},
},
{
id: "bug1570119",
platform: "desktop",
domain: "teamcoco.com",
bug: "1570119",
contentScripts: {
matches: ["*://teamcoco.com/*"],
css: [
{
file: "injections/css/bug1570119-teamcoco.com-scrollbar-width.css",
},
],
},
},
{
id: "bug1570328",
platform: "android",
domain: "developer.apple.com",
bug: "1570328",
contentScripts: {
matches: ["*://developer.apple.com/*"],
css: [
{
file:
"injections/css/bug1570328-developer-apple.com-transform-scale.css",
},
],
},
},
{
id: "bug1574973",
platform: "android",
domain: "patch.com",
bug: "1574973",
contentScripts: {
matches: ["*://patch.com/*"],
css: [
{
file: "injections/css/bug1574973-patch.com-dropdown-menu-fix.css",
},
],
},
},
{
id: "bug1575000",
platform: "all",
domain: "apply.lloydsbank.co.uk",
bug: "1575000",
contentScripts: {
matches: ["*://apply.lloydsbank.co.uk/*"],
css: [
{
file:
"injections/css/bug1575000-apply.lloydsbank.co.uk-radio-buttons-fix.css",
},
],
},
},
{
id: "bug1575011",
platform: "android",
domain: "holiday-weather.com",
bug: "1575011",
contentScripts: {
matches: ["*://*.holiday-weather.com/*"],
css: [
{
file:
"injections/css/bug1575011-holiday-weather.com-scrolling-fix.css",
},
],
},
},
{
id: "bug1575017",
platform: "desktop",
domain: "dunkindonuts.com",
bug: "1575017",
contentScripts: {
matches: ["*://*.dunkindonuts.com/en/sign-in*"],
css: [
{
file: "injections/css/bug1575017-dunkindonuts.com-flex-basis.css",
},
],
},
},
{
id: "bug1577270",
platform: "android",
domain: "binance.com",
bug: "1577270",
contentScripts: {
matches: ["*://*.binance.com/*"],
css: [
{
file: "injections/css/bug1577270-binance.com-calc-height-fix.css",
},
],
},
},
{
id: "bug1577297",
platform: "android",
domain: "kitkat.com.au",
bug: "1577297",
contentScripts: {
matches: ["*://*.kitkat.com.au/*"],
css: [
{
file: "injections/css/bug1577297-kitkat.com.au-slider-width-fix.css",
},
],
},
},
];
module.exports = AVAILABLE_INJECTIONS;

View File

@ -1,580 +0,0 @@
/* 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 module, require */
// This is a hack for the tests.
if (typeof getMatchPatternsForGoogleURL === "undefined") {
var getMatchPatternsForGoogleURL = require("../lib/google");
}
/**
* For detailed information on our policies, and a documention on this format
* and its possibilites, please check the Mozilla-Wiki at
*
* https://wiki.mozilla.org/Compatibility/Go_Faster_Addon/Override_Policies_and_Workflows#User_Agent_overrides
*/
const AVAILABLE_UA_OVERRIDES = [
{
id: "testbed-override",
platform: "all",
domain: "webcompat-addon-testbed.herokuapp.com",
bug: "0000000",
config: {
hidden: true,
matches: ["*://webcompat-addon-testbed.herokuapp.com/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36 for WebCompat"
);
},
},
},
{
/*
* Bug 1564594 - Create UA override for Enhanced Search on Firefox Android
*
* Enables the Chrome Google Search experience for Fennec users.
*/
id: "bug1564594",
platform: "android",
domain: "Enhanced Search",
bug: "1567945",
config: {
matches: [
...getMatchPatternsForGoogleURL("images.google"),
...getMatchPatternsForGoogleURL("maps.google"),
...getMatchPatternsForGoogleURL("news.google"),
...getMatchPatternsForGoogleURL("www.google"),
],
blocks: [...getMatchPatternsForGoogleURL("www.google", "serviceworker")],
permanentPref: "enable_enhanced_search",
telemetryKey: "enhancedSearch",
experiment: ["enhanced-search", "enhanced-search-control"],
uaTransformer: originalUA => {
return UAHelpers.getDeviceAppropriateChromeUA();
},
},
},
{
/*
* Bug 1563839 - rolb.santanderbank.com - Build UA override
* WebCompat issue #33462 - https://webcompat.com/issues/33462
*
* santanderbank expects UA to have 'like Gecko', otherwise it runs
* xmlDoc.onload whose support has been dropped. It results in missing labels in forms
* and some other issues. Adding 'like Gecko' fixes those issues.
*/
id: "bug1563839",
platform: "all",
domain: "rolb.santanderbank.com",
bug: "1563839",
config: {
matches: [
"*://*.santander.co.uk/*",
"*://bob.santanderbank.com/*",
"*://rolb.santanderbank.com/*",
],
uaTransformer: originalUA => {
return originalUA.replace("Gecko", "like Gecko");
},
},
},
{
/*
* Bug 1577179 - UA override for supportforms.embarcadero.com
* WebCompat issue #34682 - https://webcompat.com/issues/34682
*
* supportforms.embarcadero.com has a constant onchange event on a product selector
* which makes it unusable. Spoofing as Chrome allows to stop event from firing
*/
id: "bug1577179",
platform: "all",
domain: "supportforms.embarcadero.com",
bug: "1577179",
config: {
matches: ["*://supportforms.embarcadero.com/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
);
},
},
},
{
/*
* Bug 1577519 - att.tv - Create a UA override for att.tv for playback on desktop
* WebCompat issue #3846 - https://webcompat.com/issues/3846
*
* att.tv (atttvnow.com) is blocking Firefox via UA sniffing. Spoofing as Chrome allows
* to access the site and playback works fine. This is former directvnow.com
*/
id: "bug1577519",
platform: "desktop",
domain: "att.tv",
bug: "1577519",
config: {
matches: ["*://*.att.tv/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
);
},
},
},
{
/*
* Bug 1570108 - steamcommunity.com - UA override for steamcommunity.com
* WebCompat issue #34171 - https://webcompat.com/issues/34171
*
* steamcommunity.com blocks chat feature for Firefox users showing unsupported browser message.
* When spoofing as Chrome the chat works fine
*/
id: "bug1570108",
platform: "desktop",
domain: "steamcommunity.com",
bug: "1570108",
config: {
matches: ["*://steamcommunity.com/chat*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
);
},
},
},
{
/*
* Bug 1582582 - sling.com - UA override for sling.com
* WebCompat issue #17804 - https://webcompat.com/issues/17804
*
* sling.com blocks Firefox users showing unsupported browser message.
* When spoofing as Chrome playing content works fine
*/
id: "bug1582582",
platform: "desktop",
domain: "sling.com",
bug: "1582582",
config: {
matches: ["https://watch.sling.com/*", "https://www.sling.com/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
);
},
},
},
{
/*
* Bug 1480710 - m.imgur.com - Build UA override
* WebCompat issue #13154 - https://webcompat.com/issues/13154
*
* imgur returns a 404 for requests to CSS and JS file if requested with a Fennec
* User Agent. By removing the Fennec identifies and adding Chrome Mobile's, we
* receive the correct CSS and JS files.
*/
id: "bug1480710",
platform: "android",
domain: "m.imgur.com",
bug: "1480710",
config: {
matches: ["*://m.imgur.com/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36"
);
},
},
},
{
/*
* Bug 945963 - tieba.baidu.com serves simplified mobile content to Firefox Android
* WebCompat issue #18455 - https://webcompat.com/issues/18455
*
* tieba.baidu.com and tiebac.baidu.com serve a heavily simplified and less functional
* mobile experience to Firefox for Android users. Adding the AppleWebKit indicator
* to the User Agent gets us the same experience.
*/
id: "bug945963",
platform: "android",
domain: "tieba.baidu.com",
bug: "945963",
config: {
matches: [
"*://tieba.baidu.com/*",
"*://tiebac.baidu.com/*",
"*://zhidao.baidu.com/*",
],
uaTransformer: originalUA => {
return originalUA + " AppleWebKit/537.36 (KHTML, like Gecko)";
},
},
},
{
/*
* Bug 1177298 - Write UA overrides for top Japanese Sites
* (Imported from ua-update.json.in)
*
* To receive the proper mobile version instead of the desktop version or
* a lower grade mobile experience, the UA is spoofed.
*/
id: "bug1177298-2",
platform: "android",
domain: "lohaco.jp",
bug: "1177298",
config: {
matches: ["*://*.lohaco.jp/*"],
uaTransformer: _ => {
return "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36";
},
},
},
{
/*
* Bug 1177298 - Write UA overrides for top Japanese Sites
* (Imported from ua-update.json.in)
*
* To receive the proper mobile version instead of the desktop version or
* a lower grade mobile experience, the UA is spoofed.
*/
id: "bug1177298-3",
platform: "android",
domain: "nhk.or.jp",
bug: "1177298",
config: {
matches: ["*://*.nhk.or.jp/*"],
uaTransformer: originalUA => {
return originalUA + " AppleWebKit";
},
},
},
{
/*
* Bug 1338260 - Add UA override for directTV
* (Imported from ua-update.json.in)
*
* DirectTV has issues with scrolling and cut-off images. Pretending to be
* Chrome for Android fixes those issues.
*/
id: "bug1338260",
platform: "android",
domain: "directv.com",
bug: "1338260",
config: {
matches: ["*://*.directv.com/*"],
uaTransformer: _ => {
return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
},
},
},
{
/*
* Bug 1385206 - Create UA override for rakuten.co.jp on Firefox Android
* (Imported from ua-update.json.in)
*
* rakuten.co.jp serves a Desktop version if Firefox is included in the UA.
*/
id: "bug1385206",
platform: "android",
domain: "rakuten.co.jp",
bug: "1385206",
config: {
matches: ["*://*.rakuten.co.jp/*"],
uaTransformer: originalUA => {
return originalUA.replace(/Firefox.+$/, "");
},
},
},
{
/*
* Bug 969844 - mobile.de sends desktop site to Firefox on Android
*
* mobile.de sends the desktop site to Fennec. Spooing as Chrome works fine.
*/
id: "bug969844",
platform: "android",
domain: "mobile.de",
bug: "969844",
config: {
matches: ["*://*.mobile.de/*"],
uaTransformer: _ => {
return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
},
},
},
{
/*
* Bug 1509831 - cc.com - Add UA override for CC.com
* WebCompat issue #329 - https://webcompat.com/issues/329
*
* ComedyCentral blocks Firefox for not being able to play HLS, which was
* true in previous versions, but no longer is. With a spoofed Chrome UA,
* the site works just fine.
*/
id: "bug1509831",
platform: "android",
domain: "cc.com",
bug: "1509831",
config: {
matches: ["*://*.cc.com/*"],
uaTransformer: _ => {
return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
},
},
},
{
/*
* Bug 1508516 - cineflix.com.br - Add UA override for cineflix.com.br/m/
* WebCompat issue #21553 - https://webcompat.com/issues/21553
*
* The site renders a blank page with any Firefox snipped in the UA as it
* is running into an exception. Spoofing as Chrome makes the site work
* fine.
*/
id: "bug1508516",
platform: "android",
domain: "cineflix.com.br",
bug: "1508516",
config: {
matches: ["*://*.cineflix.com.br/m/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
);
},
},
},
{
/*
* Bug 1509852 - redbull.com - Add UA override for redbull.com
* WebCompat issue #21439 - https://webcompat.com/issues/21439
*
* Redbull.com blocks some features, for example the live video player, for
* Fennec. Spoofing as Chrome results in us rendering the video just fine,
* and everything else works as well.
*/
id: "bug1509852",
platform: "android",
domain: "redbull.com",
bug: "1509852",
config: {
matches: ["*://*.redbull.com/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
);
},
},
},
{
/*
* Bug 1509873 - zmags.com - Add UA override for secure.viewer.zmags.com
* WebCompat issue #21576 - https://webcompat.com/issues/21576
*
* The zmags viewer locks out Fennec with a "Browser unsupported" message,
* but tests showed that it works just fine with a Chrome UA. Outreach
* attempts were unsuccessful, and as the site has a relatively high rank,
* we alter the UA.
*/
id: "bug1509873",
platform: "android",
domain: "zmags.com",
bug: "1509873",
config: {
matches: ["*://*.viewer.zmags.com/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
);
},
},
},
{
/*
* Bug 1566253 - posts.google.com - Add UA override for posts.google.com
* WebCompat issue #17870 - https://webcompat.com/issues/17870
*
* posts.google.com displaying "Your browser doesn't support this page".
* Spoofing as Chrome works fine.
*/
id: "bug1566253",
platform: "android",
domain: "posts.google.com",
bug: "1566253",
config: {
matches: ["*://posts.google.com/*"],
uaTransformer: _ => {
return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G900M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.101 Mobile Safari/537.36";
},
},
},
{
/*
* Bug 1567945 - Create UA override for beeg.com on Firefox Android
* WebCompat issue #16648 - https://webcompat.com/issues/16648
*
* beeg.com is hiding content of a page with video if Firefox exists in UA,
* replacing "Firefox" with an empty string makes the page load
*/
id: "bug1567945",
platform: "android",
domain: "beeg.com",
bug: "1567945",
config: {
matches: ["*://beeg.com/*"],
uaTransformer: originalUA => {
return originalUA.replace(/Firefox.+$/, "");
},
},
},
{
/*
* Bug 1574522 - UA override for enuri.com on Firefox for Android
* WebCompat issue #37139 - https://webcompat.com/issues/37139
*
* enuri.com returns a different template for Firefox on Android
* based on server side UA detection. This results in page content cut offs.
* Spoofing as Chrome fixes the issue
*/
id: "bug1574522",
platform: "android",
domain: "enuri.com",
bug: "1574522",
config: {
matches: ["*://enuri.com/*"],
uaTransformer: _ => {
return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G900M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Mobile Safari/537.36";
},
},
},
{
/*
* Bug 1574564 - UA override for ceskatelevize.cz on Firefox for Android
* WebCompat issue #15467 - https://webcompat.com/issues/15467
*
* ceskatelevize sets streamingProtocol depending on the User-Agent it sees
* in the request headers, returning DASH for Chrome, HLS for iOS,
* and Flash for Fennec. Since Fennec has no Flash, the video doesn't work.
* Spoofing as Chrome makes the video play
*/
id: "bug1574564",
platform: "android",
domain: "ceskatelevize.cz",
bug: "1574564",
config: {
matches: ["*://*.ceskatelevize.cz/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Mobile Safari/537.36"
);
},
},
},
{
/*
* Bug 1577240 - UA override for heb.com on Firefox for Android
* WebCompat issue #33613 - https://webcompat.com/issues/33613
*
* heb.com shows desktop site on Firefox for Android for some pages based on
* UA detection. Spoofing as Chrome allows to get mobile site.
*/
id: "bug1577240",
platform: "android",
domain: "heb.com",
bug: "1577240",
config: {
matches: ["*://*.heb.com/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Mobile Safari/537.36"
);
},
},
},
{
/*
* Bug 1577250 - UA override for homebook.pl on Firefox for Android
* WebCompat issue #24044 - https://webcompat.com/issues/24044
*
* homebook.pl shows desktop site on Firefox for Android based on
* UA detection. Spoofing as Chrome allows to get mobile site.
*/
id: "bug1577250",
platform: "android",
domain: "homebook.pl",
bug: "1577250",
config: {
matches: ["*://*.homebook.pl/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Mobile Safari/537.36"
);
},
},
},
{
/*
* Bug 1577267 - UA override for metfone.com.kh on Firefox for Android
* WebCompat issue #16363 - https://webcompat.com/issues/16363
*
* metfone.com.kh has a server side UA detection which returns desktop site
* for Firefox for Android. Spoofing as Chrome allows to receive mobile version
*/
id: "bug1577267",
platform: "android",
domain: "metfone.com.kh",
bug: "1577267",
config: {
matches: ["*://*.metfone.com.kh/*"],
uaTransformer: originalUA => {
return (
UAHelpers.getPrefix(originalUA) +
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Mobile Safari/537.36"
);
},
},
},
];
const UAHelpers = {
getDeviceAppropriateChromeUA() {
if (!UAHelpers._deviceAppropriateChromeUA) {
const userAgent =
typeof navigator !== "undefined" ? navigator.userAgent : "";
const RunningFirefoxVersion = (userAgent.match(/Firefox\/([0-9.]+)/) || [
"",
"58.0",
])[1];
const RunningAndroidVersion =
userAgent.match(/Android\/[0-9.]+/) || "Android 6.0";
const ChromeVersionToMimic = "76.0.3809.111";
const ChromePhoneUA = `Mozilla/5.0 (Linux; ${RunningAndroidVersion}; Nexus 5 Build/MRA58N) FxQuantum/${RunningFirefoxVersion} AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${ChromeVersionToMimic} Mobile Safari/537.36`;
const ChromeTabletUA = `Mozilla/5.0 (Linux; ${RunningAndroidVersion}; Nexus 7 Build/JSS15Q) FxQuantum/${RunningFirefoxVersion} AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${ChromeVersionToMimic} Safari/537.36`;
const IsPhone = userAgent.includes("Mobile");
UAHelpers._deviceAppropriateChromeUA = IsPhone
? ChromePhoneUA
: ChromeTabletUA;
}
return UAHelpers._deviceAppropriateChromeUA;
},
getPrefix(originalUA) {
return originalUA.substr(0, originalUA.indexOf(")") + 1);
},
};
module.exports = AVAILABLE_UA_OVERRIDES;

View File

@ -1,50 +0,0 @@
/* 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, XPCOMUtils */
XPCOMUtils.defineLazyModuleGetters(this, {
Services: "resource://gre/modules/Services.jsm",
});
this.aboutConfigPrefs = class extends ExtensionAPI {
getAPI(context) {
const EventManager = ExtensionCommon.EventManager;
const extensionIDBase = context.extension.id.split("@")[0];
const extensionPrefNameBase = `extensions.${extensionIDBase}.`;
return {
aboutConfigPrefs: {
onPrefChange: new EventManager({
context,
name: "aboutConfigPrefs.onUAOverridesPrefChange",
register: (fire, name) => {
const prefName = `${extensionPrefNameBase}${name}`;
const callback = () => {
fire.async(name).catch(() => {}); // ignore Message Manager disconnects
};
Services.prefs.addObserver(prefName, callback);
return () => {
Services.prefs.removeObserver(prefName, callback);
};
},
}).api(),
async getPref(name) {
try {
return Services.prefs.getBoolPref(
`${extensionPrefNameBase}${name}`
);
} catch (_) {
return undefined;
}
},
async setPref(name, value) {
Services.prefs.setBoolPref(`${extensionPrefNameBase}${name}`, value);
},
},
};
}
};

View File

@ -1,53 +0,0 @@
[
{
"namespace": "aboutConfigPrefs",
"description": "experimental API extension to allow access to about:config preferences",
"events": [
{
"name": "onPrefChange",
"type": "function",
"parameters": [{
"name": "name",
"type": "string",
"description": "The preference which changed"
}],
"extraParameters": [{
"name": "name",
"type": "string",
"description": "The preference to monitor"
}]
}
],
"functions": [
{
"name": "getPref",
"type": "function",
"description": "Get a preference's value",
"parameters": [{
"name": "name",
"type": "string",
"description": "The preference name"
}],
"async": true
},
{
"name": "setPref",
"type": "function",
"description": "Set a preference's value",
"parameters": [
{
"name": "name",
"type": "string",
"description": "The preference name"
},
{
"name": "value",
"type": "boolean",
"description": "The new value"
}
],
"async": true
}
]
}
]

View File

@ -1,34 +0,0 @@
/* 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, Services, XPCOMUtils */
XPCOMUtils.defineLazyModuleGetters(this, {
EventDispatcher: "resource://gre/modules/Messaging.jsm",
Services: "resource://gre/modules/Services.jsm",
});
this.experiments = class extends ExtensionAPI {
getAPI(context) {
function promiseActiveExperiments() {
return EventDispatcher.instance.sendRequestForResult({
type: "Experiments:GetActive",
});
}
return {
experiments: {
async isActive(name) {
if (!Services.androidBridge || !Services.androidBridge.isFennec) {
return undefined;
}
return promiseActiveExperiments().then(experiments => {
return experiments.includes(name);
});
},
},
};
}
};

View File

@ -1,21 +0,0 @@
[
{
"namespace": "experiments",
"description": "experimental API extension to allow checking the status of Fennec experiments via Switchboard",
"functions": [
{
"name": "isActive",
"type": "function",
"description": "Determine if a given experiment is active.",
"parameters": [
{
"name": "name",
"type": "string",
"description": "The experiment's name"
}
],
"async": true
}
]
}
]

View File

@ -1,33 +0,0 @@
/* 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, Services, XPCOMUtils */
XPCOMUtils.defineLazyModuleGetters(this, {
Services: "resource://gre/modules/Services.jsm",
SharedPreferences: "resource://gre/modules/SharedPreferences.jsm",
});
this.sharedPreferences = class extends ExtensionAPI {
getAPI(context) {
return {
sharedPreferences: {
async setCharPref(name, value) {
if (!Services.androidBridge || !Services.androidBridge.isFennec) {
return;
}
SharedPreferences.forApp().setCharPref(name, value);
},
async setBoolPref(name, value) {
if (!Services.androidBridge || !Services.androidBridge.isFennec) {
return;
}
SharedPreferences.forApp().setBoolPref(name, value);
},
},
};
}
};

View File

@ -1,44 +0,0 @@
[
{
"namespace": "sharedPreferences",
"description": "experimental API extension to allow setting SharedPreferences on Fennec",
"functions": [
{
"name": "setBoolPref",
"type": "function",
"description": "Set the value of a boolean Fennec SharedPreference",
"parameters": [
{
"name": "name",
"type": "string",
"description": "The key name"
},
{
"name": "value",
"type": "boolean",
"description": "The new value"
}
],
"async": true
},
{
"name": "setCharPref",
"type": "function",
"description": "Set the value of a string Fennec SharedPreference",
"parameters": [
{
"name": "name",
"type": "string",
"description": "The key name"
},
{
"name": "value",
"type": "string",
"description": "The new value"
}
],
"async": true
}
]
}
]

View File

@ -1,3 +0,0 @@
#css-injection.red {
background-color: #0f0;
}

View File

@ -1,14 +0,0 @@
/**
* gaming.youtube.com - The vertical scrollbar displayed for the main pane is
* partially overlapped by the video itself
* Bug #1305028 - https://bugzilla.mozilla.org/show_bug.cgi?id=1305028
*
* The scrollbar in the main player area is overlapped by the player, making the
* design look broken. In Chrome, YouTube is using ::-webkit-scrollbar to style
* the bar to match their expectations, but this doesn't work in Firefox.
* To make it look less broken, we hide the scrollbar for the main video pane
* entirely.
*/
ytg-scroll-pane.ytg-watch-page {
scrollbar-width: none;
}

View File

@ -1,16 +0,0 @@
/**
* discordapp.com - -webkit-scrollbar dependency causes visible white line
* Part of Bug #1432935 - https://bugzilla.mozilla.org/show_bug.cgi?id=1432935
* WebCompat issue #7919 - https://webcompat.com/issues/7919
*
* Discord depends on -webkit-scrollbar for styling and hiding their scrollbars
* in the UI. Previously, the scrollbars overlapped content permanently.
* However, this issue seems to be addressed now, but a small white line
* still remains. While Discord is working on this, let's get rid of these
* lines.
*/
.themeGhostHairline-DBD-2d .pad-29zQak,
.themeGhostHairlineChannels-3G0x9_ .pad-29zQak {
width: 3px !important;
left: -3px !important;
}

View File

@ -1,14 +0,0 @@
/**
* twitch.tv - Comment interaction button is overlayed by scrollbar
* Bug #1518781 - https://bugzilla.mozilla.org/show_bug.cgi?id=1518781
*
* The interaction buttons in Twitch' chat are partly overlayed by the
* scrollbar, which makes them hard to use. Twitch uses
* ::-webkit-scrollbar to make the scrollbar thinner, which isn't working in
* Firefox.
* Given that even scrollbar-width: thin; is not enough (see Bugzilla), let's
* remove it entirely.
*/
.video-chat__message-list-wrapper {
scrollbar-width: none;
}

View File

@ -1,12 +0,0 @@
/**
* sreedharscce.in - Fix login form with CSS intervention
* Bug #1526977 - https://bugzilla.mozilla.org/show_bug.cgi?id=1526977
* WebCompat issue #21505 - https://webcompat.com/issues/21505
*
* The login form is partly moved out of the screen on sreedharscce.in in
* Firefox. Enforcing the body height to the full viewport fixes this issue,
* as the login form itself is posititoned with `position: absolute;`.
*/
body {
height: 100vh;
}

View File

@ -1,12 +0,0 @@
/**
* mail.google.com - The HTML email view does not allow horizontal scrolling
* on Fennec due to a missing CSS rule which is only served to Chrome.
* Bug #1561371 - https://bugzilla.mozilla.org/show_bug.cgi?id=1561371
*
* HTML emails may sometimes contain content that does not wrap, yet the
* CSS served to Fennec does not permit scrolling horizontally. To prevent
* this UX frustration, we enable horizontal scrolling.
*/
body > #views {
overflow: auto;
}

View File

@ -1,12 +0,0 @@
/**
* dns.google.com - Page content is shifted to the left side of the page
* Bug #1567610 - https://bugzilla.mozilla.org/show_bug.cgi?id=1567610
* WebCompat issue #22494 - https://webcompat.com/issues/22494
*
* Affected element is styled with width:fit-content; which is not
* supported by Firefox yet, see https://bugzilla.mozilla.org/show_bug.cgi?id=1495868
* Adding -moz-fit-content fixes the issue
*/
main > .ng-star-inserted > .centered {
width: -moz-fit-content;
}

View File

@ -1,12 +0,0 @@
/**
* zertifikate.commerzbank.de - clickable elements on the page are collapsed
* Bug #1568256 - https://bugzilla.mozilla.org/show_bug.cgi?id=1568256
* WebCompat issue #9102 - https://webcompat.com/issues/9102
*
* Affected elements have display:-webkit-box and display:flex applied, however,
* listed in wrong order, so display:-webkit-box is becoming the final say.
* Adding display: flex for those elements fixes the issue
*/
.x-layout-box {
display: flex !important;
}

View File

@ -1,16 +0,0 @@
/**
* console.cloud.google.com - Double scrollbar visisible on long pages
* Bug #1568908 - https://bugzilla.mozilla.org/show_bug.cgi?id=1568908
* WebCompat issue #33164 - https://webcompat.com/issues/33164
*
* For pages that have contents heigher than the page's height, a secondary
* scrollbar outside the scrollable content area is visible. This is caused
* by a difference in Flexbox behavior, which is being addressed in
* https://bugs.chromium.org/p/chromium/issues/detail?id=981134
*
* Until this fix hits release and Google has updated their UI properly,
* this intervention addresses the differences.
*/
central-page-area {
min-height: 0;
}

View File

@ -1,11 +0,0 @@
/**
* teamcoco.com - a scrollbar at the top covering navigation menu
* Bug #1570119 - https://bugzilla.mozilla.org/show_bug.cgi?id=1570119
*
* The scrollbar is covering navigation items making them unusable.
* There are ::-webkit-scrollbar css rules already applied to the scrollbar,
* hiding it in Chrome. Adding the scrollbar-width: none fixes the issue in Firefox.
*/
.css-bdnz85 {
scrollbar-width: none;
}

View File

@ -1,17 +0,0 @@
/**
* developer.apple.com - content of the page is shifted to the left
* Bug #1570328 - https://bugzilla.mozilla.org/show_bug.cgi?id=1570328
* WebCompat issue #4070 - https://webcompat.com/issues/4070
*
* The site is relying on zoom property which is not supported by Mozilla,
* see https://bugzilla.mozilla.org/show_bug.cgi?id=390936. Adding a combination
* of transform: scale(1.4), transform-origin and width fixes the issue
*/
@media only screen and (min-device-width: 320px) and (max-device-width: 980px),
(min-device-width: 1024px) and (max-device-width: 1024px) and (min-device-height: 1366px) and (max-device-height: 1366px) and (min-width: 320px) and (max-width: 980px) {
#tocContainer {
transform-origin: 0 0;
transform: scale(1.4);
width: 71.4%;
}
}

View File

@ -1,13 +0,0 @@
/**
* patch.com - sub-menu expands at the bottom of the page and overlaps other elements.
* Bug #1574973 - https://bugzilla.mozilla.org/show_bug.cgi?id=1574973
* WebCompat issue #25384 - https://webcompat.com/issues/25384
*
* patch.con has a top:100% style on the relatively-positioned element
* with class="dropdown-menu", and Firefox is incorrectly honoring that
* style (resolving it to something nonzero), whereas Chrome just treats it as "auto"
* see https://bugzilla.mozilla.org/show_bug.cgi?id=1092007
*/
#patch-nav-secondary .dropdown-menu {
top: auto;
}

View File

@ -1,11 +0,0 @@
/**
* apply.lloydsbank.co.uk - radio buttons are misplaced
* Bug #1575000 - https://bugzilla.mozilla.org/show_bug.cgi?id=1575000
* WebCompat issue #34969 - https://webcompat.com/issues/34969
*
* Radio buttons are displaced to the left due to positioning issue of ::before
* pseudo element, adding position relative to it's parent fixes the issue.
*/
.radio-content-field .radio.inline label span.text {
position: relative;
}

View File

@ -1,15 +0,0 @@
/**
* holiday-weather.com - page is not scrollable
* Bug #1575011 - https://bugzilla.mozilla.org/show_bug.cgi?id=1575011
* WebCompat issue #18478 - https://webcompat.com/issues/18478
*
* the page won't scroll since the flex container is too high,
* adding min height to parent containers fixes the issue
*/
.page-container-style__pageContent {
min-height: 0;
}
.widgets-style__root {
min-height: 0;
}

View File

@ -1,11 +0,0 @@
/**
* dunkindonuts.com - form input fields are small and misaligned
* Bug #1575017 - https://bugzilla.mozilla.org/show_bug.cgi?id=1575017
* WebCompat issue #28742 - https://webcompat.com/issues/28742
*
* Form input fields are small and misaligned due to flex-basis: min-content;
* applied on their parent element. Setting it to auto fixes the issue
*/
.grid__item {
flex-basis: auto;
}

View File

@ -1,12 +0,0 @@
/**
* binance.com - can't see the full site
* Bug #1577270 - https://bugzilla.mozilla.org/show_bug.cgi?id=1577270
* WebCompat issue #17810 - https://webcompat.com/issues/17810
*
* The site does not have a doctype and is rendered in quirks mode. The calc() percentage
* height is applied on the .main-page .viewWrap element, but its parent does not have
* a specified height property. Adding a height of 100% to the parent fixes the issue.
*/
#tradeDiv {
height: 100%;
}

View File

@ -1,11 +0,0 @@
/**
* kitkat.com.au - can't see the content
* Bug #1577297 - https://bugzilla.mozilla.org/show_bug.cgi?id=1577297
* WebCompat issue #28992 - https://webcompat.com/issues/28992
*
* Affected element is too wide due to https://bugzilla.mozilla.org/show_bug.cgi?id=1316534.
* Adding min-width: 0; fixes the issue
*/
.columns .column.main {
min-width: 0;
}

View File

@ -1,11 +0,0 @@
"use strict";
/* globals exportFunction */
Object.defineProperty(window.wrappedJSObject, "isTestFeatureSupported", {
get: exportFunction(function() {
return true;
}, window),
set: exportFunction(function() {}, window),
});

View File

@ -1,29 +0,0 @@
"use strict";
/**
* Bug 1452707 - Build site patch for ib.absa.co.za
* WebCompat issue #16401 - https://webcompat.com/issues/16401
*
* The online banking at ib.absa.co.za detect if window.controllers is a
* non-falsy value to detect if the current browser is Firefox or something
* else. In bug 1448045, this shim has been disabled for Firefox Nightly 61+,
* which breaks the UA detection on this site and results in a "Browser
* unsuppored" error message.
*
* This site patch simply sets window.controllers to a string, resulting in
* their check to work again.
*/
/* globals exportFunction */
console.info(
"window.controllers has been shimmed for compatibility reasons. See https://webcompat.com/issues/16401 for details."
);
Object.defineProperty(window.wrappedJSObject, "controllers", {
get: exportFunction(function() {
return true;
}, window),
set: exportFunction(function() {}, window),
});

View File

@ -1,34 +0,0 @@
"use strict";
/**
* Bug 1457335 - histography.io - Override UA & navigator.vendor
* WebCompat issue #1804 - https://webcompat.com/issues/1804
*
* This site is using a strict matching of navigator.userAgent and
* navigator.vendor to allow access for Safari or Chrome. Here, we set the
* values appropriately so we get recognized as Chrome.
*/
/* globals exportFunction */
console.info(
"The user agent has been overridden for compatibility reasons. See https://webcompat.com/issues/1804 for details."
);
const CHROME_UA = navigator.userAgent + " Chrome for WebCompat";
Object.defineProperty(window.navigator.wrappedJSObject, "userAgent", {
get: exportFunction(function() {
return CHROME_UA;
}, window),
set: exportFunction(function() {}, window),
});
Object.defineProperty(window.navigator.wrappedJSObject, "vendor", {
get: exportFunction(function() {
return "Google Inc.";
}, window),
set: exportFunction(function() {}, window),
});

View File

@ -1,48 +0,0 @@
"use strict";
/**
* Bug 1472075 - Build UA override for Bank of America for OSX & Linux
* WebCompat issue #2787 - https://webcompat.com/issues/2787
*
* BoA is showing a red warning to Linux and macOS users, while accepting
* Windows users without warning. From our side, there is no difference here
* and we receive a lot of user complains about the warnings, so we spoof
* as Firefox on Windows in those cases.
*/
/* globals exportFunction */
if (!navigator.platform.includes("Win")) {
console.info(
"The user agent has been overridden for compatibility reasons. See https://webcompat.com/issues/2787 for details."
);
const WINDOWS_UA = navigator.userAgent.replace(
/\(.*; rv:/i,
"(Windows NT 10.0; Win64; x64; rv:"
);
Object.defineProperty(window.navigator.wrappedJSObject, "userAgent", {
get: exportFunction(function() {
return WINDOWS_UA;
}, window),
set: exportFunction(function() {}, window),
});
Object.defineProperty(window.navigator.wrappedJSObject, "appVersion", {
get: exportFunction(function() {
return "appVersion";
}, window),
set: exportFunction(function() {}, window),
});
Object.defineProperty(window.navigator.wrappedJSObject, "platform", {
get: exportFunction(function() {
return "Win64";
}, window),
set: exportFunction(function() {}, window),
});
}

View File

@ -1,25 +0,0 @@
"use strict";
/**
* Bug 1472081 - election.gov.np - Override window.sidebar with something falsey
* WebCompat issue #11622 - https://webcompat.com/issues/11622
*
* This site is blocking onmousedown and onclick if window.sidebar is something
* that evaluates to true, rendering the form fields unusable. This patch
* overrides window.sidebar with false, so the blocking event handlers won't
* get registered.
*/
/* globals exportFunction */
console.info(
"window.sidebar has been shimmed for compatibility reasons. See https://webcompat.com/issues/11622 for details."
);
Object.defineProperty(window.wrappedJSObject, "sidebar", {
get: exportFunction(function() {
return false;
}, window),
set: exportFunction(function() {}, window),
});

View File

@ -1,25 +0,0 @@
"use strict";
/**
* portalminasnet.com - Override window.sidebar with something falsey
* WebCompat issue #18143 - https://webcompat.com/issues/18143
*
* This site is blocking onmousedown and onclick if window.sidebar is something
* that evaluates to true, rendering the login unusable. This patch overrides
* window.sidebar with false, so the blocking event handlers won't get
* registered.
*/
/* globals exportFunction */
console.info(
"window.sidebar has been shimmed for compatibility reasons. See https://webcompat.com/issues/18143 for details."
);
Object.defineProperty(window.wrappedJSObject, "sidebar", {
get: exportFunction(function() {
return false;
}, window),
set: exportFunction(function() {}, window),
});

Some files were not shown because too many files have changed in this diff Show More