Merge m-c to inbound, a=merge

This commit is contained in:
Wes Kocher 2015-10-30 16:57:55 -07:00
commit 691cc3853f
204 changed files with 3492 additions and 2483 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

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict'
const { Cc, Ci } = require('chrome');
const { Cc, Ci, Cu } = require('chrome');
const bmsrv = Cc['@mozilla.org/browser/nav-bookmarks-service;1'].
getService(Ci.nsINavBookmarksService);
const hsrv = Cc['@mozilla.org/browser/nav-history-service;1'].
@ -26,6 +26,11 @@ const {
MENU, TOOLBAR, UNSORTED
} = require('sdk/places/bookmarks');
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
function invalidResolve (assert) {
return function (e) {
assert.fail('Resolve state should not be called: ' + e);
@ -45,8 +50,7 @@ function clearAllBookmarks () {
}
function clearHistory (done) {
hsrv.removeAllPages();
once('places-expiration-finished', done);
PlacesUtils.history.clear().catch(Cu.reportError).then(done);
}
// Cleans bookmarks and history and disables maintanance

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

@ -24,7 +24,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>

View File

@ -24,7 +24,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>

View File

@ -20,7 +20,7 @@
<project name="gaia" path="gaia" remote="mozillaorg" revision="91cac94948094cfdcd00cba5c6483e27e80cb3b0"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="27de93fe66c3e80e157d157bd52ca99565351669"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>

View File

@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>

View File

@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- Stock Android things -->
<project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>

View File

@ -24,7 +24,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>

View File

@ -24,7 +24,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>

View File

@ -21,7 +21,7 @@
<project name="gaia" path="gaia" remote="mozillaorg" revision="91cac94948094cfdcd00cba5c6483e27e80cb3b0"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="27de93fe66c3e80e157d157bd52ca99565351669"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>

View File

@ -24,7 +24,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="95bc959ff0e4ce73e1c9ed5e6916d84922d0a3cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="21f7fdaf4ee341022a0416e9186e7b07218cc4b1"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- Stock Android things -->
<project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>

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

@ -99,7 +99,7 @@ var handleContentContextMenu = function (event) {
Services.obs.notifyObservers(subject, "content-contextmenu", null);
let doc = event.target.ownerDocument;
let docLocation = doc.location.href;
let docLocation = doc.location ? doc.location.href : undefined;
let charSet = doc.characterSet;
let baseURI = doc.baseURI;
let referrer = doc.referrer;

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

@ -99,7 +99,8 @@ var gTests = [
let engine = yield promiseNewEngine("searchSuggestionEngine.xml");
// Make this actually work in healthreport by giving it an ID:
engine.wrappedJSObject._identifier = 'org.mozilla.testsearchsuggestions';
Object.defineProperty(engine.wrappedJSObject, "identifier",
{value: "org.mozilla.testsearchsuggestions"});
let p = promiseContentSearchChange(engine.name);
Services.search.currentEngine = engine;

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

@ -815,7 +815,7 @@ loop.roomViews = (function(mozL10n) {
renderRemoteVideo: this.shouldRenderRemoteVideo(),
screenShareMediaElement: this.state.screenShareMediaElement,
screenSharePosterUrl: null,
showContextRoomName: false,
showInitialContext: false,
useDesktopPaths: true},
React.createElement(sharedViews.ConversationToolbar, {
audio: { enabled: !this.state.audioMuted, visible: true},

View File

@ -815,7 +815,7 @@ loop.roomViews = (function(mozL10n) {
renderRemoteVideo={this.shouldRenderRemoteVideo()}
screenShareMediaElement={this.state.screenShareMediaElement}
screenSharePosterUrl={null}
showContextRoomName={false}
showInitialContext={false}
useDesktopPaths={true}>
<sharedViews.ConversationToolbar
audio={{ enabled: !this.state.audioMuted, visible: true }}

View File

@ -545,14 +545,13 @@ html[dir="rtl"] .context-content {
}
.context-wrapper {
border: 1px solid #5cccee;
border: 2px solid #ebebeb;
border-radius: 4px;
background: #fff;
padding: .8em;
background: #fafafa;
padding: 1.1rem .8rem;
/* Use the flex row mode to position the elements next to each other. */
display: flex;
flex-flow: row nowrap;
line-height: 1.1em;
/* No underline for the text in the context view. */
text-decoration: none;
}
@ -590,9 +589,6 @@ html[dir="rtl"] .context-wrapper > .context-preview {
.clicks-allowed.context-wrapper:hover {
border: 2px solid #5cccee;
/* Due to the increased border width, reduce the padding accordingly so that
the text doesn't move. */
padding: calc(.8em - 1px);
}
/* Only underline the url, not the associated text */

View File

@ -529,14 +529,8 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
background-image: url("../img/icons-16x16.svg#add-active");
}
.context-url-view-wrapper {
padding: 14px 15px;
background-color: #dbf7ff;
}
.showing-room-name > .text-chat-entries > .text-chat-scroller > .context-url-view-wrapper {
padding-top: 0;
margin-bottom: 0;
.context-url-view-wrapper > .context-content {
margin: 0 1rem 1.5rem 1rem;
}
.room-context {
@ -947,6 +941,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
.text-chat-entries {
overflow: auto;
padding-top: .6rem;
}
.text-chat-entry,
@ -965,20 +960,16 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
text-align: start;
}
.text-chat-scroller div:nth-child(2) {
margin-top: .5em;
}
/* Sent text chat entries should be on the right */
.text-chat-entry.sent {
/* aligns paragraph to right side */
justify-content: flex-end;
margin-left: 0;
margin-right: 5px;
margin-right: 4px;
}
.text-chat-entry.received {
margin-left: 4px;
margin-left: 2px;
margin-right: 0;
}
@ -1126,20 +1117,16 @@ html[dir="rtl"] .text-chat-entry.received .text-chat-arrow {
}
.text-chat-header.special.room-name {
color: black;
color: #666;
font-weight: bold;
text-align: start;
background-color: #dbf7ff;
margin-bottom: 0;
margin-right: 0;
}
.text-chat-header.special.room-name p {
background: #dbf7ff;
max-width: 100%;
/* 18px for indent of .text-chat-arrow, 1px for border of .text-chat-entry > p,
0.5rem for padding of .text-chat-entry > p */
padding: calc(18px - 1px - 0.5rem);
width: 100%;
padding: 0.8rem 1rem 1.4rem;
margin: 0;
}
.text-chat-header.special > p {

View File

@ -118,7 +118,8 @@ loop.store.TextChatStore = (function() {
// Notify MozLoopService if appropriate that a message has been appended
// and it should therefore check if we need a different sized window or not.
if (message.contentType !== CHAT_CONTENT_TYPES.ROOM_NAME) {
if (message.contentType !== CHAT_CONTENT_TYPES.ROOM_NAME &&
message.contentType !== CHAT_CONTENT_TYPES.CONTEXT) {
if (this._storeState.textChatEnabled) {
window.dispatchEvent(new CustomEvent("LoopChatMessageAppended"));
} else {

View File

@ -106,6 +106,7 @@ loop.shared.views.chat = (function(mozL10n) {
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
messageList: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
showInitialContext: React.PropTypes.bool.isRequired,
useDesktopPaths: React.PropTypes.bool.isRequired
},
@ -174,6 +175,7 @@ loop.shared.views.chat = (function(mozL10n) {
this.props.messageList.map(function(entry, i) {
if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
if (!this.props.showInitialContext) { return null; }
switch (entry.contentType) {
case CHAT_CONTENT_TYPES.ROOM_NAME:
return (
@ -188,7 +190,6 @@ loop.shared.views.chat = (function(mozL10n) {
allowClick: true,
description: entry.message,
dispatcher: this.props.dispatcher,
showContextTitle: true,
thumbnail: entry.extraData.thumbnail,
url: entry.extraData.location,
useDesktopPaths: this.props.useDesktopPaths})
@ -357,8 +358,8 @@ loop.shared.views.chat = (function(mozL10n) {
* as a field for entering new messages.
*
* @property {loop.Dispatcher} dispatcher
* @property {Boolean} showRoomName Set to true to show the room name
* special list item.
* @property {Boolean} showInitialContext Set to true to show the room name
* and initial context tile for linker clicker's special list items
*/
var TextChatView = React.createClass({displayName: "TextChatView",
mixins: [
@ -368,7 +369,7 @@ loop.shared.views.chat = (function(mozL10n) {
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
showRoomName: React.PropTypes.bool.isRequired,
showInitialContext: React.PropTypes.bool.isRequired,
useDesktopPaths: React.PropTypes.bool.isRequired
},
@ -377,18 +378,15 @@ loop.shared.views.chat = (function(mozL10n) {
},
render: function() {
var messageList;
var showingRoomName = false;
var messageList = this.state.messageList;
if (this.props.showRoomName) {
messageList = this.state.messageList;
showingRoomName = this.state.messageList.some(function(item) {
return item.contentType === CHAT_CONTENT_TYPES.ROOM_NAME;
});
} else {
messageList = this.state.messageList.filter(function(item) {
// Filter out items not displayed when showing initial context.
// We do this here so that we can set the classes correctly on the view.
if (!this.props.showInitialContext) {
messageList = messageList.filter(function(item) {
return item.type !== CHAT_MESSAGE_TYPES.SPECIAL ||
item.contentType !== CHAT_CONTENT_TYPES.ROOM_NAME;
(item.contentType !== CHAT_CONTENT_TYPES.ROOM_NAME &&
item.contentType !== CHAT_CONTENT_TYPES.CONTEXT);
});
}
@ -398,10 +396,9 @@ loop.shared.views.chat = (function(mozL10n) {
});
var textChatViewClasses = React.addons.classSet({
"showing-room-name": showingRoomName,
"text-chat-view": true,
"text-chat-disabled": !this.state.textChatEnabled,
"text-chat-entries-empty": !messageList.length
"text-chat-entries-empty": !messageList.length,
"text-chat-disabled": !this.state.textChatEnabled
});
return (
@ -409,6 +406,7 @@ loop.shared.views.chat = (function(mozL10n) {
React.createElement(TextChatEntriesView, {
dispatcher: this.props.dispatcher,
messageList: messageList,
showInitialContext: this.props.showInitialContext,
useDesktopPaths: this.props.useDesktopPaths}),
React.createElement(TextChatInputView, {
dispatcher: this.props.dispatcher,

View File

@ -106,6 +106,7 @@ loop.shared.views.chat = (function(mozL10n) {
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
messageList: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
showInitialContext: React.PropTypes.bool.isRequired,
useDesktopPaths: React.PropTypes.bool.isRequired
},
@ -174,6 +175,7 @@ loop.shared.views.chat = (function(mozL10n) {
{
this.props.messageList.map(function(entry, i) {
if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
if (!this.props.showInitialContext) { return null; }
switch (entry.contentType) {
case CHAT_CONTENT_TYPES.ROOM_NAME:
return (
@ -188,7 +190,6 @@ loop.shared.views.chat = (function(mozL10n) {
allowClick={true}
description={entry.message}
dispatcher={this.props.dispatcher}
showContextTitle={true}
thumbnail={entry.extraData.thumbnail}
url={entry.extraData.location}
useDesktopPaths={this.props.useDesktopPaths} />
@ -357,8 +358,8 @@ loop.shared.views.chat = (function(mozL10n) {
* as a field for entering new messages.
*
* @property {loop.Dispatcher} dispatcher
* @property {Boolean} showRoomName Set to true to show the room name
* special list item.
* @property {Boolean} showInitialContext Set to true to show the room name
* and initial context tile for linker clicker's special list items
*/
var TextChatView = React.createClass({
mixins: [
@ -368,7 +369,7 @@ loop.shared.views.chat = (function(mozL10n) {
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
showRoomName: React.PropTypes.bool.isRequired,
showInitialContext: React.PropTypes.bool.isRequired,
useDesktopPaths: React.PropTypes.bool.isRequired
},
@ -377,18 +378,15 @@ loop.shared.views.chat = (function(mozL10n) {
},
render: function() {
var messageList;
var showingRoomName = false;
var messageList = this.state.messageList;
if (this.props.showRoomName) {
messageList = this.state.messageList;
showingRoomName = this.state.messageList.some(function(item) {
return item.contentType === CHAT_CONTENT_TYPES.ROOM_NAME;
});
} else {
messageList = this.state.messageList.filter(function(item) {
// Filter out items not displayed when showing initial context.
// We do this here so that we can set the classes correctly on the view.
if (!this.props.showInitialContext) {
messageList = messageList.filter(function(item) {
return item.type !== CHAT_MESSAGE_TYPES.SPECIAL ||
item.contentType !== CHAT_CONTENT_TYPES.ROOM_NAME;
(item.contentType !== CHAT_CONTENT_TYPES.ROOM_NAME &&
item.contentType !== CHAT_CONTENT_TYPES.CONTEXT);
});
}
@ -398,10 +396,9 @@ loop.shared.views.chat = (function(mozL10n) {
});
var textChatViewClasses = React.addons.classSet({
"showing-room-name": showingRoomName,
"text-chat-view": true,
"text-chat-disabled": !this.state.textChatEnabled,
"text-chat-entries-empty": !messageList.length
"text-chat-entries-empty": !messageList.length,
"text-chat-disabled": !this.state.textChatEnabled
});
return (
@ -409,6 +406,7 @@ loop.shared.views.chat = (function(mozL10n) {
<TextChatEntriesView
dispatcher={this.props.dispatcher}
messageList={messageList}
showInitialContext={this.props.showInitialContext}
useDesktopPaths={this.props.useDesktopPaths} />
<TextChatInputView
dispatcher={this.props.dispatcher}

View File

@ -810,7 +810,6 @@ loop.shared.views = (function(_, mozL10n) {
* is specified, then 'dispatcher' is also required.
* @property {String} description The description for the context url.
* @property {loop.Dispatcher} dispatcher
* @property {Boolean} showContextTitle Whether or not to show the "Let's talk about" title.
* @property {String} thumbnail The thumbnail url (expected to be a data url) to
* display. If not specified, a fallback url will be
* shown.
@ -826,7 +825,6 @@ loop.shared.views = (function(_, mozL10n) {
allowClick: React.PropTypes.bool.isRequired,
description: React.PropTypes.string.isRequired,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher),
showContextTitle: React.PropTypes.bool.isRequired,
thumbnail: React.PropTypes.string,
url: React.PropTypes.string,
useDesktopPaths: React.PropTypes.bool.isRequired
@ -845,17 +843,6 @@ loop.shared.views = (function(_, mozL10n) {
}));
},
/**
* Renders the context title ("Let's talk about") if necessary.
*/
renderContextTitle: function() {
if (!this.props.showContextTitle) {
return null;
}
return React.createElement("p", null, mozL10n.get("context_inroom_label2"));
},
render: function() {
var hostname;
@ -880,7 +867,6 @@ loop.shared.views = (function(_, mozL10n) {
return (
React.createElement("div", {className: "context-content"},
this.renderContextTitle(),
React.createElement("a", {className: wrapperClasses,
href: this.props.allowClick ? this.props.url : null,
onClick: this.handleLinkClick,
@ -1031,7 +1017,7 @@ loop.shared.views = (function(_, mozL10n) {
renderRemoteVideo: React.PropTypes.bool.isRequired,
screenShareMediaElement: React.PropTypes.object,
screenSharePosterUrl: React.PropTypes.string,
showContextRoomName: React.PropTypes.bool.isRequired,
showInitialContext: React.PropTypes.bool.isRequired,
useDesktopPaths: React.PropTypes.bool.isRequired
},
@ -1135,7 +1121,7 @@ loop.shared.views = (function(_, mozL10n) {
),
React.createElement(loop.shared.views.chat.TextChatView, {
dispatcher: this.props.dispatcher,
showRoomName: this.props.showContextRoomName,
showInitialContext: this.props.showInitialContext,
useDesktopPaths: this.props.useDesktopPaths}),
this.state.localMediaAboslutelyPositioned ?
null : this.renderLocalVideo()

View File

@ -810,7 +810,6 @@ loop.shared.views = (function(_, mozL10n) {
* is specified, then 'dispatcher' is also required.
* @property {String} description The description for the context url.
* @property {loop.Dispatcher} dispatcher
* @property {Boolean} showContextTitle Whether or not to show the "Let's talk about" title.
* @property {String} thumbnail The thumbnail url (expected to be a data url) to
* display. If not specified, a fallback url will be
* shown.
@ -826,7 +825,6 @@ loop.shared.views = (function(_, mozL10n) {
allowClick: React.PropTypes.bool.isRequired,
description: React.PropTypes.string.isRequired,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher),
showContextTitle: React.PropTypes.bool.isRequired,
thumbnail: React.PropTypes.string,
url: React.PropTypes.string,
useDesktopPaths: React.PropTypes.bool.isRequired
@ -845,17 +843,6 @@ loop.shared.views = (function(_, mozL10n) {
}));
},
/**
* Renders the context title ("Let's talk about") if necessary.
*/
renderContextTitle: function() {
if (!this.props.showContextTitle) {
return null;
}
return <p>{mozL10n.get("context_inroom_label2")}</p>;
},
render: function() {
var hostname;
@ -880,7 +867,6 @@ loop.shared.views = (function(_, mozL10n) {
return (
<div className="context-content">
{this.renderContextTitle()}
<a className={wrapperClasses}
href={this.props.allowClick ? this.props.url : null}
onClick={this.handleLinkClick}
@ -1031,7 +1017,7 @@ loop.shared.views = (function(_, mozL10n) {
renderRemoteVideo: React.PropTypes.bool.isRequired,
screenShareMediaElement: React.PropTypes.object,
screenSharePosterUrl: React.PropTypes.string,
showContextRoomName: React.PropTypes.bool.isRequired,
showInitialContext: React.PropTypes.bool.isRequired,
useDesktopPaths: React.PropTypes.bool.isRequired
},
@ -1135,7 +1121,7 @@ loop.shared.views = (function(_, mozL10n) {
</div>
<loop.shared.views.chat.TextChatView
dispatcher={this.props.dispatcher}
showRoomName={this.props.showContextRoomName}
showInitialContext={this.props.showInitialContext}
useDesktopPaths={this.props.useDesktopPaths} />
{this.state.localMediaAboslutelyPositioned ?
null : this.renderLocalVideo()}

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

@ -619,7 +619,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
renderRemoteVideo: this.shouldRenderRemoteVideo(),
screenShareMediaElement: this.state.screenShareMediaElement,
screenSharePosterUrl: this.props.screenSharePosterUrl,
showContextRoomName: true,
showInitialContext: true,
useDesktopPaths: false},
React.createElement(StandaloneOverlayWrapper, {dispatcher: this.props.dispatcher}),
React.createElement(StandaloneRoomInfoArea, {activeRoomStore: this.props.activeRoomStore,

View File

@ -619,7 +619,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
renderRemoteVideo={this.shouldRenderRemoteVideo()}
screenShareMediaElement={this.state.screenShareMediaElement}
screenSharePosterUrl={this.props.screenSharePosterUrl}
showContextRoomName={true}
showInitialContext={true}
useDesktopPaths={false}>
<StandaloneOverlayWrapper dispatcher={this.props.dispatcher} />
<StandaloneRoomInfoArea activeRoomStore={this.props.activeRoomStore}

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

@ -81,9 +81,3 @@ status_error=Something went wrong
# Text chat strings
chat_textbox_placeholder=Type here…
# LOCALIZATION NOTE (context_inroom_label2): this string is followed by the
# title/domain of the website you are having a conversation about, displayed on a
# separate line. If this structure doesn't work for your locale, you might want
# to consider this as a stand-alone title. See example screenshot:
# https://bug1084991.bugzilla.mozilla.org/attachment.cgi?id=8614721
context_inroom_label2=Let's Talk About:

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

@ -248,23 +248,6 @@ describe("loop.store.TextChatStore", function() {
}]);
});
it("should dispatch a LoopChatDisabledMessageAppended event", function() {
store.setStoreState({ textChatEnabled: false });
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
roomName: "Let's share!",
roomUrl: "fake",
roomContextUrls: [{
description: "A wonderful event2",
location: "http://wonderful.invalid2",
thumbnail: "fake2"
}]
}));
sinon.assert.calledOnce(window.dispatchEvent);
sinon.assert.calledWithExactly(window.dispatchEvent,
new CustomEvent("LoopChatDisabledMessageAppended"));
});
it("should not dispatch a LoopChatMessageAppended event", function() {
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
roomName: "Let's share!",

View File

@ -46,6 +46,7 @@ describe("loop.shared.views.TextChatView", function() {
var basicProps = {
dispatcher: dispatcher,
messageList: [],
showInitialContext: true,
useDesktopPaths: false
};
@ -58,6 +59,7 @@ describe("loop.shared.views.TextChatView", function() {
var basicProps = {
dispatcher: dispatcher,
messageList: [],
showInitialContext: true,
useDesktopPaths: false
};
@ -396,7 +398,7 @@ describe("loop.shared.views.TextChatView", function() {
function mountTestComponent(extraProps) {
var props = _.extend({
dispatcher: dispatcher,
showRoomName: false,
showInitialContext: true,
useDesktopPaths: false,
showAlways: true
}, extraProps);
@ -453,40 +455,6 @@ describe("loop.shared.views.TextChatView", function() {
expect(view.getDOMNode().classList.contains("text-chat-entries-empty")).eql(false);
});
it("should add a showing room name class when the view shows room names and it has a room name", function() {
view = mountTestComponent({
showRoomName: true
});
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
roomName: "Study",
roomUrl: "Fake"
}));
expect(view.getDOMNode().classList.contains("showing-room-name")).eql(true);
});
it("shouldn't add a showing room name class when the view doesn't show room names", function() {
view = mountTestComponent({
showRoomName: false
});
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
roomName: "Study",
roomUrl: "Fake"
}));
expect(view.getDOMNode().classList.contains("showing-room-name")).eql(false);
});
it("shouldn't add a showing room name class when the view doesn't have a name", function() {
view = mountTestComponent({
showRoomName: true
});
expect(view.getDOMNode().classList.contains("showing-room-name")).eql(false);
});
it("should show timestamps from msgs sent more than 1 min apart", function() {
view = mountTestComponent();
@ -569,7 +537,7 @@ describe("loop.shared.views.TextChatView", function() {
it("should render a room name special entry", function() {
view = mountTestComponent({
showRoomName: true
showInitialContext: true
});
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
@ -605,6 +573,27 @@ describe("loop.shared.views.TextChatView", function() {
expect(node.querySelector(".context-url-view-wrapper")).to.not.eql(null);
});
it("should not render a room title and context url when show initial context is false", function() {
view = mountTestComponent({
showInitialContext: false
});
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
roomName: "A Very Long Conversation Name",
roomUrl: "http://showcase",
roomContextUrls: [{
description: "A wonderful page!",
location: "http://wonderful.invalid"
// use the fallback thumbnail
}]
}));
var node = view.getDOMNode();
expect(node.querySelector(".showing-room-name")).to.eql(null);
expect(node.querySelector(".context-url-view-wrapper")).to.eql(null);
});
it("should dispatch SendTextChatMessage action when enter is pressed", function() {
view = mountTestComponent();

View File

@ -837,15 +837,6 @@ describe("loop.shared.views", function() {
expect(view.getDOMNode().querySelector(".context-content > p")).eql(null);
});
it("should display a title if required", function() {
view = mountTestComponent({
showContextTitle: true,
url: "http://wonderful.invalid"
});
expect(view.getDOMNode().querySelector(".context-content > p")).not.eql(null);
});
it("should set the href on the link if clicks are allowed", function() {
view = mountTestComponent({
allowClick: true,
@ -1024,7 +1015,7 @@ describe("loop.shared.views", function() {
localVideoMuted: false,
matchMedia: window.matchMedia,
renderRemoteVideo: false,
showContextRoomName: false,
showInitialContext: false,
useDesktopPaths: false
};

View File

@ -1288,7 +1288,7 @@
width: 298},
React.createElement("div", {className: "fx-embedded"},
React.createElement(TextChatView, {dispatcher: dispatcher,
showRoomName: false,
showInitialContext: false,
useDesktopPaths: false})
)
),
@ -1302,7 +1302,7 @@
React.createElement("div", {className: "media-wrapper"},
React.createElement(TextChatView, {
dispatcher: dispatcher,
showRoomName: true,
showInitialContext: true,
useDesktopPaths: false})
)
)

View File

@ -1288,7 +1288,7 @@
width={298}>
<div className="fx-embedded">
<TextChatView dispatcher={dispatcher}
showRoomName={false}
showInitialContext={false}
useDesktopPaths={false} />
</div>
</FramedExample>
@ -1302,7 +1302,7 @@
<div className="media-wrapper">
<TextChatView
dispatcher={dispatcher}
showRoomName={true}
showInitialContext={true}
useDesktopPaths={false} />
</div>
</div>

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

@ -22,6 +22,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
"resource:///modules/RemoteNewTabUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
"resource://testing-common/PlacesTestUtils.jsm");
do_get_profile();
@ -1285,8 +1287,7 @@ add_task(function test_RemoteDirectoryLinksProvider_ClickRemoval() {
testObserver = new UrlDeletionTester();
RemoteDirectoryLinksProvider.addObserver(testObserver);
// remove all hostory
PlacesUtils.bhistory.removeAllPages();
yield PlacesTestUtils.clearHistory();
yield testObserver.promise;
RemoteDirectoryLinksProvider.removeObserver(testObserver);

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

@ -211,13 +211,16 @@
</vbox>
</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/"><!--
-->&mobilePromo.androidLink;</label><!--
-->&mobilePromo.end;
&mobilePromo2.start;<!-- We put these comments to avoid inserting white spaces
--><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"><!--
-->&mobilePromo2.androidLink;</label><!--
-->&mobilePromo2.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"><!--
-->&mobilePromo2.iOSLink;</label><!--
-->&mobilePromo2.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/"><!--
-->&mobilePromo.androidLink;</label><!--
-->&mobilePromo.end;
&mobilePromo2.start;<!-- We put these comments to avoid inserting white spaces
--><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"><!--
-->&mobilePromo2.androidLink;</label><!--
-->&mobilePromo2.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"><!--
-->&mobilePromo2.iOSLink;</label><!--
-->&mobilePromo2.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

@ -11,12 +11,25 @@ function test() {
const ENGINE_NAME = "Foo";
var contextMenu;
let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
let originalValue = envService.get("XPCSHELL_TEST_PROFILE_DIR");
envService.set("XPCSHELL_TEST_PROFILE_DIR", "1");
let url = "chrome://mochitests/content/browser/browser/components/search/test/";
let resProt = Services.io.getProtocolHandler("resource")
.QueryInterface(Ci.nsIResProtocolHandler);
let originalSubstitution = resProt.getSubstitution("search-plugins");
resProt.setSubstitution("search-plugins",
Services.io.newURI(url, null, null));
function observer(aSub, aTopic, aData) {
switch (aData) {
case "engine-added":
var engine = ss.getEngineByName(ENGINE_NAME);
ok(engine, "Engine was added.");
ss.currentEngine = engine;
envService.set("XPCSHELL_TEST_PROFILE_DIR", originalValue);
resProt.setSubstitution("search-plugins", originalSubstitution);
break;
case "engine-current":
is(ss.currentEngine.name, ENGINE_NAME, "currentEngine set");
@ -30,7 +43,7 @@ function test() {
}
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine_mozsearch.xml",
ss.addEngine("resource://search-plugins/testEngine_mozsearch.xml",
null, "data:image/x-icon,%00", false);
function startTest() {

View File

@ -32,7 +32,9 @@ function test() {
// Runs tests after all 'load' event handlers have fired off
window.addEventListener("documentload", function() {
runTests(document, window, tab, finish);
runTests(document, window, tab, function () {
closePDFViewer(window, finish);
});
}, false, true);
}, true);
}
@ -45,6 +47,8 @@ function runTests(document, window, tab, callback) {
//
ok(document.querySelector('div#viewer'), "document content has viewer UI");
ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object");
ok('PDFViewerApplication' in window.wrappedJSObject,
"window content has viewer object");
//
// Browser Find
@ -84,3 +88,11 @@ function runTests(document, window, tab, callback) {
callback();
}
/**
* Destroys PDF.js viewer opened document.
*/
function closePDFViewer(window, callback) {
var viewer = window.wrappedJSObject.PDFViewerApplication;
viewer.close().then(callback);
}

View File

@ -32,7 +32,9 @@ function test() {
// Runs tests after all 'load' event handlers have fired off
window.addEventListener("documentload", function() {
runTests(document, window, finish);
runTests(document, window, function () {
closePDFViewer(window, finish);
});
}, false, true);
}, true);
}
@ -74,3 +76,11 @@ function runTests(document, window, callback) {
callback();
}
/**
* Destroys PDF.js viewer opened document.
*/
function closePDFViewer(window, callback) {
var viewer = window.wrappedJSObject.PDFViewerApplication;
viewer.close().then(callback);
}

View File

@ -90,7 +90,9 @@ function test() {
window.addEventListener("documentload", function() {
initialWidth = parseInt(document.querySelector("div#pageContainer1").style.width);
previousWidth = initialWidth;
runTests(document, window, finish);
runTests(document, window, function () {
closePDFViewer(window, finish);
});
}, false, true);
}, true);
}
@ -102,7 +104,7 @@ function runTests(document, window, callback) {
// Start the zooming tests after the document is loaded
waitForDocumentLoad(document).then(function () {
zoomPDF(document, window, TESTS.shift(), finish);
zoomPDF(document, window, TESTS.shift(), callback);
});
}
@ -173,3 +175,11 @@ function zoomPDF(document, window, test, endCallback) {
EventUtils.synthesizeKey(test.action.event, { ctrlKey: true });
}
}
/**
* Destroys PDF.js viewer opened document.
*/
function closePDFViewer(window, callback) {
var viewer = window.wrappedJSObject.PDFViewerApplication;
viewer.close().then(callback);
}

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

@ -219,12 +219,6 @@ no_conversations_start_message2=Start a new one!
# conversation window when the user edits context. It is a header to the edit
# section.
context_inroom_header=Let's Talk About…
# LOCALIZATION NOTE (context_inroom_label2): this string is followed by the
# title and domain of the website you are having a conversation about, displayed on a
# separate line. If this structure doesn't work for your locale, you might want
# to consider this as a stand-alone title. See example screenshot:
# https://bug1115342.bugzilla.mozilla.org/attachment.cgi?id=8563677
context_inroom_label2=Let's Talk About:
context_edit_name_placeholder=Conversation Name
context_edit_comments_placeholder=Comments
context_cancel_label=Cancel

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.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.">
<!-- LOCALIZATION NOTE (mobilePromo2.*): 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 mobilePromo2.start "Sync to your mobile device. Download ">
<!-- LOCALIZATION NOTE (mobilePromo2.androidLink): This is a link title that links to https://www.mozilla.org/firefox/android/ -->
<!ENTITY mobilePromo2.androidLink "Firefox for Android">
<!-- LOCALIZATION NOTE (mobilePromo2.iOSBefore): This is text displayed between mobilePromo2.androidLink and mobilePromo2.iosLink -->
<!ENTITY mobilePromo2.iOSBefore " or ">
<!-- LOCALIZATION NOTE (mobilePromo2.iOSLink): This is a link title that links to https://www.mozilla.org/firefox/ios/ -->
<!ENTITY mobilePromo2.iOSLink "Firefox for iOS">
<!ENTITY mobilePromo2.end ".">

View File

@ -22,6 +22,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
"resource://gre/modules/NewTabUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
"resource://testing-common/PlacesTestUtils.jsm");
do_get_profile();
@ -1687,8 +1689,7 @@ add_task(function test_DirectoryLinksProvider_ClickRemoval() {
testObserver = new UrlDeletionTester();
DirectoryLinksProvider.addObserver(testObserver);
// remove all hostory
PlacesUtils.bhistory.removeAllPages();
yield PlacesTestUtils.clearHistory();
yield testObserver.promise;
DirectoryLinksProvider.removeObserver(testObserver);

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

@ -102,11 +102,12 @@ idea {
// Exclude them. Convention is that object directories start with obj.
// IntelliJ is clever and will not exclude the parts of the object
// directory that are referenced, if there are any.
// In practice, indexing the entirety of the tree is taking too long.
def topsrcdirURI = file(topsrcdir).toURI()
excludeDirs += files(file(topsrcdir)
.listFiles({it.isDirectory()} as FileFilter)
.collect({topsrcdirURI.relativize(it.toURI()).toString()}) // Relative paths.
.findAll({it.startsWith('obj')}))
.findAll({it.startsWith('obj') && !it.startsWith('.') && !it.equals('mobile/')}))
// If topobjdir is below topsrcdir, hide only some portions of that tree.
def topobjdirURI = file(topobjdir).toURI()

View File

@ -17,6 +17,8 @@ loader.lazyRequireGetter(this, "DebuggerClient",
"devtools/shared/client/main", true);
loader.lazyRequireGetter(this, "DebuggerServer",
"devtools/server/main", true);
loader.lazyRequireGetter(this, "Telemetry",
"devtools/client/shared/telemetry");
loader.lazyRequireGetter(this, "WorkersComponent",
"devtools/client/aboutdebugging/components/workers", true);
loader.lazyRequireGetter(this, "Services");
@ -56,6 +58,9 @@ var AboutDebugging = {
},
init() {
let telemetry = this._telemetry = new Telemetry();
telemetry.toolOpened("aboutdebugging");
// Show the first available tab.
this.showTab();
window.addEventListener("hashchange", () => this.showTab());
@ -89,9 +94,20 @@ var AboutDebugging = {
document.querySelector("#workers"));
});
},
destroy() {
let telemetry = this._telemetry;
telemetry.toolClosed("aboutdebugging");
telemetry.destroy();
},
};
window.addEventListener("DOMContentLoaded", function load() {
window.removeEventListener("DOMContentLoaded", load);
AboutDebugging.init();
});
window.addEventListener("unload", function unload() {
window.removeEventListener("unload", unload);
AboutDebugging.destroy();
});

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

@ -33,7 +33,7 @@ var ArrayRep = React.createClass({
var items;
if (mode == "tiny") {
items = object.length;
items = DOM.span({className: "length"}, object.length);
} else {
var max = (mode == "short") ? 3 : 300;
items = this.arrayIterator(object, max);

View File

@ -44,7 +44,8 @@
.objectBox-number,
.objectLink-styleRule,
.objectLink-element,
.objectLink-textNode {
.objectLink-textNode,
.objectBox-array > .length {
color: #000088;
}
@ -54,7 +55,7 @@
.objectLink-function,
.objectBox-stackTrace,
.objectLink-profile {
.objectLink-profile {
color: DarkGreen;
}
@ -177,6 +178,8 @@
color: var(--theme-highlight-bluegrey);
}
.theme-dark .objectBox-array .length,
.theme-light .objectBox-array .length,
.theme-dark .objectBox-number,
.theme-light .objectBox-number {
color: var(--theme-highlight-green);
@ -184,7 +187,7 @@
.theme-dark .objectBox-string,
.theme-light .objectBox-string {
color: var(--theme-highlight-orange)
color: var(--theme-highlight-orange);
}
.theme-dark .objectBox-null,
@ -195,6 +198,11 @@
color: var(--theme-comment);
}
.theme-dark .objectBox-array,
.theme-light .objectBox-array {
color: var(--theme-body-color);
}
.theme-dark .objectBox-object,
.theme-light .objectBox-object {
font-family: Lucida Grande, sans-serif;

View File

@ -7,7 +7,7 @@
define(function(require, exports, module) {
// ReactJS
const React = require("react");
const ReactDOM = require("react-dom");
// RDP Inspector
const { createFactories } = require("./components/reps/rep-utils");
@ -79,7 +79,7 @@ input.actions = {
* at the top of the window. This component also represents ReacJS root.
*/
var content = document.getElementById("content");
var theApp = React.render(MainTabbedArea(input), content);
var theApp = ReactDOM.render(MainTabbedArea(input), content);
var onResize = event => {
window.document.body.style.height = window.innerHeight + "px";

View File

@ -23,7 +23,8 @@ require.config({
"react": [
"resource://devtools/client/shared/vendor/react-dev",
"resource://devtools/client/shared/vendor/react"
]
],
"react-dom": "resource://devtools/client/shared/vendor/react-dom"
}
});

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

@ -16,6 +16,7 @@ depth.
<script type="application/javascript;version=1.8">
window.onload = Task.async(function* () {
try {
let ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
let React = bRequire("devtools/client/shared/vendor/react");
let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
@ -25,7 +26,7 @@ window.onload = Task.async(function* () {
const t = Tree(TEST_TREE_INTERFACE);
ok(t, "Should be able to create Tree instances");
const tree = React.render(t, window.document.body);
const tree = ReactDOM.render(t, window.document.body);
ok(tree, "Should be able to mount Tree instances");
yield setState(tree, {

View File

@ -15,10 +15,11 @@ Test that collapsed subtrees aren't rendered.
<script type="application/javascript;version=1.8">
window.onload = Task.async(function* () {
try {
let ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
let React = bRequire("devtools/client/shared/vendor/react");
let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
yield setState(tree, {
expanded: new Set("MNO".split(""))

View File

@ -15,10 +15,11 @@ Test Tree's autoExpandDepth.
<script type="application/javascript;version=1.8">
window.onload = Task.async(function* () {
try {
let ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
let React = bRequire("devtools/client/shared/vendor/react");
let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
yield setProps(tree, {
autoExpandDepth: 1

View File

@ -15,9 +15,10 @@ Test that we only render visible tree items.
<script type="application/javascript;version=1.8">
window.onload = Task.async(function* () {
try {
const ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
const React = bRequire("devtools/client/shared/vendor/react");
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
yield setState(tree, {
expanded: new Set("ABCDEFGHIJKLMNO".split("")),

View File

@ -16,10 +16,11 @@ Test focusing with the Tree component.
window.onload = Task.async(function* () {
try {
const ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
const React = bRequire("devtools/client/shared/vendor/react");
const { Simulate } = React.addons.TestUtils;
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
yield setState(tree, {
focused: "G",

View File

@ -15,10 +15,11 @@ Test keyboard navigation with the Tree component.
<script type="application/javascript;version=1.8">
window.onload = Task.async(function* () {
try {
const ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
const React = bRequire("devtools/client/shared/vendor/react");
const { Simulate } = React.addons.TestUtils;
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
yield setState(tree, {
expanded: new Set("ABCDEFGHIJKLMNO".split(""))

View File

@ -16,9 +16,10 @@ Test that arrows get the open attribute when their item's children are expanded.
<script type="application/javascript;version=1.8">
window.onload = Task.async(function* () {
try {
const ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
const React = bRequire("devtools/client/shared/vendor/react");
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
yield setProps(tree, {
renderItem: (item, depth, focused, arrow) => {

View File

@ -17,10 +17,11 @@ other inputs.
<script type="application/javascript;version=1.8">
window.onload = Task.async(function* () {
try {
const ReactDOM = bRequire("devtools/client/shared/vendor/react-dom");
const React = bRequire("devtools/client/shared/vendor/react");
const { Simulate } = React.addons.TestUtils;
const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
const input = document.createElement("input");
document.body.appendChild(input);

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,

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