Merge m-c to inbound, a=merge

MozReview-Commit-ID: DaH1djmEY5A
This commit is contained in:
Wes Kocher 2017-04-27 13:29:07 -07:00
commit 9528ad9a03
470 changed files with 29711 additions and 22568 deletions

3
.gitignore vendored
View File

@ -35,6 +35,9 @@ security/manager/.nss.checkout
# Build directories
/obj*/
# gecko.log is generated by various test harnesses
/gecko.log
# Build directories for js shell
_DBG.OBJ/
_OPT.OBJ/

View File

@ -32,6 +32,9 @@
# Build directories
^obj
# gecko.log is generated by various test harnesses
^gecko\.log
# Build directories for js shell
_DBG\.OBJ/
_OPT\.OBJ/

View File

@ -191,7 +191,7 @@ XULTreeAccessible::ChildAtPoint(int32_t aX, int32_t aY,
nsIFrame *rootFrame = presShell->GetRootFrame();
NS_ENSURE_TRUE(rootFrame, nullptr);
nsIntRect rootRect = rootFrame->GetScreenRect();
CSSIntRect rootRect = rootFrame->GetScreenRect();
int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.x;
int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.y;

View File

@ -325,7 +325,7 @@ XULTreeGridRowAccessible::ChildAtPoint(int32_t aX, int32_t aY,
nsIFrame *rootFrame = presShell->GetRootFrame();
NS_ENSURE_TRUE(rootFrame, nullptr);
nsIntRect rootRect = rootFrame->GetScreenRect();
CSSIntRect rootRect = rootFrame->GetScreenRect();
int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.x;
int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.y;

View File

@ -70,6 +70,17 @@ pref("extensions.screenshots.system-disabled", true);
// See the SCOPE constants in AddonManager.jsm for values to use here.
pref("extensions.autoDisableScopes", 15);
// This is where the profiler WebExtension API will look for breakpad symbols.
// NOTE: deliberately http right now since https://symbols.mozilla.org is not supported.
pref("extensions.geckoProfiler.symbols.url", "http://symbols.mozilla.org/");
pref("extensions.geckoProfiler.acceptedExtensionIds", "geckoprofiler@mozilla.com");
#if defined(XP_LINUX) || defined (XP_MACOSX)
pref("extensions.geckoProfiler.getSymbolRules", "localBreakpad,remoteBreakpad,nm");
#else // defined(XP_WIN)
pref("extensions.geckoProfiler.getSymbolRules", "localBreakpad,remoteBreakpad");
#endif
// Add-on content security policies.
pref("extensions.webextensions.base-content-security-policy", "script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; object-src 'self' https://* moz-extension: blob: filesystem:;");
pref("extensions.webextensions.default-content-security-policy", "script-src 'self'; object-src 'self';");

View File

@ -287,7 +287,6 @@ var FullZoom = {
if (token.isCurrent) {
ZoomManager.setZoomForBrowser(browser, value === undefined ? 1 : value);
this._ignorePendingZoomAccesses(browser);
Services.obs.notifyObservers(browser, "browser-fullZoom:zoomReset");
}
});
this._removePref(browser);
@ -355,7 +354,6 @@ var FullZoom = {
* @param browser The zoom of this browser will be saved. Required.
*/
_applyZoomToPref: function FullZoom__applyZoomToPref(browser) {
Services.obs.notifyObservers(browser, "browser-fullZoom:zoomChange");
if (!this.siteSpecific ||
gInPrintPreviewMode ||
browser.isSyntheticDocument)
@ -376,7 +374,6 @@ var FullZoom = {
* @param browser The zoom of this browser will be removed. Required.
*/
_removePref: function FullZoom__removePref(browser) {
Services.obs.notifyObservers(browser, "browser-fullZoom:zoomReset");
if (browser.isSyntheticDocument)
return;
let ctxt = this._loadContextFromBrowser(browser);

View File

@ -24,7 +24,7 @@ add_task(function*() {
});
is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:mozilla");
let zoomChangePromise = promiseObserverNotification("browser-fullZoom:zoomChange");
let zoomChangePromise = BrowserTestUtils.waitForEvent(window, "FullZoomChange");
FullZoom.enlarge();
yield zoomChangePromise;
is(parseInt(zoomResetButton.label, 10), 110, "Zoom is changed to 110% for about:mozilla");
@ -36,7 +36,7 @@ add_task(function*() {
is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:robots");
gBrowser.selectedTab = tab1;
let zoomResetPromise = promiseObserverNotification("browser-fullZoom:zoomReset");
let zoomResetPromise = BrowserTestUtils.waitForEvent(window, "FullZoomChange");
FullZoom.reset();
yield zoomResetPromise;
is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:mozilla");

View File

@ -0,0 +1,59 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* eslint-env worker */
/* globals OS, ParseSymbols */
"use strict";
importScripts("resource://gre/modules/osfile.jsm");
importScripts("resource:///modules/ParseSymbols.jsm");
async function fetchSymbolFile(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`got error status ${response.status}`);
}
return response.text();
}
function parse(text) {
const syms = new Map();
// Lines look like this:
//
// PUBLIC 3fc74 0 test_public_symbol
//
// FUNC 40330 8e 0 test_func_symbol
const symbolRegex = /\nPUBLIC ([0-9a-f]+) [0-9a-f]+ (.*)|\nFUNC ([0-9a-f]+) [0-9a-f]+ [0-9a-f]+ (.*)/g;
let match;
let approximateLength = 0;
while ((match = symbolRegex.exec(text))) {
const [address0, symbol0, address1, symbol1] = match.slice(1);
const address = parseInt(address0 || address1, 16);
const sym = (symbol0 || symbol1).trimRight();
syms.set(address, sym);
approximateLength += sym.length;
}
return ParseSymbols.convertSymsMapToExpectedSymFormat(syms, approximateLength);
}
onmessage = async e => {
try {
let text;
if (e.data.filepath) {
text = await OS.File.read(e.data.filepath, {encoding: "utf-8"});
} else if (e.data.url) {
text = await fetchSymbolFile(e.data.url);
}
const result = parse(text);
postMessage({result}, result.map(r => r.buffer));
} catch (error) {
postMessage({error: error.toString()});
}
close();
};

View File

@ -0,0 +1,49 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
/* exported ParseSymbols */
var EXPORTED_SYMBOLS = ["ParseSymbols"];
function convertStringArrayToUint8BufferWithIndex(array, approximateLength) {
const index = new Uint32Array(array.length + 1);
const textEncoder = new TextEncoder();
let buffer = new Uint8Array(approximateLength);
let pos = 0;
for (let i = 0; i < array.length; i++) {
const encodedString = textEncoder.encode(array[i]);
let size = pos + buffer.length;
if (size < buffer.length) {
size = 2 << Math.log(size) / Math.log(2);
let newBuffer = new Uint8Array(size);
newBuffer.set(buffer);
buffer = newBuffer;
}
buffer.set(encodedString, pos);
index[i] = pos;
pos += encodedString.length;
}
index[array.length] = pos;
return {index, buffer};
}
function convertSymsMapToExpectedSymFormat(syms, approximateSymLength) {
const addresses = Array.from(syms.keys());
addresses.sort((a, b) => a - b);
const symsArray = addresses.map(addr => syms.get(addr));
const {index, buffer} =
convertStringArrayToUint8BufferWithIndex(symsArray, approximateSymLength);
return [new Uint32Array(addresses), index, buffer];
}
var ParseSymbols = {
convertSymsMapToExpectedSymFormat,
};

View File

@ -180,6 +180,14 @@ extensions.registerModules({
["pageAction"],
],
},
geckoProfiler: {
url: "chrome://browser/content/ext-geckoProfiler.js",
schema: "chrome://browser/content/schemas/geckoProfiler.json",
scopes: ["addon_parent"],
paths: [
["geckoProfiler"],
],
},
sessions: {
url: "chrome://browser/content/ext-sessions.js",
schema: "chrome://browser/content/schemas/sessions.json",

View File

@ -0,0 +1,363 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
Cu.import("resource://gre/modules/Services.jsm");
Cu.importGlobalProperties(["fetch", "TextEncoder"]);
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ParseSymbols", "resource:///modules/ParseSymbols.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Subprocess", "resource://gre/modules/Subprocess.jsm");
const PREF_ASYNC_STACK = "javascript.options.asyncstack";
const PREF_SYMBOLS_URL = "extensions.geckoProfiler.symbols.url";
const PREF_GET_SYMBOL_RULES = "extensions.geckoProfiler.getSymbolRules";
const ASYNC_STACKS_ENABLED = Services.prefs.getBoolPref(PREF_ASYNC_STACK, false);
function parseSym(data) {
const worker = new ChromeWorker("resource://app/modules/ParseSymbols-worker.js");
const promise = new Promise((resolve, reject) => {
worker.onmessage = (e) => {
if (e.data.error) {
reject(e.data.error);
} else {
resolve(e.data.result);
}
};
});
worker.postMessage(data);
return promise;
}
class NMParser {
constructor() {
this._addrToSymMap = new Map();
this._approximateLength = 0;
}
consume(data) {
const lineRegex = /.*\n?/g;
const buffer = this._currentLine + data;
let match;
while ((match = lineRegex.exec(buffer))) {
let [line] = match;
if (line[line.length - 1] === "\n") {
this._processLine(line);
} else {
this._currentLine = line;
break;
}
}
}
finish() {
this._processLine(this._currentLine);
return {syms: this._addrToSymMap, approximateLength: this._approximateLength};
}
_processLine(line) {
// Example lines:
// 00000000028c9888 t GrFragmentProcessor::MulOutputByInputUnpremulColor(sk_sp<GrFragmentProcessor>)::PremulFragmentProcessor::onCreateGLSLInstance() const::GLFP::~GLFP()
// 00000000028c9874 t GrFragmentProcessor::MulOutputByInputUnpremulColor(sk_sp<GrFragmentProcessor>)::PremulFragmentProcessor::onCreateGLSLInstance() const::GLFP::~GLFP()
// 00000000028c9874 t GrFragmentProcessor::MulOutputByInputUnpremulColor(sk_sp<GrFragmentProcessor>)::PremulFragmentProcessor::onCreateGLSLInstance() const::GLFP::~GLFP()
// 0000000003a33730 r mozilla::OggDemuxer::~OggDemuxer()::{lambda()#1}::operator()() const::__func__
// 0000000003a33930 r mozilla::VPXDecoder::Drain()::{lambda()#1}::operator()() const::__func__
//
// Some lines have the form
// <address> ' ' <letter> ' ' <symbol>
// and some have the form
// <address> ' ' <symbol>
// The letter has a meaning, but we ignore it.
const regex = /([^ ]+) (?:. )?(.*)/;
let match = regex.exec(line);
if (match) {
const [, address, symbol] = match;
this._addrToSymMap.set(parseInt(address, 16), symbol);
this._approximateLength += symbol.length;
}
}
}
class CppFiltParser {
constructor(length) {
this._index = 0;
this._results = new Array(length);
}
consume(data) {
const lineRegex = /.*\n?/g;
const buffer = this._currentLine + data;
let match;
while ((match = lineRegex.exec(buffer))) {
let [line] = match;
if (line[line.length - 1] === "\n") {
this._processLine(line);
} else {
this._currentLine = line;
break;
}
}
}
finish() {
this._processLine(this._currentLine);
return this._results;
}
_processLine(line) {
this._results[this._index++] = line.trimRight();
}
}
async function readAllData(pipe, processData) {
let data;
while ((data = await pipe.readString())) {
processData(data);
}
}
async function spawnProcess(name, cmdArgs, processData, stdin = null) {
const opts = {
command: await Subprocess.pathSearch(name),
arguments: cmdArgs,
};
const proc = await Subprocess.call(opts);
if (stdin) {
const encoder = new TextEncoder("utf-8");
proc.stdin.write(encoder.encode(stdin));
proc.stdin.close();
}
await readAllData(proc.stdout, processData);
}
async function getSymbolsFromNM(path) {
const parser = new NMParser();
const args = [path];
if (Services.appinfo.OS !== "Darwin") {
// Mac's `nm` doesn't support the demangle option, so we have to
// post-process the symbols with c++filt.
args.unshift("--demangle");
}
await spawnProcess("nm", args, data => parser.consume(data));
await spawnProcess("nm", ["-D", ...args], data => parser.consume(data));
let {syms, approximateLength} = parser.finish();
if (Services.appinfo.OS === "Darwin") {
const keys = Array.from(syms.keys());
const values = keys.map(k => syms.get(k));
const demangler = new CppFiltParser(keys.length);
await spawnProcess("c++filt", [], data => demangler.consume(data), values.join("\n"));
const newSymbols = demangler.finish();
approximateLength = 0;
for (let [i, symbol] of newSymbols.entries()) {
approximateLength += symbol.length;
syms.set(keys[i], symbol);
}
}
return ParseSymbols.convertSymsMapToExpectedSymFormat(syms, approximateLength);
}
function pathComponentsForSymbolFile(debugName, breakpadId) {
const symName = debugName.replace(/(\.pdb)?$/, ".sym");
return [debugName, breakpadId, symName];
}
function urlForSymFile(debugName, breakpadId) {
const profilerSymbolsURL = Services.prefs.getCharPref(PREF_SYMBOLS_URL,
"http://symbols.mozilla.org/");
return profilerSymbolsURL + pathComponentsForSymbolFile(debugName, breakpadId).join("/");
}
function getContainingObjdirDist(path) {
let curPath = path;
let curPathBasename = OS.Path.basename(curPath);
while (curPathBasename) {
if (curPathBasename === "dist") {
return curPath;
}
const parentDirPath = OS.Path.dirname(curPath);
if (curPathBasename === "bin") {
return parentDirPath;
}
curPath = parentDirPath;
curPathBasename = OS.Path.basename(curPath);
}
return null;
}
function filePathForSymFileInObjDir(binaryPath, debugName, breakpadId) {
// `mach buildsymbols` generates symbol files located
// at /path/to/objdir/dist/crashreporter-symbols/.
const objDirDist = getContainingObjdirDist(binaryPath);
if (!objDirDist) {
return null;
}
return OS.Path.join(objDirDist,
"crashreporter-symbols",
...pathComponentsForSymbolFile(debugName, breakpadId));
}
const symbolCache = new Map();
function primeSymbolStore(libs) {
for (const {debugName, breakpadId, path} of libs) {
symbolCache.set(urlForSymFile(debugName, breakpadId), path);
}
}
const isRunningObserver = {
_observers: new Set(),
observe(subject, topic, data) {
switch (topic) {
case "profiler-started":
case "profiler-stopped":
// Call observer(false) or observer(true), but do it through a promise
// so that it's asynchronous.
// We don't want it to be synchronous because of the observer call in
// addObserver, which is asynchronous, and we want to get the ordering
// right.
const isRunningPromise = Promise.resolve(topic === "profiler-started");
for (let observer of this._observers) {
isRunningPromise.then(observer);
}
break;
}
},
_startListening() {
Services.obs.addObserver(this, "profiler-started");
Services.obs.addObserver(this, "profiler-stopped");
},
_stopListening() {
Services.obs.removeObserver(this, "profiler-started");
Services.obs.removeObserver(this, "profiler-stopped");
},
addObserver(observer) {
if (this._observers.size === 0) {
this._startListening();
}
this._observers.add(observer);
observer(Services.profiler.IsActive());
},
removeObserver(observer) {
if (this._observers.delete(observer) && this._observers.size === 0) {
this._stopListening();
}
},
};
this.geckoProfiler = class extends ExtensionAPI {
getAPI(context) {
return {
geckoProfiler: {
async start(options) {
const {bufferSize, interval, features, threads} = options;
Services.prefs.setBoolPref(PREF_ASYNC_STACK, false);
if (threads) {
Services.profiler.StartProfiler(bufferSize, interval, features, features.length, threads, threads.length);
} else {
Services.profiler.StartProfiler(bufferSize, interval, features, features.length);
}
},
async stop() {
if (ASYNC_STACKS_ENABLED !== null) {
Services.prefs.setBoolPref(PREF_ASYNC_STACK, ASYNC_STACKS_ENABLED);
}
Services.profiler.StopProfiler();
},
async pause() {
Services.profiler.PauseSampling();
},
async resume() {
Services.profiler.ResumeSampling();
},
async getProfile() {
if (!Services.profiler.IsActive()) {
throw new Error("The profiler is stopped. " +
"You need to start the profiler before you can capture a profile.");
}
return Services.profiler.getProfileDataAsync();
},
async getSymbols(debugName, breakpadId) {
if (symbolCache.size === 0) {
primeSymbolStore(Services.profiler.sharedLibraries);
}
const path = symbolCache.get(urlForSymFile(debugName, breakpadId));
const symbolRules = Services.prefs.getCharPref(PREF_GET_SYMBOL_RULES, "localBreakpad,remoteBreakpad");
const haveAbsolutePath = path && OS.Path.split(path).absolute;
// We have multiple options for obtaining symbol information for the given
// binary.
// "localBreakpad" - Use existing symbol dumps stored in the object directory of a local
// Firefox build, generated using `mach buildsymbols` [requires path]
// "remoteBreakpad" - Use symbol dumps from the Mozilla symbol server [only requires
// debugName + breakpadId]
// "nm" - Use the command line tool `nm` [linux/mac only, requires path]
for (const rule of symbolRules.split(",")) {
try {
switch (rule) {
case "localBreakpad":
if (haveAbsolutePath) {
const filepath = filePathForSymFileInObjDir(path, debugName, breakpadId);
if (filepath) {
// NOTE: here and below, "return await" is used to ensure we catch any
// errors in the promise. A simple return would give the error to the
// caller.
return await parseSym({filepath});
}
}
break;
case "remoteBreakpad":
const url = urlForSymFile(debugName, breakpadId);
return await parseSym({url});
case "nm":
return await getSymbolsFromNM(path);
}
} catch (e) {
// Each of our options can go wrong for a variety of reasons, so on failure
// we will try the next one.
// "localBreakpad" will fail if this is not a local build that's running from the object
// directory or if the user hasn't run `mach buildsymbols` on it.
// "remoteBreakpad" will fail if this is not an official mozilla build (e.g. Nightly) or a
// known system library.
// "nm" will fail if `nm` is not available.
}
}
throw new Error(`Ran out of options to get symbols from library ${debugName} ${breakpadId}.`);
},
onRunning: new SingletonEventManager(context, "geckoProfiler.onRunning", fire => {
isRunningObserver.addObserver(fire.async);
return () => {
isRunningObserver.removeObserver(fire.async);
};
}).api(),
},
};
}
};

View File

@ -45,11 +45,11 @@ function createSession(restored, extension, sessionId) {
if (restored instanceof Ci.nsIDOMChromeWindow) {
return promiseObserved("sessionstore-single-window-restored", subject => subject == restored).then(() => {
sessionObj.window = extension.windowManager.convert(restored, {populate: true});
return Promise.resolve([sessionObj]);
return Promise.resolve(sessionObj);
});
}
sessionObj.tab = extension.tabManager.convert(restored);
return Promise.resolve([sessionObj]);
return Promise.resolve(sessionObj);
}
this.sessions = class extends ExtensionAPI {

View File

@ -23,6 +23,7 @@ browser.jar:
content/browser/ext-devtools-inspectedWindow.js
content/browser/ext-devtools-network.js
content/browser/ext-devtools-panels.js
content/browser/ext-geckoProfiler.js
content/browser/ext-history.js
content/browser/ext-omnibox.js
content/browser/ext-pageAction.js

View File

@ -15,6 +15,8 @@ EXTRA_COMPONENTS += [
EXTRA_JS_MODULES += [
'ExtensionPopups.jsm',
'ParseSymbols-worker.js',
'ParseSymbols.jsm',
]
DIRS += ['schemas']

View File

@ -0,0 +1,129 @@
[
{
"namespace": "manifest",
"types": [
{
"$extend": "Permission",
"choices": [{
"type": "string",
"enum": [
"geckoProfiler"
]
}]
}
]
},
{
"namespace": "geckoProfiler",
"description": "Exposes the browser's profiler.",
"permissions": ["geckoProfiler"],
"functions": [
{
"name": "start",
"type": "function",
"description": "Starts the profiler with the specified settings.",
"async": true,
"parameters": [
{
"name": "settings",
"type": "object",
"properties": {
"bufferSize": {
"type": "integer",
"minimum": 0,
"description": "The size in bytes of the buffer used to store profiling data. A larger value allows capturing a profile that covers a greater amount of time."
},
"interval": {
"type": "number",
"description": "Interval in milliseconds between samples of profiling data. A smaller value will increase the detail of the profiles captured."
},
"features": {
"type": "array",
"description": "A list of active features for the profiler.",
"items": {
"type": "string",
"enum": [
"js",
"stackwalk",
"tasktracer",
"leaf",
"threads"
]
}
},
"threads": {
"type": "array",
"description": "A list of thread names for which to capture profiles.",
"optional": true,
"items": {
"type": "string"
}
}
}
}
]
},
{
"name": "stop",
"type": "function",
"description": "Stops the profiler and discards any captured profile data.",
"async": true,
"parameters": []
},
{
"name": "pause",
"type": "function",
"description": "Pauses the profiler, keeping any profile data that is already written.",
"async": true,
"parameters": []
},
{
"name": "resume",
"type": "function",
"description": "Resumes the profiler with the settings that were initially used to start it.",
"async": true,
"parameters": []
},
{
"name": "getProfile",
"type": "function",
"description": "Gathers the profile data from the current profiling session.",
"async": true,
"parameters": []
},
{
"name": "getSymbols",
"type": "function",
"description": "Gets the debug symbols for a particular library.",
"async": true,
"parameters": [
{
"type": "string",
"name": "debugName",
"description": "The name of the library's debug file. For example, 'xul.pdb"
},
{
"type": "string",
"name": "breakpadId",
"description": "The Breakpad ID of the library"
}
]
}
],
"events": [
{
"name": "onRunning",
"type": "function",
"description": "Fires when the profiler starts/stops running.",
"parameters": [
{
"name": "isRunning",
"type": "boolean",
"description": "Whether the profiler is running or not. Pausing the profiler will not affect this value."
}
]
}
]
}
]

View File

@ -14,6 +14,7 @@ browser.jar:
content/browser/schemas/devtools_inspected_window.json
content/browser/schemas/devtools_network.json
content/browser/schemas/devtools_panels.json
content/browser/schemas/geckoProfiler.json
content/browser/schemas/history.json
content/browser/schemas/omnibox.json
content/browser/schemas/page_action.json

View File

@ -3,6 +3,7 @@ support-files =
head.js
head_pageAction.js
head_sessions.js
profilerSymbols.sjs
context.html
context_frame.html
ctxmenu-image.png
@ -62,6 +63,7 @@ support-files =
[browser_ext_devtools_network.js]
[browser_ext_devtools_page.js]
[browser_ext_devtools_panel.js]
[browser_ext_geckoProfiler_symbolicate.js]
[browser_ext_getViews.js]
[browser_ext_incognito_views.js]
[browser_ext_incognito_popup.js]

View File

@ -0,0 +1,57 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
let getExtension = () => {
return ExtensionTestUtils.loadExtension({
background: async function() {
const [addresses, indices, strings] = await browser.geckoProfiler.getSymbols("test.pdb",
"ASDFQWERTY");
function getSymbolAtAddress(address) {
const index = addresses.indexOf(address);
if (index == -1) {
return null;
}
const nameBuffer = strings.subarray(indices[index], indices[index + 1]);
const decoder = new TextDecoder("utf-8");
return decoder.decode(nameBuffer);
}
browser.test.assertEq(getSymbolAtAddress(0x3fc74), "test_public_symbol", "Contains public symbol");
browser.test.assertEq(getSymbolAtAddress(0x40330), "test_func_symbol", "Contains func symbol");
browser.test.sendMessage("symbolicated");
},
manifest: {
"permissions": ["geckoProfiler"],
"applications": {
"gecko": {
"id": "profilertest@mozilla.com",
},
},
},
});
};
add_task(function* testProfilerControl() {
SpecialPowers.pushPrefEnv({
set: [
[
"extensions.geckoProfiler.symbols.url",
"http://mochi.test:8888/browser/browser/components/extensions/test/browser/profilerSymbols.sjs?path=",
],
[
"extensions.geckoProfiler.acceptedExtensionIds",
"profilertest@mozilla.com",
],
],
});
let extension = getExtension();
yield extension.startup();
yield extension.awaitMessage("symbolicated");
yield extension.unload();
});

View File

@ -38,13 +38,13 @@ add_task(async function test_sessions_get_recently_closed_tabs() {
});
let win = await BrowserTestUtils.openNewBrowserWindow();
await BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, "about:addons");
await BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, "about:mozilla");
await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
let expectedTabs = [];
let tab = win.gBrowser.selectedTab;
expectedTabs.push(expectedTabInfo(tab, win));
for (let url of ["about:robots", "about:mozilla"]) {
for (let url of ["about:robots", "about:buildconfig"]) {
tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, url);
expectedTabs.push(expectedTabInfo(tab, win));
}

View File

@ -85,14 +85,13 @@ add_task(function* test_sessions_restore() {
yield assertNotificationCount(2);
let restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
is(restored[0].window.tabs.length, 3, "restore returned a window with the expected number of tabs");
checkLocalTab(restored[0].window.tabs[0], "about:config");
checkLocalTab(restored[0].window.tabs[1], "about:robots");
checkLocalTab(restored[0].window.tabs[2], "about:mozilla");
is(restored.window.tabs.length, 3, "restore returned a window with the expected number of tabs");
checkLocalTab(restored.window.tabs[0], "about:config");
checkLocalTab(restored.window.tabs[1], "about:robots");
checkLocalTab(restored.window.tabs[2], "about:mozilla");
// Close the window again.
let window = windowTracker.getWindow(restored[0].window.id);
let window = windowTracker.getWindow(restored.window.id);
yield BrowserTestUtils.closeWindow(window);
yield assertNotificationCount(3);
@ -103,14 +102,13 @@ add_task(function* test_sessions_restore() {
yield assertNotificationCount(4);
restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
is(restored[0].window.tabs.length, 3, "restore returned a window with the expected number of tabs");
checkLocalTab(restored[0].window.tabs[0], "about:config");
checkLocalTab(restored[0].window.tabs[1], "about:robots");
checkLocalTab(restored[0].window.tabs[2], "about:mozilla");
is(restored.window.tabs.length, 3, "restore returned a window with the expected number of tabs");
checkLocalTab(restored.window.tabs[0], "about:config");
checkLocalTab(restored.window.tabs[1], "about:robots");
checkLocalTab(restored.window.tabs[2], "about:mozilla");
// Close the window again.
window = windowTracker.getWindow(restored[0].window.id);
window = windowTracker.getWindow(restored.window.id);
yield BrowserTestUtils.closeWindow(window);
// notificationCount = yield extension.awaitMessage("notificationCount");
yield assertNotificationCount(5);
@ -126,8 +124,7 @@ add_task(function* test_sessions_restore() {
yield assertNotificationCount(7);
restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
tab = restored[0].tab;
tab = restored.tab;
ok(tab, "restore returned a tab");
checkLocalTab(tab, "about:robots");
@ -143,8 +140,7 @@ add_task(function* test_sessions_restore() {
yield assertNotificationCount(9);
restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
tab = restored[0].tab;
tab = restored.tab;
ok(tab, "restore returned a tab");
checkLocalTab(tab, "about:robots");

View File

@ -0,0 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const SYMBOLS_FILE =
`MODULE windows x86_64 A712ED458B2542C18785C19D17D64D842 test.pdb
INFO CODE_ID 58EE0F7F3EDD000 test.dll
PUBLIC 3fc74 0 test_public_symbol
FUNC 40330 8e 0 test_func_symbol
40330 42 22 2229
40372 3a 23 2229
403ac 12 23 2229
`;
function handleRequest(req, resp) {
let match = /path=([^\/]+)\/([^\/]+)\/([^\/]+)/.exec(req.queryString);
if (match && match[1] == "test.pdb") {
resp.write(SYMBOLS_FILE);
} else {
resp.setStatusLine(null, 404, 'Not Found');
}
}

View File

@ -0,0 +1,104 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
let getExtension = () => {
return ExtensionTestUtils.loadExtension({
background: async function() {
const runningListener = isRunning => {
if (isRunning) {
browser.test.sendMessage("started");
} else {
browser.test.sendMessage("stopped");
}
};
browser.test.onMessage.addListener(async message => {
let result;
switch (message) {
case "start":
result = await browser.geckoProfiler.start({
bufferSize: 10000,
interval: 0.5,
features: ["js"],
threads: ["GeckoMain"],
});
browser.test.assertEq(undefined, result, "start returns nothing.");
break;
case "stop":
result = await browser.geckoProfiler.stop();
browser.test.assertEq(undefined, result, "stop returns nothing.");
break;
case "pause":
result = await browser.geckoProfiler.pause();
browser.test.assertEq(undefined, result, "pause returns nothing.");
browser.test.sendMessage("paused");
break;
case "resume":
result = await browser.geckoProfiler.resume();
browser.test.assertEq(undefined, result, "resume returns nothing.");
browser.test.sendMessage("resumed");
break;
case "test profile":
result = await browser.geckoProfiler.getProfile();
browser.test.assertTrue("libs" in result, "The profile contains libs.");
browser.test.assertTrue("meta" in result, "The profile contains meta.");
browser.test.assertTrue("threads" in result, "The profile contains threads.");
browser.test.assertTrue(result.threads.some(t => t.name == "GeckoMain"),
"The profile contains a GeckoMain thread.");
browser.test.sendMessage("tested profile");
break;
case "remove runningListener":
browser.geckoProfiler.onRunning.removeListener(runningListener);
browser.test.sendMessage("removed runningListener");
break;
}
});
browser.test.sendMessage("ready");
browser.geckoProfiler.onRunning.addListener(runningListener);
},
manifest: {
"permissions": ["geckoProfiler"],
"applications": {
"gecko": {
"id": "profilertest@mozilla.com",
},
},
},
});
};
add_task(async function testProfilerControl() {
const acceptedExtensionIdsPref = "extensions.geckoProfiler.acceptedExtensionIds";
Services.prefs.setCharPref(acceptedExtensionIdsPref, "profilertest@mozilla.com");
let extension = getExtension();
await extension.startup();
await extension.awaitMessage("ready");
await extension.awaitMessage("stopped");
extension.sendMessage("start");
await extension.awaitMessage("started");
extension.sendMessage("test profile");
await extension.awaitMessage("tested profile");
extension.sendMessage("pause");
await extension.awaitMessage("paused");
extension.sendMessage("resume");
await extension.awaitMessage("resumed");
extension.sendMessage("stop");
await extension.awaitMessage("stopped");
extension.sendMessage("remove runningListener");
await extension.awaitMessage("removed runningListener");
await extension.unload();
Services.prefs.clearUserPref(acceptedExtensionIdsPref);
});

View File

@ -13,3 +13,4 @@ tags = webextensions
[test_ext_manifest_commands.js]
[test_ext_manifest_omnibox.js]
[test_ext_manifest_permissions.js]
[test_ext_geckoProfiler_control.js]

View File

@ -527,9 +527,6 @@ BrowserGlue.prototype = {
SelfSupportBackend.init();
// Ensure we keep track of places/pw-mananager undo by init'ing this early.
Cu.import("resource:///modules/AutoMigrate.jsm");
if (AppConstants.INSTALL_COMPACT_THEMES) {
let vendorShortName = gBrandBundle.GetStringFromName("vendorShortName");

View File

@ -0,0 +1,16 @@
. "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
# Add-on signing is not required for DevEdition
MOZ_REQUIRE_SIGNING=0
ac_add_options --enable-verify-mar
# This will overwrite the default of stripping everything and keep the symbol table.
# This is useful for profiling and debugging and only increases the package size
# by 2 MBs.
STRIP_FLAGS="--strip-debug"
ac_add_options --with-branding=browser/branding/aurora
. "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.cache"

View File

@ -0,0 +1,16 @@
. "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
# Add-on signing is not required for DevEdition
MOZ_REQUIRE_SIGNING=0
ac_add_options --enable-verify-mar
# This will overwrite the default of stripping everything and keep the symbol table.
# This is useful for profiling and debugging and only increases the package size
# by 2 MBs.
STRIP_FLAGS="--strip-debug"
ac_add_options --with-branding=browser/branding/aurora
. "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.cache"

View File

@ -0,0 +1,23 @@
. "$topsrcdir/browser/config/mozconfigs/macosx64/common-opt"
# Add-on signing is not required for DevEdition
MOZ_REQUIRE_SIGNING=0
ac_add_options --disable-install-strip
ac_add_options --enable-verify-mar
ac_add_options --enable-instruments
# Cross-compiled builds fail when dtrace is enabled
if test `uname -s` != Linux; then
ac_add_options --enable-dtrace
fi
if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
ac_add_options --with-macbundlename-prefix=Firefox
fi
ac_add_options --with-branding=browser/branding/aurora
. "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.cache"

View File

@ -0,0 +1,12 @@
. "$topsrcdir/build/mozconfig.win-common"
. "$topsrcdir/browser/config/mozconfigs/win32/common-opt"
# Add-on signing is not required for DevEdition
MOZ_REQUIRE_SIGNING=0
ac_add_options --enable-verify-mar
ac_add_options --with-branding=browser/branding/aurora
. "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.cache"

View File

@ -0,0 +1,13 @@
. "$topsrcdir/build/mozconfig.win-common"
. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
. "$topsrcdir/browser/config/mozconfigs/win64/common-opt"
# Add-on signing is not required for DevEdition
MOZ_REQUIRE_SIGNING=0
ac_add_options --enable-verify-mar
ac_add_options --with-branding=browser/branding/aurora
. "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.cache"

View File

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 112075416,
"digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 112206132,
"digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

View File

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 112075416,
"digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 112206132,
"digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

View File

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 112075416,
"digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 112206132,
"digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

View File

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 112075416,
"digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 112206132,
"digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

View File

@ -24,9 +24,9 @@
"size": 12072532
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 112075416,
"digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 112206132,
"digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

View File

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 112075416,
"digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 112206132,
"digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

View File

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 112075416,
"digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 112206132,
"digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

View File

@ -8,9 +8,9 @@
"unpack": true
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 124444249,
"digest": "11239565e6dde851a5229a0380da129b129c560c3fa765eb4998549e688385be314523074456afec9ebe4ddb6af71e44c64d7c669e7e904ed253f6e7b0f6317b",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 80381089,
"digest": "01bc5c4d25ffd16f51643d36449ca469eccc362b8315945edd9b7e174462659a3d5d4bd5d2738f449557254ba930ca0d70d22f8717b51ba36af68193bd3c39f7",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

View File

@ -42,9 +42,9 @@
"filename": "dmg.tar.xz"
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 142669732,
"digest": "ab74516718ac2b848f0d28df314c0588559dd77b203e8e0a20510a44e7d8b9b757b35c5eebd72524d9ce29bfa10a6e72e44fa54cb9cfe808bb42b5fc603b14bc",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 107393796,
"digest": "90dca0af2ddd14b8dc4414c8ece52498f8f96e98bb0e4c228e2af27d4f8bd7689dda5160a6994e514e516cb4851f26d94b1bf50456c4dd5c485eaa8b02ba9f95",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

View File

@ -8,9 +8,9 @@
"unpack": true
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 124444249,
"digest": "11239565e6dde851a5229a0380da129b129c560c3fa765eb4998549e688385be314523074456afec9ebe4ddb6af71e44c64d7c669e7e904ed253f6e7b0f6317b",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 80381089,
"digest": "01bc5c4d25ffd16f51643d36449ca469eccc362b8315945edd9b7e174462659a3d5d4bd5d2738f449557254ba930ca0d70d22f8717b51ba36af68193bd3c39f7",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

View File

@ -6,9 +6,9 @@
"filename": "mozmake.exe"
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 68287611,
"digest": "702b162f53970de096d1c7777f37eddd22251a910d523df4edcbead399d40bfb85b059b5745ec8d6e835149a8c7e7888aed36f7ca77ddfd57466a72505c2e86f",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 68401632,
"digest": "d55a50d61f3dbd34e68daf985c95773f6f66fa9b88364eefde03d2b009f2f4e9df7e7e72006347bb4ba0f65ff5190b4b6367fde01e79827e10534cd03fe720df",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

View File

@ -6,9 +6,9 @@
"filename": "mozmake.exe"
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 68287611,
"digest": "702b162f53970de096d1c7777f37eddd22251a910d523df4edcbead399d40bfb85b059b5745ec8d6e835149a8c7e7888aed36f7ca77ddfd57466a72505c2e86f",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 68401632,
"digest": "d55a50d61f3dbd34e68daf985c95773f6f66fa9b88364eefde03d2b009f2f4e9df7e7e72006347bb4ba0f65ff5190b4b6367fde01e79827e10534cd03fe720df",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

View File

@ -6,9 +6,9 @@
"filename": "mozmake.exe"
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 68287611,
"digest": "702b162f53970de096d1c7777f37eddd22251a910d523df4edcbead399d40bfb85b059b5745ec8d6e835149a8c7e7888aed36f7ca77ddfd57466a72505c2e86f",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 68401632,
"digest": "d55a50d61f3dbd34e68daf985c95773f6f66fa9b88364eefde03d2b009f2f4e9df7e7e72006347bb4ba0f65ff5190b4b6367fde01e79827e10534cd03fe720df",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

View File

@ -6,9 +6,9 @@
"filename": "mozmake.exe"
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 73762095,
"digest": "9241134ee89a0beca228d9867b7526621faf05560fe802bb6471cc19d5c42135933883e1abd934c8d6657a06c975e167a3712e80e01bf9aa443db6ebb03a8096",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 73942095,
"digest": "22be87ae8b6aaf1016dc4a24858caadda47dabc6f44b5730b15489727c900ff7c12e01df8d12aa1775f4b83a4fae73a1c12f69fd15b2ab7fe2603a08637f187d",
"algorithm": "sha512",
"visibility": "public",
"filename": "rustc.tar.bz2",

View File

@ -6,9 +6,9 @@
"filename": "mozmake.exe"
},
{
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
"size": 73762095,
"digest": "9241134ee89a0beca228d9867b7526621faf05560fe802bb6471cc19d5c42135933883e1abd934c8d6657a06c975e167a3712e80e01bf9aa443db6ebb03a8096",
"version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
"size": 73942095,
"digest": "22be87ae8b6aaf1016dc4a24858caadda47dabc6f44b5730b15489727c900ff7c12e01df8d12aa1775f4b83a4fae73a1c12f69fd15b2ab7fe2603a08637f187d",
"algorithm": "sha512",
"visibility": "public",
"filename": "rustc.tar.bz2",

View File

@ -17,6 +17,9 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
*/
this.FormAutofillHeuristics = {
VALID_FIELDS: [
"given-name",
"additional-name",
"family-name",
"organization",
"street-address",
"address-level2",

View File

@ -0,0 +1,280 @@
/* 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 {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
// Cu.import loads jsm files based on ISO-Latin-1 for now (see bug 530257).
// However, the references about name parts include multi-byte characters.
// Thus, we use |loadSubScript| to load the references instead.
const NAME_REFERENCES = "chrome://formautofill/content/nameReferences.js";
this.EXPORTED_SYMBOLS = ["FormAutofillNameUtils"];
// FormAutofillNameUtils is initially translated from
// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util.cc?rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
var FormAutofillNameUtils = {
// Will be loaded from NAME_REFERENCES.
NAME_PREFIXES: [],
NAME_SUFFIXES: [],
FAMILY_NAME_PREFIXES: [],
COMMON_CJK_MULTI_CHAR_SURNAMES: [],
KOREAN_MULTI_CHAR_SURNAMES: [],
// The whitespace definition based on
// https://cs.chromium.org/chromium/src/base/strings/string_util_constants.cc?l=9&rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
WHITESPACE: [
"\u0009", // CHARACTER TABULATION
"\u000A", // LINE FEED (LF)
"\u000B", // LINE TABULATION
"\u000C", // FORM FEED (FF)
"\u000D", // CARRIAGE RETURN (CR)
"\u0020", // SPACE
"\u0085", // NEXT LINE (NEL)
"\u00A0", // NO-BREAK SPACE
"\u1680", // OGHAM SPACE MARK
"\u2000", // EN QUAD
"\u2001", // EM QUAD
"\u2002", // EN SPACE
"\u2003", // EM SPACE
"\u2004", // THREE-PER-EM SPACE
"\u2005", // FOUR-PER-EM SPACE
"\u2006", // SIX-PER-EM SPACE
"\u2007", // FIGURE SPACE
"\u2008", // PUNCTUATION SPACE
"\u2009", // THIN SPACE
"\u200A", // HAIR SPACE
"\u2028", // LINE SEPARATOR
"\u2029", // PARAGRAPH SEPARATOR
"\u202F", // NARROW NO-BREAK SPACE
"\u205F", // MEDIUM MATHEMATICAL SPACE
"\u3000", // IDEOGRAPHIC SPACE
],
// The middle dot is used as a separator for foreign names in Japanese.
MIDDLE_DOT: [
"\u30FB", // KATAKANA MIDDLE DOT
"\u00B7", // A (common?) typo for "KATAKANA MIDDLE DOT"
],
// The Unicode range is based on Wiki:
// https://en.wikipedia.org/wiki/CJK_Unified_Ideographs
// https://en.wikipedia.org/wiki/Hangul
// https://en.wikipedia.org/wiki/Japanese_writing_system
CJK_RANGE: [
"\u1100-\u11FF", // Hangul Jamo
"\u3040-\u309F", // Hiragana
"\u30A0-\u30FF", // Katakana
"\u3105-\u312C", // Bopomofo
"\u3130-\u318F", // Hangul Compatibility Jamo
"\u31F0-\u31FF", // Katakana Phonetic Extensions
"\u3200-\u32FF", // Enclosed CJK Letters and Months
"\u3400-\u4DBF", // CJK unified ideographs Extension A
"\u4E00-\u9FFF", // CJK Unified Ideographs
"\uA960-\uA97F", // Hangul Jamo Extended-A
"\uAC00-\uD7AF", // Hangul Syllables
"\uD7B0-\uD7FF", // Hangul Jamo Extended-B
"\uFF00-\uFFEF", // Halfwidth and Fullwidth Forms
],
HANGUL_RANGE: [
"\u1100-\u11FF", // Hangul Jamo
"\u3130-\u318F", // Hangul Compatibility Jamo
"\uA960-\uA97F", // Hangul Jamo Extended-A
"\uAC00-\uD7AF", // Hangul Syllables
"\uD7B0-\uD7FF", // Hangul Jamo Extended-B
],
_dataLoaded: false,
// Returns true if |set| contains |token|, modulo a final period.
_containsString(set, token) {
let target = token.replace(/\.$/, "").toLowerCase();
return set.includes(target);
},
// Removes common name prefixes from |name_tokens|.
_stripPrefixes(nameTokens) {
for (let i in nameTokens) {
if (!this._containsString(this.NAME_PREFIXES, nameTokens[i])) {
return nameTokens.slice(i);
}
}
return [];
},
// Removes common name suffixes from |name_tokens|.
_stripSuffixes(nameTokens) {
for (let i = nameTokens.length - 1; i >= 0; i--) {
if (!this._containsString(this.NAME_SUFFIXES, nameTokens[i])) {
return nameTokens.slice(0, i + 1);
}
}
return [];
},
_isCJKName(name) {
// The name is considered to be a CJK name if it is only CJK characters,
// spaces, and "middle dot" separators, with at least one CJK character, and
// no more than 2 words.
//
// Chinese and Japanese names are usually spelled out using the Han
// characters (logographs), which constitute the "CJK Unified Ideographs"
// block in Unicode, also referred to as Unihan. Korean names are usually
// spelled out in the Korean alphabet (Hangul), although they do have a Han
// equivalent as well.
let previousWasCJK = false;
let wordCount = 0;
for (let c of name) {
let isMiddleDot = this.MIDDLE_DOT.includes(c);
let isCJK = !isMiddleDot && this.reCJK.test(c);
if (!isCJK && !isMiddleDot && !this.WHITESPACE.includes(c)) {
return false;
}
if (isCJK && !previousWasCJK) {
wordCount++;
}
previousWasCJK = isCJK;
}
return wordCount > 0 && wordCount < 3;
},
// Tries to split a Chinese, Japanese, or Korean name into its given name &
// surname parts. If splitting did not work for whatever reason, returns null.
_splitCJKName(nameTokens) {
// The convention for CJK languages is to put the surname (last name) first,
// and the given name (first name) second. In a continuous text, there is
// normally no space between the two parts of the name. When entering their
// name into a field, though, some people add a space to disambiguate. CJK
// names (almost) never have a middle name.
let reHangulName = new RegExp(
"^[" + this.HANGUL_RANGE.join("") + this.WHITESPACE.join("") + "]+$", "u");
let nameParts = {
given: "",
middle: "",
family: "",
};
if (nameTokens.length == 1) {
// There is no space between the surname and given name. Try to infer
// where to separate between the two. Most Chinese and Korean surnames
// have only one character, but there are a few that have 2. If the name
// does not start with a surname from a known list, default to one
// character.
let name = nameTokens[0];
let isKorean = reHangulName.test(name);
let surnameLength = 0;
// 4-character Korean names are more likely to be 2/2 than 1/3, so use
// the full list of Korean 2-char surnames. (instead of only the common
// ones)
let multiCharSurnames = (isKorean && name.length > 3) ?
this.KOREAN_MULTI_CHAR_SURNAMES :
this.COMMON_CJK_MULTI_CHAR_SURNAMES;
// Default to 1 character if the surname is not in the list.
surnameLength =
multiCharSurnames.some(surname => name.startsWith(surname)) ? 2 : 1;
nameParts.family = name.substr(0, surnameLength);
nameParts.given = name.substr(surnameLength);
} else if (nameTokens.length == 2) {
// The user entered a space between the two name parts. This makes our job
// easier. Family name first, given name second.
nameParts.family = nameTokens[0];
nameParts.given = nameTokens[1];
} else {
return null;
}
return nameParts;
},
init() {
if (this._dataLoaded) {
return;
}
let sandbox = {};
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
scriptLoader.loadSubScript(NAME_REFERENCES, sandbox, "utf-8");
Object.assign(this, sandbox.nameReferences);
this._dataLoaded = true;
this.reCJK = new RegExp("[" + this.CJK_RANGE.join("") + "]", "u");
},
splitName(name) {
let nameTokens = name.trim().split(/[ ,\u3000\u30FB\u00B7]+/);
let nameParts = {
given: "",
middle: "",
family: "",
};
nameTokens = this._stripPrefixes(nameTokens);
if (this._isCJKName(name)) {
let parts = this._splitCJKName(nameTokens);
if (parts) {
return parts;
}
}
// Don't assume "Ma" is a suffix in John Ma.
if (nameTokens.length > 2) {
nameTokens = this._stripSuffixes(nameTokens);
}
if (!nameTokens.length) {
// Bad things have happened; just assume the whole thing is a given name.
nameParts.given = name;
return nameParts;
}
// Only one token, assume given name.
if (nameTokens.length == 1) {
nameParts.given = nameTokens[0];
return nameParts;
}
// 2 or more tokens. Grab the family, which is the last word plus any
// recognizable family prefixes.
let familyTokens = [nameTokens.pop()];
while (nameTokens.length) {
let lastToken = nameTokens[nameTokens.length - 1];
if (!this._containsString(this.FAMILY_NAME_PREFIXES, lastToken)) {
break;
}
familyTokens.unshift(lastToken);
nameTokens.pop();
}
nameParts.family = familyTokens.join(" ");
// Take the last remaining token as the middle name (if there are at least 2
// tokens).
if (nameTokens.length >= 2) {
nameParts.middle = nameTokens.pop();
}
// Remainder is given name.
nameParts.given = nameTokens.join(" ");
return nameParts;
},
joinNameParts({given, middle, family}) {
if (this._isCJKName(given) && this._isCJKName(family) && middle == "") {
return family + given;
}
return [given, middle, family].filter(part => part && part.length).join(" ");
},
};
FormAutofillNameUtils.init();

View File

@ -90,18 +90,28 @@ ProfileAutoCompleteResult.prototype = {
/* TODO: Since "name" is a special case here, so the secondary "name" label
will be refined when the handling rule for "name" is ready.
*/
const possibleNameFields = ["given-name", "additional-name", "family-name"];
const possibleNameFields = [
"name",
"given-name",
"additional-name",
"family-name",
];
focusedFieldName = possibleNameFields.includes(focusedFieldName) ?
"name" : focusedFieldName;
if (!profile.name) {
profile.name = FormAutofillUtils.generateFullName(profile["given-name"],
profile["family-name"],
profile["additional-name"]);
// Clones the profile to avoid exposing our modification.
let clonedProfile = Object.assign({}, profile);
if (!clonedProfile.name) {
clonedProfile.name =
FormAutofillUtils.generateFullName(clonedProfile["given-name"],
clonedProfile["family-name"],
clonedProfile["additional-name"]);
}
const secondaryLabelOrder = [
"street-address", // Street address
"name", // Full name if needed
"name", // Full name
"address-level2", // City/Town
"organization", // Company or organization name
"address-level1", // Province/State (Standardized code if possible)
@ -112,10 +122,20 @@ ProfileAutoCompleteResult.prototype = {
];
for (const currentFieldName of secondaryLabelOrder) {
if (focusedFieldName != currentFieldName &&
allFieldNames.includes(currentFieldName) &&
profile[currentFieldName]) {
return profile[currentFieldName];
if (focusedFieldName == currentFieldName ||
!clonedProfile[currentFieldName]) {
continue;
}
let matching;
if (currentFieldName == "name") {
matching = allFieldNames.some(fieldName => possibleNameFields.includes(fieldName));
} else {
matching = allFieldNames.includes(currentFieldName);
}
if (matching) {
return clonedProfile[currentFieldName];
}
}

View File

@ -15,6 +15,9 @@
* guid, // 12 character...
*
* // profile
* given-name,
* additional-name,
* family-name,
* organization, // Company
* street-address, // (Multiline)
* address-level2, // City/Town
@ -51,6 +54,8 @@ Cu.import("resource://formautofill/FormAutofillUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "JSONFile",
"resource://gre/modules/JSONFile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillNameUtils",
"resource://formautofill/FormAutofillNameUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
"@mozilla.org/uuid-generator;1",
@ -63,6 +68,9 @@ const SCHEMA_VERSION = 1;
// Name-related fields will be handled in follow-up bugs due to the complexity.
const VALID_FIELDS = [
"given-name",
"additional-name",
"family-name",
"organization",
"street-address",
"address-level2",

View File

@ -0,0 +1,144 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* exported nameReferences */
"use strict";
// The data below is initially copied from
// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util.cc?rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
var nameReferences = {
NAME_PREFIXES: [
"1lt",
"1st",
"2lt",
"2nd",
"3rd",
"admiral",
"capt",
"captain",
"col",
"cpt",
"dr",
"gen",
"general",
"lcdr",
"lt",
"ltc",
"ltg",
"ltjg",
"maj",
"major",
"mg",
"mr",
"mrs",
"ms",
"pastor",
"prof",
"rep",
"reverend",
"rev",
"sen",
"st",
],
NAME_SUFFIXES: [
"b.a",
"ba",
"d.d.s",
"dds",
"i",
"ii",
"iii",
"iv",
"ix",
"jr",
"m.a",
"m.d",
"ma",
"md",
"ms",
"ph.d",
"phd",
"sr",
"v",
"vi",
"vii",
"viii",
"x",
],
FAMILY_NAME_PREFIXES: [
"d'",
"de",
"del",
"der",
"di",
"la",
"le",
"mc",
"san",
"st",
"ter",
"van",
"von",
],
// The common and non-ambiguous CJK surnames (last names) that have more than
// one character.
COMMON_CJK_MULTI_CHAR_SURNAMES: [
// Korean, taken from the list of surnames:
// https://ko.wikipedia.org/wiki/%ED%95%9C%EA%B5%AD%EC%9D%98_%EC%84%B1%EC%94%A8_%EB%AA%A9%EB%A1%9D
"남궁",
"사공",
"서문",
"선우",
"제갈",
"황보",
"독고",
"망절",
// Chinese, taken from the top 10 Chinese 2-character surnames:
// https://zh.wikipedia.org/wiki/%E8%A4%87%E5%A7%93#.E5.B8.B8.E8.A6.8B.E7.9A.84.E8.A4.87.E5.A7.93
// Simplified Chinese (mostly mainland China)
"欧阳",
"令狐",
"皇甫",
"上官",
"司徒",
"诸葛",
"司马",
"宇文",
"呼延",
"端木",
// Traditional Chinese (mostly Taiwan)
"張簡",
"歐陽",
"諸葛",
"申屠",
"尉遲",
"司馬",
"軒轅",
"夏侯",
],
// All Korean surnames that have more than one character, even the
// rare/ambiguous ones.
KOREAN_MULTI_CHAR_SURNAMES: [
"강전",
"남궁",
"독고",
"동방",
"망절",
"사공",
"서문",
"선우",
"소봉",
"어금",
"장곡",
"제갈",
"황목",
"황보",
],
};

View File

@ -25,6 +25,8 @@ const TESTCASES = [
<input id="email" autocomplete="email">
<input id="tel" autocomplete="tel"></form>`,
fieldDetails: [
{"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
{"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
@ -42,6 +44,8 @@ const TESTCASES = [
<input id='email' autocomplete="shipping email">
<input id="tel" autocomplete="shipping tel"></form>`,
fieldDetails: [
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country"},
@ -59,6 +63,8 @@ const TESTCASES = [
<input id='email' autocomplete="shipping email">
<input id="tel" autocomplete="shipping tel"></form>`,
fieldDetails: [
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country"},

View File

@ -0,0 +1,76 @@
/**
* Tests the "isCJKName" function of FormAutofillNameUtils object.
*/
"use strict";
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://formautofill/FormAutofillNameUtils.jsm");
// Test cases is initially copied from
// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util_unittest.cc
const TESTCASES = [
{
// Non-CJK language with only ASCII characters.
fullName: "Homer Jay Simpson",
expectedResult: false,
},
{
// Non-CJK language with some ASCII characters.
fullName: "Éloïse Paré",
expectedResult: false,
},
{
// Non-CJK language with no ASCII characters.
fullName: "Σωκράτης",
expectedResult: false,
},
{
// (Simplified) Chinese name, Unihan.
fullName: "刘翔",
expectedResult: true,
},
{
// (Simplified) Chinese name, Unihan, with an ASCII space.
fullName: "成 龙",
expectedResult: true,
},
{
// Korean name, Hangul.
fullName: "송지효",
expectedResult: true,
},
{
// Korean name, Hangul, with an 'IDEOGRAPHIC SPACE' (U+3000).
fullName: "김 종국",
expectedResult: true,
},
{
// Japanese name, Unihan.
fullName: "山田貴洋",
expectedResult: true,
},
{
// Japanese name, Katakana, with a 'KATAKANA MIDDLE DOT' (U+30FB).
fullName: "ビル・ゲイツ",
expectedResult: true,
},
{
// Japanese name, Katakana, with a 'MIDDLE DOT' (U+00B7) (likely a typo).
fullName: "ビル·ゲイツ",
expectedResult: true,
},
{
// CJK names don't have a middle name, so a 3-part name is bogus to us.
fullName: "반 기 문",
expectedResult: false,
},
];
add_task(function* test_isCJKName() {
TESTCASES.forEach(testcase => {
do_print("Starting testcase: " + testcase.fullName);
let result = FormAutofillNameUtils._isCJKName(testcase.fullName);
do_check_eq(result, testcase.expectedResult);
});
});

View File

@ -4,8 +4,11 @@ Cu.import("resource://formautofill/FormAutofillContent.jsm");
const TESTCASES = [
{
description: "Form containing 5 fields with autocomplete attribute.",
description: "Form containing 8 fields with autocomplete attribute.",
document: `<form>
<input id="given-name" autocomplete="given-name">
<input id="additional-name" autocomplete="additional-name">
<input id="family-name" autocomplete="family-name">
<input id="street-addr" autocomplete="street-address">
<input id="city" autocomplete="address-level2">
<input id="country" autocomplete="country">
@ -15,6 +18,9 @@ const TESTCASES = [
<input id="without-autocomplete-2">
</form>`,
expectedResult: [
"given-name",
"additional-name",
"family-name",
"street-addr",
"city",
"country",

View File

@ -0,0 +1,285 @@
/**
* Tests FormAutofillNameUtils object.
*/
"use strict";
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://formautofill/FormAutofillNameUtils.jsm");
// Test cases initially copied from
// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util_unittest.cc
const TESTCASES = [
{
description: "Full name including given, middle and family names",
fullName: "Homer Jay Simpson",
nameParts: {
given: "Homer",
middle: "Jay",
family: "Simpson",
},
},
{
description: "No middle name",
fullName: "Moe Szyslak",
nameParts: {
given: "Moe",
middle: "",
family: "Szyslak",
},
},
{
description: "Common name prefixes removed",
fullName: "Reverend Timothy Lovejoy",
nameParts: {
given: "Timothy",
middle: "",
family: "Lovejoy",
},
expectedFullName: "Timothy Lovejoy",
},
{
description: "Common name suffixes removed",
fullName: "John Frink Phd",
nameParts: {
given: "John",
middle: "",
family: "Frink",
},
expectedFullName: "John Frink",
},
{
description: "Exception to the name suffix removal",
fullName: "John Ma",
nameParts: {
given: "John",
middle: "",
family: "Ma",
},
},
{
description: "Common family name prefixes not considered a middle name",
fullName: "Milhouse Van Houten",
nameParts: {
given: "Milhouse",
middle: "",
family: "Van Houten",
},
},
// CJK names have reverse order (surname goes first, given name goes second).
{
description: "Chinese name, Unihan",
fullName: "孫 德明",
nameParts: {
given: "德明",
middle: "",
family: "孫",
},
expectedFullName: "孫德明",
},
{
description: "Chinese name, Unihan, \"IDEOGRAPHIC SPACE\"",
fullName: "孫 德明",
nameParts: {
given: "德明",
middle: "",
family: "孫",
},
expectedFullName: "孫德明",
},
{
description: "Korean name, Hangul",
fullName: "홍 길동",
nameParts: {
given: "길동",
middle: "",
family: "홍",
},
expectedFullName: "홍길동",
},
{
description: "Japanese name, Unihan",
fullName: "山田 貴洋",
nameParts: {
given: "貴洋",
middle: "",
family: "山田",
},
expectedFullName: "山田貴洋",
},
// In Japanese, foreign names use 'KATAKANA MIDDLE DOT' (U+30FB) as a
// separator. There is no consensus for the ordering. For now, we use the same
// ordering as regular Japanese names ("last・first").
{
description: "Foreign name in Japanese, Katakana",
fullName: "ゲイツ・ビル",
nameParts: {
given: "ビル",
middle: "",
family: "ゲイツ",
},
expectedFullName: "ゲイツビル",
},
// 'KATAKANA MIDDLE DOT' is occasionally typoed as 'MIDDLE DOT' (U+00B7).
{
description: "Foreign name in Japanese, Katakana",
fullName: "ゲイツ·ビル",
nameParts: {
given: "ビル",
middle: "",
family: "ゲイツ",
},
expectedFullName: "ゲイツビル",
},
// CJK names don't usually have a space in the middle, but most of the time,
// the surname is only one character (in Chinese & Korean).
{
description: "Korean name, Hangul",
fullName: "최성훈",
nameParts: {
given: "성훈",
middle: "",
family: "최",
},
},
{
description: "(Simplified) Chinese name, Unihan",
fullName: "刘翔",
nameParts: {
given: "翔",
middle: "",
family: "刘",
},
},
{
description: "(Traditional) Chinese name, Unihan",
fullName: "劉翔",
nameParts: {
given: "翔",
middle: "",
family: "劉",
},
},
// There are a few exceptions. Occasionally, the surname has two characters.
{
description: "Korean name, Hangul",
fullName: "남궁도",
nameParts: {
given: "도",
middle: "",
family: "남궁",
},
},
{
description: "Korean name, Hangul",
fullName: "황보혜정",
nameParts: {
given: "혜정",
middle: "",
family: "황보",
},
},
{
description: "(Traditional) Chinese name, Unihan",
fullName: "歐陽靖",
nameParts: {
given: "靖",
middle: "",
family: "歐陽",
},
},
// In Korean, some 2-character surnames are rare/ambiguous, like "강전": "강"
// is a common surname, and "전" can be part of a given name. In those cases,
// we assume it's 1/2 for 3-character names, or 2/2 for 4-character names.
{
description: "Korean name, Hangul",
fullName: "강전희",
nameParts: {
given: "전희",
middle: "",
family: "강",
},
},
{
description: "Korean name, Hangul",
fullName: "황목치승",
nameParts: {
given: "치승",
middle: "",
family: "황목",
},
},
// It occasionally happens that a full name is 2 characters, 1/1.
{
description: "Korean name, Hangul",
fullName: "이도",
nameParts: {
given: "도",
middle: "",
family: "이",
},
},
{
description: "Korean name, Hangul",
fullName: "孫文",
nameParts: {
given: "文",
middle: "",
family: "孫",
},
},
// These are no CJK names for us, they're just bogus.
{
description: "Bogus",
fullName: "Homer シンプソン",
nameParts: {
given: "Homer",
middle: "",
family: "シンプソン",
},
},
{
description: "Bogus",
fullName: "ホーマー Simpson",
nameParts: {
given: "ホーマー",
middle: "",
family: "Simpson",
},
},
{
description: "CJK has a middle-name, too unusual",
fullName: "반 기 문",
nameParts: {
given: "반",
middle: "기",
family: "문",
},
},
];
add_task(function* test_splitName() {
TESTCASES.forEach(testcase => {
if (testcase.fullName) {
do_print("Starting testcase: " + testcase.description);
let nameParts = FormAutofillNameUtils.splitName(testcase.fullName);
Assert.deepEqual(nameParts, testcase.nameParts);
}
});
});
add_task(function* test_joinName() {
TESTCASES.forEach(testcase => {
do_print("Starting testcase: " + testcase.description);
let name = FormAutofillNameUtils.joinNameParts(testcase.nameParts);
do_check_eq(name, testcase.expectedFullName || testcase.fullName);
});
});

View File

@ -4,11 +4,15 @@ Cu.import("resource://formautofill/ProfileAutoCompleteResult.jsm");
let matchingProfiles = [{
guid: "test-guid-1",
"given-name": "Timothy",
"family-name": "Berners-Lee",
organization: "Sesame Street",
"street-address": "123 Sesame Street.",
tel: "1-345-345-3456.",
}, {
guid: "test-guid-2",
"given-name": "John",
"family-name": "Doe",
organization: "Mozilla",
"street-address": "331 E. Evelyn Avenue",
tel: "1-650-903-0800",
@ -19,7 +23,7 @@ let matchingProfiles = [{
tel: "1-000-000-0000",
}];
let allFieldNames = ["street-address", "organization", "tel"];
let allFieldNames = ["given-name", "family-name", "street-address", "organization", "tel"];
let testCases = [{
description: "Focus on an `organization` field",
@ -106,7 +110,7 @@ let testCases = [{
comment: JSON.stringify(matchingProfiles[0]),
label: JSON.stringify({
primary: "123 Sesame Street.",
secondary: "Sesame Street",
secondary: "Timothy Berners-Lee",
}),
image: "",
}, {
@ -115,7 +119,7 @@ let testCases = [{
comment: JSON.stringify(matchingProfiles[1]),
label: JSON.stringify({
primary: "331 E. Evelyn Avenue",
secondary: "Mozilla",
secondary: "John Doe",
}),
image: "",
}, {

View File

@ -10,6 +10,9 @@ Cu.import("resource://formautofill/ProfileStorage.jsm");
const TEST_STORE_FILE_NAME = "test-profile.json";
const TEST_PROFILE_1 = {
"given-name": "Timothy",
"additional-name": "John",
"family-name": "Berners-Lee",
organization: "World Wide Web Consortium",
"street-address": "32 Vassar Street\nMIT Room 32-G524",
"address-level2": "Cambridge",

View File

@ -8,9 +8,10 @@ support-files =
[test_enabledStatus.js]
[test_findLabelElements.js]
[test_getFormInputDetails.js]
[test_isCJKName.js]
[test_markAsAutofillField.js]
[test_nameUtils.js]
[test_onFormSubmitted.js]
[test_profileAutocompleteResult.js]
[test_profileStorage.js]
[test_savedFieldNames.js]

View File

@ -107,14 +107,14 @@ def rust_triple_alias(host_or_target):
"""
assert host_or_target in (host, target)
@depends(rustc, host_or_target, when=rust_compiler)
@depends(rustc, host_or_target, building_with_gcc, when=rust_compiler)
@imports('os')
@imports('subprocess')
@imports(_from='mozbuild.configure.util', _import='LineIO')
@imports(_from='mozbuild.shellutil', _import='quote')
@imports(_from='tempfile', _import='mkstemp')
@imports(_from='textwrap', _import='dedent')
def rust_target(rustc, host_or_target):
def rust_target(rustc, host_or_target, building_with_gcc):
# Rust's --target options are similar to, but not exactly the same
# as, the autoconf-derived targets we use. An example would be that
# Rust uses distinct target triples for targetting the GNU C++ ABI
@ -130,6 +130,9 @@ def rust_triple_alias(host_or_target):
# Avoid having to write out os+kernel for all the platforms where
# they don't differ.
os_or_kernel = host_or_target.kernel if host_or_target.kernel == 'Linux' and host_or_target.os != 'Android' else host_or_target.os
# If we are targetting Windows but the compiler is gcc, we need to
# use a different rustc target
os_or_kernel = 'WINNT-MINGW' if building_with_gcc and host_or_target.kernel == 'WINNT' else os_or_kernel
rustc_target = {
# DragonFly
('x86_64', 'DragonFly'): 'x86_64-unknown-dragonfly',
@ -163,10 +166,10 @@ def rust_triple_alias(host_or_target):
('arm', 'Android'): 'armv7-linux-androideabi',
('x86', 'Android'): 'i686-linux-android',
# Windows
# XXX better detection of CXX needed here, to figure out whether
# we need i686-pc-windows-gnu instead, since mingw32 builds work.
('x86', 'WINNT'): 'i686-pc-windows-msvc',
('x86_64', 'WINNT'): 'x86_64-pc-windows-msvc',
('x86', 'WINNT-MINGW'): 'i686-pc-windows-gnu',
('x86_64', 'WINNT-MINGW'): 'x86_64-pc-windows-gnu',
}.get((host_or_target.cpu, os_or_kernel), None)
if rustc_target is None:

View File

@ -17,8 +17,10 @@ AR = substs['AR']
AR_EXTRACT = substs['AR_EXTRACT'].replace('$(AR)', AR)
DLL_PREFIX = substs['DLL_PREFIX']
LIB_PREFIX = substs['LIB_PREFIX']
RUST_LIB_PREFIX = substs['RUST_LIB_PREFIX']
OBJ_SUFFIX = normalize_suffix(substs['OBJ_SUFFIX'])
LIB_SUFFIX = normalize_suffix(substs['LIB_SUFFIX'])
RUST_LIB_SUFFIX = normalize_suffix(substs['RUST_LIB_SUFFIX'])
DLL_SUFFIX = normalize_suffix(substs['DLL_SUFFIX'])
IMPORT_LIB_SUFFIX = normalize_suffix(substs['IMPORT_LIB_SUFFIX'])
LIBS_DESC_SUFFIX = normalize_suffix(substs['LIBS_DESC_SUFFIX'])

View File

@ -94,6 +94,7 @@ showbuild:
ACDEFINES \
BIN_SUFFIX \
LIB_SUFFIX \
RUST_LIB_SUFFIX \
DLL_SUFFIX \
IMPORT_LIB_SUFFIX \
INSTALL \

View File

@ -33,7 +33,7 @@ process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py
# TODO we should use py_action, but that would require extra directories to be
# in the virtualenv.
%.xpt:
@echo "$(@F)"
$(REPORT_BUILD)
$(PYTHON_PATH) $(PLY_INCLUDE) -I$(topsrcdir)/xpcom/idl-parser -I$(DEPTH)/xpcom/idl-parser/xpidl \
$(process_py) --cache-dir $(DEPTH)/xpcom/idl-parser/xpidl --depsdir $(idl_deps_dir) \
$(dist_idl_dir) $(dist_include_dir) $(@D) $(libxul_sdk_includes) \

View File

@ -11,7 +11,10 @@ const {
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { NetMonitorController } = require("../netmonitor-controller");
const { getFormattedSize } = require("../utils/format-utils");
const {
getFormattedIPAndPort,
getFormattedSize,
} = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
const {
getHeadersURL,
@ -162,7 +165,7 @@ const HeadersPanel = createClass({
let summaryAddress = remoteAddress ?
this.renderSummary(SUMMARY_ADDRESS,
remotePort ? `${remoteAddress}:${remotePort}` : remoteAddress) : null;
getFormattedIPAndPort(remoteAddress, remotePort)) : null;
let summaryStatus;

View File

@ -9,6 +9,7 @@ const {
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { getFormattedIPAndPort } = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
const { propertiesEqual } = require("../utils/request-utils");
@ -34,10 +35,12 @@ const RequestListColumnDomain = createClass({
render() {
let { item, onSecurityIconClick } = this.props;
let { remoteAddress, securityState, urlDetails: { host, isLocal } } = item;
let { remoteAddress, remotePort, securityState,
urlDetails: { host, isLocal } } = item;
let iconClassList = ["requests-security-state-icon"];
let iconTitle;
let title = host + (remoteAddress ? ` (${remoteAddress})` : "");
let title = host + (remoteAddress ?
` (${getFormattedIPAndPort(remoteAddress, remotePort)})` : "");
if (isLocal) {
iconClassList.push("security-state-local");

View File

@ -9,6 +9,7 @@ const {
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { getFormattedIPAndPort } = require("../utils/format-utils");
const { div } = DOM;
@ -25,7 +26,8 @@ const RequestListColumnRemoteIP = createClass({
render() {
let { remoteAddress, remotePort } = this.props.item;
let remoteIP = remoteAddress ? `${remoteAddress}:${remotePort}` : "unknown";
let remoteIP = remoteAddress ?
getFormattedIPAndPort(remoteAddress, remotePort) : "unknown";
return (
div({ className: "requests-list-column requests-list-remoteip", title: remoteIP },

View File

@ -31,6 +31,7 @@
"use strict";
const { HEADERS } = require("../constants");
const { getFormattedIPAndPort } = require("./format-utils");
const HEADER_FILTERS = HEADERS
.filter(h => h.canFilter)
.map(h => h.filterKey || h.name);
@ -132,7 +133,8 @@ function isFlagFilterMatch(item, { type, value, negative }) {
match = item.urlDetails.host.toLowerCase().includes(value);
break;
case "remote-ip":
match = `${item.remoteAddress}:${item.remotePort}`.toLowerCase().includes(value);
match = getFormattedIPAndPort(item.remoteAddress, item.remotePort)
.toLowerCase().includes(value);
break;
case "has-response-header":
if (typeof item.responseHeaders === "object") {

View File

@ -77,7 +77,22 @@ function getFormattedTime(ms) {
return L10N.getFormatStr("networkMenu.minute", getTimeWithDecimals(min));
}
/**
* Formats IP (v4 and v6) and port
*
* @param {string} ip - IP address
* @param {string} port
* @return {string} the formatted IP + port
*/
function getFormattedIPAndPort(ip, port) {
if (!port) {
return ip;
}
return ip.match(/:+/) ? `[${ip}]:${port}` : `${ip}:${port}`;
}
module.exports = {
getFormattedIPAndPort,
getFormattedSize,
getFormattedTime,
getSizeWithDecimals,

View File

@ -14,6 +14,9 @@ Services.scriptloader.loadSubScript(
this);
const { EVENTS } = require("devtools/client/netmonitor/src/constants");
const {
getFormattedIPAndPort
} = require("devtools/client/netmonitor/src/utils/format-utils");
const {
decodeUnicodeUrl,
getUrlBaseName,
@ -376,9 +379,10 @@ function verifyRequestItemTarget(document, requestList, requestItem, method,
let unicodeUrl = decodeUnicodeUrl(url);
let name = getUrlBaseName(url);
let query = getUrlQuery(url);
let hostPort = getUrlHost(url);
let host = getUrlHost(url);
let { httpVersion = "", remoteAddress, remotePort } = requestItem;
let remoteIP = remoteAddress ? `${remoteAddress}:${remotePort}` : "unknown";
let formattedIPPort = getFormattedIPAndPort(remoteAddress, remotePort);
let remoteIP = remoteAddress ? `${formattedIPPort}` : "unknown";
if (fuzzyUrl) {
ok(requestItem.method.startsWith(method), "The attached method is correct.");
@ -411,9 +415,9 @@ function verifyRequestItemTarget(document, requestList, requestItem, method,
httpVersion, "The tooltip protocol is correct.");
is(target.querySelector(".requests-list-domain").textContent,
hostPort, "The displayed domain is correct.");
host, "The displayed domain is correct.");
let domainTooltip = hostPort + (remoteAddress ? " (" + remoteAddress + ")" : "");
let domainTooltip = host + (remoteAddress ? " (" + formattedIPPort + ")" : "");
is(target.querySelector(".requests-list-domain").getAttribute("title"),
domainTooltip, "The tooltip domain is correct.");

View File

@ -954,20 +954,6 @@ EffectCompositor::PreTraverse()
return PreTraverseInSubtree(nullptr);
}
static bool
IsFlattenedTreeDescendantOf(nsINode* aPossibleDescendant,
nsINode* aPossibleAncestor)
{
do {
if (aPossibleDescendant == aPossibleAncestor) {
return true;
}
aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
} while (aPossibleDescendant);
return false;
}
bool
EffectCompositor::PreTraverseInSubtree(Element* aRoot)
{
@ -989,7 +975,9 @@ EffectCompositor::PreTraverseInSubtree(Element* aRoot)
// Ignore restyles that aren't in the flattened tree subtree rooted at
// aRoot.
if (aRoot && !IsFlattenedTreeDescendantOf(target.mElement, aRoot)) {
if (aRoot &&
!nsContentUtils::ContentIsFlattenedTreeDescendantOf(target.mElement,
aRoot)) {
continue;
}

View File

@ -19,6 +19,7 @@
#include "mozilla/LookAndFeel.h" // For LookAndFeel::GetInt
#include "mozilla/KeyframeUtils.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoComputedValuesWithParent.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TypeTraits.h"
#include "Layers.h" // For Layer

View File

@ -43,6 +43,7 @@ class AnimValuesStyleRule;
enum class CSSPseudoElementType : uint8_t;
class ErrorResult;
struct AnimationRule;
struct ServoComputedValuesWithParent;
struct TimingParams;
namespace dom {
@ -104,13 +105,6 @@ struct AnimationProperty
}
};
struct ServoComputedValuesWithParent
{
const ServoComputedValues* mCurrentStyle;
const ServoComputedValues* mParentStyle;
explicit operator bool() const { return true; }
};
struct ElementPropertyTransition;
namespace dom {

View File

@ -11,6 +11,7 @@
#include "mozilla/RangedArray.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoBindingTypes.h"
#include "mozilla/ServoComputedValuesWithParent.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/TimingParams.h"
#include "mozilla/dom/BaseKeyframeTypesBinding.h" // For FastBaseKeyframe etc.

View File

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_ServoComputedValuesWithParent_h
#define mozilla_ServoComputedValuesWithParent_h
#include "mozilla/ServoBindingTypes.h"
namespace mozilla {
/**
* A tuple of a set of computed values along with the computed values for the
* corresponding element's parent.
*/
struct ServoComputedValuesWithParent
{
const ServoComputedValues* mCurrentStyle;
const ServoComputedValues* mParentStyle;
explicit operator bool() const { return true; }
};
} // namespace mozilla
#endif // mozilla_ServoComputedValuesWithParent_h

View File

@ -38,6 +38,7 @@ EXPORTS.mozilla += [
'KeyframeUtils.h',
'PendingAnimationTracker.h',
'PseudoElementHashEntry.h',
'ServoComputedValuesWithParent.h',
'TimingParams.h',
]

View File

@ -7,6 +7,7 @@
#o_0:before {
animation: anim 10s;
content: "";
}
</style>
<meta charset="UTF-8">

View File

@ -494,52 +494,6 @@ AllChildrenIterator::GetPreviousChild()
return nullptr;
}
static bool
IsNativeAnonymousImplementationOfPseudoElement(nsIContent* aContent)
{
// First, we need a frame. This leads to the tricky issue of what we can
// infer if the frame is null.
//
// Unlike regular nodes, native anonymous content (NAC) gets created during
// frame construction, which happens after the main style traversal. This
// means that we have to manually resolve style for those nodes shortly after
// they're created, either by (a) invoking ResolvePseudoElementStyle (for PE
// NAC), or (b) handing the subtree off to Servo for a mini-traversal (for
// non-PE NAC). We have assertions in nsCSSFrameConstructor that we don't do
// both.
//
// Once that happens, the NAC has a frame. So if we have no frame here,
// we're either not NAC, or in the process of doing (b). Either way, this
// isn't a PE.
nsIFrame* f = aContent->GetPrimaryFrame();
if (!f) {
return false;
}
// Get the pseudo type.
CSSPseudoElementType pseudoType = f->StyleContext()->GetPseudoType();
// In general nodes never get anonymous box style. However, there are a few
// special cases:
//
// * We somewhat-confusingly give text nodes a style context tagged with
// ":-moz-text", so we need to check for the anonymous box case here.
// * The primary frame for table elements is an anonymous box that inherits
// from the table's style.
if (pseudoType == CSSPseudoElementType::InheritingAnonBox) {
MOZ_ASSERT(f->StyleContext()->GetPseudo() == nsCSSAnonBoxes::mozText ||
f->StyleContext()->GetPseudo() == nsCSSAnonBoxes::tableWrapper);
return false;
}
MOZ_ASSERT(pseudoType != CSSPseudoElementType::NonInheritingAnonBox);
// Finally check the actual pseudo type.
bool isImpl = pseudoType != CSSPseudoElementType::NotPseudo;
MOZ_ASSERT_IF(isImpl, aContent->IsRootOfNativeAnonymousSubtree());
return isImpl;
}
/* static */ bool
StyleChildrenIterator::IsNeeded(const Element* aElement)
{
@ -558,6 +512,16 @@ StyleChildrenIterator::IsNeeded(const Element* aElement)
}
}
// If the node has a ::before or ::after pseudo, return true, because we want
// to visit those.
//
// TODO(emilio): Make this fast adding a bit? or, perhaps just using
// ProbePseudoElementStyle? It should be quite fast in Stylo.
if (aElement->GetProperty(nsGkAtoms::beforePseudoProperty) ||
aElement->GetProperty(nsGkAtoms::afterPseudoProperty)) {
return true;
}
// If the node has native anonymous content, return true.
nsIAnonymousContentCreator* ac = do_QueryFrame(aElement->GetPrimaryFrame());
if (ac) {
@ -571,18 +535,7 @@ StyleChildrenIterator::IsNeeded(const Element* aElement)
nsIContent*
StyleChildrenIterator::GetNextChild()
{
while (nsIContent* child = AllChildrenIterator::GetNextChild()) {
if (IsNativeAnonymousImplementationOfPseudoElement(child)) {
// Skip any native-anonymous children that are used to implement pseudo-
// elements. These match pseudo-element selectors instead of being
// considered a child of their host, and thus the style system needs to
// handle them separately.
} else {
return child;
}
}
return nullptr;
return AllChildrenIterator::GetNextChild();
}
} // namespace dom

View File

@ -255,12 +255,14 @@ private:
/**
* StyleChildrenIterator traverses the children of the element from the
* perspective of the style system, particularly the children we need to traverse
* during restyle. This is identical to AllChildrenIterator with
* (eAllChildren | eSkipDocumentLevelNativeAnonymousContent), _except_ that we
* detect and skip any native anonymous children that are used to implement
* pseudo-elements (since the style system needs to cascade those using
* different algorithms).
* perspective of the style system, particularly the children we need to
* traverse during restyle.
*
* At present, this is identical to AllChildrenIterator with
* (eAllChildren | eSkipDocumentLevelNativeAnonymousContent). We used to have
* detect and skip any native anonymous children that are used to implement some
* special magic in here that went away, but we keep the separate class so
* we can reintroduce special magic back if needed.
*
* Note: it assumes that no mutation of the DOM or frame tree takes place during
* iteration, and will break horribly if that is not true.

View File

@ -166,6 +166,13 @@ nsIContent::GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const
if (aType == eForStyle &&
IsRootOfNativeAnonymousSubtree() &&
OwnerDoc()->GetRootElement() == parent) {
// First, check if this is generated ::before/::after content for the root.
// If it is, we know what to do.
if (IsGeneratedContentContainerForBefore() ||
IsGeneratedContentContainerForAfter()) {
return parent;
}
// When getting the flattened tree parent for style, we return null
// for any "document level" native anonymous content subtree root.
// This is NAC generated by an ancestor frame of the document element's

View File

@ -2399,6 +2399,24 @@ nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
return false;
}
// static
bool
nsContentUtils::ContentIsFlattenedTreeDescendantOf(
const nsINode* aPossibleDescendant,
const nsINode* aPossibleAncestor)
{
NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
do {
if (aPossibleDescendant == aPossibleAncestor) {
return true;
}
aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
} while (aPossibleDescendant);
return false;
}
// static
nsresult

View File

@ -325,6 +325,16 @@ public:
static bool ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
nsINode* aPossibleAncestor);
/**
* As with ContentIsCrossDocDescendantOf but crosses shadow boundaries but not
* cross document boundaries.
*
* @see nsINode::GetFlattenedTreeParentNode()
*/
static bool
ContentIsFlattenedTreeDescendantOf(const nsINode* aPossibleDescendant,
const nsINode* aPossibleAncestor);
/*
* This method fills the |aArray| with all ancestor nodes of |aNode|
* including |aNode| at the zero index.

View File

@ -5783,11 +5783,6 @@ nsGlobalWindow::GetOuterSize(CallerType aCallerType, ErrorResult& aError)
return nsIntSize(0, 0);
}
nsGlobalWindow* rootWindow = nsGlobalWindow::Cast(GetPrivateRoot());
if (rootWindow) {
rootWindow->FlushPendingNotifications(FlushType::Layout);
}
nsIntSize sizeDevPixels;
aError = treeOwnerAsWin->GetSize(&sizeDevPixels.width, &sizeDevPixels.height);
if (aError.Failed()) {

View File

@ -868,10 +868,6 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvas
if ((aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL1 ||
aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL2) &&
aCropRect.isSome()) {
// The _surface_ must be a DataSourceSurface.
MOZ_ASSERT(surface->IsDataSourceSurface(),
"The snapshot SourceSurface from WebGL rendering contest is not \
DataSourceSurface.");
RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
croppedSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect);
cropRect.MoveTo(0, 0);

View File

@ -2654,7 +2654,7 @@ ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent)
LayoutDeviceIntPoint eventLoc =
aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset();
nsIntRect docFrameRect = docFrame->GetScreenRect(); // Returns CSS pixels
CSSIntRect docFrameRect = docFrame->GetScreenRect();
CSSIntPoint eventLocCSS(
mPresContext->DevPixelsToIntCSSPixels(eventLoc.x) - docFrameRect.x,
mPresContext->DevPixelsToIntCSSPixels(eventLoc.y) - docFrameRect.y);

View File

@ -234,8 +234,10 @@ WheelTransaction::OnEvent(WidgetEvent* aEvent)
if (mouseEvent->IsReal()) {
// If the cursor is moving to be outside the frame,
// terminate the scrollwheel transaction.
nsIntPoint pt = GetScreenPoint(mouseEvent);
nsIntRect r = sTargetFrame->GetScreenRect();
LayoutDeviceIntPoint pt = GetScreenPoint(mouseEvent);
auto r = LayoutDeviceIntRect::FromAppUnitsToNearest(
sTargetFrame->GetScreenRectInAppUnits(),
sTargetFrame->PresContext()->AppUnitsPerDevPixel());
if (!r.Contains(pt)) {
EndTransaction();
return;
@ -336,13 +338,12 @@ WheelTransaction::SetTimeout()
"nsITimer::InitWithFuncCallback failed");
}
/* static */ nsIntPoint
/* static */ LayoutDeviceIntPoint
WheelTransaction::GetScreenPoint(WidgetGUIEvent* aEvent)
{
NS_ASSERTION(aEvent, "aEvent is null");
NS_ASSERTION(aEvent->mWidget, "aEvent-mWidget is null");
return (aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset())
.ToUnknownPoint();
return aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset();
}
/* static */ uint32_t

View File

@ -158,7 +158,7 @@ protected:
static bool UpdateTransaction(WidgetWheelEvent* aEvent);
static void MayEndTransaction();
static nsIntPoint GetScreenPoint(WidgetGUIEvent* aEvent);
static LayoutDeviceIntPoint GetScreenPoint(WidgetGUIEvent* aEvent);
static void OnFailToScrollTarget();
static void OnTimeout(nsITimer* aTimer, void* aClosure);
static void SetTimeout();

View File

@ -85,15 +85,17 @@ function hitEventLoop(aFunc, aTimes)
}
}
const zoomResetTopic = "browser-fullZoom:zoomReset";
SpecialPowers.registerObservers(zoomResetTopic);
function onZoomReset(aCallback) {
var specialPowersTopic = "specialpowers-" + zoomResetTopic;
SpecialPowers.addObserver(function observe() {
SpecialPowers.removeObserver(observe, specialPowersTopic);
var fullZoom = SpecialPowers.getFullZoom(window);
if (fullZoom == 1) {
SimpleTest.executeSoon(aCallback);
}, specialPowersTopic);
return;
}
SpecialPowers.addChromeEventListener("FullZoomChange", function onFullZoomChange(event) {
is(SpecialPowers.getFullZoom(window), 1, "Zoom should be reset to 1");
SpecialPowers.removeChromeEventListener("FullZoomChange", onFullZoomChange);
SimpleTest.executeSoon(aCallback);
});
}
function setDeltaMultiplierSettings(aSettings, aCallback)

View File

@ -55,8 +55,9 @@ GamepadPose::GetPosition(JSContext* aJSContext,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
{
SetFloat32Array(aJSContext, aRetval, mPosition, mPoseState.position, 3,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Position), aRv);
SetFloat32Array(aJSContext, aRetval, mPosition,
mPoseState.isPositionValid ? mPoseState.position : nullptr, 3,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Position), aRv);
}
void
@ -64,8 +65,9 @@ GamepadPose::GetLinearVelocity(JSContext* aJSContext,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
{
SetFloat32Array(aJSContext, aRetval, mLinearVelocity, mPoseState.linearVelocity, 3,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Position), aRv);
SetFloat32Array(aJSContext, aRetval, mLinearVelocity,
mPoseState.isPositionValid ? mPoseState.linearVelocity : nullptr, 3,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Position), aRv);
}
void
@ -73,8 +75,9 @@ GamepadPose::GetLinearAcceleration(JSContext* aJSContext,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
{
SetFloat32Array(aJSContext, aRetval, mLinearAcceleration, mPoseState.linearAcceleration, 3,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_LinearAcceleration), aRv);
SetFloat32Array(aJSContext, aRetval, mLinearAcceleration,
mPoseState.isPositionValid ? mPoseState.linearAcceleration : nullptr, 3,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_LinearAcceleration), aRv);
}
void
@ -82,8 +85,9 @@ GamepadPose::GetOrientation(JSContext* aJSContext,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
{
SetFloat32Array(aJSContext, aRetval, mOrientation, mPoseState.orientation, 4,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Orientation), aRv);
SetFloat32Array(aJSContext, aRetval, mOrientation,
mPoseState.isOrientationValid ? mPoseState.orientation : nullptr, 4,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Orientation), aRv);
}
void
@ -91,8 +95,9 @@ GamepadPose::GetAngularVelocity(JSContext* aJSContext,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
{
SetFloat32Array(aJSContext, aRetval, mAngularVelocity, mPoseState.angularVelocity, 3,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Orientation), aRv);
SetFloat32Array(aJSContext, aRetval, mAngularVelocity,
mPoseState.isOrientationValid ? mPoseState.angularVelocity : nullptr, 3,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Orientation), aRv);
}
void
@ -100,8 +105,9 @@ GamepadPose::GetAngularAcceleration(JSContext* aJSContext,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
{
SetFloat32Array(aJSContext, aRetval, mAngularAcceleration, mPoseState.angularAcceleration, 3,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_AngularAcceleration), aRv);
SetFloat32Array(aJSContext, aRetval, mAngularAcceleration,
mPoseState.isOrientationValid ? mPoseState.angularAcceleration : nullptr, 3,
bool(mPoseState.flags & GamepadCapabilityFlags::Cap_AngularAcceleration), aRv);
}
void

View File

@ -42,6 +42,8 @@ struct GamepadPoseState
float angularAcceleration[3];
float linearVelocity[3];
float linearAcceleration[3];
bool isPositionValid;
bool isOrientationValid;
GamepadPoseState()
{
@ -69,7 +71,9 @@ struct GamepadPoseState
&& linearVelocity[2] == aPose.linearVelocity[2]
&& linearAcceleration[0] == aPose.linearAcceleration[0]
&& linearAcceleration[1] == aPose.linearAcceleration[1]
&& linearAcceleration[2] == aPose.linearAcceleration[2];
&& linearAcceleration[2] == aPose.linearAcceleration[2]
&& isPositionValid == aPose.isPositionValid
&& isOrientationValid == aPose.isOrientationValid;
}
bool operator!=(const GamepadPoseState& aPose) const

View File

@ -260,6 +260,7 @@ GamepadServiceTest::NewPoseMove(uint32_t aIndex,
poseState.orientation[1] = value.Data()[1];
poseState.orientation[2] = value.Data()[2];
poseState.orientation[3] = value.Data()[3];
poseState.isOrientationValid = true;
}
if (!aPos.IsNull()) {
const Float32Array& value = aPos.Value();
@ -268,6 +269,7 @@ GamepadServiceTest::NewPoseMove(uint32_t aIndex,
poseState.position[0] = value.Data()[0];
poseState.position[1] = value.Data()[1];
poseState.position[2] = value.Data()[2];
poseState.isPositionValid = true;
}
if (!aAngVelocity.IsNull()) {
const Float32Array& value = aAngVelocity.Value();

View File

@ -62,6 +62,8 @@ struct ParamTraits<mozilla::dom::GamepadPoseState>
WriteParam(aMsg, aParam.linearAcceleration[0]);
WriteParam(aMsg, aParam.linearAcceleration[1]);
WriteParam(aMsg, aParam.linearAcceleration[2]);
WriteParam(aMsg, aParam.isPositionValid);
WriteParam(aMsg, aParam.isOrientationValid);
}
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
@ -85,7 +87,9 @@ struct ParamTraits<mozilla::dom::GamepadPoseState>
!ReadParam(aMsg, aIter, &(aResult->linearVelocity[2])) ||
!ReadParam(aMsg, aIter, &(aResult->linearAcceleration[0])) ||
!ReadParam(aMsg, aIter, &(aResult->linearAcceleration[1])) ||
!ReadParam(aMsg, aIter, &(aResult->linearAcceleration[2]))) {
!ReadParam(aMsg, aIter, &(aResult->linearAcceleration[2])) ||
!ReadParam(aMsg, aIter, &(aResult->isPositionValid)) ||
!ReadParam(aMsg, aIter, &(aResult->isOrientationValid))) {
return false;
}
return true;

View File

@ -523,6 +523,7 @@ ContentChild::RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache)
{
mLookAndFeelCache = aLookAndFeelIntCache;
gfx::gfxVars::GotInitialVarUpdates(aXPCOMInit.gfxNonDefaultVarUpdates());
InitXPCOM(aXPCOMInit, aInitialData);
InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
@ -957,8 +958,6 @@ ContentChild::AppendProcessId(nsACString& aName)
void
ContentChild::InitGraphicsDeviceData(const ContentDeviceData& aData)
{
// Initialize the graphics platform. This may contact the parent process
// to read device preferences.
gfxPlatform::InitChild(aData);
}

View File

@ -2227,7 +2227,12 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
SerializeURI(nullptr, xpcomInit.userContentSheetURL());
}
// 1. Build ContentDeviceData first, as it may affect some gfxVars.
gfxPlatform::GetPlatform()->BuildContentDeviceData(&xpcomInit.contentDeviceData());
// 2. Gather non-default gfxVars.
xpcomInit.gfxNonDefaultVarUpdates() = gfxVars::FetchNonDefaultVars();
// 3. Start listening for gfxVars updates, to notify content process later on.
gfxVars::AddReceiver(this);
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
if (gfxInfo) {
@ -5276,6 +5281,18 @@ ContentParent::RecvClassifyLocal(const URIParams& aURI, const nsCString& aTables
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvAllocPipelineId(RefPtr<AllocPipelineIdPromise>&& aPromise)
{
GPUProcessManager* pm = GPUProcessManager::Get();
if (!pm) {
aPromise->Reject(PromiseRejectReason::HandlerRejected, __func__);
return IPC_OK();
}
aPromise->Resolve(wr::AsPipelineId(pm->AllocateLayerTreeId()), __func__);
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvFileCreationRequest(const nsID& aID,
const nsString& aFullPath,

View File

@ -646,6 +646,9 @@ public:
nsresult* aRv,
nsTArray<nsCString>* aResults) override;
virtual mozilla::ipc::IPCResult
RecvAllocPipelineId(RefPtr<AllocPipelineIdPromise>&& aPromise) override;
// Use the PHangMonitor channel to ask the child to repaint a tab.
void ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch);

View File

@ -64,6 +64,7 @@ include MemoryReportTypes;
// are put into different UnifiedProtocolsXX.cpp files.
// XXX Remove this once bug 1069073 is fixed
include "mozilla/dom/PContentBridgeParent.h";
include "mozilla/layers/WebRenderMessageUtils.h";
using GeoPosition from "nsGeoPositionIPCSerialiser.h";
using AlertNotificationType from "mozilla/AlertNotificationIPCSerializer.h";
@ -96,6 +97,7 @@ using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
union ChromeRegistryItem
{
@ -267,6 +269,7 @@ struct XPCOMInitData
FontFamilyListEntry[] fontFamilies;
OptionalURIParams userContentSheetURL;
PrefSetting[] prefs;
GfxVarUpdate[] gfxNonDefaultVarUpdates;
ContentDeviceData contentDeviceData;
GfxInfoFeatureStatus[] gfxFeatureStatus;
DataStorageEntry[] dataStorage;
@ -1110,6 +1113,8 @@ parent:
async AddMemoryReport(MemoryReport aReport);
async FinishMemoryReport(uint32_t aGeneration);
async AllocPipelineId() returns (PipelineId pipelineId);
both:
async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
Principal aPrincipal, ClonedMessageData aData);

View File

@ -98,8 +98,6 @@ public:
// Return an abstract thread on which to run main thread runnables.
virtual AbstractThread* AbstractMainThread() const = 0;
protected:
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) { };
public:
virtual VideoFrameContainer* GetVideoFrameContainer() = 0;
virtual mozilla::layers::ImageContainer* GetImageContainer() = 0;

View File

@ -144,10 +144,13 @@ Benchmark::Init()
BenchmarkPlayback::BenchmarkPlayback(Benchmark* aMainThreadState,
MediaDataDemuxer* aDemuxer)
: QueueObject(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK)))
: QueueObject(new TaskQueue(
GetMediaThreadPool(MediaThreadType::PLAYBACK),
"BenchmarkPlayback::QueueObject"))
, mMainThreadState(aMainThreadState)
, mDecoderTaskQueue(new TaskQueue(GetMediaThreadPool(
MediaThreadType::PLATFORM_DECODER)))
, mDecoderTaskQueue(new TaskQueue(
GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
"BenchmarkPlayback::mDecoderTaskQueue"))
, mDemuxer(aDemuxer)
, mSampleIndex(0)
, mFrameCount(0)

View File

@ -44,12 +44,6 @@ using namespace mozilla::media;
namespace mozilla {
// The amount of instability we tollerate in calls to
// MediaDecoder::UpdateEstimatedMediaDuration(); changes of duration
// less than this are ignored, as they're assumed to be the result of
// instability in the duration estimation.
static const uint64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
// avoid redefined macro in unified build
#undef LOG
#undef DUMP
@ -407,7 +401,6 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
, INIT_MIRROR(mIsAudioDataAudible, false)
, INIT_CANONICAL(mVolume, 0.0)
, INIT_CANONICAL(mPreservesPitch, true)
, INIT_CANONICAL(mEstimatedDuration, NullableTimeUnit())
, INIT_CANONICAL(mExplicitDuration, Maybe<double>())
, INIT_CANONICAL(mPlayState, PLAY_STATE_LOADING)
, INIT_CANONICAL(mNextState, PLAY_STATE_PAUSED)
@ -1183,8 +1176,7 @@ MediaDecoder::UpdateLogicalPositionInternal()
MOZ_ASSERT(NS_IsMainThread());
MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
double currentPosition =
static_cast<double>(CurrentPosition()) / static_cast<double>(USECS_PER_S);
double currentPosition = CurrentPosition().ToSeconds();
if (mPlayState == PLAY_STATE_ENDED) {
currentPosition = std::max(currentPosition, mDuration);
}
@ -1234,7 +1226,7 @@ MediaDecoder::DurationChanged()
GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
}
if (CurrentPosition() > TimeUnit::FromSeconds(mDuration).ToMicroseconds()) {
if (CurrentPosition() > TimeUnit::FromSeconds(mDuration)) {
Seek(mDuration, SeekTarget::Accurate);
}
}
@ -1334,29 +1326,6 @@ MediaDecoder::HasSuspendTaint() const
return mHasSuspendTaint;
}
void
MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
{
MOZ_ASSERT(NS_IsMainThread());
if (mPlayState <= PLAY_STATE_LOADING) {
return;
}
// The duration is only changed if its significantly different than the
// the current estimate, as the incoming duration is an estimate and so
// often is unstable as more data is read and the estimate is updated.
// Can result in a durationchangeevent. aDuration is in microseconds.
if (mEstimatedDuration.Ref().isSome()
&& mozilla::Abs(mEstimatedDuration.Ref().ref().ToMicroseconds()
- aDuration)
< ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
return;
}
mEstimatedDuration = Some(TimeUnit::FromMicroseconds(aDuration));
}
bool
MediaDecoder::IsTransportSeekable()
{
@ -1772,7 +1741,7 @@ MediaDecoder::NextFrameBufferedStatus()
MOZ_ASSERT(NS_IsMainThread());
// Next frame hasn't been decoded yet.
// Use the buffered range to consider if we have the next frame available.
TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition());
auto currentPosition = CurrentPosition();
media::TimeInterval interval(
currentPosition,
currentPosition + DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED);

View File

@ -252,17 +252,6 @@ public:
already_AddRefed<GMPCrashHelper> GetCrashHelper() override;
protected:
// Updates the media duration. This is called while the media is being
// played, calls before the media has reached loaded metadata are ignored.
// The duration is assumed to be an estimate, and so a degree of
// instability is expected; if the incoming duration is not significantly
// different from the existing duration, the change request is ignored.
// If the incoming duration is significantly different, the duration is
// changed, this causes a durationchanged event to fire to the media
// element.
void UpdateEstimatedMediaDuration(int64_t aDuration) override;
public:
// Returns true if this media supports random seeking. False for example with
// chained ogg files.
@ -568,9 +557,9 @@ protected:
// This corresponds to the "current position" in HTML5.
// We allow omx subclasses to substitute an alternative current position for
// usage with the audio offload player.
virtual int64_t CurrentPosition()
virtual media::TimeUnit CurrentPosition()
{
return mCurrentPosition.Ref().ToMicroseconds();
return mCurrentPosition.Ref();
}
// Official duration of the media resource as observed by script.
@ -775,13 +764,6 @@ protected:
Canonical<bool> mPreservesPitch;
// Media duration according to the demuxer's current estimate.
// Note that it's quite bizarre for this to live on the main thread - it would
// make much more sense for this to be owned by the demuxer's task queue. But
// currently this is only every changed in NotifyDataArrived, which runs on
// the main thread. That will need to be cleaned up at some point.
Canonical<media::NullableTimeUnit> mEstimatedDuration;
// Media duration set explicitly by JS. At present, this is only ever present
// for MSE.
Canonical<Maybe<double>> mExplicitDuration;
@ -827,10 +809,6 @@ public:
{
return &mPreservesPitch;
}
AbstractCanonical<media::NullableTimeUnit>* CanonicalEstimatedDuration()
{
return &mEstimatedDuration;
}
AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration()
{
return &mExplicitDuration;

View File

@ -69,8 +69,10 @@ public:
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
: mAudioCompactor(mAudioQueue)
, mDecoder(aDecoder)
, mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ true))
, mTaskQueue(new TaskQueue(
GetMediaThreadPool(MediaThreadType::PLAYBACK),
"MediaDecoderReader::mTaskQueue",
/* aSupportsTailDispatch = */ true))
, mWatchManager(this, mTaskQueue)
, mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
, mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")

View File

@ -2571,7 +2571,6 @@ ShutdownState::Enter()
// Disconnect canonicals and mirrors before shutting down our task queue.
master->mBuffered.DisconnectIfConnected();
master->mEstimatedDuration.DisconnectIfConnected();
master->mExplicitDuration.DisconnectIfConnected();
master->mPlayState.DisconnectIfConnected();
master->mNextPlayState.DisconnectIfConnected();
@ -2613,8 +2612,9 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mFrameStats(&aDecoder->GetFrameStatistics()),
mVideoFrameContainer(aDecoder->GetVideoFrameContainer()),
mAudioChannel(aDecoder->GetAudioChannel()),
mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ true)),
mTaskQueue(new TaskQueue(
GetMediaThreadPool(MediaThreadType::PLAYBACK),
"MDSM::mTaskQueue", /* aSupportsTailDispatch = */ true)),
mWatchManager(this, mTaskQueue),
mDispatchedStateMachine(false),
mDelayedScheduler(mTaskQueue),
@ -2635,7 +2635,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mVideoDecodeMode(VideoDecodeMode::Normal),
mIsMSE(aDecoder->IsMSE()),
INIT_MIRROR(mBuffered, TimeIntervals()),
INIT_MIRROR(mEstimatedDuration, NullableTimeUnit()),
INIT_MIRROR(mExplicitDuration, Maybe<double>()),
INIT_MIRROR(mPlayState, MediaDecoder::PLAY_STATE_LOADING),
INIT_MIRROR(mNextPlayState, MediaDecoder::PLAY_STATE_PAUSED),
@ -2689,7 +2688,6 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
// Connect mirrors.
mBuffered.Connect(mReader->CanonicalBuffered());
mEstimatedDuration.Connect(aDecoder->CanonicalEstimatedDuration());
mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration());
mPlayState.Connect(aDecoder->CanonicalPlayState());
mNextPlayState.Connect(aDecoder->CanonicalNextPlayState());
@ -2707,8 +2705,6 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
mWatchManager.Watch(mPreservesPitch,
&MediaDecoderStateMachine::PreservesPitchChanged);
mWatchManager.Watch(mEstimatedDuration,
&MediaDecoderStateMachine::RecomputeDuration);
mWatchManager.Watch(mExplicitDuration,
&MediaDecoderStateMachine::RecomputeDuration);
mWatchManager.Watch(mObservedDuration,
@ -2994,8 +2990,6 @@ void MediaDecoderStateMachine::RecomputeDuration()
// We don't fire duration changed for this case because it should have
// already been fired on the main thread when the explicit duration was set.
duration = TimeUnit::FromSeconds(d);
} else if (mEstimatedDuration.Ref().isSome()) {
duration = mEstimatedDuration.Ref().ref();
} else if (mInfo.isSome() && Info().mMetadataDuration.isSome()) {
// We need to check mInfo.isSome() because that this method might be invoked
// while mObservedDuration is changed which might before the metadata been

View File

@ -702,9 +702,6 @@ private:
// The buffered range. Mirrored from the decoder thread.
Mirror<media::TimeIntervals> mBuffered;
// The duration according to the demuxer's current estimate, mirrored from the main thread.
Mirror<media::NullableTimeUnit> mEstimatedDuration;
// The duration explicitly set by JS, mirrored from the main thread.
Mirror<Maybe<double>> mExplicitDuration;

View File

@ -738,7 +738,8 @@ class MediaFormatReader::DemuxerProxy
public:
explicit DemuxerProxy(MediaDataDemuxer* aDemuxer)
: mTaskQueue(new AutoTaskQueue(
GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER)))
GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
"DemuxerProxy::mTaskQueue"))
, mData(new Data(aDemuxer))
{
MOZ_COUNT_CTOR(DemuxerProxy);
@ -1220,10 +1221,13 @@ MediaFormatReader::InitInternal()
InitLayersBackendType();
mAudio.mTaskQueue =
new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
mVideo.mTaskQueue =
new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
mAudio.mTaskQueue = new TaskQueue(
GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
"MFR::mAudio::mTaskQueue");
mVideo.mTaskQueue = new TaskQueue(
GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
"MFR::mVideo::mTaskQueue");
if (mDecoder) {
// Note: GMPCrashHelper must be created on main thread, as it may use

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