mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-19 15:51:33 +00:00
Merge m-c to inbound, a=merge
MozReview-Commit-ID: DaH1djmEY5A
This commit is contained in:
commit
9528ad9a03
3
.gitignore
vendored
3
.gitignore
vendored
@ -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/
|
||||
|
@ -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/
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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';");
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
59
browser/components/extensions/ParseSymbols-worker.js
Normal file
59
browser/components/extensions/ParseSymbols-worker.js
Normal 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();
|
||||
};
|
49
browser/components/extensions/ParseSymbols.jsm
Normal file
49
browser/components/extensions/ParseSymbols.jsm
Normal 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,
|
||||
};
|
@ -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",
|
||||
|
363
browser/components/extensions/ext-geckoProfiler.js
Normal file
363
browser/components/extensions/ext-geckoProfiler.js
Normal 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(),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -15,6 +15,8 @@ EXTRA_COMPONENTS += [
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'ExtensionPopups.jsm',
|
||||
'ParseSymbols-worker.js',
|
||||
'ParseSymbols.jsm',
|
||||
]
|
||||
|
||||
DIRS += ['schemas']
|
||||
|
129
browser/components/extensions/schemas/geckoProfiler.json
Normal file
129
browser/components/extensions/schemas/geckoProfiler.json
Normal 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."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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();
|
||||
});
|
@ -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));
|
||||
}
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
@ -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]
|
||||
|
@ -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");
|
||||
|
||||
|
16
browser/config/mozconfigs/linux32/devedition
Normal file
16
browser/config/mozconfigs/linux32/devedition
Normal 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"
|
16
browser/config/mozconfigs/linux64/devedition
Normal file
16
browser/config/mozconfigs/linux64/devedition
Normal 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"
|
23
browser/config/mozconfigs/macosx64/devedition
Normal file
23
browser/config/mozconfigs/macosx64/devedition
Normal 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"
|
12
browser/config/mozconfigs/win32/devedition
Normal file
12
browser/config/mozconfigs/win32/devedition
Normal 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"
|
13
browser/config/mozconfigs/win64/devedition
Normal file
13
browser/config/mozconfigs/win64/devedition
Normal 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"
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
280
browser/extensions/formautofill/FormAutofillNameUtils.jsm
Normal file
280
browser/extensions/formautofill/FormAutofillNameUtils.jsm
Normal 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();
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
144
browser/extensions/formautofill/content/nameReferences.js
Normal file
144
browser/extensions/formautofill/content/nameReferences.js
Normal 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: [
|
||||
"강전",
|
||||
"남궁",
|
||||
"독고",
|
||||
"동방",
|
||||
"망절",
|
||||
"사공",
|
||||
"서문",
|
||||
"선우",
|
||||
"소봉",
|
||||
"어금",
|
||||
"장곡",
|
||||
"제갈",
|
||||
"황목",
|
||||
"황보",
|
||||
],
|
||||
};
|
@ -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"},
|
||||
|
76
browser/extensions/formautofill/test/unit/test_isCJKName.js
Normal file
76
browser/extensions/formautofill/test/unit/test_isCJKName.js
Normal 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);
|
||||
});
|
||||
});
|
@ -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",
|
||||
|
285
browser/extensions/formautofill/test/unit/test_nameUtils.js
Normal file
285
browser/extensions/formautofill/test/unit/test_nameUtils.js
Normal 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);
|
||||
});
|
||||
});
|
@ -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: "",
|
||||
}, {
|
||||
|
@ -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",
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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'])
|
||||
|
@ -94,6 +94,7 @@ showbuild:
|
||||
ACDEFINES \
|
||||
BIN_SUFFIX \
|
||||
LIB_SUFFIX \
|
||||
RUST_LIB_SUFFIX \
|
||||
DLL_SUFFIX \
|
||||
IMPORT_LIB_SUFFIX \
|
||||
INSTALL \
|
||||
|
@ -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) \
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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 },
|
||||
|
@ -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") {
|
||||
|
@ -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,
|
||||
|
@ -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.");
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
27
dom/animation/ServoComputedValuesWithParent.h
Normal file
27
dom/animation/ServoComputedValuesWithParent.h
Normal 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
|
@ -38,6 +38,7 @@ EXPORTS.mozilla += [
|
||||
'KeyframeUtils.h',
|
||||
'PendingAnimationTracker.h',
|
||||
'PseudoElementHashEntry.h',
|
||||
'ServoComputedValuesWithParent.h',
|
||||
'TimingParams.h',
|
||||
]
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#o_0:before {
|
||||
animation: anim 10s;
|
||||
content: "";
|
||||
}
|
||||
</style>
|
||||
<meta charset="UTF-8">
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)")
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user