merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-10-30 11:46:11 +01:00
commit 217c2b85c7
65 changed files with 1334 additions and 699 deletions

View File

@ -249,6 +249,7 @@ EXTRA_JS_MODULES.commonjs.sdk.content += [
'source/lib/sdk/content/loader.js',
'source/lib/sdk/content/mod.js',
'source/lib/sdk/content/page-mod.js',
'source/lib/sdk/content/page-worker.js',
'source/lib/sdk/content/sandbox.js',
'source/lib/sdk/content/tab-events.js',
'source/lib/sdk/content/thumbnail.js',

View File

@ -11,6 +11,7 @@ const { Ci } = require("chrome");
const core = require("../l10n/core");
const { loadSheet, removeSheet } = require("../stylesheet/utils");
const { process, frames } = require("../remote/child");
const { Services } = require("resource://gre/modules/Services.jsm");
const assetsURI = require('../self').data.url();
@ -80,7 +81,7 @@ function onDocumentReady2Translate(event) {
}
}
function onContentWindow({ target: document }) {
function onContentWindow(document) {
// Accept only HTML documents
if (!(document instanceof Ci.nsIDOMHTMLDocument))
return;
@ -109,13 +110,20 @@ function onContentWindow({ target: document }) {
// Listen to creation of content documents in order to translate them as soon
// as possible in their loading process
const ON_CONTENT = "DOMDocElementInserted";
const ON_CONTENT = "document-element-inserted";
let enabled = false;
function enable() {
frames.addEventListener(ON_CONTENT, onContentWindow, true);
if (enabled)
return;
Services.obs.addObserver(onContentWindow, ON_CONTENT, false);
enabled = true;
}
process.port.on("sdk/l10n/html/enable", enable);
function disable() {
frames.removeEventListener(ON_CONTENT, onContentWindow, true);
if (!enabled)
return;
Services.obs.removeObserver(onContentWindow, ON_CONTENT);
enabled = false;
}
process.port.on("sdk/l10n/html/disable", disable);

View File

@ -0,0 +1,153 @@
/* 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 { frames } = require("../remote/child");
const { Class } = require("../core/heritage");
const { Disposable } = require('../core/disposable');
const { data } = require("../self");
const { once } = require("../dom/events");
const { getAttachEventType } = require("./utils");
const { Rules } = require('../util/rules');
const { uuid } = require('../util/uuid');
const { WorkerChild } = require("./worker-child");
const { Cc, Ci, Cu } = require("chrome");
const { observe } = require("../event/chrome");
const { on } = require("../event/core");
const appShell = Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci.nsIAppShellService);
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const pages = new Map();
const DOC_INSERTED = "document-element-inserted";
function isValidURL(page, url) {
return !page.rules || page.rules.matchesAny(url);
}
const ChildPage = Class({
implements: [ Disposable ],
setup: function(frame, id, options) {
this.id = id;
this.frame = frame;
this.options = options;
this.webNav = appShell.createWindowlessBrowser(false);
this.docShell.allowJavascript = this.options.allow.script;
// Accessing the browser's window forces the initial about:blank document to
// be created before we start listening for notifications
this.contentWindow;
this.webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
pages.set(this.id, this);
this.contentURL = options.contentURL;
if (options.include) {
this.rules = Rules();
this.rules.add.apply(this.rules, [].concat(options.include));
}
},
dispose: function() {
pages.delete(this.id);
this.webProgress.removeProgressListener(this);
this.webNav = null;
},
attachWorker: function() {
if (!isValidURL(this, this.contentWindow.location.href))
return;
this.options.id = uuid().toString();
this.options.window = this.contentWindow;
this.frame.port.emit("sdk/frame/connect", this.id, {
id: this.options.id,
url: this.contentWindow.document.documentURIObject.spec
});
new WorkerChild(this.options);
},
get docShell() {
return this.webNav.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
},
get webProgress() {
return this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
},
get contentWindow() {
return this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
},
get contentURL() {
return this.options.contentURL;
},
set contentURL(url) {
this.options.contentURL = url;
url = this.options.contentURL ? data.url(this.options.contentURL) : "about:blank";
this.webNav.loadURI(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
},
onLocationChange: function(progress, request, location, flags) {
// Ignore inner-frame events
if (progress != this.webProgress)
return;
// Ignore events that don't change the document
if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)
return;
let event = getAttachEventType(this.options);
// Attaching at the start of the load is handled by the
// document-element-inserted listener.
if (event == DOC_INSERTED)
return;
once(this.contentWindow, event, () => {
this.attachWorker();
}, false);
},
QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"])
});
on(observe(DOC_INSERTED), "data", ({ target }) => {
let page = Array.from(pages.values()).find(p => p.contentWindow.document === target);
if (!page)
return;
if (getAttachEventType(page.options) == DOC_INSERTED)
page.attachWorker();
});
frames.port.on("sdk/frame/create", (frame, id, options) => {
new ChildPage(frame, id, options);
});
frames.port.on("sdk/frame/set", (frame, id, params) => {
let page = pages.get(id);
if (!page)
return;
if ("allowScript" in params)
page.docShell.allowJavascript = params.allowScript;
if ("contentURL" in params)
page.contentURL = params.contentURL;
});
frames.port.on("sdk/frame/destroy", (frame, id) => {
let page = pages.get(id);
if (!page)
return;
page.destroy();
});

View File

@ -53,7 +53,7 @@ function isWindowInTab(window) {
if (isChildLoader) {
let { frames } = require('../remote/child');
let frame = frames.getFrameForWindow(window.top);
return frame.isTab;
return frame && frame.isTab;
}
else {
// The deprecated sync worker API still does everything in the main process

View File

@ -51,7 +51,9 @@ const WorkerChild = Class({
this.sandbox = WorkerSandbox(this, this.window);
this.frozen = false;
// If the document is still loading wait for it to finish before passing on
// received messages
this.frozen = this.window.document.readyState == "loading";
this.frozenMessages = [];
this.on('pageshow', () => {
this.frozen = false;

View File

@ -57,38 +57,31 @@ function create(target, options) {
frame.setAttribute('type', options.type || 'content');
frame.setAttribute('src', options.uri || 'about:blank');
// Must set the remote attribute before attaching the frame to the document
if (remote && isXUL) {
// We remove XBL binding to avoid execution of code that is not going to
// work because browser has no docShell attribute in remote mode
// (for example)
frame.setAttribute('style', '-moz-binding: none;');
frame.setAttribute('remote', 'true');
}
target.appendChild(frame);
// Load in separate process if `options.remote` is `true`.
// http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1347
if (remote) {
if (isXUL) {
// We remove XBL binding to avoid execution of code that is not going to
// work because browser has no docShell attribute in remote mode
// (for example)
frame.setAttribute('style', '-moz-binding: none;');
frame.setAttribute('remote', 'true');
}
else {
frame.QueryInterface(Ci.nsIMozBrowserFrame);
frame.createRemoteFrameLoader(null);
}
if (remote && !isXUL) {
frame.QueryInterface(Ci.nsIMozBrowserFrame);
frame.createRemoteFrameLoader(null);
}
// If browser is remote it won't have a `docShell`.
if (!remote) {
let docShell = getDocShell(frame);
docShell.allowAuth = options.allowAuth || false;
docShell.allowJavascript = options.allowJavascript || false;
docShell.allowPlugins = options.allowPlugins || false;
// Control whether the document can move/resize the window. Requires
// recently added platform capability, so we test to avoid exceptions
// in cases where capability is not present yet.
if ("allowWindowControl" in docShell && "allowWindowControl" in options)
docShell.allowWindowControl = !!options.allowWindowControl;
docShell.allowWindowControl = options.allowWindowControl || false;
}
return frame;

View File

@ -8,48 +8,58 @@ module.metadata = {
};
const { Class } = require('./core/heritage');
const { on, emit, off, setListeners } = require('./event/core');
const { filter, pipe, map, merge: streamMerge, stripListeners } = require('./event/utils');
const { detach, attach, destroy, WorkerHost } = require('./content/utils');
const { Worker } = require('./deprecated/sync-worker');
const { ns } = require('./core/namespace');
const { pipe, stripListeners } = require('./event/utils');
const { connect, destroy, WorkerHost } = require('./content/utils');
const { Worker } = require('./content/worker');
const { Disposable } = require('./core/disposable');
const { WeakReference } = require('./core/reference');
const { EventTarget } = require('./event/target');
const { unload } = require('./system/unload');
const { events, streamEventsFrom } = require('./content/events');
const { getAttachEventType } = require('./content/utils');
const { setListeners } = require('./event/core');
const { window } = require('./addon/window');
const { getParentWindow } = require('./window/utils');
const { create: makeFrame, getDocShell } = require('./frame/utils');
const { contract } = require('./util/contract');
const { contract: loaderContract } = require('./content/loader');
const { has } = require('./util/array');
const { Rules } = require('./util/rules');
const { merge } = require('./util/object');
const { data } = require('./self');
const { getActiveView } = require("./view/core");
const { uuid } = require('./util/uuid');
const { useRemoteProcesses, remoteRequire, frames } = require("./remote/parent");
remoteRequire("sdk/content/page-worker");
const views = new WeakMap();
const workers = new WeakMap();
const pages = new WeakMap();
const pages = new Map();
const readyEventNames = [
'DOMContentLoaded',
'document-element-inserted',
'load'
];
const internal = ns();
function workerFor(page) {
return workers.get(page);
}
function pageFor(view) {
return pages.get(view);
}
function viewFor(page) {
return views.get(page);
}
function isDisposed (page) {
return !views.get(page, false);
let workerFor = (page) => workers.get(page);
let isDisposed = (page) => !pages.has(internal(page).id);
// The frame is used to ensure we have a remote process to load workers in
let remoteFrame = null;
let framePromise = null;
function getFrame() {
if (framePromise)
return framePromise;
framePromise = new Promise(resolve => {
let view = makeFrame(window.document, {
namespaceURI: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
nodeName: "iframe",
type: "content",
remote: useRemoteProcesses,
uri: "about:blank"
});
// Wait for the remote side to connect
let listener = (frame) => {
if (frame.frameElement != view)
return;
frames.off("attach", listener);
remoteFrame = frame;
resolve(frame);
}
frames.on("attach", listener);
});
return framePromise;
}
var pageContract = contract(merge({
@ -64,7 +74,8 @@ var pageContract = contract(merge({
is: ['string', 'array', 'regexp', 'undefined']
},
contentScriptWhen: {
is: ['string', 'undefined']
is: ['string', 'undefined'],
map: (when) => when || "end"
}
}, loaderContract.rules));
@ -78,16 +89,18 @@ function disableScript (page) {
function Allow (page) {
return {
get script() { return getDocShell(viewFor(page)).allowJavascript; },
set script(value) { return value ? enableScript(page) : disableScript(page); }
};
}
get script() {
return internal(page).options.allow.script;
},
set script(value) {
internal(page).options.allow.script = value;
function injectWorker ({page}) {
let worker = workerFor(page);
let view = viewFor(page);
if (isValidURL(page, view.contentDocument.URL))
attach(worker, view.contentWindow);
if (isDisposed(page))
return;
remoteFrame.port.emit("sdk/frame/set", internal(page).id, { allowScript: value });
}
};
}
function isValidURL(page, url) {
@ -97,31 +110,23 @@ function isValidURL(page, url) {
const Page = Class({
implements: [
EventTarget,
Disposable,
WeakReference
Disposable
],
extends: WorkerHost(workerFor),
setup: function Page(options) {
let page = this;
options = pageContract(options);
// Sanitize the options
if ("contentScriptOptions" in options)
options.contentScriptOptions = JSON.stringify(options.contentScriptOptions);
let uri = options.contentURL;
internal(this).id = uuid().toString();
internal(this).options = options;
let view = makeFrame(window.document, {
nodeName: 'iframe',
type: 'content',
uri: uri ? data.url(uri) : '',
allowJavascript: options.allow.script,
allowPlugins: true,
allowAuth: true
});
view.setAttribute('data-src', uri);
for (let prop of ['contentScriptFile', 'contentScript', 'contentScriptWhen']) {
this[prop] = options[prop];
}
['contentScriptFile', 'contentScript', 'contentScriptWhen']
.forEach(prop => page[prop] = options[prop]);
views.set(this, view);
pages.set(view, this);
pages.set(internal(this).id, this);
// Set listeners on the {Page} object itself, not the underlying worker,
// like `onMessage`, as it gets piped
@ -130,68 +135,60 @@ const Page = Class({
workers.set(this, worker);
pipe(worker, this);
if (this.include || options.include) {
if (options.include) {
this.rules = Rules();
this.rules.add.apply(this.rules, [].concat(this.include || options.include));
this.rules.add.apply(this.rules, [].concat(options.include));
}
getFrame().then(frame => {
if (isDisposed(this))
return;
frame.port.emit("sdk/frame/create", internal(this).id, stripListeners(options));
});
},
get allow() { return Allow(this); },
set allow(value) {
let allowJavascript = pageContract({ allow: value }).allow.script;
return allowJavascript ? enableScript(this) : disableScript(this);
if (isDisposed(this))
return;
this.allow.script = pageContract({ allow: value }).allow.script;
},
get contentURL() {
return internal(this).options.contentURL;
},
get contentURL() { return viewFor(this).getAttribute("data-src") },
set contentURL(value) {
if (!isValidURL(this, value)) return;
let view = viewFor(this);
let contentURL = pageContract({ contentURL: value }).contentURL;
if (!isValidURL(this, value))
return;
internal(this).options.contentURL = value;
if (isDisposed(this))
return;
// page-worker doesn't have a model like other APIs, so to be consitent
// with the behavior "what you set is what you get", we need to store
// the original `contentURL` given.
// Even if XUL elements doesn't support `dataset`, properties, to
// indicate that is a custom attribute the syntax "data-*" is used.
view.setAttribute('data-src', contentURL);
view.setAttribute('src', data.url(contentURL));
remoteFrame.port.emit("sdk/frame/set", internal(this).id, { contentURL: value });
},
dispose: function () {
if (isDisposed(this)) return;
let view = viewFor(this);
if (view.parentNode) view.parentNode.removeChild(view);
views.delete(this);
destroy(workers.get(this));
if (isDisposed(this))
return;
pages.delete(internal(this).id);
let worker = workerFor(this);
if (worker)
destroy(worker);
remoteFrame.port.emit("sdk/frame/destroy", internal(this).id);
// Destroy the remote frame if all the pages have been destroyed
if (pages.size == 0) {
framePromise = null;
remoteFrame.frameElement.remove();
remoteFrame = null;
}
},
toString: function () { return '[object Page]' }
});
exports.Page = Page;
var pageEvents = streamMerge([events, streamEventsFrom(window)]);
var readyEvents = filter(pageEvents, isReadyEvent);
var formattedEvents = map(readyEvents, function({target, type}) {
return { type: type, page: pageFromDoc(target) };
frames.port.on("sdk/frame/connect", (frame, id, params) => {
let page = pages.get(id);
if (!page)
return;
connect(workerFor(page), frame, params);
});
var pageReadyEvents = filter(formattedEvents, function({page, type}) {
return getAttachEventType(page) === type});
on(pageReadyEvents, 'data', injectWorker);
function isReadyEvent ({type}) {
return has(readyEventNames, type);
}
/*
* Takes a document, finds its doc shell tree root and returns the
* matching Page instance if found
*/
function pageFromDoc(doc) {
let parentWindow = getParentWindow(doc.defaultView), page;
if (!parentWindow) return;
let frames = parentWindow.document.getElementsByTagName('iframe');
for (let i = frames.length; i--;)
if (frames[i].contentDocument === doc && (page = pageFor(frames[i])))
return page;
return null;
}
getActiveView.define(Page, viewFor);

View File

@ -29,6 +29,11 @@ const options = require('@loader/options');
const loaderModule = require('toolkit/loader');
const { getTabForBrowser } = require('../tabs/utils');
const appInfo = Cc["@mozilla.org/xre/app-info;1"].
getService(Ci.nsIXULRuntime);
exports.useRemoteProcesses = appInfo.browserTabsRemoteAutostart;
// Chose the right function for resolving relative a module id
var moduleResolve;
if (options.isNative) {
@ -174,7 +179,10 @@ const Frame = Class({
this.port = new EventTarget();
this.port.emit = (...args) => {
ns(this).messageManager.sendAsyncMessage('sdk/remote/frame/message', {
let manager = ns(this).messageManager;
if (!manager)
return;
manager.sendAsyncMessage('sdk/remote/frame/message', {
loaderID,
args
});

View File

@ -63,107 +63,6 @@ exports["test changing result from addon extras in panel"] = function(assert, do
panel.port.emit("get-result");
}
exports["test window result from addon extras in panel"] = function*(assert) {
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, self, {
data: merge({}, self.data, {url: fixtures.url})
})
}
});
const { Panel } = loader.require('sdk/panel');
const { Page } = loader.require('sdk/page-worker');
const { getActiveView } = loader.require("sdk/view/core");
const { getDocShell } = loader.require('sdk/frame/utils');
const { events } = loader.require("sdk/content/sandbox/events");
const { on } = loader.require("sdk/event/core");
const { isAddonContent } = loader.require("sdk/content/utils");
// make a page worker and wait for it to load
var page = yield new Promise(resolve => {
assert.pass("Creating the background page");
let page = Page({
contentURL: "./test.html",
contentScriptWhen: "end",
contentScript: "self.port.emit('end', unsafeWindow.getTestURL() + '')"
});
page.port.once("end", (url) => {
assert.equal(url, fixtures.url("./test.html"), "url is correct");
resolve(page);
});
});
assert.pass("Created the background page");
var extrasVal = {
test: function() {
assert.pass("start test function");
let frame = getActiveView(page);
let window = getUnsafeWindow(frame.contentWindow);
assert.equal(typeof window.getTestURL, "function", "window.getTestURL is a function");
return window;
}
};
on(events, "content-script-before-inserted", ({ window, worker }) => {
let url = window.location.href;
assert.pass("content-script-before-inserted " + url);
if (isAddonContent({ contentURL: url })) {
let extraStuff = Cu.cloneInto(extrasVal, window, {
cloneFunctions: true
});
getUnsafeWindow(window).extras = extraStuff;
assert.pass("content-script-before-inserted done!");
}
});
let panel = Panel({
contentURL: "./test-addon-extras-window.html"
});
yield new Promise(resolve => {
panel.port.once("result1", (result) => {
assert.equal(result, fixtures.url("./test.html"), "result1 is a window");
resolve();
});
assert.pass("emit get-result");
panel.port.emit("get-result");
});
page.destroy();
page = yield new Promise(resolve => {
let page = Page({
contentURL: "./index.html",
contentScriptWhen: "end",
contentScript: "self.port.emit('end')"
});
page.port.once("end", () => resolve(page));
});
yield new Promise(resolve => {
panel.port.once("result2", (result) => {
assert.equal(result, fixtures.url("./index.html"), "result2 is a window");
resolve();
});
assert.pass("emit get-result");
panel.port.emit("get-result");
});
loader.unload();
}
function getUnsafeWindow (win) {
return win.wrappedJSObject || win;
}

View File

@ -327,19 +327,6 @@ exports.testAllowScript = function(assert, done) {
" document.documentElement.getAttribute('foo') == 3)",
contentScriptWhen: "ready"
});
let frame = getActiveView(page);
assert.equal(getDocShell(frame).allowJavascript, true, "allowJavascript is true");
}
exports.testGetActiveViewAndDestroy = function(assert) {
let page = Page({
contentURL: "data:text/html;charset=utf-8,<title>test</title>"
});
let frame = getActiveView(page);
assert.ok(frame.parentNode, "there is a parent node");
page.destroy();
assert.ok(!frame.parentNode, "there is not a parent node");
}
exports.testPingPong = function(assert, done) {

View File

@ -1430,6 +1430,13 @@ pref("dom.identity.enabled", false);
// Block insecure active content on https pages
pref("security.mixed_content.block_active_content", true);
// Show degraded UI for http pages with password fields
#ifdef NIGHTLY_BUILD
pref("security.insecure_password.ui.enabled", true);
#else
pref("security.insecure_password.ui.enabled", false);
#endif
// 1 = allow MITM for certificate pinning checks.
pref("security.cert_pinning.enforcement_level", 1);

View File

@ -18,7 +18,7 @@
- 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>
<title>&certerror.pagetitle1;</title>
<link rel="stylesheet" href="chrome://browser/skin/aboutCertError.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/content/certerror/aboutCertError.css" type="text/css" media="all" />
<!-- This page currently uses the same favicon as neterror.xhtml.
@ -51,6 +51,22 @@
return decodeURIComponent(matches[1]);
}
function goBack(buttonEl)
{
if (history.length > 1) {
history.back();
} else {
location.href = "about:home";
}
buttonEl.disabled = true;
}
function toggleVisibility(id)
{
var node = document.getElementById(id);
node.style.visibility = node.style.visibility == "" ? "hidden" : "";
}
function getDescription()
{
var url = document.documentURI;
@ -66,31 +82,20 @@
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);
for (let host of document.querySelectorAll(".hostname")) {
host.textContent = document.location.hostname;
}
var cssClass = getCSSClass();
if (cssClass == "expertBadCert") {
toggle('technicalContent');
toggle('expertContent');
toggleVisibility('advancedPanel');
}
// 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 (cssClass == "badStsCert" || window != top) {
document.getElementById("expertContent").setAttribute("hidden", "true");
document.getElementById("advancedPanel").setAttribute("hidden", "true");
}
if (cssClass != "badStsCert") {
document.getElementById("badStsCertExplanation").setAttribute("hidden", "true");
@ -167,7 +172,7 @@
* domain names are famous for having '.' characters in them,
* which would allow spurious and possibly hostile matches.
*/
if (endsWith(okHost, "." + thisHost))
if (okHost.endsWith("." + thisHost))
link.href = proto + okHost;
/* case #2:
@ -175,68 +180,46 @@
*
* The certificate is only valid for garage.maemo.org
*/
if (endsWith(thisHost, "." + okHost))
if (thisHost.endsWith("." + okHost))
link.href = proto + okHost;
// If we set a link, meaning there's something helpful for
// the user here, expand the section by default
if (link.href && getCSSClass() != "expertBadCert")
toggle("technicalContent");
}
function endsWith(haystack, needle) {
return haystack.slice(-needle.length) == needle;
}
function toggle(id) {
var el = document.getElementById(id);
if (el.getAttribute("collapsed"))
el.removeAttribute("collapsed");
else
el.setAttribute("collapsed", true);
toggleVisibility("advancedPanel");
}
]]></script>
</head>
<body dir="&locale.dir;">
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer">
<!-- Error Title -->
<div id="errorTitle">
<h1 id="errorTitleText">&certerror.longpagetitle;</h1>
<h1 id="errorTitleText">&certerror.longpagetitle1;</h1>
</div>
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<div id="introContent">
<p id="introContentP1">&certerror.introPara1;</p>
<p>&certerror.introPara2;</p>
<!-- Short Description -->
<div id="errorShortDesc">
<p>&certerror.introPara;</p>
</div>
<div id="whatShouldIDoContent">
<h2>&certerror.whatShouldIDo.heading;</h2>
<div id="whatShouldIDoContentText">
<p>&certerror.whatShouldIDo.content;</p>
<p id="badStsCertExplanation">&certerror.whatShouldIDo.badStsCertExplanation;</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 -->
<h2 id="technicalContent" class="expander" collapsed="true">
<button onclick="toggle('technicalContent');">&certerror.technical.heading;</button>
</h2>
<p id="technicalContentText"/>
<h2 id="expertContent" class="expander" collapsed="true">
<button onclick="toggle('expertContent');">&certerror.expert.heading;</button>
</h2>
<p id="badStsCertExplanation">&certerror.whatShouldIDo.badStsCertExplanation;</p>
<div>
<p>&certerror.expert.content;</p>
<p>&certerror.expert.contentPara2;</p>
<p><a href="https://support.mozilla.org/kb/tls-error-reports" id="learnMoreLink" target="new">&certerror.learnMore;</a></p>
</div>
<div id="buttonContainer">
<button id="returnButton" autocomplete="off" onclick="goBack(this);" autofocus="true">&certerror.returnToPreviousPage.label;</button>
<div id="buttonSpacer"></div>
<button id="advancedButton" autocomplete="off" onclick="toggleVisibility('advancedPanel');" autofocus="true">&certerror.advanced.label;</button>
</div>
<!-- Advanced panel, which is hidden by default -->
<div id="advancedPanel" style="visibility: hidden;">
<p id="technicalContentText"/>
<button id='exceptionDialogButton'>&certerror.addException.label;</button>
</div>
</div>

View File

@ -6927,6 +6927,13 @@ var gIdentityHandler = {
return this._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
},
get _hasInsecureLoginForms() {
// checks if the page has been flagged for an insecure login. Also checks
// if the pref to degrade the UI is set to true
return LoginManagerParent.hasInsecureLoginForms(gBrowser.selectedBrowser) &&
Services.prefs.getBoolPref("security.insecure_password.ui.enabled");
},
// smart getters
get _identityPopup () {
delete this._identityPopup;
@ -7257,7 +7264,7 @@ var gIdentityHandler = {
this._identityBox.classList.add("weakCipher");
}
}
if (LoginManagerParent.hasInsecureLoginForms(gBrowser.selectedBrowser)) {
if (this._hasInsecureLoginForms) {
// Insecure login forms can only be present on "unknown identity"
// pages, either already insecure or with mixed active content loaded.
this._identityBox.classList.add("insecureLoginForms");
@ -7301,7 +7308,7 @@ var gIdentityHandler = {
// Determine if there are insecure login forms.
let loginforms = "secure";
if (LoginManagerParent.hasInsecureLoginForms(gBrowser.selectedBrowser)) {
if (this._hasInsecureLoginForms) {
loginforms = "insecure";
}

View File

@ -1,11 +1,13 @@
[DEFAULT]
support-files =
head.js
file_dom_notifications.html
[browser_notification_close.js]
[browser_notification_do_not_disturb.js]
[browser_notification_open_settings.js]
[browser_notification_remove_permission.js]
[browser_notification_permission_migration.js]
[browser_notification_tab_switching.js]
skip-if = buildapp == 'mulet' || e10s # Bug 1100662 - content access causing uncaught exception - Error: cannot ipc non-cpow object at chrome://mochitests/content/browser/browser/base/content/test/general/browser_notification_tab_switching.js:32 (or in RemoteAddonsChild.jsm)

View File

@ -2,17 +2,6 @@
var notificationURL = "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html";
function waitForCloseWindow(window) {
return new Promise(function(resolve) {
Services.ww.registerNotification(function observer(subject, topic, data) {
if (topic == "domwindowclosed" && subject == window) {
Services.ww.unregisterNotification(observer);
resolve();
}
});
});
}
add_task(function* test_settingsOpen_observer() {
info("Opening a dummy tab so openPreferences=>switchToTabHavingURI doesn't use the blank tab.");
yield BrowserTestUtils.withNewTab({
@ -52,7 +41,7 @@ add_task(function* test_settingsOpen_button() {
return;
}
let closePromise = waitForCloseWindow(alertWindow);
let closePromise = promiseWindowClosed(alertWindow);
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#content");
let openSettingsMenuItem = alertWindow.document.getElementById("openSettingsMenuItem");
openSettingsMenuItem.click();

View File

@ -0,0 +1,43 @@
const UI_VERSION = 32;
var gBrowserGlue = Cc["@mozilla.org/browser/browserglue;1"]
.getService(Ci.nsIObserver);
var notificationURI = makeURI("http://example.org");
var pm = Services.perms;
var currentUIVersion;
add_task(function* setup() {
currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
Services.prefs.setIntPref("browser.migration.version", UI_VERSION - 1);
pm.add(notificationURI, "desktop-notification", pm.ALLOW_ACTION);
});
add_task(function* test_permissionMigration() {
if ("@mozilla.org/system-alerts-service;1" in Cc) {
ok(true, "Notifications don't use XUL windows on all platforms.");
return;
}
info("Waiting for migration notification");
let alertWindowPromise = promiseAlertWindow();
gBrowserGlue.observe(null, "browser-glue-test", "force-ui-migration");
let alertWindow = yield alertWindowPromise;
info("Clicking on notification");
let url = Services.urlFormatter.formatURLPref("browser.push.warning.infoURL");
let closePromise = promiseWindowClosed(alertWindow);
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url);
EventUtils.synthesizeMouseAtCenter(alertWindow.document.getElementById("alertTitleLabel"), {}, alertWindow);
info("Waiting for migration info tab");
let tab = yield tabPromise;
ok(tab, "The migration info tab opened");
yield closePromise;
yield BrowserTestUtils.removeTab(tab);
});
add_task(function* cleanup() {
Services.prefs.setIntPref("browser.migration.version", currentUIVersion);
pm.remove(notificationURI, "desktop-notification");
});

View File

@ -0,0 +1,31 @@
function promiseAlertWindow() {
return new Promise(function(resolve) {
let listener = {
onOpenWindow(window) {
let alertWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
alertWindow.addEventListener("load", function onLoad() {
alertWindow.removeEventListener("load", onLoad);
let windowType = alertWindow.document.documentElement.getAttribute("windowtype");
if (windowType == "alert:alert") {
Services.wm.removeListener(listener);
resolve(alertWindow);
}
});
},
};
Services.wm.addListener(listener);
});
}
// `promiseWindowClosed` is similar to `BrowserTestUtils.closeWindow`, but
// doesn't call `window.close()`.
function promiseWindowClosed(window) {
return new Promise(function(resolve) {
Services.ww.registerNotification(function observer(subject, topic, data) {
if (topic == "domwindowclosed" && subject == window) {
Services.ww.unregisterNotification(observer);
resolve();
}
});
});
}

View File

@ -122,6 +122,7 @@ skip-if = (os == "linux" || os == "mac") && debug # bug 970052, bug 970053
skip-if = os == "linux" # Bug 958026
support-files =
content_aboutAccounts.js
[browser_aboutCertError.js]
[browser_aboutSupport_newtab_security_state.js]
[browser_aboutHealthReport.js]
skip-if = os == "linux" # Bug 924307

View File

@ -0,0 +1,79 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// This is testing the aboutCertError page (Bug 1207107). It's a start,
// but should be expanded to include cert_domain_link / badStsCert
const GOOD_PAGE = "https://example.com/";
const BAD_CERT = "https://expired.example.com/";
add_task(function* checkReturnToAboutHome() {
info("Loading a bad cert page directly and making sure 'return to previous page' goes to about:home");
let browser;
let certErrorLoaded;
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
gBrowser.selectedTab = gBrowser.addTab(BAD_CERT);
browser = gBrowser.selectedBrowser;
certErrorLoaded = waitForCertErrorLoad(browser);
}, false);
info("Loading and waiting for the cert error");
yield certErrorLoaded;
is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward");
info("Clicking the go back button on about:certerror");
let pageshowPromise = promiseWaitForEvent(browser, "pageshow");
yield ContentTask.spawn(browser, null, function* () {
let doc = content.document;
let returnButton = doc.getElementById("returnButton");
returnButton.click();
});
yield pageshowPromise;
is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward");
is(gBrowser.currentURI.spec, "about:home", "Went back");
gBrowser.removeCurrentTab();
});
add_task(function* checkReturnToPreviousPage() {
info("Loading a bad cert page and making sure 'return to previous page' goes back");
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
let browser = gBrowser.selectedBrowser;
info("Loading and waiting for the cert error");
let certErrorLoaded = waitForCertErrorLoad(browser);
BrowserTestUtils.loadURI(browser, BAD_CERT);
yield certErrorLoaded;
is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward");
info("Clicking the go back button on about:certerror");
let pageshowPromise = promiseWaitForEvent(browser, "pageshow");
yield ContentTask.spawn(browser, null, function* () {
let doc = content.document;
let returnButton = doc.getElementById("returnButton");
returnButton.click();
});
yield pageshowPromise;
is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
is(browser.webNavigation.canGoForward, true, "webNavigation.canGoForward");
is(gBrowser.currentURI.spec, GOOD_PAGE, "Went back");
gBrowser.removeCurrentTab();
});
function waitForCertErrorLoad(browser) {
return new Promise(resolve => {
info("Waiting for DOMContentLoaded event");
browser.addEventListener("DOMContentLoaded", function load() {
browser.removeEventListener("DOMContentLoaded", load, false, true);
resolve();
}, false, true);
});
}

View File

@ -9,15 +9,17 @@ function test() {
}
function testBrokenCert() {
if (gBrowser.contentDocument.documentURI === "about:blank")
return;
window.removeEventListener("DOMContentLoaded", testBrokenCert, true);
// Confirm that we are displaying the contributed error page, not the default
ok(gBrowser.contentDocument.documentURI.startsWith("about:certerror"), "Broken page should go to about:certerror, not about:neterror");
// Confirm that the expert section is collapsed
var expertDiv = gBrowser.contentDocument.getElementById("expertContent");
ok(expertDiv, "Expert content div should exist");
ok(expertDiv.hasAttribute("collapsed"), "Expert content should be collapsed by default");
var advancedDiv = gBrowser.contentDocument.getElementById("advancedPanel");
ok(advancedDiv, "Advanced content div should exist");
is_element_hidden(advancedDiv, "Advanced content should not be visible by default");
// Tweak the expert mode pref
gPrefService.setBoolPref("browser.xul.error_pages.expert_bad_cert", true);
@ -28,10 +30,9 @@ function testBrokenCert() {
function testExpertPref() {
window.removeEventListener("DOMContentLoaded", testExpertPref, true);
var expertDiv = gBrowser.contentDocument.getElementById("expertContent");
var technicalDiv = gBrowser.contentDocument.getElementById("technicalContent");
ok(!expertDiv.hasAttribute("collapsed"), "Expert content should not be collapsed with the expert mode pref set");
ok(!technicalDiv.hasAttribute("collapsed"), "Technical content should not be collapsed with the expert mode pref set");
var advancedDiv = gBrowser.contentDocument.getElementById("advancedPanel");
ok(advancedDiv, "Advanced content div should exist");
is_element_visible(advancedDiv, "Advanced content should be visible by default");
// Clean up
gBrowser.removeCurrentTab();

View File

@ -17,12 +17,12 @@ function testIframeCert(e) {
gBrowser.selectedBrowser.removeEventListener("load", testIframeCert, true);
// Confirm that the expert section is hidden
var doc = gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument;
var eC = doc.getElementById("expertContent");
ok(eC, "Expert content should exist")
ok(eC.hasAttribute("hidden"), "Expert content should be hidded by default");
var aP = doc.getElementById("advancedPanel");
ok(aP, "Advanced content should exist");
is_element_hidden(aP, "Advanced content should not be visible by default")
// Clean up
gBrowser.removeCurrentTab();
finish();
}

View File

@ -18,6 +18,10 @@ function waitForInsecureLoginFormsStateChange(browser, count) {
* Checks the insecure login forms logic for the identity block.
*/
add_task(function* test_simple() {
yield new Promise(resolve => SpecialPowers.pushPrefEnv({
"set": [["security.insecure_password.ui.enabled", true]],
}, resolve));
for (let scheme of ["http", "https"]) {
let tab = gBrowser.addTab(scheme + testUrlPath + "form_basic.html");
let browser = tab.linkedBrowser;

View File

@ -42,9 +42,9 @@
*
*/
function require(url) {
var devTimeUrl = url.replace(/^script!/, '');
var devTimeUrl = devTimeUrl.replace(/^imports.*!/, '');
var devTimeUrl = devTimeUrl.replace(/^exports.*!/, '');
// strip off everything up to and including the last !, as those are
// all directives to webpack
var devTimeUrl = url.replace(/^.*!/, '');
insertScript(devTimeUrl, true);
}
@ -174,16 +174,60 @@
// We don't use the SDK's CSS. This will prevent spurious 404 errors.
window.OTProperties.cssURL = "about:blank";
</script>
<script>
// Explicitly create this here so that it's deterministic and easier to
// understand, rather than having it created as a side-effect of loading
// one of our faux-modules.
var loop = {};
</script>
<script type="text/javascript" src="config.js"></script>
<!-- note that for 'make dist', webappEntryPoint.js is replaced by -->
<!-- the standalone.js bundle -->
<script>
// Wait for both "localized" to fire and webapp.js to finish executing.
// This needs to be available ahead of either of those things happening,
// so we declare it here.
//
// The localized event fires asynchronously after l10n.js loads,
// so it could happen before or after loop.webapp.init has been
// defined.
//
// This means at least in the (current) dev setup where our
// require shim injects script tags dynamically. The race could conceivably
// happen in production as well, after l10n.js loads and before webpack.js
// is loaded.
//
// Fortunately, that dev setup nastiness will go away once we get to a
// single build setup using real modules and obsolete build-jsx.
var localizedHasFired = false;
function initIfReady() {
if (localizedHasFired && "webapp" in loop) {
loop.webapp.init();
}
}
</script>
<!-- We'd like to bundle/minify this at some point, but we need to work
out how to configure webpack to run this in a module container
rather than directly in the global scope. -->
<!-- Note that as long as the various closures in the files loaded by
webppEntryPoint.js take navigator.mozL10n as an argument, this
JS must run to completion before that file loads, so this one must be
loaded synchronously first -->
<script type="text/javascript" src="libs/l10n-gaia-02ca67948fe8.js">
</script>
<!-- Note that for 'make dist', webappEntryPoint.js is replaced by
the standalone.js bundle -->
<script type="text/javascript" src="webappEntryPoint.js"></script>
<script>
// Wait for all the localization notes to load
window.addEventListener("localized", function() {
loop.webapp.init();
// see the initIfReady() comments in this file for details
localizedHasFired = true;
initIfReady();
}, false);
</script>

View File

@ -246,3 +246,10 @@ loop.webapp = (function(_, OT, mozL10n) {
WebappRootView: WebappRootView
};
})(_, window.OT, navigator.mozL10n);
// This should only be available when this file is loaded into index.html,
// where it's used to actually start the app.
/* global initIfReady */
if (typeof initIfReady === "function") {
initIfReady();
}

View File

@ -246,3 +246,10 @@ loop.webapp = (function(_, OT, mozL10n) {
WebappRootView: WebappRootView
};
})(_, window.OT, navigator.mozL10n);
// This should only be available when this file is loaded into index.html,
// where it's used to actually start the app.
/* global initIfReady */
if (typeof initIfReady === "function") {
initIfReady();
}

View File

@ -19,8 +19,6 @@
// we turn that off by forcing require to false in that context.
require("imports?require=>false!shared/libs/sdk.js");
require("script!./libs/l10n-gaia-02ca67948fe8.js");
// Ultimately, we'll likely want to pull the vendor libraries from npm, as that
// makes upgrading easier, and it's generally better practice to minify the
// "source" versions of libraries rather than built artifacts. We probably do
@ -28,29 +26,64 @@ require("script!./libs/l10n-gaia-02ca67948fe8.js");
// elimination, but that can be a bit of judgement call.
require("exports?_!shared/libs/lodash-3.9.3.js");
// Note that anything that uses the script loader doesn't get minified, so
// these need to be shimmed to use to other loaders (easiest first cut) or
// turned into real modules:
require("script!shared/libs/backbone-1.2.1.js");
require("script!shared/libs/react-0.12.2.js");
// Disable Backbone's AMD auto-detection, as described at:
//
// https://github.com/jashkenas/backbone/wiki/Using-Backbone-without-jQuery
//
require("expose?Backbone!imports?define=>false!shared/libs/backbone-1.2.1.js");
require("script!shared/js/utils.js");
require("script!shared/js/crypto.js");
require("script!shared/js/mixins.js");
require("script!shared/js/actions.js");
require("script!shared/js/validate.js");
require("script!shared/js/dispatcher.js");
require("script!shared/js/otSdkDriver.js");
require("script!shared/js/store.js");
require("script!shared/js/activeRoomStore.js");
require("script!shared/js/views.js");
require("script!shared/js/urlRegExps.js");
require("script!shared/js/textChatStore.js");
require("script!shared/js/textChatView.js");
require("script!shared/js/linkifiedTextView.js");
/* global: __PROD__ */
if (typeof __PROD__ !== "undefined") {
// webpack warns if we try to minify some prebuilt libraries, so we
// pull in the unbuilt version from node_modules
require("expose?React!react");
require("expose?React!react/addons");
} else {
// our development server setup doesn't yet handle real modules, so for now...
require("shared/libs/react-0.12.2.js");
}
require("script!./js/standaloneAppStore.js");
require("script!./js/standaloneMozLoop.js");
require("script!./js/standaloneRoomViews.js");
require("script!./js/standaloneMetricsStore.js");
require("script!./js/webapp.js");
// Someday, these will be real modules. For now, we're chaining loaders
// to teach webpack how to treat them like modules anyway.
//
// We do it in this file rather than globally in webpack.config.js
// because:
//
// * it's easiest to understand magic (like loader chaining
// interactions) when it's explicit and in one place
// * migrating stuff over to real modules is easier to do gradually
//
// The strategy works like this (webpack loaders chain from right to left):
//
// In standalone, loop is defined for the first time in index.html.
//
// The exports?loop loader sets up webpack to ensure that exports is
// actually exposed to outside world, rather than held privately.
//
// The imports=?loop=>window.loop loader puts the existing window.loop
// reference into that exported container for further modification.
//
// See https://webpack.github.io/docs/shimming-modules.html for more
// context.
//
require("imports?loop=>window.loop!exports?loop!shared/js/utils.js");
require("imports?this=>window,loop=>window.loop!exports?loop!shared/js/crypto.js");
require("imports?loop=>window.loop!exports?loop!shared/js/mixins.js");
require("imports?loop=>window.loop!exports?loop!shared/js/actions.js");
require("imports?loop=>window.loop!exports?loop!shared/js/validate.js");
require("imports?loop=>window.loop!exports?loop!shared/js/dispatcher.js");
require("imports?loop=>window.loop!exports?loop!shared/js/otSdkDriver.js");
require("imports?loop=>window.loop!exports?loop!shared/js/store.js");
require("imports?loop=>window.loop!exports?loop!shared/js/activeRoomStore.js");
require("imports?loop=>window.loop!exports?loop!shared/js/views.js");
require("imports?loop=>window.loop!exports?loop!shared/js/urlRegExps.js");
require("imports?loop=>window.loop!exports?loop!shared/js/textChatStore.js");
require("imports?loop=>window.loop!exports?loop!shared/js/textChatView.js");
require("imports?loop=>window.loop!exports?loop!shared/js/linkifiedTextView.js");
require("imports?loop=>window.loop!exports?loop!./js/standaloneAppStore.js");
require("imports?loop=>window.loop!exports?loop!./js/standaloneMozLoop.js");
require("imports?loop=>window.loop!exports?loop!./js/standaloneRoomViews.js");
require("imports?loop=>window.loop!exports?loop!./js/standaloneMetricsStore.js");
require("imports?loop=>window.loop!exports?loop!./js/webapp.js");

View File

@ -17,8 +17,10 @@
"eslint-plugin-mozilla": "../../../../testing/eslint-plugin-mozilla",
"eslint-plugin-react": "3.5.x",
"exports-loader": "0.6.x",
"expose-loader": "0.7.x",
"express": "4.x",
"imports-loader": "0.6.x",
"react": "0.12.x",
"script-loader": "0.6.x",
"webpack": "1.12.x"
},

View File

@ -37,8 +37,17 @@ function getSharedDir() {
module.exports = {
entry: "./content/webappEntryPoint.js",
// These symbols come in either via <script> tags or the expose loader,
// so we tell webpack to allow them to be unresolved
externals: {
"Backbone": "Backbone",
"navigator.mozL10n": "navigator.mozL10n",
"React": "React",
"underscore": "_"
},
// We want the shared modules to be available without the requirer needing
// to know the path to them, especially that path is different in
// to know the path to them, especially since that path is different in
// mozilla-central and loop-client.
resolve: {
alias: {
@ -55,6 +64,21 @@ module.exports = {
},
plugins: [
// This is a soft-dependency for Backbone, and since we don't use it,
// we need to tell webpack to ignore the unresolved reference. Cribbed
// from
// https://github.com/jashkenas/backbone/wiki/Using-Backbone-without-jQuery
new webpack.IgnorePlugin(/^jquery$/),
// definePlugin takes raw strings and inserts them, so we can put a
// JS string for evaluation at build time. Right now, we test NODE_ENV
// to so we can do different things at build time based on whether we're
// in production mode.
new webpack.DefinePlugin({
__PROD__: JSON.stringify(JSON.parse(
process.env.NODE_ENV && process.env.NODE_ENV === "production"))
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
// XXX I'd _like_ to only suppress the warnings for vendor code, since

View File

@ -582,11 +582,53 @@ this.MigrationUtils = Object.freeze({
}
#endif
// nsIWindowWatcher doesn't deal with raw arrays, so we convert the input
let params;
if (Array.isArray(aParams)) {
params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
for (let item of aParams) {
let comtaminatedVal;
if (item && item instanceof Ci.nsISupports) {
comtaminatedVal = item;
} else {
switch (typeof item) {
case "boolean":
comtaminatedVal = Cc["@mozilla.org/supports-PRBool;1"].
createInstance(Ci.nsISupportsPRBool);
comtaminatedVal.data = item;
break;
case "number":
comtaminatedVal = Cc["@mozilla.org/supports-PRUint32;1"].
createInstance(Ci.nsISupportsPRUint32);
comtaminatedVal.data = item;
break;
case "string":
comtaminatedVal = Cc["@mozilla.org/supports-cstring;1"].
createInstance(Ci.nsISupportsCString);
comtaminatedVal.data = item;
break;
case "undefined":
case "object":
if (!item) {
comtaminatedVal = null;
break;
}
default:
throw new Error("Unexpected parameter type " + (typeof item) + ": " + item);
}
}
params.appendElement(comtaminatedVal, false);
}
} else {
params = aParams;
}
Services.ww.openWindow(aOpener,
"chrome://browser/content/migration/migration.xul",
"_blank",
features,
aParams);
params);
},
/**

View File

@ -31,7 +31,7 @@ var MigrationWizard = {
this._wiz = document.documentElement;
let args = (window.arguments && window.arguments[0]) || [];
let args = window.arguments;
let entryPointId = args[0] || MigrationUtils.MIGRATION_ENTRYPOINT_UNKNOWN;
Services.telemetry.getHistogramById("FX_MIGRATION_ENTRY_POINT").add(entryPointId);

View File

@ -187,6 +187,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
"@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
XPCOMUtils.defineLazyServiceGetter(this, "AlertsService",
"@mozilla.org/alerts-service;1", "nsIAlertsService");
const ABOUT_NEWTAB = "about:newtab";
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
@ -1564,16 +1567,6 @@ BrowserGlue.prototype = {
if (actions.indexOf("showAlert") == -1)
return;
let notifier;
try {
notifier = Cc["@mozilla.org/alerts-service;1"].
getService(Ci.nsIAlertsService);
}
catch (e) {
// nsIAlertsService is not available for this platform
return;
}
let title = getNotifyString({propName: "alertTitle",
stringName: "puAlertTitle",
stringParams: [appName]});
@ -1594,10 +1587,11 @@ BrowserGlue.prototype = {
try {
// This will throw NS_ERROR_NOT_AVAILABLE if the notification cannot
// be displayed per the idl.
notifier.showAlertNotification(null, title, text,
true, url, clickCallback);
AlertsService.showAlertNotification(null, title, text,
true, url, clickCallback);
}
catch (e) {
Cu.reportError(e);
}
},
@ -1877,7 +1871,7 @@ BrowserGlue.prototype = {
},
_migrateUI: function BG__migrateUI() {
const UI_VERSION = 31;
const UI_VERSION = 32;
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
let currentUIVersion = 0;
try {
@ -2218,10 +2212,49 @@ BrowserGlue.prototype = {
xulStore.removeValue(BROWSER_DOCURL, "home-button", "class");
}
if (currentUIVersion < 32) {
this._notifyNotificationsUpgrade();
}
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
},
_hasExistingNotificationPermission: function BG__hasExistingNotificationPermission() {
let enumerator = Services.perms.enumerator;
while (enumerator.hasMoreElements()) {
let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
if (permission.type == "desktop-notification") {
return true;
}
}
return false;
},
_notifyNotificationsUpgrade: function BG__notifyNotificationsUpgrade() {
if (!this._hasExistingNotificationPermission()) {
return;
}
function clickCallback(subject, topic, data) {
if (topic != "alertclickcallback")
return;
let win = RecentWindow.getMostRecentBrowserWindow();
win.openUILinkIn(data, "tab");
}
let imageURL = "chrome://browser/skin/web-notifications-icon.svg";
let title = gBrowserBundle.GetStringFromName("webNotifications.upgradeTitle");
let text = gBrowserBundle.GetStringFromName("webNotifications.upgradeInfo");
let url = Services.urlFormatter.formatURLPref("browser.push.warning.infoURL");
try {
AlertsService.showAlertNotification(imageURL, title, text,
true, url, clickCallback);
}
catch (e) {
Cu.reportError(e);
}
},
// ------------------------------
// public nsIBrowserGlue members
// ------------------------------

View File

@ -74,6 +74,10 @@ var gContentPane = {
setEventListener("notificationsDoNotDisturb", "command",
gContentPane.toggleDoNotDisturbNotifications);
let notificationInfoURL = Services.urlFormatter.formatURLPref("browser.push.warning.infoURL");
document.getElementById("notificationsPolicyLearnMore").setAttribute("href",
notificationInfoURL);
let drmInfoURL =
Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL);

View File

@ -68,10 +68,13 @@
<column/>
</columns>
<rows>
<row id="notificationsPolicyRow">
<vbox align="start">
<row id="notificationsPolicyRow" align="center">
<hbox align="start">
<label id="notificationsPolicy">&notificationsPolicyDesc2.label;</label>
</vbox>
<label id="notificationsPolicyLearnMore"
class="text-link"
value="&notificationsPolicyLearnMore.label;"/>
</hbox>
<hbox pack="end">
<button id="notificationsPolicyButton" label="&notificationsPolicyButton.label;"
accesskey="&notificationsPolicyButton.accesskey;"/>

View File

@ -212,12 +212,15 @@
</hbox>
<label class="fxaMobilePromo">
&mobilePromo.start;<!-- We put these comments to avoid inserting white spaces
--><image class="androidLogo"/><!--
--><label class="androidLink text-link" href="https://www.mozilla.org/firefox/android/"><!--
--><label class="androidLink text-link"
href="https://www.mozilla.org/firefox/android/?utm_source=firefox-browser&amp;utm_medium=firefox-browser&amp;utm_campaign=sync-preferences"><!--
-->&mobilePromo.androidLink;</label><!--
-->&mobilePromo.iOSBefore;<!--
--><label class="iOSLink text-link"
href="https://www.mozilla.org/firefox/ios/?utm_source=firefox-browser&amp;utm_medium=firefox-browser&amp;utm_campaign=sync-preferences"><!--
-->&mobilePromo.iOSLink;</label><!--
-->&mobilePromo.end;
</label>
<label class="androidAttribution">&androidAttribution;</label>
</vbox>
<vbox id="hasFxaAccount">
@ -342,15 +345,18 @@
</hbox>
</hbox>
</groupbox>
<spacer class="separator"/>
<label class="fxaMobilePromo">
&mobilePromo.start;<!-- We put these comments to avoid inserting white spaces
--><image class="androidLogo"/><!--
--><label class="androidLink text-link" href="https://www.mozilla.org/firefox/android/"><!--
--><label class="androidLink text-link"
href="https://www.mozilla.org/firefox/android/?utm_source=firefox-browser&amp;utm_medium=firefox-browser&amp;utm_campaign=sync-preferences"><!--
-->&mobilePromo.androidLink;</label><!--
-->&mobilePromo.iOSBefore;<!--
--><label class="iOSLink text-link"
href="https://www.mozilla.org/firefox/ios/?utm_source=firefox-browser&amp;utm_medium=firefox-browser&amp;utm_campaign=sync-preferences"><!--
-->&mobilePromo.iOSLink;</label><!--
-->&mobilePromo.end;
</label>
<spacer flex="1"/>
<spacer class="separator" flex="1"/>
<vbox id="tosPP-small" align="start">
<label id="tosPP-small-ToS" class="text-link">
&prefs.tosLink.label;
@ -359,6 +365,5 @@
&fxaPrivacyNotice.link.label;
</label>
</vbox>
<label class="androidAttribution">&androidAttribution;</label>
</vbox>
</deck>

View File

@ -10,23 +10,9 @@
a replacement for the standard security certificate errors produced
by NSS/PSM via netError.xhtml. -->
<!ENTITY certerror.pagetitle "Untrusted Connection">
<!ENTITY certerror.longpagetitle "This Connection is Untrusted">
<!-- These are going to be used for the updated design in Bug 1207107 -->
<!ENTITY certerror.pagetitle1 "Insecure Connection">
<!ENTITY certerror.longpagetitle1 "Your connection is not secure">
<!-- Localization note (certerror.introPara1) - The string "#1" will
be replaced at runtime with the name of the server to which the user
was trying to connect. -->
<!ENTITY certerror.introPara1 "You have asked &brandShortName; to connect
securely to <b>#1</b>, but we can't confirm that your connection is secure.">
<!ENTITY certerror.introPara2 "Normally, when you try to connect securely,
sites will present trusted identification to prove that you are
going to the right place. However, this site's identity can't be verified.">
<!-- These are going to be used for the udpated design in Bug 1207107 -->
<!-- Localization note (certerror.introPara) - The text content of the span tag
will be replaced at runtime with the name of the server to which the user
was trying to connect. -->
@ -35,17 +21,11 @@ was trying to connect. -->
<!ENTITY certerror.learnMore "Learn more…">
<!ENTITY certerror.advanced.label "Advanced">
<!ENTITY certerror.whatShouldIDo.heading "What Should I Do?">
<!ENTITY certerror.whatShouldIDo.content "If you usually connect to
this site without problems, this error could mean that someone is
trying to impersonate the site, and you shouldn't continue.">
<!ENTITY certerror.whatShouldIDo.badStsCertExplanation "This site uses HTTP
Strict Transport Security (HSTS) to specify that &brandShortName; only connect
to it securely. As a result, it is not possible to add an exception for this
certificate.">
<!ENTITY certerror.getMeOutOfHere.label "Get me out of here!">
<!ENTITY certerror.expert.heading "I Understand the Risks">
<!ENTITY certerror.expert.content "If you understand what's going on, you
can tell &brandShortName; to start trusting this site's identification.
<b>Even if you trust the site, this error could mean that someone is
@ -53,5 +33,3 @@ tampering with your connection.</b>">
<!ENTITY certerror.expert.contentPara2 "Don't add an exception unless
you know there's a good reason why this site doesn't use trusted identification.">
<!ENTITY certerror.addException.label "Add Exception…">
<!ENTITY certerror.technical.heading "Technical Details">

View File

@ -382,6 +382,8 @@ webNotifications.alwaysReceive.accesskey=A
webNotifications.neverShow=Always Block Notifications
webNotifications.neverShow.accesskey=N
webNotifications.receiveFromSite=Would you like to receive notifications from this site?
webNotifications.upgradeTitle=Upgraded notifications
webNotifications.upgradeInfo=You will receive notifications from sites, even those not open in a tab. Click to learn more.
# Pointer lock UI

View File

@ -61,6 +61,9 @@ tree-item.nostack=(no stack available)
# that represents the root of the tree when inverted.
tree-item.root=(root)
# LOCALIZATION NOTE (tree-item.percent): A percent of bytes or count displayed in the tree view.
tree-item.percent=%S%
# LOCALIZATION NOTE (snapshot.state.saving.full): The label describing the snapshot
# state SAVING, used in the main heap view.
snapshot.state.saving.full=Saving snapshot…
@ -114,3 +117,8 @@ heapview.field.totalcount=Total Count
# LOCALIZATION NOTE (heapview.field.name): The name of the column in the heap view for name.
heapview.field.name=Name
# LOCALIZATION NOTE (unknownSource): When we do not know the source filename of
# a frame in the allocation stack breakdown tree view, we use this string
# instead.
unknownSource=(unknown)

View File

@ -8,7 +8,8 @@
<!ENTITY blockPopups.accesskey "B">
<!ENTITY notificationsPolicy.label "Notifications">
<!ENTITY notificationsPolicyDesc2.label "Choose which sites are allowed to receive notifications">
<!ENTITY notificationsPolicyLearnMore.label "Learn more">
<!ENTITY notificationsPolicyDesc2.label "Choose which sites are allowed to receive notifications">
<!ENTITY notificationsPolicyButton.accesskey "h">
<!ENTITY notificationsPolicyButton.label "Choose…">
<!ENTITY notificationsDoNotDisturb.label "Do not disturb me">

View File

@ -91,8 +91,18 @@ both, to better adapt this sentence to their language.
<!ENTITY signedIn.engines.label "Sync across all devices">
<!ENTITY mobilePromo.start "Download Firefox for ">
<!-- LOCALIZATION NOTE (mobilePromo.*): the following strings will be used to
create a single sentence with active links.
The resulting sentence in English is: "Sync to your mobile device.
Download Firefox for Android or Firefox for iOS." -->
<!ENTITY mobilePromo.start "Sync to your mobile device. Download ">
<!-- LOCALIZATION NOTE (mobilePromo.androidLink): This is a link title that links to https://www.mozilla.org/firefox/android/ -->
<!ENTITY mobilePromo.androidLink "Android™">
<!ENTITY mobilePromo.end " to sync with your mobile device.">
<!ENTITY androidAttribution "Android is a trademark of Google Inc.">
<!ENTITY mobilePromo.androidLink "Firefox for Android">
<!-- LOCALIZATION NOTE (mobilePromo.iOSBefore): This is text displayed between mobilePromo.androidLink and mobilePromo.iosLink -->
<!ENTITY mobilePromo.iOSBefore " or ">
<!-- LOCALIZATION NOTE (mobilePromo.iOSLink): This is a link title that links to https://www.mozilla.org/firefox/ios/ -->
<!ENTITY mobilePromo.iOSLink "Firefox for iOS">
<!ENTITY mobilePromo.end ".">

View File

@ -2,71 +2,97 @@
* 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 {
background: -moz-Dialog;
}
@import url("chrome://global/skin/in-content/common.css");
body {
margin: 0;
padding: 0 1em;
color: -moz-FieldText;
font: message-box;
}
h1 {
margin: 0 0 .6em 0;
border-bottom: 1px solid ThreeDLightShadow;
font-size: 160%;
}
h2 {
font-size: 130%;
display: flex;
box-sizing: border-box;
min-height: 100vh;
padding: 0 48px;
align-items: center;
justify-content: center;
}
#errorPageContainer {
position: relative;
min-width: 13em;
max-width: 52em;
margin: 4em auto;
border: 1px solid #FFBD09; /* pale yellow extracted from yellow passport icon */
border-radius: 10px;
padding: 3em;
-moz-padding-start: 30px;
background: url("chrome://global/skin/icons/sslWarning.png") left 0 no-repeat -moz-Field;
background-origin: content-box;
}
#errorPageContainer:-moz-dir(rtl) {
background-position: right 0;
min-width: 320px;
max-width: 512px;
}
#errorTitle {
-moz-margin-start: 80px;
background: url("chrome://browser/skin/cert-error.svg") left 0 no-repeat;
background-size: 3em;
margin-inline-start: -5em;
padding-inline-start: 5em;
}
#errorLongContent {
-moz-margin-start: 80px;
#errorTitle:-moz-dir(rtl) {
background-position: right 0;
}
.expander > button {
-moz-padding-start: 20px;
-moz-margin-start: -20px;
background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat;
border: none;
font: inherit;
color: inherit;
#errorTitleText {
border-bottom: 1px solid #C1C1C1;
padding-bottom: 0.4em;
}
@media (max-width: 675px) {
#errorTitle {
padding-top: 0;
background-image: none;
margin-inline-start: 0;
padding-inline-start: 0;
}
}
#buttonContainer {
display: flex;
flex-flow: row wrap;
}
#buttonSpacer {
flex: 1;
}
/* Pressing the retry button will cause the cursor to flicker from a pointer to
* not-allowed. Override the disabled cursor behaviour since we will never show
* the button disabled as the initial state. Remove this in Bug 1219861. */
button:disabled {
cursor: pointer;
}
.expander > button:-moz-dir(rtl) {
background-position: right center;
#returnButton {
background-color: var(--in-content-primary-button-background);
border: none;
color: var(--in-content-selected-text);
min-width: 250px;
margin-inline-start: 0;
}
.expander[collapsed] > button {
background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png");
#returnButton:hover {
background-color: var(--in-content-primary-button-background-hover) !important;
}
.expander[collapsed] > button:-moz-dir(rtl) {
background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png");
#returnButton:hover:active {
background-color: var(--in-content-primary-button-background-active) !important;
}
#advancedButton {
min-width: 150px;
}
/* Advanced section is hidden via inline styles until the link is clicked */
#advancedPanel {
background-color: white;
color: var(--in-content-text-color);
border: 1px lightgray solid;
/* Don't use top padding because the default p style has top padding, and it
* makes the overall div look uneven */
padding: 0 12px 10px;
margin-top: 10px;
box-shadow: 0 0 4px #ddd;
font-size: 0.9em;
}
.hostname {
font-weight: bold;
}

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="45"
height="45"
viewBox="0 0 45 45">
<style>
.icon-default {
fill: #999;
}
</style>
<defs>
<rect id="shape-lock-clasp-outer" x="8" y="2" width="28" height="40" rx="14" ry="14" />
<rect id="shape-lock-clasp-inner" x="14" y="8" width="16" height="28" rx="8" ry="8" />
<rect id="shape-lock-base" x="4" y="18" width="36" height="24" rx="3" ry="3" />
<mask id="mask-clasp-cutout">
<rect width="48" height="48" fill="#000" />
<use xlink:href="#shape-lock-clasp-outer" fill="#fff" />
<use xlink:href="#shape-lock-clasp-inner" fill="#000" />
<line x1="4" y1="38" x2="41" y2="3" stroke="#000" stroke-width="5.5" />
<line x1="4" y1="46" x2="41" y2="11" stroke="#000" stroke-width="5.5" />
<rect x="4" y="18" width="36" height="26" rx="6" ry="6" />
</mask>
<mask id="mask-base-cutout">
<rect width="45" height="45" fill="#000" />
<use xlink:href="#shape-lock-base" fill="#fff" />
<line x1="2.5" y1="41.5" x2="41" y2="5" stroke="#000" stroke-width="8.5" />
</mask>
</defs>
<use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" fill="#999" />
<use xlink:href="#shape-lock-base" mask="url(#mask-base-cutout)" fill="#999" />
<line x1="2.5" y1="41.5" x2="41" y2="5" stroke="#d92d21" stroke-width="5.5" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -171,6 +171,10 @@ treecol {
margin-right: 4px !important;
}
#notificationsPolicyLearnMore {
-moz-margin-start: 1.5em !important;
}
#defaultFontSizeLabel {
/* !important needed to override common !important rule */
-moz-margin-start: 4px !important;
@ -620,8 +624,7 @@ description > html|a {
}
.fxaMobilePromo {
margin-top: 14px;
margin-bottom: 41px;
margin-bottom: 31px;
}
#fxaLoginRejectedWarning {
@ -634,28 +637,16 @@ description > html|a {
margin-bottom: 27.5px;
}
.androidLogo {
list-style-image: url(chrome://browser/skin/fxa/android.png);
max-width: 24px;
position: relative;
top: 8px;
margin: 0px;
margin-inline-end: 5px;
}
.androidLink {
.androidLink,
.iOSLink {
margin: 0;
}
#tosPP-small {
margin-top: 20px;
margin-bottom: 20px;
}
.androidAttribution {
font-size: 12px;
color: #D1D2D3;
}
@media (min-resolution: 1.1dppx) {
.fxaSyncIllustration {
list-style-image: url(chrome://browser/skin/fxa/sync-illustration@2x.png)
@ -663,9 +654,6 @@ description > html|a {
.fxaFirefoxLogo {
list-style-image: url(chrome://browser/skin/fxa/logo@2x.png);
}
.androidLogo {
list-style-image: url(chrome://browser/skin/fxa/android@2x.png);
}
#fxaProfileImage {
list-style-image: url(chrome://browser/skin/fxa/default-avatar@2x.png);
}

View File

@ -114,6 +114,7 @@
skin/classic/browser/urlbar-arrow.png (../shared/urlbar-arrow.png)
skin/classic/browser/urlbar-arrow@2x.png (../shared/urlbar-arrow@2x.png)
skin/classic/browser/warning.svg (../shared/warning.svg)
skin/classic/browser/cert-error.svg (../shared/incontent-icons/cert-error.svg)
skin/classic/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg)
skin/classic/browser/tab-crashed.svg (../shared/incontent-icons/tab-crashed.svg)
skin/classic/browser/welcome-back.svg (../shared/incontent-icons/welcome-back.svg)

View File

@ -12,6 +12,12 @@
// rate changed have a delay = delay/rate and a duration = duration/rate.
add_task(function*() {
yield new Promise(resolve => {
SpecialPowers.pushPrefEnv({"set": [
["dom.animations-api.core.enabled", true]
]}, resolve);
});
yield addTab(TEST_URL_ROOT + "doc_modify_playbackRate.html");
let {panel} = yield openAnimationInspector();

View File

@ -3,8 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
const { L10N } = require("../utils");
const { URL } = require("sdk/url");
const { L10N, parseSource } = require("../utils");
const Frame = module.exports = createClass({
displayName: "frame-view",
@ -16,25 +15,24 @@ const Frame = module.exports = createClass({
render() {
let { toolbox, frame } = this.props;
const { short, long, host } = parseSource(frame.source);
let url = new URL(frame.source);
let spec = url.toString();
let func = frame.functionDisplayName || "";
let tooltip = `${func} (${spec}:${frame.line}:${frame.column})`;
let viewTooltip = L10N.getFormatStr("viewsourceindebugger", `${spec}:${frame.line}:${frame.column}`);
let onClick = () => toolbox.viewSourceInDebugger(spec, frame.line);
let tooltip = `${func} (${long}:${frame.line}:${frame.column})`;
let viewTooltip = L10N.getFormatStr("viewsourceindebugger", `${long}:${frame.line}:${frame.column}`);
let onClick = () => toolbox.viewSourceInDebugger(long, frame.line);
let fields = [
dom.span({ className: "frame-link-function-display-name" }, func),
dom.a({ className: "frame-link-filename", onClick, title: viewTooltip }, url.fileName),
dom.a({ className: "frame-link-filename", onClick, title: viewTooltip }, short),
dom.span({ className: "frame-link-colon" }, ":"),
dom.span({ className: "frame-link-line" }, frame.line),
dom.span({ className: "frame-link-colon" }, ":"),
dom.span({ className: "frame-link-column" }, frame.column)
];
if (url.scheme === "http" || url.scheme === "https" || url.scheme === "ftp") {
fields.push(dom.span({ className: "frame-link-host" }, url.host));
if (host) {
fields.push(dom.span({ className: "frame-link-host" }, host));
}
return dom.span({ className: "frame-link", title: tooltip }, ...fields);

View File

@ -6,7 +6,7 @@ const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/cl
const { safeErrorString } = require("devtools/shared/DevToolsUtils");
const Tree = createFactory(require("./tree"));
const TreeItem = createFactory(require("./tree-item"));
const { getSnapshotStatusTextFull, L10N } = require("../utils");
const { getSnapshotStatusTextFull, getSnapshotTotals, L10N } = require("../utils");
const { snapshotState: states } = require("../constants");
const { snapshot: snapshotModel } = require("../models");
// If HEAP_TREE_ROW_HEIGHT changes, be sure to change `var(--heap-tree-row-height)`
@ -36,13 +36,27 @@ function createParentMap (node, aggregator=Object.create(null)) {
* @param {CensusTreeNode} census
* @return {Object}
*/
function createTreeProperties (census, toolbox) {
function createTreeProperties (snapshot, toolbox) {
const census = snapshot.census;
let map = createParentMap(census);
const totals = getSnapshotTotals(snapshot);
return {
getParent: node => map[node.id],
getParent: node => {
const parent = map[node.id];
return parent === census ? null : parent;
},
getChildren: node => node.children || [],
renderItem: (item, depth, focused, arrow) => new TreeItem({ toolbox, item, depth, focused, arrow }),
renderItem: (item, depth, focused, arrow) =>
new TreeItem({
toolbox,
item,
depth,
focused,
arrow,
getPercentBytes: bytes => bytes / totals.bytes * 100,
getPercentCount: count => count / totals.count * 100,
}),
getRoots: () => census.children,
getKey: node => node.id,
itemHeight: HEAP_TREE_ROW_HEIGHT,
@ -115,7 +129,7 @@ const Heap = module.exports = createClass({
dom.span({ className: "heap-tree-item-total-count" }, L10N.getStr("heapview.field.totalcount")),
dom.span({ className: "heap-tree-item-name" }, L10N.getStr("heapview.field.name"))
),
Tree(createTreeProperties(snapshot.census, toolbox))
Tree(createTreeProperties(snapshot, toolbox))
);
break;
}

View File

@ -44,4 +44,3 @@ const SnapshotListItem = module.exports = createClass({
);
}
});

View File

@ -40,33 +40,35 @@ const Toolbar = module.exports = createClass({
title: L10N.getStr("take-snapshot")
}),
dom.label({},
L10N.getStr("toolbar.breakdownBy"),
dom.select({
id: "select-breakdown",
className: `select-breakdown`,
onChange: e => onBreakdownChange(e.target.value),
}, ...breakdowns.map(({ name, displayName }) => dom.option({ key: name, value: name }, displayName)))
),
dom.div({ className: "toolbar-group" },
dom.label({ className: "breakdown-by" },
L10N.getStr("toolbar.breakdownBy"),
dom.select({
id: "select-breakdown",
className: `select-breakdown`,
onChange: e => onBreakdownChange(e.target.value),
}, ...breakdowns.map(({ name, displayName }) => dom.option({ key: name, value: name }, displayName)))
),
dom.label({},
dom.input({
id: "invert-tree-checkbox",
type: "checkbox",
checked: inverted,
onChange: onToggleInverted,
}),
L10N.getStr("checkbox.invertTree")
),
dom.label({},
dom.input({
id: "invert-tree-checkbox",
type: "checkbox",
checked: inverted,
onChange: onToggleInverted,
}),
L10N.getStr("checkbox.invertTree")
),
dom.label({},
dom.input({
type: "checkbox",
checked: allocations.recording,
disabled: allocations.togglingInProgress,
onChange: onToggleRecordAllocationStacks,
}),
L10N.getStr("checkbox.recordAllocationStacks")
dom.label({},
dom.input({
type: "checkbox",
checked: allocations.recording,
disabled: allocations.togglingInProgress,
onChange: onToggleRecordAllocationStacks,
}),
L10N.getStr("checkbox.recordAllocationStacks")
)
)
)
);

View File

@ -18,14 +18,39 @@ const MAX_SOURCE_LENGTH = 200;
const TreeItem = module.exports = createClass({
displayName: "tree-item",
formatPercent(percent) {
return L10N.getFormatStr("tree-item.percent", Math.round(percent));
},
render() {
let { item, depth, arrow, focused, toolbox } = this.props;
let {
item,
depth,
arrow,
focused,
toolbox,
getPercentBytes,
getPercentCount,
} = this.props;
const percentBytes = this.formatPercent(getPercentBytes(item.bytes));
const percentCount = this.formatPercent(getPercentCount(item.count));
const percentTotalBytes = this.formatPercent(getPercentBytes(item.totalBytes));
const percentTotalCount = this.formatPercent(getPercentBytes(item.totalCount));
return dom.div({ className: `heap-tree-item ${focused ? "focused" :""}` },
dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" }, item.bytes),
dom.span({ className: "heap-tree-item-field heap-tree-item-count" }, item.count),
dom.span({ className: "heap-tree-item-field heap-tree-item-total-bytes" }, item.totalBytes),
dom.span({ className: "heap-tree-item-field heap-tree-item-total-count" }, item.totalCount),
dom.span({ className: "heap-tree-item-field heap-tree-item-bytes" },
dom.span({ className: "heap-tree-number" }, item.bytes),
dom.span({ className: "heap-tree-percent" }, percentBytes)),
dom.span({ className: "heap-tree-item-field heap-tree-item-count" },
dom.span({ className: "heap-tree-number" }, item.count),
dom.span({ className: "heap-tree-percent" }, percentCount)),
dom.span({ className: "heap-tree-item-field heap-tree-item-total-bytes" },
dom.span({ className: "heap-tree-number" }, item.totalBytes),
dom.span({ className: "heap-tree-percent" }, percentTotalBytes)),
dom.span({ className: "heap-tree-item-field heap-tree-item-total-count" },
dom.span({ className: "heap-tree-number" }, item.totalCount),
dom.span({ className: "heap-tree-percent" }, percentTotalCount)),
dom.span({ className: "heap-tree-item-field heap-tree-item-name", style: { marginLeft: depth * INDENT }},
arrow,
this.toLabel(item.name, toolbox)

View File

@ -51,7 +51,7 @@ const breakdowns = exports.breakdowns = {
displayName: "Coarse Type",
breakdown: {
by: "coarseType",
objects: ALLOCATION_STACK,
objects: OBJECT_CLASS,
strings: COUNT,
scripts: INTERNAL_TYPE,
other: INTERNAL_TYPE,

View File

@ -9,6 +9,7 @@ support-files =
[browser_memory-breakdowns-01.js]
skip-if = debug # bug 1219554
[browser_memory_no_allocation_stacks.js]
[browser_memory_percents_01.js]
[browser_memory-simple-01.js]
skip-if = debug # bug 1219554
[browser_memory_transferHeapSnapshot_e10s_01.js]

View File

@ -0,0 +1,48 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Sanity test that we calculate percentages in the tree.
"use strict";
const { breakdowns } = require("devtools/client/memory/constants");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const breakdownActions = require("devtools/client/memory/actions/breakdown");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
function checkCells(cells) {
ok(cells.length > 1, "Should have found some");
// Ignore the first header cell.
for (let cell of cells.slice(1)) {
const percent = cell.querySelector(".heap-tree-percent");
ok(percent, "should have a percent cell");
ok(percent.textContent.match(/^\d?\d%$/), "should be of the form nn% or n%");
}
}
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const heapWorker = panel.panelWin.gHeapAnalysesClient;
const front = panel.panelWin.gFront;
const { getState, dispatch } = panel.panelWin.gStore;
const doc = panel.panelWin.document;
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
yield dispatch(breakdownActions.setBreakdownAndRefresh(heapWorker,
breakdowns.objectClass.breakdown));
is(getState().breakdown.by, "objectClass",
"Should be using object class breakdown");
const bytesCells = [...doc.querySelectorAll(".heap-tree-item-bytes")];
checkCells(bytesCells);
const totalBytesCells = [...doc.querySelectorAll(".heap-tree-item-total-bytes")];
checkCells(totalBytesCells);
const countCells = [...doc.querySelectorAll(".heap-tree-item-count")];
checkCells(countCells);
const totalCountCells = [...doc.querySelectorAll(".heap-tree-item-total-count")];
checkCells(totalCountCells);
});

View File

@ -49,3 +49,22 @@ add_task(function *() {
ok(utils.breakdownEquals(utils.getCustomBreakdowns()["My Breakdown"], custom),
"utils.getCustomBreakdowns() returns custom breakdowns");
});
// Test `utils.parseSource`.
add_task(function* () {
const url = "http://example.com/foo/bar/baz.js";
let results = utils.parseSource(url);
equal(results.short, "baz.js");
equal(results.long, url);
equal(results.host, "example.com");
results = utils.parseSource("self-hosted");
equal(results.short, "self-hosted");
equal(results.long, "self-hosted");
equal(results.host, undefined);
results = utils.parseSource("");
equal(typeof results.short, "string");
equal(typeof results.long, "string");
equal(results.host, undefined);
});

View File

@ -3,9 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { Cu } = require("chrome");
Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
const STRINGS_URI = "chrome://browser/locale/devtools/memory.properties"
const L10N = exports.L10N = new ViewHelpers.L10N(STRINGS_URI);
const { URL } = require("sdk/url");
const { assert } = require("devtools/shared/DevToolsUtils");
const { Preferences } = require("resource://gre/modules/Preferences.jsm");
const CUSTOM_BREAKDOWN_PREF = "devtools.memory.custom-breakdowns";
@ -257,8 +260,10 @@ exports.getSnapshotTotals = function (snapshot) {
census = census.children && census.children[0];
}
} else {
bytes = census.totalBytes;
count = census.totalCount;
if (census) {
bytes = census.totalBytes;
count = census.totalCount;
}
}
return {
@ -267,3 +272,45 @@ exports.getSnapshotTotals = function (snapshot) {
};
};
/**
* Parse a source into a short and long name as well as a host name.
*
* @param {String} source
* The source to parse.
*
* @returns {Object}
* An object with the following properties:
* - {String} short: A short name for the source.
* - {String} long: The full, long name for the source.
* - {String?} host: If available, the host name for the source.
*/
exports.parseSource = function (source) {
const sourceStr = source ? String(source) : "";
let short;
let long;
let host;
try {
const url = new URL(sourceStr);
short = url.fileName;
host = url.host;
long = url.toString();
} catch (e) {
// Malformed URI.
long = sourceStr;
short = sourceStr.slice(0, 100);
}
if (!short) {
// Last ditch effort.
if (!long) {
long = L10N.getStr("unknownSource");
}
short = long.slice(0, 100);
}
return { short, long, host };
};

View File

@ -19,51 +19,62 @@
--row-hover-background-color: rgba(76,158,217,0.2);
}
html, .theme-body, #app, #memory-tool, #memory-tool-container {
html, body, #app, #memory-tool {
height: 100%;
}
.theme-body {
overflow: hidden;
/* Not sure why .theme-body declares it as `background` and overrides */
background-color: var(--theme-toolbar-background) !important;
}
#memory-tool-container {
#memory-tool {
display: flex;
flex-direction: row;
--toolbar-height: 20px;
flex-direction: column;
--sidebar-width: 185px;
/**
* If --heap-tree-row-height changes, be sure to change HEAP_TREE_ROW_HEIGHT
* in `devtools/client/memory/components/heap.js`.
*/
--heap-tree-row-height: 14px;
--heap-tree-header-height: 17px;
}
#memory-tool .devtools-toolbar {
display: flex;
align-items: center;
}
.devtools-toolbar .toolbar-group {
position: absolute;
left: var(--sidebar-width);
top: -2px;
white-space: nowrap;
}
.toolbar-group > label {
margin-right: 5px;
}
.toolbar-group .breakdown-by span {
margin-right: 5px;
}
#memory-tool-container {
overflow: hidden;
display: flex;
flex: 1;
}
/**
* TODO bug 1213100
* should generalize toolbar buttons with images in them
* toolbars.inc.css contains definitions for .devtools-button,
* I wager that many of the below styles can be rolled into that
* Toolbar
*/
.devtools-toolbar .devtools-button.take-snapshot {
margin: 2px 1px;
padding: 1px;
border-width: 0px;
/* [standalone] buttons override min-height from 18px to 24px -- why? */
min-height: 18px;
/* not sure why this is needed for positioning */
display: -moz-box;
-moz-appearance: none;
margin-inline-start: 1px;
margin-inline-end: 1px;
}
.devtools-toolbar .devtools-button.take-snapshot::before {
background-image: url(images/command-screenshot.png);
-moz-appearance: none;
width: 16px;
height: 16px;
background-size: 64px 16px;
background-position: 0 center;
background-repeat: no-repeat;
}
@media (min-resolution: 1.1dppx) {
.devtools-toolbar .devtools-button.take-snapshot::before {
@ -71,17 +82,6 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
}
}
/**
* TODO bug 1213100
* Should this be codified in .devtools-toolbar itself?
*/
#memory-tool .devtools-toolbar {
display: flex;
flex-direction: row;
align-items: center;
height: var(--toolbar-height);
}
/**
* TODO bug 1213100
* Once we figure out how to store invertable buttons (pseudo element like in this case?)
@ -94,30 +94,19 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
filter: url(images/filters.svg#invert);
}
/**
* TODO bug 1213100
* The .list style is for a generalized React list component. It's children (.list > li)
* are generally styled here, as the component can take any type of child component.
* Memory tool specific styling are handling in (li.snapshot-list-item).
*/
.list {
min-width: var(--sidebar-width);
width: var(--sidebar-width);
overflow-y: auto;
margin: 0;
padding: 0;
width: 186px;
list-style-type: none;
font-size: 12px;
height: 100%;
overflow-y: scroll;
background-color: var(--theme-toolbar-background);
color: var(--theme-body-color-alt);
border-color: var(--theme-splitter-color);
border-style: solid;
border-width: 0px 1px 0px 0px;
background-color: var(--theme-sidebar-background);
border-inline-end: 1px solid var(--theme-splitter-color);
}
.list > li {
height: 40px;
display: flex;
flex-direction: column;
color: var(--theme-body-color);
border-bottom: 1px solid rgba(128,128,128,0.15);
padding: 8px;
@ -129,27 +118,19 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
color: var(--theme-selection-color);
}
.snapshot-list-item {
position: relative;
}
.snapshot-list-item span {
.snapshot-list-item .snapshot-title {
display: block;
margin-bottom: 14px;
}
.snapshot-list-item .snapshot-state, .snapshot-list-item .snapshot-totals {
.snapshot-list-item .snapshot-state,
.snapshot-list-item .snapshot-totals {
font-size: 90%;
color: var(--theme-body-color-alt);
position: absolute;
}
.snapshot-list-item .snapshot-state {
top: 38px;
}
.snapshot-list-item .snapshot-totals {
top: 38px;
}
.snapshot-list-item .total-bytes {
float: left;
}
.snapshot-list-item.selected .snapshot-state, .snapshot-list-item.selected .snapshot-totals {
.snapshot-list-item.selected .snapshot-state,
.snapshot-list-item.selected .snapshot-totals {
/* Text inside a selected item should not be custom colored. */
color: inherit !important;
}
@ -159,26 +140,22 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
*/
#heap-view {
flex: 1 1 auto;
border-color: var(--theme-splitter-color);
color: var(--theme-body-color);
border-left-width: 1px;
display: flex;
flex: 1;
justify-content: center;
background-color: var(--theme-toolbar-background);
min-width: 400px;
}
#heap-view .heap-view-panel {
width: 100%;
height: 100%;
background-color: var(--theme-toolbar-background)
}
#heap-view .heap-view-panel .snapshot-status, #heap-view .take-snapshot {
#heap-view .snapshot-status,
#heap-view .take-snapshot {
margin: auto;
margin-top: 65px;
font-size: 120%;
display: block;
margin: 65px auto;
}
#heap-view .heap-view-panel .snapshot-status {
width: 500px;
#heap-view .snapshot-status {
display: block;
text-align: center;
}
@ -186,10 +163,16 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
padding: 5px;
}
#heap-view .heap-view-panel {
display: flex;
flex-direction: column;
flex: 1;
}
#heap-view .heap-view-panel[data-state="snapshot-state-error"] pre {
/* TODO */
background-color: var(--theme-body-background);
overflow-y: scroll;
height: 100px;
overflow-y: auto;
margin: 20px;
padding: 20px;
}
@ -202,35 +185,48 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
float: left;
}
.tree {
height: 100%;
overflow-y: scroll;
background-color: var(--theme-body-background);
}
.tree .header {
height: 17px;
overflow: hidden;
.header {
height: var(--heap-tree-header-height);
display: flex;
align-items: center;
color: var(--theme-body-color);
background-color: var(--theme-tab-toolbar-background);
}
.tree span {
border-left-color: var(--cell-border-color);
border-left-width: 1px;
line-height: var(--heap-tree-row-height);
}
.tree {
flex: 1;
overflow-y: auto;
background-color: var(--theme-body-background);
}
.tree-node {
height: var(--heap-tree-row-height);
clear: left;
}
.heap-tree-percent {
width: 30%;
}
.heap-tree-number {
width: 70%;
color: var(--theme-content-color3);
padding-right: 5px;
}
.focused .heap-tree-number {
color: var(--theme-selection-color);
}
.heap-tree-item, .header {
list-style-type: none;
height: var(--heap-tree-row-height);
}
.heap-tree-item-field, .header span {
float: left;
display: flex;
flex-direction: row;
}
.tree-node:nth-child(2n) {
@ -248,52 +244,69 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
.header {
background-color: var(--theme-tab-toolbar-background);
border-color: var(--cell-border-color);
border-style: solid;
border-width: 0px 0px 1px 0px;
}
.header span {
text-align: center;
line-height: var(--heap-tree-header-height);
font-size: 90%;
display: inline;
}
.header span, .heap-tree-number, .heap-tree-percent, .heap-tree-item-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
font-size: 90%;
}
.header .heap-tree-item-name {
text-align: left;
}
.heap-tree-item-bytes,
.heap-tree-item-count,
.heap-tree-item-total-bytes,
.heap-tree-item-total-count {
text-align: right;
border-right: var(--cell-border-color) 1px solid;
padding-right: 5px;
text-align: end;
border-inline-end: var(--cell-border-color) 1px solid;
padding-inline-end: 5px;
display: flex;
flex-direction: row;
}
.heap-tree-item-count,
.heap-tree-item-total-count {
width: 5vw;
width: 8%;
}
.heap-tree-item-bytes,
.heap-tree-item-total-bytes {
width: 7vw;
width: 10%;
}
.heap-tree-item-name {
width: 50%;
padding-left: 5px;
}
.error::before {
content: "";
background-image: url(chrome://devtools/skin/themes/images/webconsole.svg);
background-repeat: no-repeat;
background-size: 72px 60px;
display: inline-block;
width: 12px;
height: 12px;
display: inline-block;
background-image: url(chrome://devtools/skin/themes/images/webconsole.svg);
background-size: 72px 60px;
background-position: -24px -24px;
margin: 2px 5px 0 0;
background-repeat: no-repeat;
margin: 0px;
margin-top: 2px;
margin-inline-end: 5px;
max-height: 12px;
}
.theme-light .error::before {
background-image: url(chrome://devtools/skin/themes/images/webconsole.svg#light-icons);
}
@ -340,3 +353,7 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
text-align: center;
padding: 5px;
}
label select {
margin: 5px;
}

View File

@ -1364,28 +1364,6 @@ public class BrowserApp extends GeckoApp
});
}
private void shareCurrentUrl() {
Tab tab = Tabs.getInstance().getSelectedTab();
if (tab == null) {
return;
}
String url = tab.getURL();
if (url == null) {
return;
}
if (AboutPages.isAboutReader(url)) {
url = ReaderModeUtils.getUrlFromAboutReader(url);
}
GeckoAppShell.openUriExternal(url, "text/plain", "", "",
Intent.ACTION_SEND, tab.getDisplayTitle(), false);
// Context: Sharing via chrome list (no explicit session is active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
}
private void setToolbarMargin(int margin) {
((RelativeLayout.LayoutParams) mGeckoLayout.getLayoutParams()).topMargin = margin;
mGeckoLayout.requestLayout();
@ -3248,7 +3226,20 @@ public class BrowserApp extends GeckoApp
}
if (itemId == R.id.share) {
shareCurrentUrl();
tab = Tabs.getInstance().getSelectedTab();
if (tab != null) {
String url = tab.getURL();
if (url != null) {
if (AboutPages.isAboutReader(url)) {
url = ReaderModeUtils.getUrlFromAboutReader(url);
}
// Context: Sharing via chrome list (no explicit session is active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "menu");
GeckoAppShell.openUriExternal(url, "text/plain", "", "", Intent.ACTION_SEND, tab.getDisplayTitle(), false);
}
}
return true;
}

View File

@ -646,7 +646,7 @@ public abstract class GeckoApp
GeckoAppShell.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, title, false);
// Context: Sharing via chrome list (no explicit session is active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "text");
} else if ("Snackbar:Show".equals(event)) {
final String msg = message.getString("message");

View File

@ -20,6 +20,7 @@ import org.json.JSONObject;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.text.TextUtils;
import android.util.Log;
@ -52,8 +53,6 @@ public final class IntentHelper implements GeckoEventListener,
private static String EXTRA_BROWSER_FALLBACK_URL = "browser_fallback_url";
/** A partial URI to an error page - the encoded error URI should be appended before loading. */
private static final String GENERIC_URI_PREFIX = "about:neterror?e=generic&u=";
private static final String MALFORMED_URI_PREFIX = "about:neterror?e=malformedURI&u=";
private static String UNKNOWN_PROTOCOL_URI_PREFIX = "about:neterror?e=unknownProtocolFound&u=";
private static IntentHelper instance;
@ -186,24 +185,10 @@ public final class IntentHelper implements GeckoEventListener,
// For this flow, we follow Chrome's lead:
// https://developer.chrome.com/multidevice/android/intents
if (intent.hasExtra(EXTRA_BROWSER_FALLBACK_URL)) {
final String fallbackUrl = intent.getStringExtra(EXTRA_BROWSER_FALLBACK_URL);
String urlToLoad;
try {
final String anyCaseScheme = new URI(fallbackUrl).getScheme();
final String scheme = (anyCaseScheme == null) ? null : anyCaseScheme.toLowerCase(Locale.US);
if ("http".equals(scheme) || "https".equals(scheme)) {
urlToLoad = fallbackUrl;
} else {
Log.w(LOGTAG, "Fallback URI uses unsupported scheme: " + scheme);
urlToLoad = GENERIC_URI_PREFIX + fallbackUrl;
}
} catch (final URISyntaxException e) {
// Do not include Exception to avoid leaking uris.
Log.w(LOGTAG, "Exception parsing fallback URI");
urlToLoad = MALFORMED_URI_PREFIX + fallbackUrl;
}
callback.sendError(urlToLoad);
final String fallbackUrl = intent.getStringExtra(EXTRA_BROWSER_FALLBACK_URL);
if (isFallbackUrlValid(fallbackUrl)) {
// Opens the page in JS.
callback.sendError(fallbackUrl);
} else if (intent.getPackage() != null) {
// Note on alternative flows: we could get the intent package from a component, however, for
@ -232,6 +217,26 @@ public final class IntentHelper implements GeckoEventListener,
}
}
private static boolean isFallbackUrlValid(@Nullable final String fallbackUrl) {
if (fallbackUrl == null) {
return false;
}
try {
final String anyCaseScheme = new URI(fallbackUrl).getScheme();
final String scheme = (anyCaseScheme == null) ? null : anyCaseScheme.toLowerCase(Locale.US);
if ("http".equals(scheme) || "https".equals(scheme)) {
return true;
} else {
Log.w(LOGTAG, "Fallback URI uses unsupported scheme: " + scheme + ". Try http or https.");
}
} catch (final URISyntaxException e) {
// Do not include Exception to avoid leaking uris.
Log.w(LOGTAG, "URISyntaxException parsing fallback URI");
}
return false;
}
/**
* Returns an about:neterror uri with the unknownProtocolFound text as a parameter.
* @param encodedUri The encoded uri. While the page does not open correctly without specifying

View File

@ -206,7 +206,7 @@ public abstract class HomeFragment extends Fragment {
Intent.ACTION_SEND, info.getDisplayTitle(), false);
// Context: Sharing via chrome homepage contextmenu list (home session should be active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "home_contextmenu");
return true;
}
}

View File

@ -210,7 +210,7 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
provider.chooseActivity(p);
// Context: Sharing via content contextmenu list (no explicit session is active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "promptlist");
}
@Override

View File

@ -14,7 +14,7 @@
<style name="FxAccountTheme" parent="@style/Gecko" />
<style name="FxAccountTheme.FxAccountStatusActivity" parent="@FxAccountTheme">
<style name="FxAccountTheme.FxAccountStatusActivity" parent="Gecko.Preferences">
<item name="android:windowActionBar">true</item>
<item name="android:windowNoTitle">false</item>
<item name="android:actionBarStyle">@style/ActionBar.FxAccountStatusActivity</item>

View File

@ -233,7 +233,7 @@ public class GeckoActionProvider {
chooseActivity(item.getItemId());
// Context: Sharing via chrome mainmenu list (no explicit session is active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST);
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "actionprovider");
return true;
}
@ -243,7 +243,7 @@ public class GeckoActionProvider {
chooseActivity(index);
// Context: Sharing via chrome mainmenu and content contextmenu quickshare (no explicit session is active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.BUTTON);
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.BUTTON, "actionprovider");
}
}

View File

@ -721,6 +721,9 @@ var BrowserApp = {
NativeWindow.contextmenus.add(stringGetter("contextmenu.addToReadingList"),
NativeWindow.contextmenus.linkOpenableContext,
function(aTarget) {
UITelemetry.addEvent("action.1", "contextmenu", null, "web_reading_list");
UITelemetry.addEvent("save.1", "contextmenu", null, "reading_list");
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
Messaging.sendRequestForResult({
type: "Reader:AddToList",
@ -840,6 +843,7 @@ var BrowserApp = {
NativeWindow.contextmenus._disableRestricted("BOOKMARK", NativeWindow.contextmenus.linkBookmarkableContext),
function(aTarget) {
UITelemetry.addEvent("action.1", "contextmenu", null, "web_bookmark");
UITelemetry.addEvent("save.1", "contextmenu", null, "bookmark");
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
let title = aTarget.textContent || aTarget.title || url;

View File

@ -115,6 +115,9 @@ user_pref("network.sntp.pools", "%(server)s");
// at least once will mean that codepath is still tested in automation.
user_pref("network.sntp.maxRetryCount", 1);
// Make sure the notification permission migration test doesn't hit the network.
user_pref("browser.push.warning.infoURL", "http://%(server)s/alerts-dummy/infoURL");
// Existing tests don't wait for the notification button security delay
user_pref("security.notification_enable_delay", 0);

View File

@ -351,7 +351,7 @@ AboutReader.prototype = {
url: this._article.url,
title: this._article.title
});
UITelemetry.addEvent("share.1", "list", null);
UITelemetry.addEvent("share.1", "list", null, "reader");
},
/**