зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-inbound to mozilla-central r=merge a=merge
This commit is contained in:
Коммит
25357802c6
|
@ -185,3 +185,4 @@ tasks:
|
|||
taskId: {$eval: 'taskId'}
|
||||
input: {$eval: 'input'}
|
||||
parameters: {$eval: 'parameters'}
|
||||
- tasks_for: '${tasks_for}'
|
||||
|
|
|
@ -68,6 +68,9 @@ var whitelist = [
|
|||
platforms: ["macosx"]},
|
||||
{file: "chrome://global/locale/printProgress.dtd", platforms: ["macosx"]},
|
||||
|
||||
// toolkit/content/aboutRights-unbranded.xhtml doesn't use aboutRights.css
|
||||
{file: "chrome://global/skin/aboutRights.css", skipUnofficial: true},
|
||||
|
||||
// devtools/client/inspector/bin/dev-server.js
|
||||
{file: "chrome://devtools/content/inspector/markup/markup.xhtml",
|
||||
isFromDevTools: true},
|
||||
|
@ -75,6 +78,9 @@ var whitelist = [
|
|||
// Kept for add-on compatibility, should be removed in bug 851471.
|
||||
{file: "chrome://mozapps/skin/downloads/downloadIcon.png"},
|
||||
|
||||
// SpiderMonkey parser API, currently unused in browser/ and toolkit/
|
||||
{file: "resource://gre/modules/reflect.jsm"},
|
||||
|
||||
// extensions/pref/autoconfig/src/nsReadConfig.cpp
|
||||
{file: "resource://gre/defaults/autoconfig/prefcalls.js"},
|
||||
|
||||
|
@ -132,8 +138,8 @@ var whitelist = [
|
|||
platforms: ["linux", "macosx"]},
|
||||
// Bug 1316187
|
||||
{file: "chrome://global/content/customizeToolbar.xul"},
|
||||
// Bug 1343837
|
||||
{file: "chrome://global/content/findUtils.js"},
|
||||
// Bug 1356031 (only used by devtools)
|
||||
{file: "chrome://global/skin/icons/error-16.png"},
|
||||
// Bug 1348362
|
||||
{file: "chrome://global/skin/icons/warning-64.png", platforms: ["linux"]},
|
||||
// Bug 1348525
|
||||
|
@ -164,11 +170,20 @@ var whitelist = [
|
|||
{file: "resource://gre/modules/Manifest.jsm"},
|
||||
// Bug 1351097
|
||||
{file: "resource://gre/modules/accessibility/AccessFu.jsm"},
|
||||
// Bug 1356043
|
||||
{file: "resource://gre/modules/PerfMeasurement.jsm"},
|
||||
// Bug 1356045
|
||||
{file: "chrome://global/content/test-ipc.xul"},
|
||||
// Bug 1356036
|
||||
{file: "resource://gre/modules/PerformanceWatcher-content.js"},
|
||||
{file: "resource://gre/modules/PerformanceWatcher.jsm"},
|
||||
// Bug 1378173 (warning: still used by devtools)
|
||||
{file: "resource://gre/modules/Promise.jsm"},
|
||||
];
|
||||
|
||||
whitelist = new Set(whitelist.filter(item =>
|
||||
("isFromDevTools" in item) == isDevtools &&
|
||||
(!item.skipNightly || !AppConstants.NIGHTLY_BUILD) &&
|
||||
(!item.skipUnofficial || !AppConstants.MOZILLA_OFFICIAL) &&
|
||||
(!item.platforms || item.platforms.includes(AppConstants.platform))
|
||||
).map(item => item.file));
|
||||
|
||||
|
@ -198,6 +213,8 @@ for (let entry of ignorableWhitelist) {
|
|||
}
|
||||
|
||||
if (!isDevtools) {
|
||||
// services/sync/modules/main.js
|
||||
whitelist.add("resource://services-sync/service.js");
|
||||
// services/sync/modules/service.js
|
||||
for (let module of ["addons.js", "bookmarks.js", "forms.js", "history.js",
|
||||
"passwords.js", "prefs.js", "tabs.js",
|
||||
|
@ -217,9 +234,17 @@ var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
|
|||
.getService(Ci.nsIChromeRegistry);
|
||||
var gChromeMap = new Map();
|
||||
var gOverrideMap = new Map();
|
||||
var gReferencesFromCode = new Set();
|
||||
var gComponentsSet = new Set();
|
||||
|
||||
// In this map when the value is a Set of URLs, the file is referenced if any
|
||||
// of the files in the Set is referenced.
|
||||
// When the value is null, the file is referenced unconditionally.
|
||||
// When the value is a string, "whitelist-direct" means that we have not found
|
||||
// any reference in the code, but have a matching whitelist entry for this file.
|
||||
// "whitelist" means that the file is indirectly whitelisted, ie. a whitelisted
|
||||
// file causes this file to be referenced.
|
||||
var gReferencesFromCode = new Map();
|
||||
|
||||
var resHandler = Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
var gResourceMap = [];
|
||||
|
@ -254,7 +279,7 @@ function parseManifest(manifestUri) {
|
|||
Services.io.newURI(argv[0]).specIgnoringRef);
|
||||
}
|
||||
} else if (type == "category" && gInterestingCategories.has(argv[0])) {
|
||||
gReferencesFromCode.add(argv[2]);
|
||||
gReferencesFromCode.set(argv[2], null);
|
||||
} else if (type == "resource") {
|
||||
trackResourcePrefix(argv[0]);
|
||||
} else if (type == "component") {
|
||||
|
@ -264,6 +289,42 @@ function parseManifest(manifestUri) {
|
|||
});
|
||||
}
|
||||
|
||||
function addCodeReference(url, fromURI) {
|
||||
let from = convertToCodeURI(fromURI.spec);
|
||||
|
||||
// Ignore self references.
|
||||
if (url == from)
|
||||
return;
|
||||
|
||||
let ref;
|
||||
if (gReferencesFromCode.has(url)) {
|
||||
ref = gReferencesFromCode.get(url);
|
||||
if (ref === null)
|
||||
return;
|
||||
} else {
|
||||
// Mark any file referenced by a 'features' bootstrap.js file as
|
||||
// unconditionally referenced. The features folder is only in
|
||||
// resource://app/ for non-packaged builds.
|
||||
if (/resource:\/\/app\/features\/[^/]+\/bootstrap\.js/.test(from)) {
|
||||
gReferencesFromCode.set(url, null);
|
||||
return;
|
||||
}
|
||||
ref = new Set();
|
||||
gReferencesFromCode.set(url, ref);
|
||||
}
|
||||
ref.add(from);
|
||||
}
|
||||
|
||||
function listCodeReferences(refs) {
|
||||
let refList = [];
|
||||
if (refs) {
|
||||
for (let ref of refs) {
|
||||
refList.push(ref);
|
||||
}
|
||||
}
|
||||
return refList.join(",");
|
||||
}
|
||||
|
||||
function parseCSSFile(fileUri) {
|
||||
return fetchFile(fileUri.spec).then(data => {
|
||||
for (let line of data.split("\n")) {
|
||||
|
@ -273,7 +334,7 @@ function parseCSSFile(fileUri) {
|
|||
let importMatch = line.match(/@import ['"]?([^'"]*)['"]?/);
|
||||
if (importMatch && importMatch[1]) {
|
||||
let url = Services.io.newURI(importMatch[1], null, fileUri).spec;
|
||||
gReferencesFromCode.add(convertToCodeURI(url));
|
||||
addCodeReference(convertToCodeURI(url), fileUri);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -288,7 +349,7 @@ function parseCSSFile(fileUri) {
|
|||
|
||||
try {
|
||||
url = Services.io.newURI(url, null, fileUri).specIgnoringRef;
|
||||
gReferencesFromCode.add(convertToCodeURI(url));
|
||||
addCodeReference(convertToCodeURI(url), fileUri);
|
||||
} catch (e) {
|
||||
ok(false, "unexpected error while resolving this URI: " + url);
|
||||
}
|
||||
|
@ -302,7 +363,7 @@ function parseCodeFile(fileUri) {
|
|||
let baseUri;
|
||||
for (let line of data.split("\n")) {
|
||||
let urls =
|
||||
line.match(/["'`]chrome:\/\/[a-zA-Z0-9 -]+\/(content|skin|locale)\/[^"'` ]*["'`]/g);
|
||||
line.match(/["'`]chrome:\/\/[a-zA-Z0-9-]+\/(content|skin|locale)\/[^"'` ]*["'`]/g);
|
||||
if (!urls) {
|
||||
urls = line.match(/["']resource:\/\/[^"']+["']/g);
|
||||
if (urls && isDevtools &&
|
||||
|
@ -317,7 +378,7 @@ function parseCodeFile(fileUri) {
|
|||
let match = line.match("(?:src|href)=[\"']([^$&\"']+)");
|
||||
if (match && match[1]) {
|
||||
let url = Services.io.newURI(match[1], null, fileUri).spec;
|
||||
gReferencesFromCode.add(convertToCodeURI(url));
|
||||
addCodeReference(convertToCodeURI(url), fileUri);
|
||||
}
|
||||
|
||||
if (isDevtools) {
|
||||
|
@ -337,7 +398,7 @@ function parseCodeFile(fileUri) {
|
|||
path = path.replace(rule[0], rule[1]);
|
||||
if (!/\.(properties|js|jsm|json|css)$/.test(path))
|
||||
path += ".js";
|
||||
gReferencesFromCode.add(path);
|
||||
addCodeReference(path, fileUri);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -351,7 +412,7 @@ function parseCodeFile(fileUri) {
|
|||
if (!/\.(properties|js|jsm|json|css)$/.test(url))
|
||||
url += ".js";
|
||||
if (url.startsWith("resource://")) {
|
||||
gReferencesFromCode.add(url);
|
||||
addCodeReference(url, fileUri);
|
||||
} else {
|
||||
// if we end up with a chrome:// url here, it's likely because
|
||||
// a baseURI to a resource:// path has been defined in another
|
||||
|
@ -383,7 +444,7 @@ function parseCodeFile(fileUri) {
|
|||
!/\.(properties|js|jsm|json|css)$/.test(url))
|
||||
url += ".js";
|
||||
|
||||
gReferencesFromCode.add(url);
|
||||
addCodeReference(url, fileUri);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -458,10 +519,9 @@ function findChromeUrlsFromArray(array, prefix) {
|
|||
}
|
||||
|
||||
// Only keep strings that look like real chrome or resource urls.
|
||||
if (/chrome:\/\/[a-zA-Z09 -]+\/(content|skin|locale)\//.test(string) ||
|
||||
/resource:\/\/gre.*\.[a-z]+/.test(string) ||
|
||||
string.startsWith("resource://content-accessible/"))
|
||||
gReferencesFromCode.add(string);
|
||||
if (/chrome:\/\/[a-zA-Z09-]+\/(content|skin|locale)\//.test(string) ||
|
||||
/resource:\/\/[a-zA-Z09-]*\/.*\.[a-z]+/.test(string))
|
||||
gReferencesFromCode.set(string, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -526,34 +586,94 @@ add_task(async function checkAllTheFiles() {
|
|||
"resource://devtools-client-shared/",
|
||||
"resource://app/modules/devtools",
|
||||
"resource://gre/modules/devtools"];
|
||||
let hasDevtoolsPrefix =
|
||||
uri => devtoolsPrefixes.some(prefix => uri.startsWith(prefix));
|
||||
let chromeFiles = [];
|
||||
for (let uri of uris) {
|
||||
uri = convertToCodeURI(uri.spec);
|
||||
if ((uri.startsWith("chrome://") || uri.startsWith("resource://")) &&
|
||||
isDevtools == devtoolsPrefixes.some(prefix => uri.startsWith(prefix)))
|
||||
isDevtools == hasDevtoolsPrefix(uri))
|
||||
chromeFiles.push(uri);
|
||||
}
|
||||
|
||||
let isUnreferenced =
|
||||
file => !gReferencesFromCode.has(file) &&
|
||||
!gExceptionPaths.some(e => file.startsWith(e)) &&
|
||||
(!gOverrideMap.has(file) || isUnreferenced(gOverrideMap.get(file)));
|
||||
if (isDevtools) {
|
||||
// chrome://devtools/skin/devtools-browser.css is included from browser.xul
|
||||
gReferencesFromCode.set("chrome://browser/content/browser.xul", null);
|
||||
// devtools' css is currently included from browser.css, see bug 1204810.
|
||||
gReferencesFromCode.set("chrome://browser/skin/browser.css", null);
|
||||
}
|
||||
|
||||
let notWhitelisted = file => {
|
||||
if (!whitelist.has(file))
|
||||
return true;
|
||||
whitelist.delete(file);
|
||||
return false;
|
||||
let isUnreferenced = file => {
|
||||
if (gExceptionPaths.some(e => file.startsWith(e)))
|
||||
return false;
|
||||
if (gReferencesFromCode.has(file)) {
|
||||
let refs = gReferencesFromCode.get(file);
|
||||
if (refs === null)
|
||||
return false;
|
||||
for (let ref of refs) {
|
||||
if (ref.endsWith("!/bootstrap.js"))
|
||||
return false;
|
||||
|
||||
if (isDevtools) {
|
||||
if (ref.startsWith("resource://app/components/") ||
|
||||
(file.startsWith("chrome://") && ref.startsWith("resource://")))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gReferencesFromCode.has(ref)) {
|
||||
let refType = gReferencesFromCode.get(ref);
|
||||
if (refType === null || // unconditionally referenced
|
||||
refType == "whitelist" || refType == "whitelist-direct")
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !gOverrideMap.has(file) || isUnreferenced(gOverrideMap.get(file));
|
||||
};
|
||||
|
||||
let unreferencedFiles = chromeFiles.filter(f => {
|
||||
let rv = isUnreferenced(f);
|
||||
if (rv && f.startsWith("resource://app/"))
|
||||
rv = isUnreferenced(f.replace("resource://app/", "resource:///"));
|
||||
if (rv && /^resource:\/\/(?:app|gre)\/components\/[^/]+\.js$/.test(f))
|
||||
rv = !gComponentsSet.has(f.replace(/.*\//, ""));
|
||||
return rv;
|
||||
}).filter(notWhitelisted).sort();
|
||||
let unreferencedFiles = chromeFiles;
|
||||
|
||||
let removeReferenced = useWhitelist => {
|
||||
let foundReference = false;
|
||||
unreferencedFiles = unreferencedFiles.filter(f => {
|
||||
let rv = isUnreferenced(f);
|
||||
if (rv && f.startsWith("resource://app/"))
|
||||
rv = isUnreferenced(f.replace("resource://app/", "resource:///"));
|
||||
if (rv && /^resource:\/\/(?:app|gre)\/components\/[^/]+\.js$/.test(f))
|
||||
rv = !gComponentsSet.has(f.replace(/.*\//, ""));
|
||||
if (!rv) {
|
||||
foundReference = true;
|
||||
if (useWhitelist) {
|
||||
info("indirectly whitelisted file: " + f + " used from " +
|
||||
listCodeReferences(gReferencesFromCode.get(f)));
|
||||
}
|
||||
gReferencesFromCode.set(f, useWhitelist ? "whitelist" : null);
|
||||
}
|
||||
return rv;
|
||||
});
|
||||
return foundReference;
|
||||
};
|
||||
// First filter out the files that are referenced.
|
||||
while (removeReferenced(false)) {
|
||||
// As long as removeReferenced returns true, some files have been marked
|
||||
// as referenced, so we need to run it again.
|
||||
}
|
||||
// Marked as referenced the files that have been explicitly whitelisted.
|
||||
unreferencedFiles = unreferencedFiles.filter(file => {
|
||||
if (whitelist.has(file)) {
|
||||
whitelist.delete(file);
|
||||
gReferencesFromCode.set(file, "whitelist-direct");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
// Run the process again, this time when more files are marked as referenced,
|
||||
// it's a consequence of the whitelist.
|
||||
while (removeReferenced(true)) {
|
||||
// As long as removeReferenced returns true, we need to run it again.
|
||||
}
|
||||
|
||||
unreferencedFiles.sort();
|
||||
|
||||
if (isDevtools) {
|
||||
// Bug 1351878 - handle devtools resource files
|
||||
|
@ -585,7 +705,16 @@ add_task(async function checkAllTheFiles() {
|
|||
|
||||
is(unreferencedFiles.length, 0, "there should be no unreferenced files");
|
||||
for (let file of unreferencedFiles) {
|
||||
ok(false, "unreferenced file: " + file);
|
||||
let refs = gReferencesFromCode.get(file);
|
||||
if (refs === undefined) {
|
||||
ok(false, "unreferenced file: " + file);
|
||||
} else {
|
||||
let refList = listCodeReferences(refs);
|
||||
let msg = "file only referenced from unreferenced files: " + file;
|
||||
if (refList)
|
||||
msg += " referenced from " + refList;
|
||||
ok(false, msg);
|
||||
}
|
||||
}
|
||||
|
||||
for (let file of whitelist) {
|
||||
|
@ -595,7 +724,7 @@ add_task(async function checkAllTheFiles() {
|
|||
ok(false, "unused whitelist entry: " + file);
|
||||
}
|
||||
|
||||
for (let file of gReferencesFromCode) {
|
||||
for (let [file, refs] of gReferencesFromCode) {
|
||||
if (isDevtools != devtoolsPrefixes.some(prefix => file.startsWith(prefix)))
|
||||
continue;
|
||||
|
||||
|
@ -605,11 +734,16 @@ add_task(async function checkAllTheFiles() {
|
|||
let pathParts =
|
||||
file.match("chrome://([^/]+)/content/([^/.]+)\.xul") ||
|
||||
file.match("chrome://([^/]+)/skin/([^/.]+)\.css");
|
||||
if (!pathParts || pathParts[1] != pathParts[2]) {
|
||||
// TODO: bug 1349010 - add a whitelist and make this reliable enough
|
||||
// that we could make the test fail when this catches something new.
|
||||
info("missing file with code reference: " + file);
|
||||
}
|
||||
if (pathParts && pathParts[1] == pathParts[2])
|
||||
continue;
|
||||
|
||||
// TODO: bug 1349010 - add a whitelist and make this reliable enough
|
||||
// that we could make the test fail when this catches something new.
|
||||
let refList = listCodeReferences(refs);
|
||||
let msg = "missing file: " + file;
|
||||
if (refList)
|
||||
msg += " referenced from " + refList;
|
||||
info(msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,13 +7,18 @@ add_task(async function() {
|
|||
let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, uri);
|
||||
let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, uri);
|
||||
|
||||
let updates = 0;
|
||||
function countUpdates(event) { updates++; }
|
||||
let updates = [];
|
||||
function countUpdates(event) { updates.push((new Error()).stack); }
|
||||
let updater = document.getElementById("editMenuCommandSetAll");
|
||||
updater.addEventListener("commandupdate", countUpdates, true);
|
||||
await BrowserTestUtils.switchTab(gBrowser, tab1);
|
||||
|
||||
is(updates, 1, "only one command update per tab switch");
|
||||
is(updates.length, 1, "only one command update per tab switch");
|
||||
if (updates.length > 1) {
|
||||
for (let stack of updates) {
|
||||
info("Update stack:\n" + stack);
|
||||
}
|
||||
}
|
||||
|
||||
updater.removeEventListener("commandupdate", countUpdates, true);
|
||||
await BrowserTestUtils.removeTab(tab1);
|
||||
|
|
|
@ -105,13 +105,14 @@ build_binutils() {
|
|||
../binutils-$binutils_version/configure --prefix=${prefix-/tools/gcc}/ $binutils_configure_flags
|
||||
make $make_flags
|
||||
make install $make_flags DESTDIR=$root_dir
|
||||
export PATH=$root_dir/${prefix-/tools/gcc}/bin:$PATH
|
||||
popd
|
||||
}
|
||||
|
||||
build_gcc() {
|
||||
mkdir $root_dir/gcc-objdir
|
||||
pushd $root_dir/gcc-objdir
|
||||
../gcc-$gcc_version/configure --prefix=${prefix-/tools/gcc} --enable-languages=c,c++ --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro
|
||||
../gcc-$gcc_version/configure --prefix=${prefix-/tools/gcc} --enable-languages=c,c++ --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro --disable-initfini-array
|
||||
make $make_flags
|
||||
make $make_flags install DESTDIR=$root_dir
|
||||
|
||||
|
|
|
@ -153,6 +153,10 @@ DOMInterfaces = {
|
|||
'nativeType': 'mozilla::dom::Console',
|
||||
},
|
||||
|
||||
'ConsoleInstance': {
|
||||
'implicitJSContext': ['clear', 'count', 'groupEnd', 'time', 'timeEnd'],
|
||||
},
|
||||
|
||||
'ConvolverNode': {
|
||||
'implicitJSContext': [ 'buffer' ],
|
||||
},
|
||||
|
|
|
@ -1150,12 +1150,12 @@ WebGLContext::ClearBackbufferIfNeeded()
|
|||
void
|
||||
WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
|
||||
{
|
||||
const size_t maxWebGLContexts = gfxPrefs::WebGLMaxContexts();
|
||||
size_t maxWebGLContextsPerPrincipal = gfxPrefs::WebGLMaxContextsPerPrincipal();
|
||||
const auto maxWebGLContexts = gfxPrefs::WebGLMaxContexts();
|
||||
auto maxWebGLContextsPerPrincipal = gfxPrefs::WebGLMaxContextsPerPrincipal();
|
||||
|
||||
// maxWebGLContextsPerPrincipal must be less than maxWebGLContexts
|
||||
MOZ_ASSERT(maxWebGLContextsPerPrincipal < maxWebGLContexts);
|
||||
maxWebGLContextsPerPrincipal = std::min(maxWebGLContextsPerPrincipal, maxWebGLContexts - 1);
|
||||
MOZ_ASSERT(maxWebGLContextsPerPrincipal <= maxWebGLContexts);
|
||||
maxWebGLContextsPerPrincipal = std::min(maxWebGLContextsPerPrincipal, maxWebGLContexts);
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
// XXX mtseng: bug 709490, WebGLMemoryTracker is not thread safe.
|
||||
|
@ -1220,12 +1220,12 @@ WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
|
|||
}
|
||||
|
||||
if (numContextsThisPrincipal > maxWebGLContextsPerPrincipal) {
|
||||
GenerateWarning("Exceeded %zu live WebGL contexts for this principal, losing the "
|
||||
GenerateWarning("Exceeded %u live WebGL contexts for this principal, losing the "
|
||||
"least recently used one.", maxWebGLContextsPerPrincipal);
|
||||
MOZ_ASSERT(oldestContextThisPrincipal); // if we reach this point, this can't be null
|
||||
const_cast<WebGLContext*>(oldestContextThisPrincipal)->LoseContext();
|
||||
} else if (numContexts > maxWebGLContexts) {
|
||||
GenerateWarning("Exceeded %zu live WebGL contexts, losing the least "
|
||||
GenerateWarning("Exceeded %u live WebGL contexts, losing the least "
|
||||
"recently used one.", maxWebGLContexts);
|
||||
MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
|
||||
const_cast<WebGLContext*>(oldestContext)->LoseContext();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/Console.h"
|
||||
#include "mozilla/dom/ConsoleInstance.h"
|
||||
#include "mozilla/dom/ConsoleBinding.h"
|
||||
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
|
@ -663,9 +664,11 @@ private:
|
|||
class ConsoleProfileRunnable final : public ConsoleRunnable
|
||||
{
|
||||
public:
|
||||
ConsoleProfileRunnable(Console* aConsole, const nsAString& aAction,
|
||||
ConsoleProfileRunnable(Console* aConsole, Console::MethodName aName,
|
||||
const nsAString& aAction,
|
||||
const Sequence<JS::Value>& aArguments)
|
||||
: ConsoleRunnable(aConsole)
|
||||
, mName(aName)
|
||||
, mAction(aAction)
|
||||
, mArguments(aArguments)
|
||||
{
|
||||
|
@ -746,13 +749,14 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
mConsole->ProfileMethodInternal(aCx, mAction, arguments);
|
||||
mConsole->ProfileMethodInternal(aCx, mName, mAction, arguments);
|
||||
}
|
||||
|
||||
virtual void
|
||||
ReleaseData() override
|
||||
{}
|
||||
|
||||
Console::MethodName mName;
|
||||
nsString mAction;
|
||||
|
||||
// This is a reference of the sequence of arguments we receive from the DOM
|
||||
|
@ -770,12 +774,14 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(Console)
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Console)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsoleEventNotifier)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDumpFunction)
|
||||
tmp->Shutdown();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsoleEventNotifier)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDumpFunction)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console)
|
||||
|
@ -816,6 +822,9 @@ Console::Console(nsPIDOMWindowInner* aWindow)
|
|||
: mWindow(aWindow)
|
||||
, mOuterID(0)
|
||||
, mInnerID(0)
|
||||
, mDumpToStdout(false)
|
||||
, mChromeInstance(false)
|
||||
, mMaxLogLevel(ConsoleLogLevel::All)
|
||||
, mStatus(eUnknown)
|
||||
, mCreationTimeStamp(TimeStamp::Now())
|
||||
{
|
||||
|
@ -950,13 +959,6 @@ METHOD(Debug, "debug")
|
|||
METHOD(Table, "table")
|
||||
METHOD(Trace, "trace")
|
||||
|
||||
/* static */ void
|
||||
Console::Clear(const GlobalObject& aGlobal)
|
||||
{
|
||||
const Sequence<JS::Value> data;
|
||||
Method(aGlobal, MethodClear, NS_LITERAL_STRING("clear"), data);
|
||||
}
|
||||
|
||||
// Displays an interactive listing of all the properties of an object.
|
||||
METHOD(Dir, "dir");
|
||||
METHOD(Dirxml, "dirxml");
|
||||
|
@ -964,6 +966,15 @@ METHOD(Dirxml, "dirxml");
|
|||
METHOD(Group, "group")
|
||||
METHOD(GroupCollapsed, "groupCollapsed")
|
||||
|
||||
#undef METHOD
|
||||
|
||||
/* static */ void
|
||||
Console::Clear(const GlobalObject& aGlobal)
|
||||
{
|
||||
const Sequence<JS::Value> data;
|
||||
Method(aGlobal, MethodClear, NS_LITERAL_STRING("clear"), data);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Console::GroupEnd(const GlobalObject& aGlobal)
|
||||
{
|
||||
|
@ -987,15 +998,27 @@ Console::TimeEnd(const GlobalObject& aGlobal, const nsAString& aLabel)
|
|||
Console::StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel,
|
||||
MethodName aMethodName, const nsAString& aMethodString)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
RefPtr<Console> console = GetConsole(aGlobal);
|
||||
if (!console) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClearException ce(cx);
|
||||
console->StringMethodInternal(aGlobal.Context(), aLabel, aMethodName,
|
||||
aMethodString);
|
||||
}
|
||||
|
||||
void
|
||||
Console::StringMethodInternal(JSContext* aCx, const nsAString& aLabel,
|
||||
MethodName aMethodName,
|
||||
const nsAString& aMethodString)
|
||||
{
|
||||
ClearException ce(aCx);
|
||||
|
||||
Sequence<JS::Value> data;
|
||||
SequenceRooter<JS::Value> rooter(cx, &data);
|
||||
SequenceRooter<JS::Value> rooter(aCx, &data);
|
||||
|
||||
JS::Rooted<JS::Value> value(cx);
|
||||
if (!dom::ToJSValue(cx, aLabel, &value)) {
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!dom::ToJSValue(aCx, aLabel, &value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1003,7 +1026,7 @@ Console::StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel,
|
|||
return;
|
||||
}
|
||||
|
||||
Method(aGlobal, aMethodName, aMethodString, data);
|
||||
MethodInternal(aCx, aMethodName, aMethodString, data);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
|
@ -1027,18 +1050,20 @@ Console::TimeStamp(const GlobalObject& aGlobal,
|
|||
/* static */ void
|
||||
Console::Profile(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData)
|
||||
{
|
||||
ProfileMethod(aGlobal, NS_LITERAL_STRING("profile"), aData);
|
||||
ProfileMethod(aGlobal, MethodProfile, NS_LITERAL_STRING("profile"), aData);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Console::ProfileEnd(const GlobalObject& aGlobal,
|
||||
const Sequence<JS::Value>& aData)
|
||||
{
|
||||
ProfileMethod(aGlobal, NS_LITERAL_STRING("profileEnd"), aData);
|
||||
ProfileMethod(aGlobal, MethodProfileEnd, NS_LITERAL_STRING("profileEnd"),
|
||||
aData);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Console::ProfileMethod(const GlobalObject& aGlobal, const nsAString& aAction,
|
||||
Console::ProfileMethod(const GlobalObject& aGlobal, MethodName aName,
|
||||
const nsAString& aAction,
|
||||
const Sequence<JS::Value>& aData)
|
||||
{
|
||||
RefPtr<Console> console = GetConsole(aGlobal);
|
||||
|
@ -1047,22 +1072,40 @@ Console::ProfileMethod(const GlobalObject& aGlobal, const nsAString& aAction,
|
|||
}
|
||||
|
||||
JSContext* cx = aGlobal.Context();
|
||||
console->ProfileMethodInternal(cx, aAction, aData);
|
||||
console->ProfileMethodInternal(cx, aName, aAction, aData);
|
||||
}
|
||||
|
||||
bool
|
||||
Console::IsEnabled(JSContext* aCx) const
|
||||
{
|
||||
// Console is always enabled if it is a custom Chrome-Only instance.
|
||||
if (mChromeInstance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Make all Console API no-op if DevTools aren't enabled.
|
||||
return nsContentUtils::DevToolsEnabled(aCx);
|
||||
}
|
||||
|
||||
void
|
||||
Console::ProfileMethodInternal(JSContext* aCx, const nsAString& aAction,
|
||||
Console::ProfileMethodInternal(JSContext* aCx, MethodName aMethodName,
|
||||
const nsAString& aAction,
|
||||
const Sequence<JS::Value>& aData)
|
||||
{
|
||||
// Make all Console API no-op if DevTools aren't enabled.
|
||||
if (!nsContentUtils::DevToolsEnabled(aCx)) {
|
||||
if (!IsEnabled(aCx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ShouldProceed(aMethodName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MaybeExecuteDumpFunction(aCx, aAction, aData);
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
// Here we are in a worker thread.
|
||||
RefPtr<ConsoleProfileRunnable> runnable =
|
||||
new ConsoleProfileRunnable(this, aAction, aData);
|
||||
new ConsoleProfileRunnable(this, aMethodName, aAction, aData);
|
||||
|
||||
runnable->Dispatch(aCx);
|
||||
return;
|
||||
|
@ -1207,10 +1250,14 @@ Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
|
|||
const nsAString& aMethodString,
|
||||
const Sequence<JS::Value>& aData)
|
||||
{
|
||||
// Make all Console API no-op if DevTools aren't enabled.
|
||||
if (!nsContentUtils::DevToolsEnabled(aCx)) {
|
||||
if (!IsEnabled(aCx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ShouldProceed(aMethodName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
RefPtr<ConsoleCallData> callData(new ConsoleCallData());
|
||||
|
@ -1321,9 +1368,19 @@ Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
|
|||
}
|
||||
}
|
||||
|
||||
// Before processing this CallData differently, it's time to call the dump
|
||||
// function.
|
||||
if (aMethodName == MethodTrace) {
|
||||
MaybeExecuteDumpFunctionForTrace(aCx, stack);
|
||||
} else {
|
||||
MaybeExecuteDumpFunction(aCx, aMethodString, aData);
|
||||
}
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
if (mWindow) {
|
||||
callData->SetIDs(mOuterID, mInnerID);
|
||||
} else if (!mPassedInnerID.IsEmpty()) {
|
||||
callData->SetIDs(NS_LITERAL_STRING("jsm"), mPassedInnerID);
|
||||
} else {
|
||||
nsAutoString filename;
|
||||
if (callData->mTopStackFrame.isSome()) {
|
||||
|
@ -1492,6 +1549,7 @@ Console::PopulateConsoleNotificationInTheTargetScope(JSContext* aCx,
|
|||
event.mInnerID.Value().SetAsUnsignedLongLong() = 0;
|
||||
}
|
||||
|
||||
event.mConsoleID = mConsoleID;
|
||||
event.mLevel = aData->mMethodString;
|
||||
event.mFilename = frame.mFilename;
|
||||
|
||||
|
@ -2537,5 +2595,197 @@ Console::MonotonicTimer(JSContext* aCx, MethodName aMethodName,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<ConsoleInstance>
|
||||
Console::CreateInstance(const GlobalObject& aGlobal,
|
||||
const ConsoleInstanceOptions& aOptions)
|
||||
{
|
||||
RefPtr<ConsoleInstance> console = new ConsoleInstance(aOptions);
|
||||
return console.forget();
|
||||
}
|
||||
|
||||
void
|
||||
Console::MaybeExecuteDumpFunction(JSContext* aCx,
|
||||
const nsAString& aMethodName,
|
||||
const Sequence<JS::Value>& aData)
|
||||
{
|
||||
if (!mDumpFunction && !mDumpToStdout) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString message;
|
||||
message.AssignLiteral("console.");
|
||||
message.Append(aMethodName);
|
||||
message.AppendLiteral(": ");
|
||||
|
||||
if (!mDumpPrefix.IsEmpty()) {
|
||||
message.Append(mDumpPrefix);
|
||||
message.AppendLiteral(": ");
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aData.Length(); ++i) {
|
||||
JS::Rooted<JS::Value> v(aCx, aData[i]);
|
||||
JS::Rooted<JSString*> jsString(aCx, JS_ValueToSource(aCx, v));
|
||||
if (!jsString) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoJSString string;
|
||||
if (NS_WARN_IF(!string.init(aCx, jsString))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (i != 0) {
|
||||
message.AppendLiteral(" ");
|
||||
}
|
||||
|
||||
message.Append(string);
|
||||
}
|
||||
|
||||
message.AppendLiteral("\n");
|
||||
ExecuteDumpFunction(message);
|
||||
}
|
||||
|
||||
void
|
||||
Console::MaybeExecuteDumpFunctionForTrace(JSContext* aCx, nsIStackFrame* aStack)
|
||||
{
|
||||
if (!aStack || (!mDumpFunction && !mDumpToStdout)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString message;
|
||||
message.AssignLiteral("console.trace:\n");
|
||||
|
||||
if (!mDumpPrefix.IsEmpty()) {
|
||||
message.Append(mDumpPrefix);
|
||||
message.AppendLiteral(": ");
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStackFrame> stack(aStack);
|
||||
|
||||
while (stack) {
|
||||
nsAutoString filename;
|
||||
nsresult rv = stack->GetFilename(aCx, filename);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
message.Append(filename);
|
||||
message.AppendLiteral(" ");
|
||||
|
||||
int32_t lineNumber;
|
||||
rv = stack->GetLineNumber(aCx, &lineNumber);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
message.AppendInt(lineNumber);
|
||||
message.AppendLiteral(" ");
|
||||
|
||||
nsAutoString functionName;
|
||||
rv = stack->GetName(aCx, functionName);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
message.Append(filename);
|
||||
message.AppendLiteral("\n");
|
||||
|
||||
nsCOMPtr<nsIStackFrame> caller;
|
||||
rv = stack->GetCaller(aCx, getter_AddRefs(caller));
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
if (!caller) {
|
||||
rv = stack->GetAsyncCaller(aCx, getter_AddRefs(caller));
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
}
|
||||
|
||||
stack.swap(caller);
|
||||
}
|
||||
|
||||
message.AppendLiteral("\n");
|
||||
ExecuteDumpFunction(message);
|
||||
}
|
||||
|
||||
void
|
||||
Console::ExecuteDumpFunction(const nsAString& aMessage)
|
||||
{
|
||||
if (mDumpFunction) {
|
||||
IgnoredErrorResult rv;
|
||||
mDumpFunction->Call(aMessage, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 str(aMessage);
|
||||
MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, ("%s", str.get()));
|
||||
#ifdef ANDROID
|
||||
__android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get());
|
||||
#endif
|
||||
fputs(str.get(), stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
bool
|
||||
Console::ShouldProceed(MethodName aName) const
|
||||
{
|
||||
return WebIDLLogLevelToInteger(mMaxLogLevel) <=
|
||||
InternalLogLevelToInteger(aName);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Console::WebIDLLogLevelToInteger(ConsoleLogLevel aLevel) const
|
||||
{
|
||||
switch (aLevel) {
|
||||
case ConsoleLogLevel::All: return 0;
|
||||
case ConsoleLogLevel::Debug: return 2;
|
||||
case ConsoleLogLevel::Log: return 3;
|
||||
case ConsoleLogLevel::Info: return 3;
|
||||
case ConsoleLogLevel::Clear: return 3;
|
||||
case ConsoleLogLevel::Trace: return 3;
|
||||
case ConsoleLogLevel::TimeEnd: return 3;
|
||||
case ConsoleLogLevel::Time: return 3;
|
||||
case ConsoleLogLevel::Group: return 3;
|
||||
case ConsoleLogLevel::GroupEnd: return 3;
|
||||
case ConsoleLogLevel::Profile: return 3;
|
||||
case ConsoleLogLevel::ProfileEnd: return 3;
|
||||
case ConsoleLogLevel::Dir: return 3;
|
||||
case ConsoleLogLevel::Dirxml: return 3;
|
||||
case ConsoleLogLevel::Warn: return 4;
|
||||
case ConsoleLogLevel::Error: return 5;
|
||||
case ConsoleLogLevel::Off: return UINT32_MAX;
|
||||
default:
|
||||
MOZ_CRASH("ConsoleLogLevel is out of sync with the Console implementation!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Console::InternalLogLevelToInteger(MethodName aName) const
|
||||
{
|
||||
switch (aName) {
|
||||
case MethodLog: return 3;
|
||||
case MethodInfo: return 3;
|
||||
case MethodWarn: return 4;
|
||||
case MethodError: return 5;
|
||||
case MethodException: return 5;
|
||||
case MethodDebug: return 2;
|
||||
case MethodTable: return 3;
|
||||
case MethodTrace: return 3;
|
||||
case MethodDir: return 3;
|
||||
case MethodDirxml: return 3;
|
||||
case MethodGroup: return 3;
|
||||
case MethodGroupCollapsed: return 3;
|
||||
case MethodGroupEnd: return 3;
|
||||
case MethodTime: return 3;
|
||||
case MethodTimeEnd: return 3;
|
||||
case MethodTimeStamp: return 3;
|
||||
case MethodAssert: return 3;
|
||||
case MethodCount: return 3;
|
||||
case MethodClear: return 3;
|
||||
case MethodProfile: return 3;
|
||||
case MethodProfileEnd: return 3;
|
||||
default:
|
||||
MOZ_CRASH("MethodName is out of sync with the Console implementation!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
#ifndef mozilla_dom_Console_h
|
||||
#define mozilla_dom_Console_h
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/ConsoleBinding.h"
|
||||
#include "mozilla/JSObjectHolder.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
@ -21,17 +20,18 @@
|
|||
|
||||
class nsIConsoleAPIStorage;
|
||||
class nsIPrincipal;
|
||||
class nsIStackFrame;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AnyCallback;
|
||||
class ConsoleCallData;
|
||||
class ConsoleInstance;
|
||||
class ConsoleInstanceDumpCallback;
|
||||
class ConsoleRunnable;
|
||||
class ConsoleCallDataRunnable;
|
||||
class ConsoleProfileRunnable;
|
||||
struct ConsoleTimerError;
|
||||
struct ConsoleStackEntry;
|
||||
|
||||
class Console final : public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
|
@ -114,6 +114,10 @@ public:
|
|||
static void
|
||||
Clear(const GlobalObject& aGlobal);
|
||||
|
||||
static already_AddRefed<ConsoleInstance>
|
||||
CreateInstance(const GlobalObject& aGlobal,
|
||||
const ConsoleInstanceOptions& aOptions);
|
||||
|
||||
void
|
||||
ClearStorage();
|
||||
|
||||
|
@ -154,7 +158,9 @@ private:
|
|||
MethodTimeStamp,
|
||||
MethodAssert,
|
||||
MethodCount,
|
||||
MethodClear
|
||||
MethodClear,
|
||||
MethodProfile,
|
||||
MethodProfileEnd,
|
||||
};
|
||||
|
||||
static already_AddRefed<Console>
|
||||
|
@ -164,11 +170,12 @@ private:
|
|||
GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult &aRv);
|
||||
|
||||
static void
|
||||
ProfileMethod(const GlobalObject& aGlobal, const nsAString& aAction,
|
||||
const Sequence<JS::Value>& aData);
|
||||
ProfileMethod(const GlobalObject& aGlobal, MethodName aName,
|
||||
const nsAString& aAction, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
ProfileMethodInternal(JSContext* aCx, const nsAString& aAction,
|
||||
ProfileMethodInternal(JSContext* aCx, MethodName aName,
|
||||
const nsAString& aAction,
|
||||
const Sequence<JS::Value>& aData);
|
||||
|
||||
static void
|
||||
|
@ -183,6 +190,10 @@ private:
|
|||
StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel,
|
||||
MethodName aMethodName, const nsAString& aMethodString);
|
||||
|
||||
void
|
||||
StringMethodInternal(JSContext* aCx, const nsAString& aLabel,
|
||||
MethodName aMethodName, const nsAString& aMethodString);
|
||||
|
||||
// This method must receive aCx and aArguments in the same JSCompartment.
|
||||
void
|
||||
ProcessCallData(JSContext* aCx,
|
||||
|
@ -376,6 +387,28 @@ private:
|
|||
const Sequence<JS::Value>& aData,
|
||||
DOMHighResTimeStamp* aTimeStamp);
|
||||
|
||||
void
|
||||
MaybeExecuteDumpFunction(JSContext* aCx, const nsAString& aMethodName,
|
||||
const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
MaybeExecuteDumpFunctionForTrace(JSContext* aCx, nsIStackFrame* aStack);
|
||||
|
||||
void
|
||||
ExecuteDumpFunction(const nsAString& aMessage);
|
||||
|
||||
bool
|
||||
IsEnabled(JSContext* aCx) const;
|
||||
|
||||
bool
|
||||
ShouldProceed(MethodName aName) const;
|
||||
|
||||
uint32_t
|
||||
WebIDLLogLevelToInteger(ConsoleLogLevel aLevel) const;
|
||||
|
||||
uint32_t
|
||||
InternalLogLevelToInteger(MethodName aName) const;
|
||||
|
||||
// All these nsCOMPtr are touched on main thread only.
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
nsCOMPtr<nsIConsoleAPIStorage> mStorage;
|
||||
|
@ -406,6 +439,15 @@ private:
|
|||
uint64_t mOuterID;
|
||||
uint64_t mInnerID;
|
||||
|
||||
// Set only by ConsoleInstance:
|
||||
nsString mConsoleID;
|
||||
nsString mPassedInnerID;
|
||||
RefPtr<ConsoleInstanceDumpCallback> mDumpFunction;
|
||||
bool mDumpToStdout;
|
||||
nsString mDumpPrefix;
|
||||
bool mChromeInstance;
|
||||
ConsoleLogLevel mMaxLogLevel;
|
||||
|
||||
enum {
|
||||
eUnknown,
|
||||
eInitialized,
|
||||
|
@ -417,6 +459,7 @@ private:
|
|||
mozilla::TimeStamp mCreationTimeStamp;
|
||||
|
||||
friend class ConsoleCallData;
|
||||
friend class ConsoleInstance;
|
||||
friend class ConsoleRunnable;
|
||||
friend class ConsoleCallDataRunnable;
|
||||
friend class ConsoleProfileRunnable;
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "mozilla/dom/ConsoleInstance.h"
|
||||
#include "mozilla/dom/ConsoleBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ConsoleInstance, mConsole)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(ConsoleInstance)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(ConsoleInstance)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ConsoleInstance)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
namespace {
|
||||
|
||||
ConsoleLogLevel
|
||||
PrefToValue(const nsCString& aPref)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_WARNING("Console.maxLogLevelPref is not supported on workers!");
|
||||
return ConsoleLogLevel::All;
|
||||
}
|
||||
|
||||
nsAutoCString value;
|
||||
nsresult rv = Preferences::GetCString(aPref.get(), value);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return ConsoleLogLevel::All;
|
||||
}
|
||||
|
||||
int index = FindEnumStringIndexImpl(value.get(), value.Length(),
|
||||
ConsoleLogLevelValues::strings);
|
||||
if (NS_WARN_IF(index < 0)) {
|
||||
return ConsoleLogLevel::All;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(index < (int)ConsoleLogLevel::EndGuard_);
|
||||
return static_cast<ConsoleLogLevel>(index);
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
|
||||
ConsoleInstance::ConsoleInstance(const ConsoleInstanceOptions& aOptions)
|
||||
: mConsole(new Console(nullptr))
|
||||
{
|
||||
mConsole->mConsoleID = aOptions.mConsoleID;
|
||||
mConsole->mPassedInnerID = aOptions.mInnerID;
|
||||
|
||||
if (aOptions.mDump.WasPassed()) {
|
||||
mConsole->mDumpFunction = &aOptions.mDump.Value();
|
||||
} else {
|
||||
// For historical reasons, ConsoleInstance prints messages on stdout.
|
||||
mConsole->mDumpToStdout = true;
|
||||
}
|
||||
|
||||
mConsole->mDumpPrefix = aOptions.mPrefix;
|
||||
|
||||
// Let's inform that this is a custom instance.
|
||||
mConsole->mChromeInstance = true;
|
||||
|
||||
if (aOptions.mMaxLogLevel.WasPassed()) {
|
||||
mConsole->mMaxLogLevel = aOptions.mMaxLogLevel.Value();
|
||||
}
|
||||
|
||||
if (!aOptions.mMaxLogLevelPref.IsEmpty()) {
|
||||
mConsole->mMaxLogLevel =
|
||||
PrefToValue(NS_ConvertUTF16toUTF8(aOptions.mMaxLogLevelPref));
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleInstance::~ConsoleInstance()
|
||||
{}
|
||||
|
||||
JSObject*
|
||||
ConsoleInstance::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return ConsoleInstanceBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
#define METHOD(name, string) \
|
||||
void \
|
||||
ConsoleInstance::name(JSContext* aCx, const Sequence<JS::Value>& aData) \
|
||||
{ \
|
||||
mConsole->MethodInternal(aCx, Console::Method##name, \
|
||||
NS_LITERAL_STRING(string), aData); \
|
||||
}
|
||||
|
||||
METHOD(Log, "log")
|
||||
METHOD(Info, "info")
|
||||
METHOD(Warn, "warn")
|
||||
METHOD(Error, "error")
|
||||
METHOD(Exception, "exception")
|
||||
METHOD(Debug, "debug")
|
||||
METHOD(Table, "table")
|
||||
METHOD(Trace, "trace")
|
||||
METHOD(Dir, "dir");
|
||||
METHOD(Dirxml, "dirxml");
|
||||
METHOD(Group, "group")
|
||||
METHOD(GroupCollapsed, "groupCollapsed")
|
||||
|
||||
#undef METHOD
|
||||
|
||||
void
|
||||
ConsoleInstance::GroupEnd(JSContext* aCx)
|
||||
{
|
||||
const Sequence<JS::Value> data;
|
||||
mConsole->MethodInternal(aCx, Console::MethodGroupEnd,
|
||||
NS_LITERAL_STRING("groupEnd"), data);
|
||||
}
|
||||
|
||||
void
|
||||
ConsoleInstance::Time(JSContext* aCx, const nsAString& aLabel)
|
||||
{
|
||||
mConsole->StringMethodInternal(aCx, aLabel, Console::MethodTime,
|
||||
NS_LITERAL_STRING("time"));
|
||||
}
|
||||
|
||||
void
|
||||
ConsoleInstance::TimeEnd(JSContext* aCx, const nsAString& aLabel)
|
||||
{
|
||||
mConsole->StringMethodInternal(aCx, aLabel, Console::MethodTimeEnd,
|
||||
NS_LITERAL_STRING("timeEnd"));
|
||||
}
|
||||
|
||||
void
|
||||
ConsoleInstance::TimeStamp(JSContext* aCx, const JS::Handle<JS::Value> aData)
|
||||
{
|
||||
ClearException ce(aCx);
|
||||
|
||||
Sequence<JS::Value> data;
|
||||
SequenceRooter<JS::Value> rooter(aCx, &data);
|
||||
|
||||
if (aData.isString() && !data.AppendElement(aData, fallible)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mConsole->MethodInternal(aCx, Console::MethodTimeStamp,
|
||||
NS_LITERAL_STRING("timeStamp"), data);
|
||||
}
|
||||
|
||||
void
|
||||
ConsoleInstance::Profile(JSContext* aCx, const Sequence<JS::Value>& aData)
|
||||
{
|
||||
mConsole->ProfileMethodInternal(aCx, Console::MethodProfile,
|
||||
NS_LITERAL_STRING("profile"), aData);
|
||||
}
|
||||
|
||||
void
|
||||
ConsoleInstance::ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData)
|
||||
{
|
||||
mConsole->ProfileMethodInternal(aCx, Console::MethodProfileEnd,
|
||||
NS_LITERAL_STRING("profileEnd"), aData);
|
||||
}
|
||||
|
||||
void
|
||||
ConsoleInstance::Assert(JSContext* aCx, bool aCondition,
|
||||
const Sequence<JS::Value>& aData)
|
||||
{
|
||||
if (!aCondition) {
|
||||
mConsole->MethodInternal(aCx, Console::MethodAssert,
|
||||
NS_LITERAL_STRING("assert"), aData);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConsoleInstance::Count(JSContext* aCx, const nsAString& aLabel)
|
||||
{
|
||||
mConsole->StringMethodInternal(aCx, aLabel, Console::MethodCount,
|
||||
NS_LITERAL_STRING("count"));
|
||||
}
|
||||
|
||||
void
|
||||
ConsoleInstance::Clear(JSContext* aCx)
|
||||
{
|
||||
const Sequence<JS::Value> data;
|
||||
mConsole->MethodInternal(aCx, Console::MethodClear,
|
||||
NS_LITERAL_STRING("clear"), data);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,106 @@
|
|||
/* -*- 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_dom_ConsoleInstance_h
|
||||
#define mozilla_dom_ConsoleInstance_h
|
||||
|
||||
#include "mozilla/dom/Console.h"
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ConsoleInstance final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ConsoleInstance)
|
||||
|
||||
explicit ConsoleInstance(const ConsoleInstanceOptions& aOptions);
|
||||
|
||||
// WebIDL methods
|
||||
JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Log(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Info(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Warn(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Error(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Exception(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Debug(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Table(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Trace(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Dir(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Dirxml(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Group(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
GroupCollapsed(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
GroupEnd(JSContext* aCx);
|
||||
|
||||
void
|
||||
Time(JSContext* aCx, const nsAString& aLabel);
|
||||
|
||||
void
|
||||
TimeEnd(JSContext* aCx, const nsAString& aLabel);
|
||||
|
||||
void
|
||||
TimeStamp(JSContext* aCx, const JS::Handle<JS::Value> aData);
|
||||
|
||||
void
|
||||
Profile(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Assert(JSContext* aCx, bool aCondition, const Sequence<JS::Value>& aData);
|
||||
|
||||
void
|
||||
Count(JSContext* aCx, const nsAString& aLabel);
|
||||
|
||||
void
|
||||
Clear(JSContext* aCx);
|
||||
|
||||
private:
|
||||
~ConsoleInstance();
|
||||
|
||||
RefPtr<Console> mConsole;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_ConsoleInstance_h
|
|
@ -23,10 +23,12 @@ EXPORTS.mozilla += [
|
|||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Console.h',
|
||||
'ConsoleInstance.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Console.cpp',
|
||||
'ConsoleInstance.cpp',
|
||||
'ConsoleReportCollector.cpp',
|
||||
]
|
||||
|
||||
|
|
|
@ -5,7 +5,23 @@
|
|||
this.EXPORTED_SYMBOLS = [ "ConsoleTest" ];
|
||||
|
||||
this.ConsoleTest = {
|
||||
go: function() {
|
||||
go: function(dumpFunction) {
|
||||
console.log("Hello world!");
|
||||
console.createInstance().log("Hello world!");
|
||||
|
||||
let c = console.createInstance({
|
||||
consoleID: "wow",
|
||||
innerID: "CUSTOM INNER",
|
||||
dump: dumpFunction,
|
||||
prefix: "_PREFIX_",
|
||||
});
|
||||
|
||||
c.log("Hello world!");
|
||||
c.trace("Hello world!");
|
||||
|
||||
console.createInstance({ innerID: "LEVEL", maxLogLevel: "off" }).log("Invisible!");
|
||||
console.createInstance({ innerID: "LEVEL", maxLogLevel: "all" }).log("Hello world!");
|
||||
console.createInstance({ innerID: "LEVEL", maxLogLevelPref: "foo.pref" }).log("Invisible!");
|
||||
console.createInstance({ innerID: "LEVEL", maxLogLevelPref: "pref.test.console" }).log("Hello world!");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,18 +17,45 @@
|
|||
|
||||
const JSM = "chrome://mochitests/content/chrome/dom/console/tests/console.jsm";
|
||||
|
||||
let dumpCalled = 0;
|
||||
function dumpFunction(msg) {
|
||||
ok(msg.indexOf("_PREFIX_") != -1, "we have a prefix");
|
||||
dump("Message received: " + msg); // Just for debugging
|
||||
dumpCalled++;
|
||||
}
|
||||
|
||||
function consoleListener() {
|
||||
SpecialPowers.addObserver(this, "console-api-log-event");
|
||||
}
|
||||
|
||||
consoleListener.prototype = {
|
||||
count: 0,
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "console-api-log-event") {
|
||||
var obj = aSubject.wrappedJSObject;
|
||||
if (obj.innerID == JSM) {
|
||||
is(obj.ID, "jsm", "ID and InnerID are correctly set.");
|
||||
is (obj.arguments[0], "Hello world!", "Message matches");
|
||||
is(obj.arguments[0], "Hello world!", "Message matches");
|
||||
is(obj.consoleID, "", "No consoleID for console API");
|
||||
|
||||
// We want to see 2 messages from this innerID, the first is generated
|
||||
// by console.log, the second one from createInstance().log();
|
||||
++this.count;
|
||||
} else if (obj.innerID == "CUSTOM INNER") {
|
||||
is(obj.ID, "jsm", "ID and InnerID are correctly set.");
|
||||
is(obj.arguments[0], "Hello world!", "Message matches");
|
||||
is(obj.consoleID, "wow", "consoleID is set by consoleInstance");
|
||||
++this.count;
|
||||
} else if (obj.innerID == "LEVEL") {
|
||||
// Nothing special... just we don't want to see 'invisible' messages.
|
||||
is(obj.ID, "jsm", "ID and InnerID are correctly set.");
|
||||
is(obj.arguments[0], "Hello world!", "Message matches");
|
||||
++this.count;
|
||||
}
|
||||
|
||||
if (this.count == 4) {
|
||||
is(dumpCalled, 2, "Dump has been called!");
|
||||
SpecialPowers.removeObserver(this, "console-api-log-event");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
@ -38,9 +65,11 @@ consoleListener.prototype = {
|
|||
function test() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var cl = new consoleListener();
|
||||
Components.utils.import(JSM);
|
||||
ConsoleTest.go();
|
||||
SpecialPowers.pushPrefEnv({set: [["pref.test.console", "log"]]}).then(() => {
|
||||
var cl = new consoleListener();
|
||||
Components.utils.import(JSM);
|
||||
ConsoleTest.go(dumpFunction);
|
||||
});
|
||||
}
|
||||
|
||||
]]>
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
ClassString="Console",
|
||||
ProtoObjectHack]
|
||||
namespace console {
|
||||
|
||||
// NOTE: if you touch this namespace, remember to update the ConsoleInstance
|
||||
// interface as well!
|
||||
|
||||
// Logging
|
||||
void assert(optional boolean condition = false, any... data);
|
||||
void clear();
|
||||
|
@ -45,12 +49,16 @@ namespace console {
|
|||
|
||||
[ChromeOnly]
|
||||
const boolean IS_NATIVE_CONSOLE = true;
|
||||
|
||||
[ChromeOnly, NewObject]
|
||||
ConsoleInstance createInstance(optional ConsoleInstanceOptions options);
|
||||
};
|
||||
|
||||
// This is used to propagate console events to the observers.
|
||||
dictionary ConsoleEvent {
|
||||
(unsigned long long or DOMString) ID;
|
||||
(unsigned long long or DOMString) innerID;
|
||||
DOMString consoleID = "";
|
||||
DOMString addonId = "";
|
||||
DOMString level = "";
|
||||
DOMString filename = "";
|
||||
|
@ -109,3 +117,72 @@ dictionary ConsoleCounter {
|
|||
dictionary ConsoleCounterError {
|
||||
DOMString error = "maxCountersExceeded";
|
||||
};
|
||||
|
||||
[ChromeOnly,
|
||||
Exposed=(Window,Worker,WorkerDebugger,Worklet,System)]
|
||||
// This is basically a copy of the console namespace.
|
||||
interface ConsoleInstance {
|
||||
// Logging
|
||||
void assert(optional boolean condition = false, any... data);
|
||||
void clear();
|
||||
void count(optional DOMString label = "default");
|
||||
void debug(any... data);
|
||||
void error(any... data);
|
||||
void info(any... data);
|
||||
void log(any... data);
|
||||
void table(any... data); // FIXME: The spec is still unclear about this.
|
||||
void trace(any... data);
|
||||
void warn(any... data);
|
||||
void dir(any... data); // FIXME: This doesn't follow the spec yet.
|
||||
void dirxml(any... data);
|
||||
|
||||
// Grouping
|
||||
void group(any... data);
|
||||
void groupCollapsed(any... data);
|
||||
void groupEnd();
|
||||
|
||||
// Timing
|
||||
void time(optional DOMString label = "default");
|
||||
void timeEnd(optional DOMString label = "default");
|
||||
|
||||
// Mozilla only or Webcompat methods
|
||||
|
||||
void _exception(any... data);
|
||||
void timeStamp(optional any data);
|
||||
|
||||
void profile(any... data);
|
||||
void profileEnd(any... data);
|
||||
};
|
||||
|
||||
callback ConsoleInstanceDumpCallback = void (DOMString message);
|
||||
|
||||
enum ConsoleLogLevel {
|
||||
"all", "debug", "log", "info", "clear", "trace", "timeEnd", "time", "group",
|
||||
"groupEnd", "profile", "profileEnd", "dir", "dirxml", "warn", "error", "off"
|
||||
};
|
||||
|
||||
dictionary ConsoleInstanceOptions {
|
||||
// An optional function to intercept all strings written to stdout.
|
||||
ConsoleInstanceDumpCallback dump;
|
||||
|
||||
// An optional prefix string to be printed before the actual logged message.
|
||||
DOMString prefix = "";
|
||||
|
||||
// An ID representing the source of the message. Normally the inner ID of a
|
||||
// DOM window.
|
||||
DOMString innerID = "";
|
||||
|
||||
// String identified for the console, this will be passed through the console
|
||||
// notifications.
|
||||
DOMString consoleID = "";
|
||||
|
||||
// Identifier that allows to filter which messages are logged based on their
|
||||
// log level.
|
||||
ConsoleLogLevel maxLogLevel;
|
||||
|
||||
// String pref name which contains the level to use for maxLogLevel. If the
|
||||
// pref doesn't exist, gets removed or it is used in workers, the maxLogLevel
|
||||
// will default to the value passed to this constructor (or "all" if it wasn't
|
||||
// specified).
|
||||
DOMString maxLogLevelPref = "";
|
||||
};
|
||||
|
|
|
@ -746,8 +746,8 @@ private:
|
|||
DECL_GFX_PREF(Once, "webgl.force-layers-readback", WebGLForceLayersReadback, bool, false);
|
||||
DECL_GFX_PREF(Live, "webgl.force-index-validation", WebGLForceIndexValidation, int32_t, 0);
|
||||
DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
|
||||
DECL_GFX_PREF(Once, "webgl.max-contexts", WebGLMaxContexts, uint32_t, 32);
|
||||
DECL_GFX_PREF(Once, "webgl.max-contexts-per-principal", WebGLMaxContextsPerPrincipal, uint32_t, 16);
|
||||
DECL_GFX_PREF(Live, "webgl.max-contexts", WebGLMaxContexts, uint32_t, 32);
|
||||
DECL_GFX_PREF(Live, "webgl.max-contexts-per-principal", WebGLMaxContextsPerPrincipal, uint32_t, 16);
|
||||
DECL_GFX_PREF(Live, "webgl.max-warnings-per-context", WebGLMaxWarningsPerContext, uint32_t, 32);
|
||||
DECL_GFX_PREF(Live, "webgl.min_capability_mode", WebGLMinCapabilityMode, bool, false);
|
||||
DECL_GFX_PREF(Live, "webgl.msaa-force", WebGLForceMSAA, bool, false);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
// |jit-test| --arm-asm-nop-fill=1
|
||||
|
||||
var code = "(module ";
|
||||
for (var i = 0; i < 100; i++)
|
||||
code += "(func (param i32) (result i32) (i32.add (i32.const 1) (get_local 0))) ";
|
||||
code += ")";
|
||||
var buf = wasmTextToBinary(code);
|
||||
WebAssembly.compile(buf);
|
|
@ -72,7 +72,7 @@ static_assert(!IsPointer<IsPointerTest>::value,
|
|||
static_assert(IsPointer<IsPointerTest*>::value,
|
||||
"IsPointerTest* is a pointer");
|
||||
static_assert(!IsPointer<bool(IsPointerTest::*)()>::value,
|
||||
"bool(IsPointerTest::*) not a pointer");
|
||||
"bool(IsPointerTest::*)() not a pointer");
|
||||
static_assert(!IsPointer<void(IsPointerTest::*)(void)>::value,
|
||||
"void(IsPointerTest::*)(void) not a pointer");
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
interface nsIArray;
|
||||
interface nsIPrincipal;
|
||||
%{C++
|
||||
namespace mozilla {
|
||||
|
@ -12,6 +13,13 @@ class TimeStamp;
|
|||
|
||||
native TimeStamp(mozilla::TimeStamp);
|
||||
|
||||
[scriptable, uuid(c2d9e95b-9cc9-4f47-9ef6-1de0cf7ebc75)]
|
||||
interface nsIServerTiming : nsISupports {
|
||||
[must_use] readonly attribute ACString name;
|
||||
[must_use] readonly attribute double duration;
|
||||
[must_use] readonly attribute ACString description;
|
||||
};
|
||||
|
||||
// All properties return zero if the value is not available
|
||||
[scriptable, uuid(ca63784d-959c-4c3a-9a59-234a2a520de0)]
|
||||
interface nsITimedChannel : nsISupports {
|
||||
|
@ -100,4 +108,6 @@ interface nsITimedChannel : nsISupports {
|
|||
|
||||
// If this attribute is false, this resource MUST NOT be reported in resource timing.
|
||||
[noscript] attribute boolean reportResourceTiming;
|
||||
|
||||
readonly attribute nsIArray serverTiming;
|
||||
};
|
||||
|
|
|
@ -179,9 +179,10 @@ HttpBackgroundChannelChild::RecvOnTransportAndData(
|
|||
|
||||
IPCResult
|
||||
HttpBackgroundChannelChild::RecvOnStopRequest(
|
||||
const nsresult& aChannelStatus,
|
||||
const ResourceTimingStruct& aTiming,
|
||||
const TimeStamp& aLastActiveTabOptHit)
|
||||
const nsresult& aChannelStatus,
|
||||
const ResourceTimingStruct& aTiming,
|
||||
const TimeStamp& aLastActiveTabOptHit,
|
||||
const nsHttpHeaderArray& aResponseTrailers)
|
||||
{
|
||||
LOG(("HttpBackgroundChannelChild::RecvOnStopRequest [this=%p]\n", this));
|
||||
MOZ_ASSERT(OnSocketThread());
|
||||
|
@ -200,18 +201,22 @@ HttpBackgroundChannelChild::RecvOnStopRequest(
|
|||
static_cast<uint32_t>(aChannelStatus)));
|
||||
|
||||
mQueuedRunnables.AppendElement(
|
||||
NewRunnableMethod<const nsresult, const ResourceTimingStruct, const TimeStamp>(
|
||||
NewRunnableMethod<const nsresult,
|
||||
const ResourceTimingStruct,
|
||||
const TimeStamp,
|
||||
const nsHttpHeaderArray>(
|
||||
"HttpBackgroundChannelChild::RecvOnStopRequest",
|
||||
this,
|
||||
&HttpBackgroundChannelChild::RecvOnStopRequest,
|
||||
aChannelStatus,
|
||||
aTiming,
|
||||
aLastActiveTabOptHit));
|
||||
aLastActiveTabOptHit,
|
||||
aResponseTrailers));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mChannelChild->ProcessOnStopRequest(aChannelStatus, aTiming);
|
||||
mChannelChild->ProcessOnStopRequest(aChannelStatus, aTiming, aResponseTrailers);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
|
|
@ -49,7 +49,8 @@ protected:
|
|||
|
||||
IPCResult RecvOnStopRequest(const nsresult& aChannelStatus,
|
||||
const ResourceTimingStruct& aTiming,
|
||||
const TimeStamp& aLastActiveTabOptHit) override;
|
||||
const TimeStamp& aLastActiveTabOptHit,
|
||||
const nsHttpHeaderArray& aResponseTrailers) override;
|
||||
|
||||
IPCResult RecvOnProgress(const int64_t& aProgress,
|
||||
const int64_t& aProgressMax) override;
|
||||
|
|
|
@ -220,7 +220,8 @@ HttpBackgroundChannelParent::OnTransportAndData(
|
|||
|
||||
bool
|
||||
HttpBackgroundChannelParent::OnStopRequest(const nsresult& aChannelStatus,
|
||||
const ResourceTimingStruct& aTiming)
|
||||
const ResourceTimingStruct& aTiming,
|
||||
const nsHttpHeaderArray& aResponseTrailers)
|
||||
{
|
||||
LOG(("HttpBackgroundChannelParent::OnStopRequest [this=%p "
|
||||
"status=%" PRIx32 "]\n", this, static_cast<uint32_t>(aChannelStatus)));
|
||||
|
@ -233,12 +234,15 @@ HttpBackgroundChannelParent::OnStopRequest(const nsresult& aChannelStatus,
|
|||
if (!IsOnBackgroundThread()) {
|
||||
MutexAutoLock lock(mBgThreadMutex);
|
||||
nsresult rv = mBackgroundThread->Dispatch(
|
||||
NewRunnableMethod<const nsresult, const ResourceTimingStruct>(
|
||||
NewRunnableMethod<const nsresult,
|
||||
const ResourceTimingStruct,
|
||||
const nsHttpHeaderArray>(
|
||||
"net::HttpBackgroundChannelParent::OnStopRequest",
|
||||
this,
|
||||
&HttpBackgroundChannelParent::OnStopRequest,
|
||||
aChannelStatus,
|
||||
aTiming),
|
||||
aTiming,
|
||||
aResponseTrailers),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
@ -249,7 +253,7 @@ HttpBackgroundChannelParent::OnStopRequest(const nsresult& aChannelStatus,
|
|||
// See the child code for why we do this.
|
||||
TimeStamp lastActTabOpt = nsHttp::GetLastActiveTabLoadOptimizationHit();
|
||||
|
||||
return SendOnStopRequest(aChannelStatus, aTiming, lastActTabOpt);
|
||||
return SendOnStopRequest(aChannelStatus, aTiming, lastActTabOpt, aResponseTrailers);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -52,7 +52,8 @@ public:
|
|||
|
||||
// To send OnStopRequest message over background channel.
|
||||
bool OnStopRequest(const nsresult& aChannelStatus,
|
||||
const ResourceTimingStruct& aTiming);
|
||||
const ResourceTimingStruct& aTiming,
|
||||
const nsHttpHeaderArray& aResponseTrailers);
|
||||
|
||||
// To send OnProgress message over background channel.
|
||||
bool OnProgress(const int64_t& aProgress,
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "nsIStreamConverterService.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
@ -65,6 +66,7 @@
|
|||
#include "nsIDOMWindowUtils.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "nsRedirectHistoryEntry.h"
|
||||
#include "nsServerTiming.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "HttpBaseChannel.h"
|
||||
|
@ -4450,5 +4452,46 @@ HttpBaseChannel::CallTypeSniffers(void *aClosure, const uint8_t *aData,
|
|||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void
|
||||
ParseServerTimingHeader(const nsAutoPtr<T> &aHeader,
|
||||
nsIMutableArray* aOutput)
|
||||
{
|
||||
if (!aHeader) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString serverTimingHeader;
|
||||
Unused << aHeader->GetHeader(nsHttp::Server_Timing, serverTimingHeader);
|
||||
if (serverTimingHeader.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerTimingParser parser(serverTimingHeader);
|
||||
parser.Parse();
|
||||
|
||||
nsTArray<nsCOMPtr<nsIServerTiming>> array = parser.TakeServerTimingHeaders();
|
||||
for (const auto &data : array) {
|
||||
aOutput->AppendElement(data);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::GetServerTiming(nsIArray **aServerTiming)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aServerTiming);
|
||||
|
||||
nsTArray<nsCOMPtr<nsIServerTiming>> data;
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ParseServerTimingHeader(mResponseHead, array);
|
||||
ParseServerTimingHeader(mResponseTrailers, array);
|
||||
|
||||
array.forget(aServerTiming);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -339,6 +339,7 @@ public:
|
|||
|
||||
nsHttpResponseHead * GetResponseHead() const { return mResponseHead; }
|
||||
nsHttpRequestHead * GetRequestHead() { return &mRequestHead; }
|
||||
nsHttpHeaderArray * GetResponseTrailers() const { return mResponseTrailers; }
|
||||
|
||||
const NetAddr& GetSelfAddr() { return mSelfAddr; }
|
||||
const NetAddr& GetPeerAddr() { return mPeerAddr; }
|
||||
|
@ -520,6 +521,7 @@ protected:
|
|||
nsCOMPtr<nsIInputStream> mUploadStream;
|
||||
nsCOMPtr<nsIRunnable> mUploadCloneableCallback;
|
||||
nsAutoPtr<nsHttpResponseHead> mResponseHead;
|
||||
nsAutoPtr<nsHttpHeaderArray> mResponseTrailers;
|
||||
RefPtr<nsHttpConnectionInfo> mConnectionInfo;
|
||||
nsCOMPtr<nsIProxyInfo> mProxyInfo;
|
||||
nsCOMPtr<nsISupports> mSecurityInfo;
|
||||
|
|
|
@ -979,28 +979,33 @@ class StopRequestEvent : public NeckoTargetChannelEvent<HttpChannelChild>
|
|||
public:
|
||||
StopRequestEvent(HttpChannelChild* child,
|
||||
const nsresult& channelStatus,
|
||||
const ResourceTimingStruct& timing)
|
||||
const ResourceTimingStruct& timing,
|
||||
const nsHttpHeaderArray& aResponseTrailers)
|
||||
: NeckoTargetChannelEvent<HttpChannelChild>(child)
|
||||
, mChannelStatus(channelStatus)
|
||||
, mTiming(timing) {}
|
||||
, mTiming(timing)
|
||||
, mResponseTrailers(aResponseTrailers) {}
|
||||
|
||||
void Run() { mChild->OnStopRequest(mChannelStatus, mTiming); }
|
||||
void Run() { mChild->OnStopRequest(mChannelStatus, mTiming, mResponseTrailers); }
|
||||
|
||||
private:
|
||||
nsresult mChannelStatus;
|
||||
ResourceTimingStruct mTiming;
|
||||
nsHttpHeaderArray mResponseTrailers;
|
||||
};
|
||||
|
||||
void
|
||||
HttpChannelChild::ProcessOnStopRequest(const nsresult& aChannelStatus,
|
||||
const ResourceTimingStruct& aTiming)
|
||||
const ResourceTimingStruct& aTiming,
|
||||
const nsHttpHeaderArray& aResponseTrailers)
|
||||
{
|
||||
LOG(("HttpChannelChild::ProcessOnStopRequest [this=%p]\n", this));
|
||||
MOZ_ASSERT(OnSocketThread());
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be receiving any more callbacks from parent!");
|
||||
|
||||
mEventQ->RunOrEnqueue(new StopRequestEvent(this, aChannelStatus, aTiming),
|
||||
mEventQ->RunOrEnqueue(new StopRequestEvent(this, aChannelStatus,
|
||||
aTiming, aResponseTrailers),
|
||||
mDivertingToParent);
|
||||
}
|
||||
|
||||
|
@ -1036,7 +1041,8 @@ HttpChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus)
|
|||
|
||||
void
|
||||
HttpChannelChild::OnStopRequest(const nsresult& channelStatus,
|
||||
const ResourceTimingStruct& timing)
|
||||
const ResourceTimingStruct& timing,
|
||||
const nsHttpHeaderArray& aResponseTrailers)
|
||||
{
|
||||
LOG(("HttpChannelChild::OnStopRequest [this=%p status=%" PRIx32 "]\n",
|
||||
this, static_cast<uint32_t>(channelStatus)));
|
||||
|
@ -1089,6 +1095,8 @@ HttpChannelChild::OnStopRequest(const nsresult& channelStatus,
|
|||
mCacheReadStart = timing.cacheReadStart;
|
||||
mCacheReadEnd = timing.cacheReadEnd;
|
||||
|
||||
mResponseTrailers = new nsHttpHeaderArray(aResponseTrailers);
|
||||
|
||||
DoPreOnStopRequest(channelStatus);
|
||||
|
||||
{ // We must flush the queue before we Send__delete__
|
||||
|
|
|
@ -242,7 +242,8 @@ private:
|
|||
const uint32_t& aCount,
|
||||
const nsCString& aData);
|
||||
void ProcessOnStopRequest(const nsresult& aStatusCode,
|
||||
const ResourceTimingStruct& aTiming);
|
||||
const ResourceTimingStruct& aTiming,
|
||||
const nsHttpHeaderArray& aResponseTrailers);
|
||||
void ProcessOnProgress(const int64_t& aProgress, const int64_t& aProgressMax);
|
||||
void ProcessOnStatus(const nsresult& aStatus);
|
||||
void ProcessFlushedForDiversion();
|
||||
|
@ -415,7 +416,9 @@ private:
|
|||
const uint64_t& offset,
|
||||
const uint32_t& count,
|
||||
const nsCString& data);
|
||||
void OnStopRequest(const nsresult& channelStatus, const ResourceTimingStruct& timing);
|
||||
void OnStopRequest(const nsresult& channelStatus,
|
||||
const ResourceTimingStruct& timing,
|
||||
const nsHttpHeaderArray& aResponseTrailers);
|
||||
void MaybeDivertOnStop(const nsresult& aChannelStatus);
|
||||
void OnProgress(const int64_t& progress, const int64_t& progressMax);
|
||||
void OnStatus(const nsresult& status);
|
||||
|
|
|
@ -1596,12 +1596,16 @@ HttpChannelParent::OnStopRequest(nsIRequest *aRequest,
|
|||
httpChannelImpl->SetWarningReporter(nullptr);
|
||||
}
|
||||
|
||||
nsHttpHeaderArray *responseTrailer = mChannel->GetResponseTrailers();
|
||||
|
||||
// Either IPC channel is closed or background channel
|
||||
// is ready to send OnStopRequest.
|
||||
MOZ_ASSERT(mIPCClosed || mBgParent);
|
||||
|
||||
if (mIPCClosed ||
|
||||
!mBgParent || !mBgParent->OnStopRequest(aStatusCode, timing)) {
|
||||
!mBgParent ||
|
||||
!mBgParent->OnStopRequest(aStatusCode, timing,
|
||||
responseTrailer ? *responseTrailer : nsHttpHeaderArray())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
|
|
|
@ -895,6 +895,12 @@ NullHttpChannel::GetReportResourceTiming(bool* _retval) {
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetServerTiming(nsIArray **aServerTiming)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
#define IMPL_TIMING_ATTR(name) \
|
||||
NS_IMETHODIMP \
|
||||
NullHttpChannel::Get##name##Time(PRTime* _retval) { \
|
||||
|
|
|
@ -11,6 +11,7 @@ include PURLClassifierInfo;
|
|||
|
||||
include "mozilla/net/NeckoMessageUtils.h";
|
||||
|
||||
using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
|
||||
using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -36,8 +37,10 @@ child:
|
|||
uint32_t count,
|
||||
nsCString data);
|
||||
|
||||
async OnStopRequest(nsresult channelStatus, ResourceTimingStruct timing,
|
||||
TimeStamp lastActiveTabOptimization);
|
||||
async OnStopRequest(nsresult channelStatus,
|
||||
ResourceTimingStruct timing,
|
||||
TimeStamp lastActiveTabOptimization,
|
||||
nsHttpHeaderArray responseTrailers);
|
||||
|
||||
async OnProgress(int64_t progress, int64_t progressMax);
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ EXPORTS.mozilla.net += [
|
|||
'HttpChannelChild.h',
|
||||
'HttpChannelParent.h',
|
||||
'HttpInfo.h',
|
||||
'nsServerTiming.h',
|
||||
'NullHttpChannel.h',
|
||||
'PHttpChannelParams.h',
|
||||
'PSpdyPush.h',
|
||||
|
@ -95,6 +96,7 @@ UNIFIED_SOURCES += [
|
|||
'nsHttpRequestHead.cpp',
|
||||
'nsHttpResponseHead.cpp',
|
||||
'nsHttpTransaction.cpp',
|
||||
'nsServerTiming.cpp',
|
||||
'NullHttpChannel.cpp',
|
||||
'NullHttpTransaction.cpp',
|
||||
'TunnelUtils.cpp',
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "nsICacheEntry.h"
|
||||
#include "nsIRequest.h"
|
||||
#include <errno.h>
|
||||
#include <functional>
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
@ -603,127 +604,235 @@ void EnsureBuffer(UniquePtr<uint8_t[]> &buf, uint32_t newSize,
|
|||
{
|
||||
localEnsureBuffer<uint8_t> (buf, newSize, preserve, objSize);
|
||||
}
|
||||
///
|
||||
|
||||
static bool
|
||||
IsTokenSymbol(signed char chr) {
|
||||
if (chr < 33 || chr == 127 ||
|
||||
chr == '(' || chr == ')' || chr == '<' || chr == '>' ||
|
||||
chr == '@' || chr == ',' || chr == ';' || chr == ':' ||
|
||||
chr == '"' || chr == '/' || chr == '[' || chr == ']' ||
|
||||
chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ParsedHeaderPair::ParsedHeaderPair(const char *name, int32_t nameLen,
|
||||
const char *val, int32_t valLen,
|
||||
bool isQuotedValue)
|
||||
: mName(nsDependentCSubstring(nullptr, 0u))
|
||||
, mValue(nsDependentCSubstring(nullptr, 0u))
|
||||
, mIsQuotedValue(isQuotedValue)
|
||||
{
|
||||
if (nameLen > 0) {
|
||||
mName.Rebind(name, name + nameLen);
|
||||
}
|
||||
if (valLen > 0) {
|
||||
if (mIsQuotedValue) {
|
||||
RemoveQuotedStringEscapes(val, valLen);
|
||||
mValue.Rebind(mUnquotedValue.BeginWriting(), mUnquotedValue.Length());
|
||||
} else {
|
||||
mValue.Rebind(val, val + valLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ParsedHeaderValueList::Tokenize(char *input, uint32_t inputLen, char **token,
|
||||
uint32_t *tokenLen, bool *foundEquals, char **next)
|
||||
ParsedHeaderPair::RemoveQuotedStringEscapes(const char *val, int32_t valLen)
|
||||
{
|
||||
if (foundEquals) {
|
||||
*foundEquals = false;
|
||||
}
|
||||
if (next) {
|
||||
*next = nullptr;
|
||||
}
|
||||
if (inputLen < 1 || !input || !token) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool foundFirst = false;
|
||||
bool inQuote = false;
|
||||
bool foundToken = false;
|
||||
*token = input;
|
||||
*tokenLen = inputLen;
|
||||
|
||||
for (uint32_t index = 0; !foundToken && index < inputLen; ++index) {
|
||||
// strip leading cruft
|
||||
if (!foundFirst &&
|
||||
(input[index] == ' ' || input[index] == '"' || input[index] == '\t')) {
|
||||
(*token)++;
|
||||
} else {
|
||||
foundFirst = true;
|
||||
mUnquotedValue.Truncate();
|
||||
const char *c = val;
|
||||
for (int32_t i = 0; i < valLen; ++i) {
|
||||
if (c[i] == '\\' && c[i + 1]) {
|
||||
++i;
|
||||
}
|
||||
mUnquotedValue.Append(c[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void Tokenize(const char *input, uint32_t inputLen, const char token,
|
||||
const std::function<void(const char *, uint32_t)>& consumer)
|
||||
{
|
||||
auto trimWhitespace =
|
||||
[] (const char *in, uint32_t inLen, const char **out, uint32_t *outLen) {
|
||||
*out = in;
|
||||
*outLen = inLen;
|
||||
if (inLen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Trim leading space
|
||||
while (nsCRT::IsAsciiSpace(**out)) {
|
||||
(*out)++;
|
||||
--(*outLen);
|
||||
}
|
||||
|
||||
// Trim tailing space
|
||||
for (const char *i = *out + *outLen - 1; i >= *out; --i) {
|
||||
if (!nsCRT::IsAsciiSpace(*i)) {
|
||||
break;
|
||||
}
|
||||
--(*outLen);
|
||||
}
|
||||
};
|
||||
|
||||
const char *first = input;
|
||||
bool inQuote = false;
|
||||
const char *result = nullptr;
|
||||
uint32_t resultLen = 0;
|
||||
for (uint32_t index = 0; index < inputLen; ++index) {
|
||||
if (inQuote && input[index] == '\\' && input[index + 1]) {
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
if (input[index] == '"') {
|
||||
inQuote = !inQuote;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inQuote) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input[index] == '=' || input[index] == ';') {
|
||||
*tokenLen = (input + index) - *token;
|
||||
if (next && ((index + 1) < inputLen)) {
|
||||
*next = input + index + 1;
|
||||
}
|
||||
foundToken = true;
|
||||
if (foundEquals && input[index] == '=') {
|
||||
*foundEquals = true;
|
||||
}
|
||||
break;
|
||||
if (input[index] == token) {
|
||||
trimWhitespace(first, (input + index) - first,
|
||||
&result, &resultLen);
|
||||
consumer(result, resultLen);
|
||||
first = input + index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundToken) {
|
||||
*tokenLen = (input + inputLen) - *token;
|
||||
}
|
||||
|
||||
// strip trailing cruft
|
||||
for (char *index = *token + *tokenLen - 1; index >= *token; --index) {
|
||||
if (*index != ' ' && *index != '\t' && *index != '"') {
|
||||
break;
|
||||
}
|
||||
--(*tokenLen);
|
||||
if (*index == '"') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
trimWhitespace(first, (input + inputLen) - first,
|
||||
&result, &resultLen);
|
||||
consumer(result, resultLen);
|
||||
}
|
||||
|
||||
ParsedHeaderValueList::ParsedHeaderValueList(char *t, uint32_t len)
|
||||
ParsedHeaderValueList::ParsedHeaderValueList(const char *t,
|
||||
uint32_t len,
|
||||
bool allowInvalidValue)
|
||||
{
|
||||
char *name = nullptr;
|
||||
uint32_t nameLen = 0;
|
||||
char *value = nullptr;
|
||||
uint32_t valueLen = 0;
|
||||
char *next = nullptr;
|
||||
bool foundEquals;
|
||||
|
||||
while (t) {
|
||||
Tokenize(t, len, &name, &nameLen, &foundEquals, &next);
|
||||
if (next) {
|
||||
len -= next - t;
|
||||
}
|
||||
t = next;
|
||||
if (foundEquals && t) {
|
||||
Tokenize(t, len, &value, &valueLen, nullptr, &next);
|
||||
if (next) {
|
||||
len -= next - t;
|
||||
}
|
||||
t = next;
|
||||
}
|
||||
mValues.AppendElement(ParsedHeaderPair(name, nameLen, value, valueLen));
|
||||
value = name = nullptr;
|
||||
valueLen = nameLen = 0;
|
||||
next = nullptr;
|
||||
if (!len) {
|
||||
return;
|
||||
}
|
||||
|
||||
ParsedHeaderValueList *self = this;
|
||||
auto consumer = [=] (const char *output, uint32_t outputLength) {
|
||||
self->ParseNameAndValue(output, allowInvalidValue);
|
||||
};
|
||||
|
||||
Tokenize(t, len, ';', consumer);
|
||||
}
|
||||
|
||||
ParsedHeaderValueListList::ParsedHeaderValueListList(const nsCString &fullHeader)
|
||||
void
|
||||
ParsedHeaderValueList::ParseNameAndValue(const char *input, bool allowInvalidValue)
|
||||
{
|
||||
const char *nameStart = input;
|
||||
const char *nameEnd = nullptr;
|
||||
const char *valueStart = input;
|
||||
const char *valueEnd = nullptr;
|
||||
bool isQuotedString = false;
|
||||
bool invalidValue = false;
|
||||
|
||||
for (; *input && *input != ';' && *input != ',' &&
|
||||
!nsCRT::IsAsciiSpace(*input) && *input != '='; input++)
|
||||
;
|
||||
|
||||
nameEnd = input;
|
||||
|
||||
if (!(nameEnd - nameStart)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether param name is a valid token.
|
||||
for (const char *c = nameStart; c < nameEnd; c++) {
|
||||
if (!IsTokenSymbol(*c)) {
|
||||
nameEnd = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(nameEnd - nameStart)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (nsCRT::IsAsciiSpace(*input)) {
|
||||
++input;
|
||||
}
|
||||
|
||||
if (!*input || *input++ != '=') {
|
||||
mValues.AppendElement(ParsedHeaderPair(nameStart, nameEnd - nameStart,
|
||||
nullptr, 0, false));
|
||||
return;
|
||||
}
|
||||
|
||||
while (nsCRT::IsAsciiSpace(*input)) {
|
||||
++input;
|
||||
}
|
||||
|
||||
if (*input != '"') {
|
||||
// The value is a token, not a quoted string.
|
||||
valueStart = input;
|
||||
for (valueEnd = input;
|
||||
*valueEnd && !nsCRT::IsAsciiSpace (*valueEnd) &&
|
||||
*valueEnd != ';' && *valueEnd != ',';
|
||||
valueEnd++)
|
||||
;
|
||||
input = valueEnd;
|
||||
if (!allowInvalidValue) {
|
||||
for (const char *c = valueStart; c < valueEnd; c++) {
|
||||
if (!IsTokenSymbol(*c)) {
|
||||
valueEnd = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool foundQuotedEnd = false;
|
||||
isQuotedString = true;
|
||||
|
||||
++input;
|
||||
valueStart = input;
|
||||
for (valueEnd = input; *valueEnd; ++valueEnd) {
|
||||
if (*valueEnd == '\\' && *(valueEnd + 1)) {
|
||||
++valueEnd;
|
||||
}
|
||||
else if (*valueEnd == '"') {
|
||||
foundQuotedEnd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundQuotedEnd) {
|
||||
invalidValue = true;
|
||||
}
|
||||
|
||||
input = valueEnd;
|
||||
// *valueEnd != null means that *valueEnd is quote character.
|
||||
if (*valueEnd) {
|
||||
input++;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidValue) {
|
||||
valueEnd = valueStart;
|
||||
}
|
||||
|
||||
mValues.AppendElement(ParsedHeaderPair(nameStart, nameEnd - nameStart,
|
||||
valueStart, valueEnd - valueStart,
|
||||
isQuotedString));
|
||||
}
|
||||
|
||||
ParsedHeaderValueListList::ParsedHeaderValueListList(const nsCString &fullHeader,
|
||||
bool allowInvalidValue)
|
||||
: mFull(fullHeader)
|
||||
{
|
||||
char *t = mFull.BeginWriting();
|
||||
uint32_t len = mFull.Length();
|
||||
char *last = t;
|
||||
bool inQuote = false;
|
||||
for (uint32_t index = 0; index < len; ++index) {
|
||||
if (t[index] == '"') {
|
||||
inQuote = !inQuote;
|
||||
continue;
|
||||
}
|
||||
if (inQuote) {
|
||||
continue;
|
||||
}
|
||||
if (t[index] == ',') {
|
||||
mValues.AppendElement(ParsedHeaderValueList(last, (t + index) - last));
|
||||
last = t + index + 1;
|
||||
}
|
||||
}
|
||||
if (!inQuote) {
|
||||
mValues.AppendElement(ParsedHeaderValueList(last, (t + len) - last));
|
||||
}
|
||||
auto &values = mValues;
|
||||
auto consumer =
|
||||
[&values, allowInvalidValue] (const char *output, uint32_t outputLength) {
|
||||
values.AppendElement(ParsedHeaderValueList(output,
|
||||
outputLength,
|
||||
allowInvalidValue));
|
||||
};
|
||||
|
||||
Tokenize(mFull.BeginReading(), mFull.Length(), ',', consumer);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -266,42 +266,50 @@ class ParsedHeaderPair
|
|||
{
|
||||
public:
|
||||
ParsedHeaderPair(const char *name, int32_t nameLen,
|
||||
const char *val, int32_t valLen)
|
||||
{
|
||||
if (nameLen > 0) {
|
||||
mName.Rebind(name, name + nameLen);
|
||||
}
|
||||
if (valLen > 0) {
|
||||
mValue.Rebind(val, val + valLen);
|
||||
}
|
||||
}
|
||||
const char *val, int32_t valLen, bool isQuotedValue);
|
||||
|
||||
ParsedHeaderPair(ParsedHeaderPair const ©)
|
||||
: mName(copy.mName)
|
||||
, mValue(copy.mValue)
|
||||
, mUnquotedValue(copy.mUnquotedValue)
|
||||
, mIsQuotedValue(copy.mIsQuotedValue)
|
||||
{
|
||||
if (mIsQuotedValue) {
|
||||
mValue.Rebind(mUnquotedValue.BeginReading(), mUnquotedValue.Length());
|
||||
}
|
||||
}
|
||||
|
||||
nsDependentCSubstring mName;
|
||||
nsDependentCSubstring mValue;
|
||||
|
||||
private:
|
||||
nsCString mUnquotedValue;
|
||||
bool mIsQuotedValue;
|
||||
|
||||
void RemoveQuotedStringEscapes(const char *val, int32_t valLen);
|
||||
};
|
||||
|
||||
class ParsedHeaderValueList
|
||||
{
|
||||
public:
|
||||
ParsedHeaderValueList(char *t, uint32_t len);
|
||||
ParsedHeaderValueList(const char *t, uint32_t len, bool allowInvalidValue);
|
||||
nsTArray<ParsedHeaderPair> mValues;
|
||||
|
||||
private:
|
||||
void ParsePair(char *t, uint32_t len);
|
||||
void Tokenize(char *input, uint32_t inputLen, char **token,
|
||||
uint32_t *tokenLen, bool *foundEquals, char **next);
|
||||
void ParseNameAndValue(const char *input, bool allowInvalidValue);
|
||||
};
|
||||
|
||||
class ParsedHeaderValueListList
|
||||
{
|
||||
public:
|
||||
explicit ParsedHeaderValueListList(const nsCString &txt);
|
||||
// RFC 7231 section 3.2.6 defines the syntax of the header field values.
|
||||
// |allowInvalidValue| indicates whether the rule will be used to check
|
||||
// the input text.
|
||||
// Note that ParsedHeaderValueListList is currently used to parse
|
||||
// Alt-Svc and Server-Timing header. |allowInvalidValue| is set to true
|
||||
// when parsing Alt-Svc for historical reasons.
|
||||
explicit ParsedHeaderValueListList(const nsCString &txt,
|
||||
bool allowInvalidValue = true);
|
||||
nsTArray<ParsedHeaderValueList> mValues;
|
||||
|
||||
private:
|
||||
|
|
|
@ -74,6 +74,7 @@ HTTP_ATOM(Range, "Range")
|
|||
HTTP_ATOM(Referer, "Referer")
|
||||
HTTP_ATOM(Retry_After, "Retry-After")
|
||||
HTTP_ATOM(Server, "Server")
|
||||
HTTP_ATOM(Server_Timing, "Server-Timing")
|
||||
HTTP_ATOM(Service_Worker_Allowed, "Service-Worker-Allowed")
|
||||
HTTP_ATOM(Set_Cookie, "Set-Cookie")
|
||||
HTTP_ATOM(Set_Cookie2, "Set-Cookie2")
|
||||
|
|
|
@ -7213,6 +7213,8 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
|
|||
Unused << mCacheEntry->SetNetworkTimes(onStartTime, onStopTime);
|
||||
}
|
||||
|
||||
mResponseTrailers = mTransaction->TakeResponseTrailers();
|
||||
|
||||
// at this point, we're done with the transaction
|
||||
mTransactionTimings = mTransaction->Timings();
|
||||
mTransaction = nullptr;
|
||||
|
|
|
@ -120,7 +120,17 @@ nsHttpChunkedDecoder::ParseChunkRemaining(char *buf,
|
|||
if (!mTrailers) {
|
||||
mTrailers = new nsHttpHeaderArray();
|
||||
}
|
||||
Unused << mTrailers->ParseHeaderLine(nsDependentCSubstring(buf, count));
|
||||
|
||||
nsHttpAtom hdr = {0};
|
||||
nsAutoCString headerNameOriginal;
|
||||
nsAutoCString val;
|
||||
if (NS_SUCCEEDED(mTrailers->ParseHeaderLine(nsDependentCSubstring(buf, count),
|
||||
&hdr, &headerNameOriginal, &val))) {
|
||||
if (hdr == nsHttp::Server_Timing) {
|
||||
Unused << mTrailers->SetHeaderFromNet(hdr, headerNameOriginal,
|
||||
val, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mWaitEOF = false;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef nsHttpChunkedDecoder_h__
|
||||
#define nsHttpChunkedDecoder_h__
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsError.h"
|
||||
#include "nsString.h"
|
||||
#include "nsHttpHeaderArray.h"
|
||||
|
@ -29,11 +30,9 @@ public:
|
|||
uint32_t *contentRead,
|
||||
uint32_t *contentRemaining);
|
||||
|
||||
nsHttpHeaderArray *Trailers() { return mTrailers; }
|
||||
nsHttpHeaderArray *Trailers() { return mTrailers.get(); }
|
||||
|
||||
nsHttpHeaderArray *TakeTrailers() { nsHttpHeaderArray *h = mTrailers;
|
||||
mTrailers = nullptr;
|
||||
return h; }
|
||||
nsHttpHeaderArray *TakeTrailers() { return mTrailers.forget(); }
|
||||
|
||||
uint32_t GetChunkRemaining() { return mChunkRemaining; }
|
||||
|
||||
|
@ -43,11 +42,11 @@ private:
|
|||
uint32_t *countRead);
|
||||
|
||||
private:
|
||||
nsHttpHeaderArray *mTrailers;
|
||||
uint32_t mChunkRemaining;
|
||||
nsCString mLineBuf; // may hold a partial line
|
||||
bool mReachedEOF;
|
||||
bool mWaitEOF;
|
||||
nsAutoPtr<nsHttpHeaderArray> mTrailers;
|
||||
uint32_t mChunkRemaining;
|
||||
nsCString mLineBuf; // may hold a partial line
|
||||
bool mReachedEOF;
|
||||
bool mWaitEOF;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -136,6 +136,8 @@ nsHttpTransaction::nsHttpTransaction()
|
|||
, mReportedResponseHeader(false)
|
||||
, mForTakeResponseHead(nullptr)
|
||||
, mResponseHeadTaken(false)
|
||||
, mForTakeResponseTrailers(nullptr)
|
||||
, mResponseTrailersTaken(false)
|
||||
, mTopLevelOuterContentWindowId(0)
|
||||
, mSubmittedRatePacing(false)
|
||||
, mPassedRatePacing(false)
|
||||
|
@ -486,6 +488,18 @@ nsHttpTransaction::TakeResponseHead()
|
|||
return head;
|
||||
}
|
||||
|
||||
nsHttpHeaderArray *
|
||||
nsHttpTransaction::TakeResponseTrailers()
|
||||
{
|
||||
MOZ_ASSERT(!mResponseTrailersTaken, "TakeResponseTrailers called 2x");
|
||||
|
||||
// Lock TakeResponseTrailers() against main thread
|
||||
MutexAutoLock lock(*nsHttp::GetLock());
|
||||
|
||||
mResponseTrailersTaken = true;
|
||||
return mForTakeResponseTrailers.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpTransaction::SetProxyConnectFailed()
|
||||
{
|
||||
|
@ -1776,6 +1790,11 @@ nsHttpTransaction::HandleContent(char *buf,
|
|||
// check for end-of-file
|
||||
if ((mContentRead == mContentLength) ||
|
||||
(mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
|
||||
MutexAutoLock lock(*nsHttp::GetLock());
|
||||
mForTakeResponseTrailers = mChunkedDecoder
|
||||
? mChunkedDecoder->TakeTrailers()
|
||||
: nullptr;
|
||||
|
||||
// the transaction is done with a complete response.
|
||||
mTransactionDone = true;
|
||||
mResponseIsComplete = true;
|
||||
|
|
|
@ -30,6 +30,7 @@ class nsIRequestContext;
|
|||
namespace mozilla { namespace net {
|
||||
|
||||
class nsHttpChunkedDecoder;
|
||||
class nsHttpHeaderArray;
|
||||
class nsHttpRequestHead;
|
||||
class nsHttpResponseHead;
|
||||
|
||||
|
@ -104,6 +105,10 @@ public:
|
|||
// will drop any reference to the response headers after this call.
|
||||
nsHttpResponseHead *TakeResponseHead();
|
||||
|
||||
// Called to take ownership of the trailer headers.
|
||||
// Returning null if there is no trailer.
|
||||
nsHttpHeaderArray *TakeResponseTrailers();
|
||||
|
||||
// Provides a thread safe reference of the connection
|
||||
// nsHttpTransaction::Connection should only be used on the socket thread
|
||||
already_AddRefed<nsAHttpConnection> GetConnectionReference();
|
||||
|
@ -377,6 +382,8 @@ private:
|
|||
// protected by nsHttp::GetLock()
|
||||
nsHttpResponseHead *mForTakeResponseHead;
|
||||
bool mResponseHeadTaken;
|
||||
nsAutoPtr<nsHttpHeaderArray> mForTakeResponseTrailers;
|
||||
bool mResponseTrailersTaken;
|
||||
|
||||
// The time when the transaction was submitted to the Connection Manager
|
||||
TimeStamp mPendingTime;
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "nsServerTiming.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsServerTiming, nsIServerTiming)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsServerTiming::GetName(nsACString &aName)
|
||||
{
|
||||
aName.Assign(mName);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsServerTiming::GetDuration(double *aDuration)
|
||||
{
|
||||
*aDuration = mDuration;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsServerTiming::GetDescription(nsACString &aDescription)
|
||||
{
|
||||
aDescription.Assign(mDescription);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
static double
|
||||
ParseDouble(const nsACString& aString)
|
||||
{
|
||||
nsresult rv;
|
||||
double val = PromiseFlatCString(aString).ToDouble(&rv);
|
||||
return NS_FAILED(rv) ? 0.0f : val;
|
||||
}
|
||||
|
||||
void
|
||||
ServerTimingParser::Parse()
|
||||
{
|
||||
// https://w3c.github.io/server-timing/#the-server-timing-header-field
|
||||
// Server-Timing = #server-timing-metric
|
||||
// server-timing-metric = metric-name *( OWS ";" OWS server-timing-param )
|
||||
// metric-name = token
|
||||
// server-timing-param = server-timing-param-name OWS "=" OWS server-timing-param-value
|
||||
// server-timing-param-name = token
|
||||
// server-timing-param-value = token / quoted-string
|
||||
|
||||
ParsedHeaderValueListList parsedHeader(mValue, false);
|
||||
for (uint32_t index = 0; index < parsedHeader.mValues.Length(); ++index) {
|
||||
if (parsedHeader.mValues[index].mValues.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// According to spec, the first ParsedHeaderPair's name is metric-name.
|
||||
RefPtr<nsServerTiming> timingHeader = new nsServerTiming();
|
||||
mServerTimingHeaders.AppendElement(timingHeader);
|
||||
timingHeader->SetName(parsedHeader.mValues[index].mValues[0].mName);
|
||||
|
||||
if (parsedHeader.mValues[index].mValues.Length() == 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to find duration and description from the rest ParsedHeaderPairs.
|
||||
bool foundDuration = false;
|
||||
bool foundDescription = false;
|
||||
for (uint32_t pairIndex = 1;
|
||||
pairIndex < parsedHeader.mValues[index].mValues.Length();
|
||||
++pairIndex) {
|
||||
nsDependentCSubstring ¤tName =
|
||||
parsedHeader.mValues[index].mValues[pairIndex].mName;
|
||||
nsDependentCSubstring ¤tValue =
|
||||
parsedHeader.mValues[index].mValues[pairIndex].mValue;
|
||||
|
||||
// We should only take the value from the first
|
||||
// occurrence of server-timing-param-name ("dur" and "desc").
|
||||
if (currentName.LowerCaseEqualsASCII("dur") &&
|
||||
currentValue.BeginReading() &&
|
||||
!foundDuration) {
|
||||
timingHeader->SetDuration(ParseDouble(currentValue));
|
||||
foundDuration = true;
|
||||
} else if (currentName.LowerCaseEqualsASCII("desc") &&
|
||||
!currentValue.IsEmpty() &&
|
||||
!foundDescription) {
|
||||
timingHeader->SetDescription(currentValue);
|
||||
foundDescription = true;
|
||||
}
|
||||
|
||||
if (foundDuration && foundDescription) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<nsCOMPtr<nsIServerTiming>>&&
|
||||
ServerTimingParser::TakeServerTimingHeaders()
|
||||
{
|
||||
return Move(mServerTimingHeaders);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,65 @@
|
|||
/* -*- 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 nsServerTiming_h__
|
||||
#define nsServerTiming_h__
|
||||
|
||||
#include "nsITimedChannel.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsServerTiming final : public nsIServerTiming
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISERVERTIMING
|
||||
|
||||
nsServerTiming() = default;
|
||||
|
||||
void SetName(const nsACString &aName)
|
||||
{
|
||||
mName = aName;
|
||||
}
|
||||
|
||||
void SetDuration(double aDuration)
|
||||
{
|
||||
mDuration = aDuration;
|
||||
}
|
||||
|
||||
void SetDescription(const nsACString &aDescription)
|
||||
{
|
||||
mDescription = aDescription;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~nsServerTiming() = default;
|
||||
|
||||
nsCString mName;
|
||||
double mDuration;
|
||||
nsCString mDescription;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class ServerTimingParser
|
||||
{
|
||||
public:
|
||||
explicit ServerTimingParser(const nsCString &value)
|
||||
: mValue(value)
|
||||
{}
|
||||
void Parse();
|
||||
nsTArray<nsCOMPtr<nsIServerTiming>>&& TakeServerTimingHeaders();
|
||||
|
||||
private:
|
||||
nsCString mValue;
|
||||
nsTArray<nsCOMPtr<nsIServerTiming>> mServerTimingHeaders;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -0,0 +1,232 @@
|
|||
#include "gtest/gtest.h"
|
||||
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/net/nsServerTiming.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
void testServerTimingHeader(const char* headerValue,
|
||||
std::vector<std::vector<std::string>> expectedResults)
|
||||
{
|
||||
nsAutoCString header(headerValue);
|
||||
ServerTimingParser parser(header);
|
||||
parser.Parse();
|
||||
|
||||
nsTArray<nsCOMPtr<nsIServerTiming>> results = parser.TakeServerTimingHeaders();
|
||||
|
||||
ASSERT_EQ(results.Length(), expectedResults.size());
|
||||
|
||||
unsigned i = 0;
|
||||
for (const auto& header : results) {
|
||||
std::vector<std::string> expectedResult(expectedResults[i++]);
|
||||
nsCString name;
|
||||
mozilla::Unused << header->GetName(name);
|
||||
ASSERT_TRUE(name.Equals(expectedResult[0].c_str()));
|
||||
|
||||
double duration;
|
||||
mozilla::Unused << header->GetDuration(&duration);
|
||||
ASSERT_EQ(duration, atof(expectedResult[1].c_str()));
|
||||
|
||||
nsCString description;
|
||||
mozilla::Unused << header->GetDescription(description);
|
||||
ASSERT_TRUE(description.Equals(expectedResult[2].c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestServerTimingHeader, HeaderParsing) {
|
||||
// Test cases below are copied from
|
||||
// https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/network/HTTPParsersTest.cpp
|
||||
|
||||
testServerTimingHeader("", {});
|
||||
testServerTimingHeader("metric", {{"metric", "0", ""}});
|
||||
testServerTimingHeader("metric;dur", {{"metric", "0", ""}});
|
||||
testServerTimingHeader("metric;dur=123.4", {{"metric", "123.4", ""}});
|
||||
testServerTimingHeader("metric;dur=\"123.4\"", {{"metric", "123.4", ""}});
|
||||
|
||||
testServerTimingHeader("metric;desc", {{"metric", "0", ""}});
|
||||
testServerTimingHeader("metric;desc=description",
|
||||
{{"metric", "0", "description"}});
|
||||
testServerTimingHeader("metric;desc=\"description\"",
|
||||
{{"metric", "0", "description"}});
|
||||
|
||||
testServerTimingHeader("metric;dur;desc", {{"metric", "0", ""}});
|
||||
testServerTimingHeader("metric;dur=123.4;desc", {{"metric", "123.4", ""}});
|
||||
testServerTimingHeader("metric;dur;desc=description",
|
||||
{{"metric", "0", "description"}});
|
||||
testServerTimingHeader("metric;dur=123.4;desc=description",
|
||||
{{"metric", "123.4", "description"}});
|
||||
testServerTimingHeader("metric;desc;dur", {{"metric", "0", ""}});
|
||||
testServerTimingHeader("metric;desc;dur=123.4", {{"metric", "123.4", ""}});
|
||||
testServerTimingHeader("metric;desc=description;dur",
|
||||
{{"metric", "0", "description"}});
|
||||
testServerTimingHeader("metric;desc=description;dur=123.4",
|
||||
{{"metric", "123.4", "description"}});
|
||||
|
||||
// special chars in name
|
||||
testServerTimingHeader("aB3!#$%&'*+-.^_`|~",
|
||||
{{"aB3!#$%&'*+-.^_`|~", "0", ""}});
|
||||
|
||||
// delimiter chars in quoted description
|
||||
testServerTimingHeader("metric;desc=\"descr;,=iption\";dur=123.4",
|
||||
{{"metric", "123.4", "descr;,=iption"}});
|
||||
|
||||
// whitespace
|
||||
testServerTimingHeader("metric ; ", {{"metric", "0", ""}});
|
||||
testServerTimingHeader("metric , ", {{"metric", "0", ""}});
|
||||
testServerTimingHeader("metric ; dur = 123.4 ; desc = description",
|
||||
{{"metric", "123.4", "description"}});
|
||||
testServerTimingHeader("metric ; desc = description ; dur = 123.4",
|
||||
{{"metric", "123.4", "description"}});
|
||||
|
||||
// multiple entries
|
||||
testServerTimingHeader(
|
||||
"metric1;dur=12.3;desc=description1,metric2;dur=45.6;"
|
||||
"desc=description2,metric3;dur=78.9;desc=description3",
|
||||
{{"metric1", "12.3", "description1"},
|
||||
{"metric2", "45.6", "description2"},
|
||||
{"metric3", "78.9", "description3"}});
|
||||
testServerTimingHeader("metric1,metric2 ,metric3, metric4 , metric5",
|
||||
{{"metric1", "0", ""},
|
||||
{"metric2", "0", ""},
|
||||
{"metric3", "0", ""},
|
||||
{"metric4", "0", ""},
|
||||
{"metric5", "0", ""}});
|
||||
|
||||
// quoted-strings
|
||||
// metric;desc=\ --> ''
|
||||
testServerTimingHeader("metric;desc=\\", {{"metric", "0", ""}});
|
||||
// metric;desc=" --> ''
|
||||
testServerTimingHeader("metric;desc=\"", {{"metric", "0", ""}});
|
||||
// metric;desc=\\ --> ''
|
||||
testServerTimingHeader("metric;desc=\\\\", {{"metric", "0", ""}});
|
||||
// metric;desc=\" --> ''
|
||||
testServerTimingHeader("metric;desc=\\\"", {{"metric", "0", ""}});
|
||||
// metric;desc="\ --> ''
|
||||
testServerTimingHeader("metric;desc=\"\\", {{"metric", "0", ""}});
|
||||
// metric;desc="" --> ''
|
||||
testServerTimingHeader("metric;desc=\"\"", {{"metric", "0", ""}});
|
||||
// metric;desc=\\\ --> ''
|
||||
testServerTimingHeader("metric;desc=\\\\\\", {{"metric", "0", ""}});
|
||||
// metric;desc=\\" --> ''
|
||||
testServerTimingHeader("metric;desc=\\\\\"", {{"metric", "0", ""}});
|
||||
// metric;desc=\"\ --> ''
|
||||
testServerTimingHeader("metric;desc=\\\"\\", {{"metric", "0", ""}});
|
||||
// metric;desc=\"" --> ''
|
||||
testServerTimingHeader("metric;desc=\\\"\"", {{"metric", "0", ""}});
|
||||
// metric;desc="\\ --> ''
|
||||
testServerTimingHeader("metric;desc=\"\\\\", {{"metric", "0", ""}});
|
||||
// metric;desc="\" --> ''
|
||||
testServerTimingHeader("metric;desc=\"\\\"", {{"metric", "0", ""}});
|
||||
// metric;desc=""\ --> ''
|
||||
testServerTimingHeader("metric;desc=\"\"\\", {{"metric", "0", ""}});
|
||||
// metric;desc=""" --> ''
|
||||
testServerTimingHeader("metric;desc=\"\"\"", {{"metric", "0", ""}});
|
||||
// metric;desc=\\\\ --> ''
|
||||
testServerTimingHeader("metric;desc=\\\\\\\\", {{"metric", "0", ""}});
|
||||
// metric;desc=\\\" --> ''
|
||||
testServerTimingHeader("metric;desc=\\\\\\\"", {{"metric", "0", ""}});
|
||||
// metric;desc=\\"\ --> ''
|
||||
testServerTimingHeader("metric;desc=\\\\\"\\", {{"metric", "0", ""}});
|
||||
// metric;desc=\\"" --> ''
|
||||
testServerTimingHeader("metric;desc=\\\\\"\"", {{"metric", "0", ""}});
|
||||
// metric;desc=\"\\ --> ''
|
||||
testServerTimingHeader("metric;desc=\\\"\\\\", {{"metric", "0", ""}});
|
||||
// metric;desc=\"\" --> ''
|
||||
testServerTimingHeader("metric;desc=\\\"\\\"", {{"metric", "0", ""}});
|
||||
// metric;desc=\""\ --> ''
|
||||
testServerTimingHeader("metric;desc=\\\"\"\\", {{"metric", "0", ""}});
|
||||
// metric;desc=\""" --> ''
|
||||
testServerTimingHeader("metric;desc=\\\"\"\"", {{"metric", "0", ""}});
|
||||
// metric;desc="\\\ --> ''
|
||||
testServerTimingHeader("metric;desc=\"\\\\\\", {{"metric", "0", ""}});
|
||||
// metric;desc="\\" --> '\'
|
||||
testServerTimingHeader("metric;desc=\"\\\\\"", {{"metric", "0", "\\"}});
|
||||
// metric;desc="\"\ --> ''
|
||||
testServerTimingHeader("metric;desc=\"\\\"\\", {{"metric", "0", ""}});
|
||||
// metric;desc="\"" --> '"'
|
||||
testServerTimingHeader("metric;desc=\"\\\"\"", {{"metric", "0", "\""}});
|
||||
// metric;desc=""\\ --> ''
|
||||
testServerTimingHeader("metric;desc=\"\"\\\\", {{"metric", "0", ""}});
|
||||
// metric;desc=""\" --> ''
|
||||
testServerTimingHeader("metric;desc=\"\"\\\"", {{"metric", "0", ""}});
|
||||
// metric;desc="""\ --> ''
|
||||
testServerTimingHeader("metric;desc=\"\"\"\\", {{"metric", "0", ""}});
|
||||
// metric;desc="""" --> ''
|
||||
testServerTimingHeader("metric;desc=\"\"\"\"", {{"metric", "0", ""}});
|
||||
|
||||
// duplicate entry names
|
||||
testServerTimingHeader(
|
||||
"metric;dur=12.3;desc=description1,metric;dur=45.6;"
|
||||
"desc=description2",
|
||||
{{"metric", "12.3", "description1"}, {"metric", "45.6", "description2"}});
|
||||
|
||||
// non-numeric durations
|
||||
testServerTimingHeader("metric;dur=foo", {{"metric", "0", ""}});
|
||||
testServerTimingHeader("metric;dur=\"foo\"", {{"metric", "0", ""}});
|
||||
|
||||
// unrecognized param names
|
||||
testServerTimingHeader(
|
||||
"metric;foo=bar;desc=description;foo=bar;dur=123.4;foo=bar",
|
||||
{{"metric", "123.4", "description"}});
|
||||
|
||||
// duplicate param names
|
||||
testServerTimingHeader("metric;dur=123.4;dur=567.8",
|
||||
{{"metric", "123.4", ""}});
|
||||
testServerTimingHeader("metric;desc=description1;desc=description2",
|
||||
{{"metric", "0", "description1"}});
|
||||
testServerTimingHeader("metric;dur=foo;dur=567.8", {{"metric", "", ""}});
|
||||
|
||||
// unspecified param values
|
||||
testServerTimingHeader("metric;dur;dur=123.4", {{"metric", "123.4", ""}});
|
||||
testServerTimingHeader("metric;desc;desc=description",
|
||||
{{"metric", "0", "description"}});
|
||||
|
||||
// param name case
|
||||
testServerTimingHeader("metric;DuR=123.4;DeSc=description",
|
||||
{{"metric", "123.4", "description"}});
|
||||
|
||||
// nonsense
|
||||
testServerTimingHeader("metric=foo;dur;dur=123.4,metric2",
|
||||
{{"metric", "123.4", ""}, {"metric2", "0", ""}});
|
||||
testServerTimingHeader("metric\"foo;dur;dur=123.4,metric2",
|
||||
{{"metric", "", ""}});
|
||||
|
||||
// nonsense - return zero entries
|
||||
testServerTimingHeader(" ", {});
|
||||
testServerTimingHeader("=", {});
|
||||
testServerTimingHeader("[", {});
|
||||
testServerTimingHeader("]", {});
|
||||
testServerTimingHeader(";", {});
|
||||
testServerTimingHeader(",", {});
|
||||
testServerTimingHeader("=;", {});
|
||||
testServerTimingHeader(";=", {});
|
||||
testServerTimingHeader("=,", {});
|
||||
testServerTimingHeader(",=", {});
|
||||
testServerTimingHeader(";,", {});
|
||||
testServerTimingHeader(",;", {});
|
||||
testServerTimingHeader("=;,", {});
|
||||
|
||||
// Invalid token
|
||||
testServerTimingHeader("met=ric", {{"met", "0", ""}});
|
||||
testServerTimingHeader("met ric", {{"met", "0", ""}});
|
||||
testServerTimingHeader("met[ric", {{"met", "0", ""}});
|
||||
testServerTimingHeader("met]ric", {{"met", "0", ""}});
|
||||
testServerTimingHeader("metric;desc=desc=123, metric2",
|
||||
{{"metric", "0", "desc"}, {"metric2", "0", ""}});
|
||||
testServerTimingHeader("met ric;desc=de sc , metric2",
|
||||
{{"met", "0", "de"}, {"metric2", "0", ""}});
|
||||
|
||||
// test cases from https://w3c.github.io/server-timing/#examples
|
||||
testServerTimingHeader(" miss, ,db;dur=53, app;dur=47.2 ",
|
||||
{{"miss", "0", ""}, {"db", "53", ""}, {"app", "47.2", ""}});
|
||||
testServerTimingHeader(" customView, dc;desc=atl ",
|
||||
{{"customView", "0", ""}, {"dc", "0", "atl"}});
|
||||
testServerTimingHeader(" total;dur=123.4 ",
|
||||
{{"total", "123.4", ""}});
|
||||
|
||||
// test cases for comma in quoted string
|
||||
testServerTimingHeader(" metric ; desc=\"descr\\\"\\\";,=iption\";dur=123.4",
|
||||
{{"metric", "123.4", "descr\"\";,=iption"}});
|
||||
testServerTimingHeader(" metric2;dur=\"123.4\";;desc=\",;\\\",;,\";;, metric ; desc = \" \\\", ;\\\" \"; dur=123.4,",
|
||||
{{"metric2", "123.4", ",;\",;,"}, {"metric", "123.4", " \", ;\" "}});
|
||||
}
|
|
@ -12,6 +12,7 @@ UNIFIED_SOURCES += [
|
|||
'TestPartiallySeekableInputStream.cpp',
|
||||
'TestProtocolProxyService.cpp',
|
||||
'TestReadStreamToString.cpp',
|
||||
'TestServerTimingHeader.cpp',
|
||||
'TestStandardURL.cpp',
|
||||
'TestURIMutator.cpp',
|
||||
]
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/* 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/. */
|
||||
|
||||
//
|
||||
// HTTP Server-Timing header test
|
||||
//
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "URL", function() {
|
||||
return "http://localhost:" + httpServer.identity.primaryPort + "/content";
|
||||
});
|
||||
|
||||
let httpServer = null;
|
||||
|
||||
function make_and_open_channel(url, callback) {
|
||||
let chan = NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true});
|
||||
chan.asyncOpen2(new ChannelListener(callback, null, CL_ALLOW_UNKNOWN_CL));
|
||||
}
|
||||
|
||||
var respnseServerTiming = [{metric:"metric", duration:"123.4", description:"description"},
|
||||
{metric:"metric2", duration:"456.78", description:"description1"}];
|
||||
var trailerServerTiming = [{metric:"metric3", duration:"789.11", description:"description2"},
|
||||
{metric:"metric4", duration:"1112.13", description:"description3"}];
|
||||
|
||||
function createServerTimingHeader(headerData) {
|
||||
var header = "";
|
||||
for (var i = 0; i < headerData.length; i++) {
|
||||
header += "Server-Timing:" + headerData[i].metric + ";" +
|
||||
"dur=" + headerData[i].duration + ";" +
|
||||
"desc=" + headerData[i].description + "\r\n";
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
function contentHandler(metadata, response)
|
||||
{
|
||||
var body = "c\r\ndata reached\r\n3\r\nhej\r\n0\r\n";
|
||||
|
||||
response.seizePower();
|
||||
response.write("HTTP/1.1 200 OK\r\n");
|
||||
response.write("Content-Type: text/plain\r\n");
|
||||
response.write(createServerTimingHeader(respnseServerTiming));
|
||||
|
||||
response.write("Transfer-Encoding: chunked\r\n");
|
||||
response.write("\r\n");
|
||||
response.write(body);
|
||||
response.write(createServerTimingHeader(trailerServerTiming));
|
||||
response.write("\r\n");
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/content", contentHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
do_test_pending();
|
||||
make_and_open_channel(URL, readServerContent);
|
||||
}
|
||||
|
||||
function checkServerTimingContent(headers) {
|
||||
var expectedResult = respnseServerTiming.concat(trailerServerTiming);
|
||||
Assert.equal(headers.length, expectedResult.length);
|
||||
|
||||
for (var i = 0; i < expectedResult.length; i++) {
|
||||
let header = headers.queryElementAt(i, Ci.nsIServerTiming);
|
||||
Assert.equal(header.name, expectedResult[i].metric);
|
||||
Assert.equal(header.description, expectedResult[i].description);
|
||||
Assert.equal(header.duration, parseFloat(expectedResult[i].duration));
|
||||
}
|
||||
}
|
||||
|
||||
function readServerContent(request, buffer)
|
||||
{
|
||||
let channel = request.QueryInterface(Ci.nsITimedChannel);
|
||||
let headers = channel.serverTiming.QueryInterface(Ci.nsIArray);
|
||||
checkServerTimingContent(headers);
|
||||
|
||||
httpServer.stop(do_test_finished);
|
||||
}
|
|
@ -410,3 +410,4 @@ skip-if = os == "android"
|
|||
[test_tls_flags.js]
|
||||
[test_uri_mutator.js]
|
||||
[test_bug1411316_http1.js]
|
||||
[test_header_Server_Timing.js]
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "nsISupports.h"
|
||||
#include "nsRDFBaseDataSources.h"
|
||||
#include "nsRDFBuiltInDataSources.h"
|
||||
#include "nsFileSystemDataSource.h"
|
||||
#include "nsRDFCID.h"
|
||||
#include "nsIComponentManager.h"
|
||||
#include "rdf.h"
|
||||
|
@ -66,7 +65,6 @@ MAKE_CTOR(RDFContentSink,RDFContentSink,RDFContentSink)
|
|||
MAKE_CTOR(RDFDefaultResource,DefaultResource,RDFResource)
|
||||
|
||||
NS_DEFINE_NAMED_CID(NS_RDFCOMPOSITEDATASOURCE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_RDFFILESYSTEMDATASOURCE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_RDFINMEMORYDATASOURCE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_RDFXMLDATASOURCE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_RDFDEFAULTRESOURCE_CID);
|
||||
|
@ -81,7 +79,6 @@ NS_DEFINE_NAMED_CID(NS_LOCALSTORE_CID);
|
|||
|
||||
static const mozilla::Module::CIDEntry kRDFCIDs[] = {
|
||||
{ &kNS_RDFCOMPOSITEDATASOURCE_CID, false, nullptr, CreateNewRDFCompositeDataSource },
|
||||
{ &kNS_RDFFILESYSTEMDATASOURCE_CID, false, nullptr, FileSystemDataSource::Create },
|
||||
{ &kNS_RDFINMEMORYDATASOURCE_CID, false, nullptr, NS_NewRDFInMemoryDataSource },
|
||||
{ &kNS_RDFXMLDATASOURCE_CID, false, nullptr, CreateNewRDFXMLDataSource },
|
||||
{ &kNS_RDFDEFAULTRESOURCE_CID, false, nullptr, CreateNewRDFDefaultResource },
|
||||
|
@ -97,7 +94,6 @@ static const mozilla::Module::CIDEntry kRDFCIDs[] = {
|
|||
|
||||
static const mozilla::Module::ContractIDEntry kRDFContracts[] = {
|
||||
{ NS_RDF_DATASOURCE_CONTRACTID_PREFIX "composite-datasource", &kNS_RDFCOMPOSITEDATASOURCE_CID },
|
||||
{ NS_RDF_DATASOURCE_CONTRACTID_PREFIX "files", &kNS_RDFFILESYSTEMDATASOURCE_CID },
|
||||
{ NS_RDF_DATASOURCE_CONTRACTID_PREFIX "in-memory-datasource", &kNS_RDFINMEMORYDATASOURCE_CID },
|
||||
{ NS_RDF_DATASOURCE_CONTRACTID_PREFIX "xml-datasource", &kNS_RDFXMLDATASOURCE_CID },
|
||||
{ NS_RDF_RESOURCE_FACTORY_CONTRACTID, &kNS_RDFDEFAULTRESOURCE_CID },
|
||||
|
|
|
@ -9,7 +9,6 @@ EXPORTS += [
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsFileSystemDataSource.cpp',
|
||||
'nsLocalStore.cpp',
|
||||
]
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,79 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 nsFileSystemDataSource_h__
|
||||
#define nsFileSystemDataSource_h__
|
||||
|
||||
#include "nsIRDFDataSource.h"
|
||||
#include "nsIRDFLiteral.h"
|
||||
#include "nsIRDFResource.h"
|
||||
#include "nsIRDFService.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#if defined(XP_UNIX) || defined(XP_WIN)
|
||||
#define USE_NC_EXTENSION
|
||||
#endif
|
||||
|
||||
class FileSystemDataSource final : public nsIRDFDataSource
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIRDFDATASOURCE
|
||||
|
||||
static nsresult Create(nsISupports* aOuter,
|
||||
const nsIID& aIID, void **aResult);
|
||||
|
||||
nsresult Init();
|
||||
|
||||
private:
|
||||
FileSystemDataSource() { }
|
||||
~FileSystemDataSource() { }
|
||||
|
||||
// helper methods
|
||||
bool isFileURI(nsIRDFResource* aResource);
|
||||
bool isDirURI(nsIRDFResource* aSource);
|
||||
nsresult GetVolumeList(nsISimpleEnumerator **aResult);
|
||||
nsresult GetFolderList(nsIRDFResource *source, bool allowHidden, bool onlyFirst, nsISimpleEnumerator **aResult);
|
||||
nsresult GetName(nsIRDFResource *source, nsIRDFLiteral** aResult);
|
||||
nsresult GetURL(nsIRDFResource *source, bool *isFavorite, nsIRDFLiteral** aResult);
|
||||
nsresult GetFileSize(nsIRDFResource *source, nsIRDFInt** aResult);
|
||||
nsresult GetLastMod(nsIRDFResource *source, nsIRDFDate** aResult);
|
||||
|
||||
nsCOMPtr<nsIRDFService> mRDFService;
|
||||
|
||||
// pseudo-constants
|
||||
nsCOMPtr<nsIRDFResource> mNC_FileSystemRoot;
|
||||
nsCOMPtr<nsIRDFResource> mNC_Child;
|
||||
nsCOMPtr<nsIRDFResource> mNC_Name;
|
||||
nsCOMPtr<nsIRDFResource> mNC_URL;
|
||||
nsCOMPtr<nsIRDFResource> mNC_Icon;
|
||||
nsCOMPtr<nsIRDFResource> mNC_Length;
|
||||
nsCOMPtr<nsIRDFResource> mNC_IsDirectory;
|
||||
nsCOMPtr<nsIRDFResource> mWEB_LastMod;
|
||||
nsCOMPtr<nsIRDFResource> mNC_FileSystemObject;
|
||||
nsCOMPtr<nsIRDFResource> mNC_pulse;
|
||||
nsCOMPtr<nsIRDFResource> mRDF_InstanceOf;
|
||||
nsCOMPtr<nsIRDFResource> mRDF_type;
|
||||
|
||||
nsCOMPtr<nsIRDFLiteral> mLiteralTrue;
|
||||
nsCOMPtr<nsIRDFLiteral> mLiteralFalse;
|
||||
|
||||
#ifdef USE_NC_EXTENSION
|
||||
nsresult GetExtension(nsIRDFResource *source, nsIRDFLiteral** aResult);
|
||||
nsCOMPtr<nsIRDFResource> mNC_extension;
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
bool isValidFolder(nsIRDFResource *source);
|
||||
nsresult getIEFavoriteURL(nsIRDFResource *source, nsString aFileURL, nsIRDFLiteral **urlLiteral);
|
||||
nsCOMPtr<nsIRDFResource> mNC_IEFavoriteObject;
|
||||
nsCOMPtr<nsIRDFResource> mNC_IEFavoriteFolder;
|
||||
nsCString ieFavoritesDir;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // nsFileSystemDataSource_h__
|
|
@ -177,7 +177,8 @@ config = {
|
|||
'tests': ["tests/reftest/tests/testing/crashtest/crashtests.list"]
|
||||
},
|
||||
"jsreftest": {
|
||||
'options':["--extra-profile-file=tests/jsreftest/tests/user.js"],
|
||||
'options':["--extra-profile-file=tests/jsreftest/tests/user.js",
|
||||
"--suite=jstestbrowser"],
|
||||
'tests': ["tests/jsreftest/tests/jstests.list"]
|
||||
},
|
||||
"reftest": {
|
||||
|
|
|
@ -201,7 +201,8 @@ config = {
|
|||
'tests': ["tests/reftest/tests/testing/crashtest/crashtests.list"]
|
||||
},
|
||||
"jsreftest": {
|
||||
'options':["--extra-profile-file=tests/jsreftest/tests/user.js"],
|
||||
'options':["--extra-profile-file=tests/jsreftest/tests/user.js",
|
||||
"--suite=jstestbrowser"],
|
||||
'tests': ["tests/jsreftest/tests/jstests.list"]
|
||||
},
|
||||
"reftest": {
|
||||
|
|
|
@ -189,7 +189,8 @@ config = {
|
|||
'tests': ["tests/reftest/tests/testing/crashtest/crashtests.list"]
|
||||
},
|
||||
"jsreftest": {
|
||||
'options':["--extra-profile-file=tests/jsreftest/tests/user.js"],
|
||||
'options':["--extra-profile-file=tests/jsreftest/tests/user.js",
|
||||
"--suite=jstestbrowser"],
|
||||
'tests': ["tests/jsreftest/tests/jstests.list"]
|
||||
},
|
||||
"reftest": {
|
||||
|
|
|
@ -31,6 +31,7 @@ class VerifyToolsMixin(object):
|
|||
self.verify_suites = {}
|
||||
self.verify_downloaded = False
|
||||
self.reftest_test_dir = None
|
||||
self.jsreftest_test_dir = None
|
||||
|
||||
def _find_misc_tests(self, dirs, changed_files):
|
||||
manifests = [
|
||||
|
@ -51,7 +52,6 @@ class VerifyToolsMixin(object):
|
|||
ref_manifests = [
|
||||
(os.path.join(dirs['abs_reftest_dir'], 'tests', 'layout', 'reftests', 'reftest.list'), 'reftest'),
|
||||
(os.path.join(dirs['abs_reftest_dir'], 'tests', 'testing', 'crashtest', 'crashtests.list'), 'crashtest'),
|
||||
# TODO (os.path.join(dirs['abs_test_install_dir'], 'jsreftest', 'tests', 'jstests.list'), 'jstestbrowser'),
|
||||
]
|
||||
sys.path.append(dirs['abs_reftest_dir'])
|
||||
import manifest
|
||||
|
@ -63,6 +63,26 @@ class VerifyToolsMixin(object):
|
|||
tests_by_path.update({os.path.relpath(t,self.reftest_test_dir):(suite,None) for t in man.files})
|
||||
self.info("Verification updated with manifest %s" % path)
|
||||
|
||||
suite = 'jsreftest'
|
||||
self.jsreftest_test_dir = os.path.join(dirs['abs_test_install_dir'], 'jsreftest', 'tests')
|
||||
path = os.path.join(self.jsreftest_test_dir, 'jstests.list')
|
||||
if os.path.exists(path):
|
||||
man = manifest.ReftestManifest()
|
||||
man.load(path)
|
||||
for t in man.files:
|
||||
# expect manifest test to look like:
|
||||
# ".../tests/jsreftest/tests/jsreftest.html?test=test262/.../some_test.js"
|
||||
# while the test is in mercurial at:
|
||||
# js/src/tests/test262/.../some_test.js
|
||||
epos = t.find('=')
|
||||
if epos > 0:
|
||||
relpath = t[epos+1:]
|
||||
relpath = os.path.join('js', 'src', 'tests', relpath)
|
||||
tests_by_path.update({relpath:(suite,None)})
|
||||
else:
|
||||
self.warning("unexpected jsreftest test format: %s" % str(t))
|
||||
self.info("Verification updated with manifest %s" % path)
|
||||
|
||||
# for each changed file, determine if it is a test file, and what suite it is in
|
||||
for file in changed_files:
|
||||
# manifest paths use os.sep (like backslash on Windows) but
|
||||
|
@ -191,10 +211,17 @@ class VerifyToolsMixin(object):
|
|||
# otherwise, run once for each file in requested suite
|
||||
references = re.compile(r"(-ref|-noref|-noref.)\.")
|
||||
files = []
|
||||
jsreftest_extra_dir = os.path.join('js', 'src', 'tests')
|
||||
# For some suites, the test path needs to be updated before passing to
|
||||
# the test harness.
|
||||
for file in self.verify_suites.get(suite):
|
||||
if (self.config.get('verify_category') != "web-platform" and
|
||||
suite in ['reftest', 'crashtest']):
|
||||
file = os.path.join(self.reftest_test_dir, file)
|
||||
elif (self.config.get('verify_category') != "web-platform" and
|
||||
suite == 'jsreftest'):
|
||||
file = os.path.relpath(file, jsreftest_extra_dir)
|
||||
file = os.path.join(self.jsreftest_test_dir, file)
|
||||
files.append(file)
|
||||
for file in files:
|
||||
if self.config.get('verify_category') == "web-platform":
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[instantiation-error-3.html]
|
||||
disabled: bug 1426195
|
|
@ -1,5 +0,0 @@
|
|||
[module-in-xhtml.xhtml]
|
||||
type: testharness
|
||||
[module script in XHTML documents should be evaluated.]
|
||||
expected: FAIL
|
||||
|
|
@ -28,4 +28,4 @@
|
|||
<script type="module" src="./cycle-unresolvable-a.js"
|
||||
onerror="unreachable()" onload="log.push(2)"></script>
|
||||
<script type="module" src="./cycle-unresolvable.js"
|
||||
onerror="unreachable()" onload="log.push(3)" async></script>
|
||||
onerror="unreachable()" onload="log.push(3)"></script>
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
window.evaluated_module_script = true;
|
||||
</script>
|
||||
<script>
|
||||
test(() => assert_true(window.evaluated_module_script), "module script in XHTML documents should be evaluated.");
|
||||
var test = async_test("module script in XHTML documents should be evaluated.");
|
||||
window.addEventListener("load", () => {
|
||||
test.step(() => { assert_true(window.evaluated_module_script); });
|
||||
test.done();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var gFindBundle;
|
||||
|
||||
function nsFindInstData() {}
|
||||
nsFindInstData.prototype =
|
||||
{
|
||||
// set the next three attributes on your object to override the defaults
|
||||
browser: null,
|
||||
|
||||
get rootSearchWindow() { return this._root || this.window.content; },
|
||||
set rootSearchWindow(val) { this._root = val; },
|
||||
|
||||
get currentSearchWindow() {
|
||||
if (this._current)
|
||||
return this._current;
|
||||
|
||||
var focusedWindow = this.window.document.commandDispatcher.focusedWindow;
|
||||
if (!focusedWindow || focusedWindow == this.window)
|
||||
focusedWindow = this.window.content;
|
||||
|
||||
return focusedWindow;
|
||||
},
|
||||
set currentSearchWindow(val) { this._current = val; },
|
||||
|
||||
get webBrowserFind() { return this.browser.webBrowserFind; },
|
||||
|
||||
init() {
|
||||
var findInst = this.webBrowserFind;
|
||||
// set up the find to search the focussedWindow, bounded by the content window.
|
||||
var findInFrames = findInst.QueryInterface(Components.interfaces.nsIWebBrowserFindInFrames);
|
||||
findInFrames.rootSearchFrame = this.rootSearchWindow;
|
||||
findInFrames.currentSearchFrame = this.currentSearchWindow;
|
||||
|
||||
// always search in frames for now. We could add a checkbox to the dialog for this.
|
||||
findInst.searchFrames = true;
|
||||
},
|
||||
|
||||
window,
|
||||
_root: null,
|
||||
_current: null
|
||||
};
|
||||
|
||||
// browser is the <browser> element
|
||||
// rootSearchWindow is the window to constrain the search to (normally window.content)
|
||||
// currentSearchWindow is the frame to start searching (can be, and normally, rootSearchWindow)
|
||||
function findInPage(findInstData) {
|
||||
// is the dialog up already?
|
||||
if ("findDialog" in window && window.findDialog)
|
||||
window.findDialog.focus();
|
||||
else {
|
||||
findInstData.init();
|
||||
window.findDialog = window.openDialog("chrome://global/content/finddialog.xul", "_blank", "chrome,resizable=no,dependent=yes", findInstData);
|
||||
}
|
||||
}
|
||||
|
||||
function findAgainInPage(findInstData, reverse) {
|
||||
if ("findDialog" in window && window.findDialog)
|
||||
window.findDialog.focus();
|
||||
else {
|
||||
// get the find service, which stores global find state, and init the
|
||||
// nsIWebBrowser find with it. We don't assume that there was a previous
|
||||
// Find that set this up.
|
||||
var findService = Components.classes["@mozilla.org/find/find_service;1"]
|
||||
.getService(Components.interfaces.nsIFindService);
|
||||
|
||||
var searchString = findService.searchString;
|
||||
if (searchString.length == 0) {
|
||||
// no previous find text
|
||||
findInPage(findInstData);
|
||||
return;
|
||||
}
|
||||
|
||||
findInstData.init();
|
||||
var findInst = findInstData.webBrowserFind;
|
||||
findInst.searchString = searchString;
|
||||
findInst.matchCase = findService.matchCase;
|
||||
findInst.wrapFind = findService.wrapFind;
|
||||
findInst.entireWord = findService.entireWord;
|
||||
findInst.findBackwards = findService.findBackwards ^ reverse;
|
||||
|
||||
var found = findInst.findNext();
|
||||
if (!found) {
|
||||
if (!gFindBundle)
|
||||
gFindBundle = document.getElementById("findBundle");
|
||||
|
||||
Services.prompt.alert(window, gFindBundle.getString("notFoundTitle"), gFindBundle.getString("notFoundWarning"));
|
||||
}
|
||||
|
||||
// Reset to normal value, otherwise setting can get changed in find dialog
|
||||
findInst.findBackwards = findService.findBackwards;
|
||||
}
|
||||
}
|
||||
|
||||
function canFindAgainInPage() {
|
||||
var findService = Components.classes["@mozilla.org/find/find_service;1"]
|
||||
.getService(Components.interfaces.nsIFindService);
|
||||
return (findService.searchString.length > 0);
|
||||
}
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Defined in dialog.xml.
|
||||
/* globals moveToAlertPosition */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
var dialog; // Quick access to document/form elements.
|
||||
var gFindInst; // nsIWebBrowserFind that we're going to use
|
||||
var gFindInstData; // use this to update the find inst data
|
||||
|
||||
function initDialogObject() {
|
||||
// Create dialog object and initialize.
|
||||
dialog = {};
|
||||
dialog.findKey = document.getElementById("dialog.findKey");
|
||||
dialog.caseSensitive = document.getElementById("dialog.caseSensitive");
|
||||
dialog.wrap = document.getElementById("dialog.wrap");
|
||||
dialog.find = document.getElementById("btnFind");
|
||||
dialog.up = document.getElementById("radioUp");
|
||||
dialog.down = document.getElementById("radioDown");
|
||||
dialog.rg = dialog.up.radioGroup;
|
||||
dialog.bundle = null;
|
||||
|
||||
// Move dialog to center, if it not been shown before
|
||||
var windowElement = document.getElementById("findDialog");
|
||||
if (!windowElement.hasAttribute("screenX") || !windowElement.hasAttribute("screenY")) {
|
||||
sizeToContent();
|
||||
moveToAlertPosition();
|
||||
}
|
||||
}
|
||||
|
||||
function fillDialog() {
|
||||
// get the find service, which stores global find state
|
||||
var findService = Components.classes["@mozilla.org/find/find_service;1"]
|
||||
.getService(Components.interfaces.nsIFindService);
|
||||
|
||||
// Set initial dialog field contents. Use the gFindInst attributes first,
|
||||
// this is necessary for window.find()
|
||||
dialog.findKey.value = gFindInst.searchString ? gFindInst.searchString : findService.searchString;
|
||||
dialog.caseSensitive.checked = gFindInst.matchCase ? gFindInst.matchCase : findService.matchCase;
|
||||
dialog.wrap.checked = gFindInst.wrapFind ? gFindInst.wrapFind : findService.wrapFind;
|
||||
var findBackwards = gFindInst.findBackwards ? gFindInst.findBackwards : findService.findBackwards;
|
||||
if (findBackwards)
|
||||
dialog.rg.selectedItem = dialog.up;
|
||||
else
|
||||
dialog.rg.selectedItem = dialog.down;
|
||||
}
|
||||
|
||||
function saveFindData() {
|
||||
// get the find service, which stores global find state
|
||||
var findService = Components.classes["@mozilla.org/find/find_service;1"]
|
||||
.getService(Components.interfaces.nsIFindService);
|
||||
|
||||
// Set data attributes per user input.
|
||||
findService.searchString = dialog.findKey.value;
|
||||
findService.matchCase = dialog.caseSensitive.checked;
|
||||
findService.wrapFind = dialog.wrap.checked;
|
||||
findService.findBackwards = dialog.up.selected;
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
initDialogObject();
|
||||
|
||||
// get the find instance
|
||||
var arg0 = window.arguments[0];
|
||||
// If the dialog was opened from window.find(),
|
||||
// arg0 will be an instance of nsIWebBrowserFind
|
||||
if (arg0 instanceof Components.interfaces.nsIWebBrowserFind) {
|
||||
gFindInst = arg0;
|
||||
} else {
|
||||
gFindInstData = arg0;
|
||||
gFindInst = gFindInstData.webBrowserFind;
|
||||
}
|
||||
|
||||
fillDialog();
|
||||
doEnabling();
|
||||
|
||||
if (dialog.findKey.value)
|
||||
dialog.findKey.select();
|
||||
dialog.findKey.focus();
|
||||
}
|
||||
|
||||
function onUnload() {
|
||||
window.opener.findDialog = 0;
|
||||
}
|
||||
|
||||
function onAccept() {
|
||||
if (gFindInstData && gFindInst != gFindInstData.webBrowserFind) {
|
||||
gFindInstData.init();
|
||||
gFindInst = gFindInstData.webBrowserFind;
|
||||
}
|
||||
|
||||
// Transfer dialog contents to the find service.
|
||||
saveFindData();
|
||||
updateFormHistory();
|
||||
|
||||
// set up the find instance
|
||||
gFindInst.searchString = dialog.findKey.value;
|
||||
gFindInst.matchCase = dialog.caseSensitive.checked;
|
||||
gFindInst.wrapFind = dialog.wrap.checked;
|
||||
gFindInst.findBackwards = dialog.up.selected;
|
||||
|
||||
// Search.
|
||||
var result = gFindInst.findNext();
|
||||
|
||||
if (!result) {
|
||||
if (!dialog.bundle)
|
||||
dialog.bundle = document.getElementById("findBundle");
|
||||
Services.prompt.alert(window, dialog.bundle.getString("notFoundTitle"),
|
||||
dialog.bundle.getString("notFoundWarning"));
|
||||
dialog.findKey.select();
|
||||
dialog.findKey.focus();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function doEnabling() {
|
||||
dialog.find.disabled = !dialog.findKey.value;
|
||||
}
|
||||
|
||||
function updateFormHistory() {
|
||||
if (window.opener.PrivateBrowsingUtils &&
|
||||
window.opener.PrivateBrowsingUtils.isWindowPrivate(window.opener) ||
|
||||
!dialog.findKey.value)
|
||||
return;
|
||||
|
||||
if (FormHistory.enabled) {
|
||||
FormHistory.update({
|
||||
op: "bump",
|
||||
fieldname: "find-dialog",
|
||||
value: dialog.findKey.value
|
||||
}, {
|
||||
handleError(aError) {
|
||||
Components.utils.reportError("Saving find to form history failed: " +
|
||||
aError.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
<?xml version="1.0"?> <!-- -*- Mode: HTML -*- -->
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window SYSTEM "chrome://global/locale/finddialog.dtd">
|
||||
|
||||
<dialog id="findDialog"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
orient="horizontal"
|
||||
windowtype="findInPage"
|
||||
onload="onLoad();"
|
||||
onunload="onUnload();"
|
||||
ondialogaccept="return onAccept();"
|
||||
buttons="accept,cancel"
|
||||
title="&findDialog.title;"
|
||||
persist="screenX screenY">
|
||||
|
||||
<script type="application/javascript" src="chrome://global/content/finddialog.js"/>
|
||||
<stringbundle id="findBundle" src="chrome://global/locale/finddialog.properties"/>
|
||||
|
||||
<hbox>
|
||||
<vbox>
|
||||
<hbox align="center">
|
||||
<label value="&findField.label;" accesskey="&findField.accesskey;" control="dialog.findKey"/>
|
||||
<textbox id="dialog.findKey" flex="1"
|
||||
type="autocomplete"
|
||||
autocompletesearch="form-history"
|
||||
autocompletesearchparam="find-dialog"
|
||||
oninput="doEnabling();"/>
|
||||
</hbox>
|
||||
<hbox align="center">
|
||||
<vbox>
|
||||
<checkbox id="dialog.caseSensitive" label="&caseSensitiveCheckbox.label;" accesskey="&caseSensitiveCheckbox.accesskey;"/>
|
||||
<checkbox id="dialog.wrap" label="&wrapCheckbox.label;" accesskey="&wrapCheckbox.accesskey;" checked="true"/>
|
||||
</vbox>
|
||||
<groupbox orient="horizontal">
|
||||
<caption label="&direction.label;"/>
|
||||
<radiogroup orient="horizontal">
|
||||
<radio id="radioUp" label="&up.label;" accesskey="&up.accesskey;"/>
|
||||
<radio id="radioDown" label="&down.label;" accesskey="&down.accesskey;" selected="true"/>
|
||||
</radiogroup>
|
||||
</groupbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<vbox>
|
||||
<button id="btnFind" label="&findButton.label;" accesskey="&findButton.accesskey;"
|
||||
dlgtype="accept" icon="find"/>
|
||||
#ifdef XP_UNIX
|
||||
<button label="&closeButton.label;" icon="close" dlgtype="cancel"/>
|
||||
#else
|
||||
<button label="&cancelButton.label;" icon="cancel" dlgtype="cancel"/>
|
||||
#endif
|
||||
</vbox>
|
||||
</hbox>
|
||||
</dialog>
|
|
@ -49,9 +49,6 @@ toolkit.jar:
|
|||
#ifndef MOZ_FENNEC
|
||||
content/global/editMenuOverlay.js
|
||||
* content/global/editMenuOverlay.xul
|
||||
content/global/finddialog.js
|
||||
* content/global/finddialog.xul
|
||||
content/global/findUtils.js
|
||||
#endif
|
||||
content/global/filepicker.properties
|
||||
content/global/globalOverlay.js
|
||||
|
|
|
@ -263,9 +263,6 @@ with Files('direction*'):
|
|||
with Files('edit*'):
|
||||
BUG_COMPONENT = ('Toolkit', 'XUL Widgets')
|
||||
|
||||
with Files('*find*'):
|
||||
BUG_COMPONENT = ('Toolkit', 'Find Toolbar')
|
||||
|
||||
with Files('globalOverlay.*'):
|
||||
BUG_COMPONENT = ('Toolkit', 'General')
|
||||
|
||||
|
|
|
@ -1111,6 +1111,7 @@ timepicker {
|
|||
/*********** findbar ************/
|
||||
findbar {
|
||||
-moz-binding: url('chrome://global/content/bindings/findbar.xml#findbar');
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.findbar-textbox {
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- extracted from finddialog.xul -->
|
||||
|
||||
<!ENTITY findDialog.title "Find in This Page">
|
||||
<!ENTITY findField.label "Find what:">
|
||||
<!ENTITY findField.accesskey "n">
|
||||
<!ENTITY caseSensitiveCheckbox.label "Match case">
|
||||
<!ENTITY caseSensitiveCheckbox.accesskey "c">
|
||||
<!ENTITY wrapCheckbox.label "Wrap">
|
||||
<!ENTITY wrapCheckbox.accesskey "W">
|
||||
<!ENTITY findButton.label "Find Next">
|
||||
<!ENTITY findButton.accesskey "F">
|
||||
<!ENTITY cancelButton.label "Cancel">
|
||||
<!ENTITY closeButton.label "Close">
|
||||
<!ENTITY up.label "Up">
|
||||
<!ENTITY up.accesskey "U">
|
||||
<!ENTITY down.label "Down">
|
||||
<!ENTITY down.accesskey "D">
|
||||
<!ENTITY direction.label "Direction">
|
|
@ -1,6 +0,0 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
notFoundWarning=The text you entered was not found.
|
||||
notFoundTitle=Find
|
|
@ -54,8 +54,6 @@
|
|||
#ifndef MOZ_FENNEC
|
||||
locale/@AB_CD@/global/findbar.dtd (%chrome/global/findbar.dtd)
|
||||
locale/@AB_CD@/global/findbar.properties (%chrome/global/findbar.properties)
|
||||
locale/@AB_CD@/global/finddialog.dtd (%chrome/global/finddialog.dtd)
|
||||
locale/@AB_CD@/global/finddialog.properties (%chrome/global/finddialog.properties)
|
||||
#endif
|
||||
locale/@AB_CD@/global/globalKeys.dtd (%chrome/global/globalKeys.dtd)
|
||||
locale/@AB_CD@/global/intl.css (%chrome/global/intl.css)
|
||||
|
|
|
@ -23,14 +23,3 @@ dialog {
|
|||
font: menu;
|
||||
}
|
||||
|
||||
/*XXX - belongs to toolkit/content/finddialog.xul: */
|
||||
|
||||
#findDialog,
|
||||
#findDialog > menu,
|
||||
#findDialog > groupbox {
|
||||
font: menu !important;
|
||||
}
|
||||
|
||||
#dialog\.caseSensitive {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче