Merge autoland to m-c, a=merge

MozReview-Commit-ID: 3unzaMHXBGa
This commit is contained in:
Phil Ringnalda 2016-11-29 19:16:09 -08:00
Родитель cba49f2392 cd67cdf6df
Коммит b6cc8777ce
54 изменённых файлов: 1393 добавлений и 315 удалений

Просмотреть файл

@ -5631,7 +5631,12 @@ function middleMousePaste(event) {
function stripUnsafeProtocolOnPaste(pasteData) {
// Don't allow pasting javascript URIs since we don't support
// LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL for those.
return pasteData.replace(/\r?\n/g, "").replace(/^(?:\s*javascript:)+/i, "");
let changed = false;
let pasteDataNoJS = pasteData.replace(/\r?\n/g, "")
.replace(/^(?:\s*javascript:)+/i,
() => { changed = true;
return ""; });
return changed ? pasteDataNoJS : pasteData;
}
// handleDroppedLink has the following 2 overloads:

Просмотреть файл

@ -4612,7 +4612,11 @@
} else {
label = tab.getAttribute("label") +
(this.AppConstants.E10S_TESTING_ONLY && tab.linkedBrowser && tab.linkedBrowser.isRemoteBrowser ? " - e10s" : "");
if (tab.userContextId) {
label = this.mStringBundle.getFormattedString("tabs.containers.tooltip", [label, ContextualIdentityService.getUserContextLabel(tab.userContextId)]);
}
}
event.target.setAttribute("label", label);
]]></body>
</method>

Просмотреть файл

@ -38,6 +38,8 @@ support-files =
skip-if = true # bug 917535, bug 1289765
[browser_locationBarExternalLoad.js]
[browser_moz_action_link.js]
[browser_pasteAndGo.js]
subsuite = clipboard
[browser_removeUnsafeProtocolsFromURLBarPaste.js]
subsuite = clipboard
[browser_search_favicon.js]

Просмотреть файл

@ -0,0 +1,38 @@
"use strict";
let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
add_task(function*() {
const kURLs = [
"http://example.com/1",
"http://example.org/2\n",
"http://\nexample.com/3\n",
];
for (let url of kURLs) {
yield BrowserTestUtils.withNewTab("about:blank", function* (browser) {
gURLBar.focus();
yield new Promise((resolve, reject) => {
waitForClipboard(url, function() {
clipboardHelper.copyString(url);
}, resolve,
() => reject(new Error(`Failed to copy string '${url}' to clipboard`))
);
});
let textBox = document.getAnonymousElementByAttribute(gURLBar,
"anonid", "textbox-input-box");
let cxmenu = document.getAnonymousElementByAttribute(textBox,
"anonid", "input-box-contextmenu");
let cxmenuPromise = BrowserTestUtils.waitForEvent(cxmenu, "popupshown");
EventUtils.synthesizeMouseAtCenter(gURLBar, {type: "contextmenu", button: 2});
yield cxmenuPromise;
let menuitem = document.getAnonymousElementByAttribute(textBox,
"anonid", "paste-and-go");
let browserLoadedPromise = BrowserTestUtils.browserLoaded(browser, url.replace(/\n/g, ""));
EventUtils.synthesizeMouseAtCenter(menuitem, {});
// Using toSource in order to get the newlines escaped:
info("Paste and go, loading " + url.toSource());
yield browserLoadedPromise;
ok(true, "Successfully loaded " + url);
});
}
});

Просмотреть файл

@ -7,6 +7,9 @@ var pairs = [
["javascript:", ""],
["javascript:1+1", "1+1"],
["javascript:document.domain", "document.domain"],
["java\nscript:foo", "foo"],
["http://\nexample.com", "http://example.com"],
["http://\nexample.com\n", "http://example.com"],
["data:text/html,<body>hi</body>", "data:text/html,<body>hi</body>"],
// Nested things get confusing because some things don't parse as URIs:
["javascript:javascript:alert('hi!')", "alert('hi!')"],

Просмотреть файл

@ -49,3 +49,9 @@ tabs.unblockAudio.tooltip=Play tab
# LOCALIZATION NOTE (tabs.allowTabFocusByPromptForSite):
# %S is the hostname of the site where dialogs are allowed to switch tabs
tabs.allowTabFocusByPromptForSite=Allow dialogs from %S to take you to their tab
# LOCALIZATION NOTE (tabs.containers.tooltip):
# Displayed as a tooltip on container tabs
# %1$S is the title of the current tab
# %2$S is the name of the current container
tabs.containers.tooltip=%1$S - %2$S

Просмотреть файл

@ -60,7 +60,7 @@ table.headers.indexedDB.value=Value
table.headers.indexedDB.origin=Origin
table.headers.indexedDB.version=Version
table.headers.indexedDB.objectStores=Object Stores
table.headers.indexedDB.keyPath=Key
table.headers.indexedDB.keyPath2=Key Path
table.headers.indexedDB.autoIncrement=Auto Increment
table.headers.indexedDB.indexes=Indexes

Просмотреть файл

@ -7,6 +7,7 @@ support-files =
storage-cookies.html
storage-empty-objectstores.html
storage-idb-delete-blocked.html
storage-indexeddb-duplicate-names.html
storage-listings.html
storage-localstorage.html
storage-overflow.html
@ -35,6 +36,7 @@ support-files =
[browser_storage_empty_objectstores.js]
[browser_storage_indexeddb_delete.js]
[browser_storage_indexeddb_delete_blocked.js]
[browser_storage_indexeddb_duplicate_names.js]
[browser_storage_localstorage_edit.js]
[browser_storage_localstorage_error.js]
[browser_storage_overflow.js]

Просмотреть файл

@ -53,28 +53,28 @@ const testCases = [
[["sessionStorage", "https://sectest1.example.org"],
["iframe-s-ss1"]],
[["indexedDB", "http://test1.example.org"],
["idb1", "idb2"]],
[["indexedDB", "http://test1.example.org", "idb1"],
["idb1 (default)", "idb2 (default)"]],
[["indexedDB", "http://test1.example.org", "idb1 (default)"],
["obj1", "obj2"]],
[["indexedDB", "http://test1.example.org", "idb2"],
[["indexedDB", "http://test1.example.org", "idb2 (default)"],
["obj3"]],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
[1, 2, 3]],
[["indexedDB", "http://test1.example.org", "idb1", "obj2"],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj2"],
[1]],
[["indexedDB", "http://test1.example.org", "idb2", "obj3"],
[["indexedDB", "http://test1.example.org", "idb2 (default)", "obj3"],
[]],
[["indexedDB", "http://sectest1.example.org"],
[]],
[["indexedDB", "https://sectest1.example.org"],
["idb-s1", "idb-s2"]],
[["indexedDB", "https://sectest1.example.org", "idb-s1"],
["idb-s1 (default)", "idb-s2 (default)"]],
[["indexedDB", "https://sectest1.example.org", "idb-s1 (default)"],
["obj-s1"]],
[["indexedDB", "https://sectest1.example.org", "idb-s2"],
[["indexedDB", "https://sectest1.example.org", "idb-s2 (default)"],
["obj-s2"]],
[["indexedDB", "https://sectest1.example.org", "idb-s1", "obj-s1"],
[["indexedDB", "https://sectest1.example.org", "idb-s1 (default)", "obj-s1"],
[6, 7]],
[["indexedDB", "https://sectest1.example.org", "idb-s2", "obj-s2"],
[["indexedDB", "https://sectest1.example.org", "idb-s2 (default)", "obj-s2"],
[16]],
[["Cache", "http://test1.example.org", "plop"],
[MAIN_DOMAIN + "404_cached_file.js",

Просмотреть файл

@ -17,7 +17,7 @@ const TEST_CASES = [
["cookies", "test1.example.org"],
getCookieId("c1", "test1.example.org", "/browser"), "name"
],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
1, "name"],
[["Cache", "http://test1.example.org", "plop"],
MAIN_DOMAIN + "404_cached_file.js", "url"],

Просмотреть файл

@ -29,7 +29,7 @@ add_task(function* () {
["iframe-u-ss1", "iframe-u-ss2"]],
[["sessionStorage", "https://sectest1.example.org"],
["iframe-s-ss1"]],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
[1, 2, 3]],
[["Cache", "http://test1.example.org", "plop"],
[MAIN_DOMAIN + "404_cached_file.js", MAIN_DOMAIN + "browser_storage_basic.js"]],
@ -41,7 +41,7 @@ add_task(function* () {
const deleteHosts = [
[["localStorage", "https://sectest1.example.org"], "iframe-s-ls1", "name"],
[["sessionStorage", "https://sectest1.example.org"], "iframe-s-ss1", "name"],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"], 1, "name"],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], 1, "name"],
[["Cache", "http://test1.example.org", "plop"],
MAIN_DOMAIN + "404_cached_file.js", "url"],
];
@ -78,7 +78,7 @@ add_task(function* () {
["iframe-u-ss1", "iframe-u-ss2"]],
[["sessionStorage", "https://sectest1.example.org"],
[]],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
[]],
[["Cache", "http://test1.example.org", "plop"],
[]],

Просмотреть файл

@ -28,7 +28,7 @@ add_task(function* () {
],
[["localStorage", "http://test1.example.org"], ["ls1", "ls2"]],
[["sessionStorage", "http://test1.example.org"], ["ss1"]],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"], [1, 2, 3]],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], [1, 2, 3]],
[["Cache", "http://test1.example.org", "plop"],
[MAIN_DOMAIN + "404_cached_file.js", MAIN_DOMAIN + "browser_storage_basic.js"]],
]);
@ -38,7 +38,7 @@ add_task(function* () {
["cookies", "test1.example.org"],
["localStorage", "http://test1.example.org"],
["sessionStorage", "http://test1.example.org"],
["indexedDB", "http://test1.example.org", "idb1", "obj1"],
["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
["Cache", "http://test1.example.org", "plop"],
];
@ -67,7 +67,7 @@ add_task(function* () {
[["cookies", "test1.example.org"], []],
[["localStorage", "http://test1.example.org"], []],
[["sessionStorage", "http://test1.example.org"], []],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"], []],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], []],
[["Cache", "http://test1.example.org", "plop"], []],
]);

Просмотреть файл

@ -21,14 +21,14 @@
// storage-secured-iframe.html and storage-unsecured-iframe.html
const storeItems = [
[["indexedDB", "http://test1.example.org"],
["idb1", "idb2"]],
[["indexedDB", "http://test1.example.org", "idb1"],
["idb1 (default)", "idb2 (default)"]],
[["indexedDB", "http://test1.example.org", "idb1 (default)"],
["obj1", "obj2"]],
[["indexedDB", "http://test1.example.org", "idb2"],
[["indexedDB", "http://test1.example.org", "idb2 (default)"],
[]],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
[1, 2, 3]],
[["indexedDB", "http://test1.example.org", "idb1", "obj2"],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj2"],
[1]]
];

Просмотреть файл

@ -16,11 +16,11 @@ add_task(function* () {
info("test state before delete");
yield checkState([
[["indexedDB", "http://test1.example.org"], ["idb1", "idb2"]],
[["indexedDB", "http://test1.example.org"], ["idb1 (default)", "idb2 (default)"]],
]);
info("do the delete");
const deletedDb = ["indexedDB", "http://test1.example.org", "idb1"];
const deletedDb = ["indexedDB", "http://test1.example.org", "idb1 (default)"];
yield selectTreeItem(deletedDb);
@ -40,7 +40,7 @@ add_task(function* () {
info("test state after delete");
yield checkState([
[["indexedDB", "http://test1.example.org"], ["idb2"]],
[["indexedDB", "http://test1.example.org"], ["idb2 (default)"]],
]);
yield finishTests();

Просмотреть файл

@ -13,19 +13,19 @@ add_task(function* () {
info("test state before delete");
yield checkState([
[["indexedDB", "http://test1.example.org"], ["idb"]]
[["indexedDB", "http://test1.example.org"], ["idb (default)"]]
]);
info("do the delete");
yield selectTreeItem(["indexedDB", "http://test1.example.org"]);
let actor = gUI.getCurrentActor();
let result = yield actor.removeDatabase("http://test1.example.org", "idb");
let result = yield actor.removeDatabase("http://test1.example.org", "idb (default)");
ok(result.blocked, "removeDatabase attempt is blocked");
info("test state after blocked delete");
yield checkState([
[["indexedDB", "http://test1.example.org"], ["idb"]]
[["indexedDB", "http://test1.example.org"], ["idb (default)"]]
]);
let eventWait = gUI.once("store-objects-updated");
@ -47,7 +47,7 @@ add_task(function* () {
info("try to delete database from nonexistent host");
let errorThrown = false;
try {
result = yield actor.removeDatabase("http://test2.example.org", "idb");
result = yield actor.removeDatabase("http://test2.example.org", "idb (default)");
} catch (ex) {
errorThrown = true;
}

Просмотреть файл

@ -0,0 +1,31 @@
/* 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/. */
// Test to verify that indexedDBs with duplicate names (different types / paths)
// work as expected.
"use strict";
add_task(function* () {
const TESTPAGE = MAIN_DOMAIN + "storage-indexeddb-duplicate-names.html";
setPermission(TESTPAGE, "indexedDB");
yield openTabAndSetupStorage(TESTPAGE);
yield checkState([
[
["indexedDB", "http://test1.example.org"], [
"idb1 (default)",
"idb1 (temporary)",
"idb1 (persistent)",
"idb2 (default)",
"idb2 (temporary)",
"idb2 (persistent)"
]
]
]);
yield finishTests();
});

Просмотреть файл

@ -72,17 +72,17 @@ const testCases = [
sidebarHidden: true
},
{
location: "idb2",
location: "idb2 (default)",
sidebarHidden: false
},
{
location: ["indexedDB", "http://test1.example.org", "idb2", "obj3"],
location: ["indexedDB", "http://test1.example.org", "idb2 (default)", "obj3"],
sidebarHidden: true
},
{
location: ["indexedDB", "https://sectest1.example.org", "idb-s2"],
location: ["indexedDB", "https://sectest1.example.org", "idb-s2 (default)"],
sidebarHidden: true
},
{

Просмотреть файл

@ -124,7 +124,7 @@ const testCases = [
{name: "ss5.3", value: `${LONG_WORD}&${LONG_WORD}`},
{name: "ss5.4", value: `${LONG_WORD}&${LONG_WORD}`},
], true],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"]],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"]],
[1, [
{name: 1, value: JSON.stringify({id: 1, name: "foo", email: "foo@bar.com"})}
]],
@ -133,7 +133,7 @@ const testCases = [
{name: "1.name", value: "foo"},
{name: "1.email", value: "foo@bar.com"},
], true],
[["indexedDB", "http://test1.example.org", "idb1", "obj2"]],
[["indexedDB", "http://test1.example.org", "idb1 (default)", "obj2"]],
[1, [
{name: 1, value: JSON.stringify({
id2: 1, name: "foo", email: "foo@bar.com", extra: "baz"

Просмотреть файл

@ -880,3 +880,19 @@ var focusSearchBoxUsingShortcut = Task.async(function* (panelWin, callback) {
function getCookieId(name, domain, path) {
return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}`;
}
function setPermission(url, permission) {
const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
let uri = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.newURI(url, null, null);
let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
let principal = ssm.createCodebasePrincipal(uri, {});
Components.classes["@mozilla.org/permissionmanager;1"]
.getService(nsIPermissionManager)
.addFromPrincipal(principal, permission,
nsIPermissionManager.ALLOW_ACTION);
}

Просмотреть файл

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>Storage inspector IndexedDBs with duplicate names</title>
<script type="application/javascript;version=1.7">
"use strict";
function createIndexedDBs() {
createIndexedDB("idb1", "temporary");
createIndexedDB("idb1", "default");
createIndexedDB("idb1", "persistent");
createIndexedDB("idb2", "temporary");
createIndexedDB("idb2", "default");
createIndexedDB("idb2", "persistent");
}
function createIndexedDB(name, storage) {
let open = indexedDB.open(name, {storage: storage});
open.onsuccess = function () {
let db = open.result;
db.close();
};
}
function deleteDB(dbName, storage) {
return new Promise(resolve => {
dump(`removing database ${dbName} (${storage}) from ${document.location}\n`);
indexedDB.deleteDatabase(dbName, { storage: storage }).onsuccess = resolve;
});
}
window.clear = function* () {
yield deleteDB("idb1", "temporary");
yield deleteDB("idb1", "default");
yield deleteDB("idb1", "persistent");
yield deleteDB("idb2", "temporary");
yield deleteDB("idb2", "default");
yield deleteDB("idb2", "persistent");
dump(`removed indexedDB data from ${document.location}\n`);
};
</script>
</head>
<body onload="createIndexedDBs()">
<h1>storage-indexeddb-duplicate-names.html</h1>
</body>
</html>

Просмотреть файл

@ -809,7 +809,10 @@ StorageUI.prototype = {
columns[f.name] = f.name;
let columnName;
try {
columnName = L10N.getStr("table.headers." + type + "." + f.name);
// Path key names for l10n in the case of a string change.
let name = f.name === "keyPath" ? "keyPath2" : f.name;
columnName = L10N.getStr("table.headers." + type + "." + name);
} catch (e) {
columnName = COOKIE_KEY_MAP[f.name];
}

Просмотреть файл

@ -12,24 +12,6 @@ const { l10n } = require("devtools/client/webconsole/new-console-output/utils/me
add_task(function* () {
let hud = yield openNewTabAndConsole(TEST_URI);
const store = hud.ui.newConsoleOutput.getStore();
// Adding loggin each time the store is modified in order to check
// the store state in case of failure.
store.subscribe(() => {
const messages = store.getState().messages.messagesById.toJS()
.map(message => {
return {
id: message.id,
type: message.type,
parameters: message.parameters,
messageText: message.messageText
};
}
);
info("messages : " + JSON.stringify(messages));
});
const messageNumber = 100;
yield testSimpleBatchLogging(hud, messageNumber);
yield testBatchLoggingAndClear(hud, messageNumber);

Просмотреть файл

@ -95,7 +95,7 @@ function waitForMessages({ hud, messages }) {
* @return object
* A promise that is resolved with the result of the condition.
*/
function* waitFor(condition, message = "waitFor", interval = 100, maxTries = 50) {
function* waitFor(condition, message = "waitFor", interval = 10, maxTries = 500) {
return new Promise(resolve => {
BrowserTestUtils.waitForCondition(condition, message, interval, maxTries)
.then(() => resolve(condition()));

Просмотреть файл

@ -2,6 +2,8 @@
* 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/. */
/* globals StopIteration */
"use strict";
const {Cc, Ci, Cu, CC} = require("chrome");
@ -93,7 +95,7 @@ var StorageActors = {};
* - observe : Method which gets triggered on the notificaiton of the watched
* topic.
* - getNamesForHost : Given a host, get list of all known store names.
* - getValuesForHost : Given a host (and optianally a name) get all known
* - getValuesForHost : Given a host (and optionally a name) get all known
* store objects.
* - toStoreObject : Given a store object, convert it to the required format
* so that it can be transferred over wire.
@ -141,6 +143,9 @@ StorageActors.defaults = function (typeName, observationTopics) {
* Converts the window.location object into host.
*/
getHostName(location) {
if (location.protocol === "chrome:") {
return location.href;
}
return location.hostname || location.href;
},
@ -755,6 +760,7 @@ var cookieHelpers = {
let enumerator =
Services.cookies.getCookiesFromHost(origHost, data.originAttributes || {});
while (enumerator.hasMoreElements()) {
let nsiCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
if (nsiCookie.name === origName &&
@ -1052,6 +1058,9 @@ function getObjectForLocalOrSessionStorage(type) {
if (!location.host) {
return location.href;
}
if (location.protocol === "chrome:") {
return location.href;
}
return location.protocol + "//" + location.host;
},
@ -1264,6 +1273,9 @@ StorageActors.createActor({
if (!location.host) {
return location.href;
}
if (location.protocol === "chrome:") {
return location.href;
}
return location.protocol + "//" + location.host;
},
@ -1436,12 +1448,15 @@ ObjectStoreMetadata.prototype = {
* The host associated with this indexed db.
* @param {IDBDatabase} db
* The particular indexed db.
* @param {String} storage
* Storage type, either "temporary", "default" or "persistent".
*/
function DatabaseMetadata(origin, db) {
function DatabaseMetadata(origin, db, storage) {
this._origin = origin;
this._name = db.name;
this._version = db.version;
this._objectStores = [];
this.storage = storage;
if (db.objectStoreNames.length) {
let transaction = db.transaction(db.objectStoreNames, "readonly");
@ -1461,7 +1476,7 @@ DatabaseMetadata.prototype = {
toObject() {
return {
name: this._name,
name: `${this._name} (${this.storage})`,
origin: this._origin,
version: this._version,
objectStores: this._objectStores.size
@ -1541,6 +1556,9 @@ StorageActors.createActor({
if (!location.host) {
return location.href;
}
if (location.protocol === "chrome:") {
return location.href;
}
return location.protocol + "//" + location.host;
},
@ -1633,15 +1651,17 @@ StorageActors.createActor({
populateStoresForHost: Task.async(function* (host) {
let storeMap = new Map();
let {names} = yield this.getDBNamesForHost(host);
let win = this.storageActor.getWindowFromHost(host);
if (win) {
let principal = win.document.nodePrincipal;
for (let name of names) {
let metadata = yield this.getDBMetaData(host, principal, name);
for (let {name, storage} of names) {
let metadata = yield this.getDBMetaData(host, principal, name, storage);
metadata = indexedDBHelpers.patchMetadataMapsAndProtos(metadata);
storeMap.set(name, metadata);
storeMap.set(`${name} (${storage})`, metadata);
}
}
@ -1674,10 +1694,22 @@ StorageActors.createActor({
objectStores: item.objectStores
};
}
let value = JSON.stringify(item.value);
// FIXME: Bug 1318029 - Due to a bug that is thrown whenever a
// LongStringActor string reaches DebuggerServer.LONG_STRING_LENGTH we need
// to trim the value. When the bug is fixed we should stop trimming the
// string here.
let maxLength = DebuggerServer.LONG_STRING_LENGTH - 1;
if (value.length > maxLength) {
value = value.substr(0, maxLength);
}
// Indexed db entry
return {
name: item.name,
value: new LongStringActor(this.conn, JSON.stringify(item.value))
value: new LongStringActor(this.conn, value)
};
},
@ -1713,16 +1745,18 @@ StorageActors.createActor({
maybeSetupChildProcess() {
if (!DebuggerServer.isInChildProcess) {
this.backToChild = (func, rv) => rv;
this.clearDBStore = indexedDBHelpers.clearDBStore;
this.gatherFilesOrFolders = indexedDBHelpers.gatherFilesOrFolders;
this.getDBMetaData = indexedDBHelpers.getDBMetaData;
this.openWithPrincipal = indexedDBHelpers.openWithPrincipal;
this.getDBNamesForHost = indexedDBHelpers.getDBNamesForHost;
this.getSanitizedHost = indexedDBHelpers.getSanitizedHost;
this.getNameFromDatabaseFile = indexedDBHelpers.getNameFromDatabaseFile;
this.getValuesForHost = indexedDBHelpers.getValuesForHost;
this.getObjectStoreData = indexedDBHelpers.getObjectStoreData;
this.getSanitizedHost = indexedDBHelpers.getSanitizedHost;
this.getValuesForHost = indexedDBHelpers.getValuesForHost;
this.openWithPrincipal = indexedDBHelpers.openWithPrincipal;
this.removeDB = indexedDBHelpers.removeDB;
this.removeDBRecord = indexedDBHelpers.removeDBRecord;
this.clearDBStore = indexedDBHelpers.clearDBStore;
this.splitNameAndStorage = indexedDBHelpers.splitNameAndStorage;
return;
}
@ -1735,6 +1769,7 @@ StorageActors.createActor({
});
this.getDBMetaData = callParentProcessAsync.bind(null, "getDBMetaData");
this.splitNameAndStorage = callParentProcessAsync.bind(null, "splitNameAndStorage");
this.getDBNamesForHost = callParentProcessAsync.bind(null, "getDBNamesForHost");
this.getValuesForHost = callParentProcessAsync.bind(null, "getValuesForHost");
this.removeDB = callParentProcessAsync.bind(null, "removeDB");
@ -1830,14 +1865,13 @@ var indexedDBHelpers = {
* `name` for the given `host` with its `principal`. The stored metadata
* information is of `DatabaseMetadata` type.
*/
getDBMetaData: Task.async(function* (host, principal, name) {
let request = this.openWithPrincipal(principal, name);
getDBMetaData: Task.async(function* (host, principal, name, storage) {
let request = this.openWithPrincipal(principal, name, storage);
let success = promise.defer();
request.onsuccess = event => {
let db = event.target.result;
let dbData = new DatabaseMetadata(host, db);
let dbData = new DatabaseMetadata(host, db, storage);
db.close();
success.resolve(this.backToChild("getDBMetaData", dbData));
@ -1850,21 +1884,37 @@ var indexedDBHelpers = {
return success.promise;
}),
splitNameAndStorage: function (name) {
let lastOpenBracketIndex = name.lastIndexOf("(");
let lastCloseBracketIndex = name.lastIndexOf(")");
let delta = lastCloseBracketIndex - lastOpenBracketIndex - 1;
let storage = name.substr(lastOpenBracketIndex + 1, delta);
name = name.substr(0, lastOpenBracketIndex - 1);
return { storage, name };
},
/**
* Opens an indexed db connection for the given `principal` and
* database `name`.
*/
openWithPrincipal(principal, name) {
return indexedDBForStorage.openForPrincipal(principal, name);
openWithPrincipal: function (principal, name, storage) {
return indexedDBForStorage.openForPrincipal(principal, name,
{ storage: storage });
},
removeDB: Task.async(function* (host, principal, name) {
removeDB: Task.async(function* (host, principal, dbName) {
let result = new promise(resolve => {
let request = indexedDBForStorage.deleteForPrincipal(principal, name);
let {name, storage} = this.splitNameAndStorage(dbName);
let request =
indexedDBForStorage.deleteForPrincipal(principal, name,
{ storage: storage });
request.onsuccess = () => {
resolve({});
this.onItemUpdated("deleted", host, [name]);
this.onItemUpdated("deleted", host, [dbName]);
};
request.onblocked = () => {
@ -1890,10 +1940,11 @@ var indexedDBHelpers = {
removeDBRecord: Task.async(function* (host, principal, dbName, storeName, id) {
let db;
let {name, storage} = this.splitNameAndStorage(dbName);
try {
db = yield new promise((resolve, reject) => {
let request = this.openWithPrincipal(principal, dbName);
let request = this.openWithPrincipal(principal, name, storage);
request.onsuccess = ev => resolve(ev.target.result);
request.onerror = ev => reject(ev.target.error);
});
@ -1922,10 +1973,11 @@ var indexedDBHelpers = {
clearDBStore: Task.async(function* (host, principal, dbName, storeName) {
let db;
let {name, storage} = this.splitNameAndStorage(dbName);
try {
db = yield new promise((resolve, reject) => {
let request = this.openWithPrincipal(principal, dbName);
let request = this.openWithPrincipal(principal, name, storage);
request.onsuccess = ev => resolve(ev.target.result);
request.onerror = ev => reject(ev.target.error);
});
@ -1957,46 +2009,107 @@ var indexedDBHelpers = {
*/
getDBNamesForHost: Task.async(function* (host) {
let sanitizedHost = this.getSanitizedHost(host);
let directory = OS.Path.join(OS.Constants.Path.profileDir, "storage",
"default", sanitizedHost, "idb");
let exists = yield OS.File.exists(directory);
if (!exists && host.startsWith("about:")) {
// try for moz-safe-about directory
sanitizedHost = this.getSanitizedHost("moz-safe-" + host);
directory = OS.Path.join(OS.Constants.Path.profileDir, "storage",
"permanent", sanitizedHost, "idb");
exists = yield OS.File.exists(directory);
}
if (!exists) {
return this.backToChild("getDBNamesForHost", {names: []});
}
let profileDir = OS.Constants.Path.profileDir;
let files = [];
let names = [];
let dirIterator = new OS.File.DirectoryIterator(directory);
try {
yield dirIterator.forEach(file => {
// Skip directories.
if (file.isDir) {
return null;
}
let storagePath = OS.Path.join(profileDir, "storage");
// Skip any non-sqlite files.
if (!file.name.endsWith(".sqlite")) {
return null;
}
// We expect sqlite DB paths to look something like this:
// - PathToProfileDir/storage/default/http+++www.example.com/
// idb/1556056096MeysDaabta.sqlite
// - PathToProfileDir/storage/permanent/http+++www.example.com/
// idb/1556056096MeysDaabta.sqlite
// - PathToProfileDir/storage/temporary/http+++www.example.com/
// idb/1556056096MeysDaabta.sqlite
//
// The subdirectory inside the storage folder is determined by the storage
// type:
// - default: { storage: "default" } or not specified.
// - permanent: { storage: "persistent" }.
// - temporary: { storage: "temporary" }.
let sqliteFiles = yield this.gatherFilesOrFolders(storagePath, path => {
if (path.endsWith(".sqlite")) {
let { components } = OS.Path.split(path);
let isIDB = components[components.length - 2] === "idb";
return this.getNameFromDatabaseFile(file.path).then(name => {
if (name) {
names.push(name);
}
return null;
return isIDB;
}
return false;
});
for (let file of sqliteFiles) {
let splitPath = OS.Path.split(file).components;
let idbIndex = splitPath.indexOf("idb");
let name = splitPath[idbIndex - 1];
let storage = splitPath[idbIndex - 2];
let relative = file.substr(profileDir.length + 1);
if (name.startsWith(sanitizedHost)) {
files.push({
file: relative,
storage: storage === "permanent" ? "persistent" : storage
});
});
} finally {
dirIterator.close();
}
}
return this.backToChild("getDBNamesForHost", {names: names});
if (files.length > 0) {
for (let {file, storage} of files) {
let name = yield this.getNameFromDatabaseFile(file);
if (name) {
names.push({
name,
storage
});
}
}
}
return this.backToChild("getDBNamesForHost", {names});
}),
/**
* Gather together all of the files in path and pass each path through a
* validation function.
*
* @param {String}
* Path in which to begin searching.
* @param {Function}
* Validation function, which checks each file path. If this function
* Returns true the file path is kept.
*
* @returns {Array}
* An array of file paths.
*/
gatherFilesOrFolders: Task.async(function* (path, validationFunc) {
let files = [];
let iterator;
let paths = [path];
while (paths.length > 0) {
try {
iterator = new OS.File.DirectoryIterator(paths.pop());
for (let child in iterator) {
child = yield child;
path = child.path;
if (child.isDir) {
paths.push(path);
} else if (validationFunc(path)) {
files.push(path);
}
}
} catch (ex) {
// Ignore StopIteration to prevent exiting the loop.
if (ex != StopIteration) {
throw ex;
}
}
}
iterator.close();
return files;
}),
/**
@ -2004,6 +2117,9 @@ var indexedDBHelpers = {
* name.
*/
getSanitizedHost(host) {
if (host.startsWith("about:")) {
host = "moz-safe-" + host;
}
return host.replace(ILLEGAL_CHAR_REGEX, "+");
},
@ -2017,7 +2133,7 @@ var indexedDBHelpers = {
// Content pages might be having an open transaction for the same indexed db
// which this sqlite file belongs to. In that case, sqlite.openConnection
// will throw. Thus we retey for some time to see if lock is removed.
// will throw. Thus we retry for some time to see if lock is removed.
while (!connection && retryCount++ < 25) {
try {
connection = yield Sqlite.openConnection({ path: path });
@ -2078,8 +2194,14 @@ var indexedDBHelpers = {
return this.backToChild("getValuesForHost", {objectStores: objectStores});
}
// Get either all entries from the object store, or a particular id
let result = yield this.getObjectStoreData(host, principal, db2,
objectStore, id, options.index, options.size);
let storage = hostVsStores.get(host).get(db2).storage;
let result = yield this.getObjectStoreData(host, principal, db2, storage, {
objectStore: objectStore,
id: id,
index: options.index,
offset: 0,
size: options.size
});
return this.backToChild("getValuesForHost", {result: result});
}),
@ -2093,23 +2215,27 @@ var indexedDBHelpers = {
* The principal of the given document.
* @param {string} dbName
* The name of the indexed db from the above host.
* @param {string} objectStore
* The name of the object store from the above db.
* @param {string} id
* id of the requested entry from the above object store.
* null if all entries from the above object store are requested.
* @param {string} index
* name of the IDBIndex to be iterated on while fetching entries.
* null or "name" if no index is to be iterated.
* @param {number} offset
* ofsset of the entries to be fetched.
* @param {number} size
* The intended size of the entries to be fetched.
* @param {String} storage
* Storage type, either "temporary", "default" or "persistent".
* @param {Object} requestOptions
* An object in the following format:
* {
* objectStore: The name of the object store from the above db,
* id: Id of the requested entry from the above object
* store. null if all entries from the above object
* store are requested,
* index: Name of the IDBIndex to be iterated on while fetching
* entries. null or "name" if no index is to be
* iterated,
* offset: offset of the entries to be fetched,
* size: The intended size of the entries to be fetched
* }
*/
getObjectStoreData(host, principal, dbName, objectStore, id, index,
offset, size) {
let request = this.openWithPrincipal(principal, dbName);
getObjectStoreData(host, principal, dbName, storage, requestOptions) {
let {name} = this.splitNameAndStorage(dbName);
let request = this.openWithPrincipal(principal, name, storage);
let success = promise.defer();
let {objectStore, id, index, offset, size} = requestOptions;
let data = [];
let db;
@ -2211,8 +2337,12 @@ var indexedDBHelpers = {
switch (msg.json.method) {
case "getDBMetaData": {
let [host, principal, name] = args;
return indexedDBHelpers.getDBMetaData(host, principal, name);
let [host, principal, name, storage] = args;
return indexedDBHelpers.getDBMetaData(host, principal, name, storage);
}
case "splitNameAndStorage": {
let [name] = args;
return indexedDBHelpers.splitNameAndStorage(name);
}
case "getDBNamesForHost": {
let [host] = args;
@ -2224,8 +2354,8 @@ var indexedDBHelpers = {
hostVsStores, principal);
}
case "removeDB": {
let [host, principal, name] = args;
return indexedDBHelpers.removeDB(host, principal, name);
let [host, principal, dbName] = args;
return indexedDBHelpers.removeDB(host, principal, dbName);
}
case "removeDBRecord": {
let [host, principal, db, store, id] = args;

Просмотреть файл

@ -121,24 +121,24 @@ const storeMap = {
const IDBValues = {
listStoresResponse: {
"http://test1.example.org": [
["idb1", "obj1"], ["idb1", "obj2"], ["idb2", "obj3"]
["idb1 (default)", "obj1"], ["idb1 (default)", "obj2"], ["idb2 (default)", "obj3"]
],
"http://sectest1.example.org": [
],
"https://sectest1.example.org": [
["idb-s1", "obj-s1"], ["idb-s2", "obj-s2"]
["idb-s1 (default)", "obj-s1"], ["idb-s2 (default)", "obj-s2"]
]
},
dbDetails : {
dbDetails: {
"http://test1.example.org": [
{
db: "idb1",
db: "idb1 (default)",
origin: "http://test1.example.org",
version: 1,
objectStores: 2
},
{
db: "idb2",
db: "idb2 (default)",
origin: "http://test1.example.org",
version: 1,
objectStores: 1
@ -148,13 +148,13 @@ const IDBValues = {
],
"https://sectest1.example.org": [
{
db: "idb-s1",
db: "idb-s1 (default)",
origin: "https://sectest1.example.org",
version: 1,
objectStores: 1
},
{
db: "idb-s2",
db: "idb-s2 (default)",
origin: "https://sectest1.example.org",
version: 1,
objectStores: 1
@ -163,7 +163,7 @@ const IDBValues = {
},
objectStoreDetails: {
"http://test1.example.org": {
idb1: [
"idb1 (default)": [
{
objectStore: "obj1",
keyPath: "id",
@ -190,7 +190,7 @@ const IDBValues = {
indexes: []
}
],
idb2: [
"idb2 (default)": [
{
objectStore: "obj3",
keyPath: "id3",
@ -208,7 +208,7 @@ const IDBValues = {
},
"http://sectest1.example.org" : {},
"https://sectest1.example.org": {
"idb-s1": [
"idb-s1 (default)": [
{
objectStore: "obj-s1",
keyPath: "id",
@ -216,7 +216,7 @@ const IDBValues = {
indexes: []
},
],
"idb-s2": [
"idb-s2 (default)": [
{
objectStore: "obj-s2",
keyPath: "id3",
@ -236,7 +236,7 @@ const IDBValues = {
},
entries: {
"http://test1.example.org": {
"idb1#obj1": [
"idb1 (default)#obj1": [
{
name: 1,
value: {
@ -262,7 +262,7 @@ const IDBValues = {
}
}
],
"idb1#obj2": [
"idb1 (default)#obj2": [
{
name: 1,
value: {
@ -273,11 +273,11 @@ const IDBValues = {
}
}
],
"idb2#obj3": []
"idb2 (default)#obj3": []
},
"http://sectest1.example.org" : {},
"https://sectest1.example.org": {
"idb-s1#obj-s1": [
"idb-s1 (default)#obj-s1": [
{
name: 6,
value: {
@ -295,7 +295,7 @@ const IDBValues = {
}
}
],
"idb-s2#obj-s2": [
"idb-s2 (default)#obj-s2": [
{
name: 13,
value: {

Просмотреть файл

@ -61,7 +61,7 @@ types.addDictType("cookiestoreobject", {
// Common methods for edit/remove
const editRemoveMethods = {
getEditableFields: {
getFields: {
request: {},
response: {
value: RetVal("json")

Просмотреть файл

@ -74,7 +74,16 @@ MediaShutdownManager::EnsureCorrectShutdownObserverState()
nsresult rv = GetShutdownBarrier()->AddBlocker(
this, NS_LITERAL_STRING(__FILE__), __LINE__,
NS_LITERAL_STRING("MediaShutdownManager shutdown"));
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
// Leak the buffer on the heap to make sure that it lives long enough,
// as MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to
// the end of the program.
const size_t CAPACITY = 256;
auto buf = new char[CAPACITY];
snprintf(buf, CAPACITY, "Failed to add shutdown blocker! rv=%x", uint32_t(rv));
MOZ_CRASH_ANNOTATE(buf);
MOZ_REALLY_CRASH();
}
} else {
GetShutdownBarrier()->RemoveBlocker(this);
// Clear our singleton reference. This will probably delete
@ -89,7 +98,7 @@ void
MediaShutdownManager::Register(MediaDecoder* aDecoder)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_DIAGNOSTIC_ASSERT(!mIsDoingXPCOMShutDown);
MOZ_RELEASE_ASSERT(!mIsDoingXPCOMShutDown);
// Don't call Register() after you've Unregistered() all the decoders,
// that's not going to work.
MOZ_ASSERT(!mDecoders.Contains(aDecoder));

Просмотреть файл

@ -1548,7 +1548,8 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
stateenum.addId(_deadState().name)
stateenum.addId(_nullState().name)
stateenum.addId(_errorState().name)
stateenum.addId(_dyingState().name)
if self.protocol.decl.type.hasReentrantDelete:
stateenum.addId(_dyingState().name)
for ts in p.transitionStmts:
stateenum.addId(ts.state.decl.cxxname)
if len(p.transitionStmts):
@ -1769,19 +1770,15 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
fromswitch.addcase(CaseLabel(_deadState().name), deadblock)
# special case for Dying
dyingblock = Block()
if ptype.hasReentrantDelete:
dyingblock = Block()
ifdelete = StmtIf(ExprBinary(_deleteReplyId(), '==', msgexpr))
ifdelete.addifstmt(
StmtExpr(ExprAssn(ExprDeref(nextvar), _deadState())))
dyingblock.addstmt(ifdelete)
dyingblock.addstmt(
StmtReturn(ExprLiteral.TRUE))
else:
dyingblock.addstmts([
_logicError('__delete__()d (and unexpectedly dying) actor'),
StmtReturn(ExprLiteral.FALSE) ])
fromswitch.addcase(CaseLabel(_dyingState().name), dyingblock)
fromswitch.addcase(CaseLabel(_dyingState().name), dyingblock)
unreachedblock = Block()
unreachedblock.addstmts([

Просмотреть файл

@ -7,11 +7,6 @@ from ply import lex, yacc
from ipdl.ast import *
def _getcallerpath():
'''Return the absolute path of the file containing the code that
**CALLED** this function.'''
return os.path.abspath(sys._getframe(1).f_code.co_filename)
##-----------------------------------------------------------------------------
class ParseError(Exception):

Просмотреть файл

@ -104,6 +104,18 @@ Char16ToUChar(char16_t* chars)
MOZ_CRASH("Char16ToUChar: Intl API disabled");
}
inline char16_t*
UCharToChar16(UChar* chars)
{
MOZ_CRASH("UCharToChar16: Intl API disabled");
}
inline const char16_t*
UCharToChar16(const UChar* chars)
{
MOZ_CRASH("UCharToChar16: Intl API disabled");
}
struct UEnumeration;
int32_t
@ -352,6 +364,27 @@ enum UCalendarDateFields {
UCAL_DAY_OF_MONTH = UCAL_DATE
};
enum UCalendarMonths {
UCAL_JANUARY,
UCAL_FEBRUARY,
UCAL_MARCH,
UCAL_APRIL,
UCAL_MAY,
UCAL_JUNE,
UCAL_JULY,
UCAL_AUGUST,
UCAL_SEPTEMBER,
UCAL_OCTOBER,
UCAL_NOVEMBER,
UCAL_DECEMBER,
UCAL_UNDECIMBER
};
enum UCalendarAMPMs {
UCAL_AM,
UCAL_PM
};
UCalendar*
ucal_open(const UChar* zoneID, int32_t len, const char* locale,
UCalendarType type, UErrorCode* status)
@ -416,6 +449,13 @@ ucal_getDefaultTimeZone(UChar* result, int32_t resultCapacity, UErrorCode* statu
MOZ_CRASH("ucal_getDefaultTimeZone: Intl API disabled");
}
enum UDateTimePatternField {
UDATPG_YEAR_FIELD,
UDATPG_MONTH_FIELD,
UDATPG_WEEK_OF_YEAR_FIELD,
UDATPG_DAY_FIELD,
};
typedef void* UDateTimePatternGenerator;
UDateTimePatternGenerator*
@ -432,6 +472,14 @@ udatpg_getBestPattern(UDateTimePatternGenerator* dtpg, const UChar* skeleton,
MOZ_CRASH("udatpg_getBestPattern: Intl API disabled");
}
static const UChar *
udatpg_getAppendItemName(const UDateTimePatternGenerator *dtpg,
UDateTimePatternField field,
int32_t *pLength)
{
MOZ_CRASH("udatpg_getAppendItemName: Intl API disabled");
}
void
udatpg_close(UDateTimePatternGenerator* dtpg)
{
@ -484,10 +532,46 @@ enum UDateFormatField {
};
enum UDateFormatStyle {
UDAT_FULL,
UDAT_LONG,
UDAT_MEDIUM,
UDAT_SHORT,
UDAT_DEFAULT = UDAT_MEDIUM,
UDAT_PATTERN = -2,
UDAT_IGNORE = UDAT_PATTERN
};
enum UDateFormatSymbolType {
UDAT_ERAS,
UDAT_MONTHS,
UDAT_SHORT_MONTHS,
UDAT_WEEKDAYS,
UDAT_SHORT_WEEKDAYS,
UDAT_AM_PMS,
UDAT_LOCALIZED_CHARS,
UDAT_ERA_NAMES,
UDAT_NARROW_MONTHS,
UDAT_NARROW_WEEKDAYS,
UDAT_STANDALONE_MONTHS,
UDAT_STANDALONE_SHORT_MONTHS,
UDAT_STANDALONE_NARROW_MONTHS,
UDAT_STANDALONE_WEEKDAYS,
UDAT_STANDALONE_SHORT_WEEKDAYS,
UDAT_STANDALONE_NARROW_WEEKDAYS,
UDAT_QUARTERS,
UDAT_SHORT_QUARTERS,
UDAT_STANDALONE_QUARTERS,
UDAT_STANDALONE_SHORT_QUARTERS,
UDAT_SHORTER_WEEKDAYS,
UDAT_STANDALONE_SHORTER_WEEKDAYS,
UDAT_CYCLIC_YEARS_WIDE,
UDAT_CYCLIC_YEARS_ABBREVIATED,
UDAT_CYCLIC_YEARS_NARROW,
UDAT_ZODIAC_NAMES_WIDE,
UDAT_ZODIAC_NAMES_ABBREVIATED,
UDAT_ZODIAC_NAMES_NARROW
};
int32_t
udat_countAvailable()
{
@ -561,6 +645,13 @@ udat_close(UDateFormat* format)
} // anonymous namespace
static int32_t
udat_getSymbols(const UDateFormat *fmt, UDateFormatSymbolType type, int32_t symbolIndex,
UChar *result, int32_t resultLength, UErrorCode *status)
{
MOZ_CRASH("udat_getSymbols: Intl API disabled");
}
#endif
@ -2922,6 +3013,296 @@ js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
return true;
}
template<size_t N>
inline bool
MatchPart(const char** pattern, const char (&part)[N])
{
if (strncmp(*pattern, part, N - 1))
return false;
*pattern += N - 1;
return true;
}
bool
js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 3);
// 1. Assert: locale is a string.
MOZ_ASSERT(args[0].isString());
// 2. Assert: style is a string.
MOZ_ASSERT(args[1].isString());
// 3. Assert: keys is an Array.
MOZ_ASSERT(args[2].isObject());
JSAutoByteString locale(cx, args[0].toString());
if (!locale)
return false;
JSAutoByteString style(cx, args[1].toString());
if (!style)
return false;
RootedArrayObject keys(cx, &args[2].toObject().as<ArrayObject>());
if (!keys)
return false;
// 4. Let result be ArrayCreate(0).
RootedArrayObject result(cx, NewDenseUnallocatedArray(cx, keys->length()));
if (!result)
return false;
UErrorCode status = U_ZERO_ERROR;
UDateFormat* fmt =
udat_open(UDAT_DEFAULT, UDAT_DEFAULT, icuLocale(locale.ptr()),
nullptr, 0, nullptr, 0, &status);
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
ScopedICUObject<UDateFormat, udat_close> datToClose(fmt);
// UDateTimePatternGenerator will be needed for translations of date and
// time fields like "month", "week", "day" etc.
UDateTimePatternGenerator* dtpg = udatpg_open(icuLocale(locale.ptr()), &status);
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
ScopedICUObject<UDateTimePatternGenerator, udatpg_close> datPgToClose(dtpg);
RootedValue keyValue(cx);
RootedString keyValStr(cx);
RootedValue wordVal(cx);
Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
return false;
// 5. For each element of keys,
for (uint32_t i = 0; i < keys->length(); i++) {
/**
* We iterate over keys array looking for paths that we have code
* branches for.
*
* For any unknown path branch, the wordVal will keep NullValue and
* we'll throw at the end.
*/
if (!GetElement(cx, keys, keys, i, &keyValue))
return false;
JSAutoByteString pattern;
keyValStr = keyValue.toString();
if (!pattern.encodeUtf8(cx, keyValStr))
return false;
wordVal.setNull();
// 5.a. Perform an implementation dependent algorithm to map a key to a
// corresponding display name.
const char* pat = pattern.ptr();
if (!MatchPart(&pat, "dates")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
if (MatchPart(&pat, "fields")) {
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
UDateTimePatternField fieldType;
if (MatchPart(&pat, "year")) {
fieldType = UDATPG_YEAR_FIELD;
} else if (MatchPart(&pat, "month")) {
fieldType = UDATPG_MONTH_FIELD;
} else if (MatchPart(&pat, "week")) {
fieldType = UDATPG_WEEK_OF_YEAR_FIELD;
} else if (MatchPart(&pat, "day")) {
fieldType = UDATPG_DAY_FIELD;
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
// This part must be the final part with no trailing data.
if (*pat != '\0') {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
int32_t resultSize;
const UChar* value = udatpg_getAppendItemName(dtpg, fieldType, &resultSize);
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
JSString* word = NewStringCopyN<CanGC>(cx, UCharToChar16(value), resultSize);
if (!word)
return false;
wordVal.setString(word);
} else if (MatchPart(&pat, "gregorian")) {
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
UDateFormatSymbolType symbolType;
int32_t index;
if (MatchPart(&pat, "months")) {
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
if (equal(style, "narrow")) {
symbolType = UDAT_STANDALONE_NARROW_MONTHS;
} else if (equal(style, "short")) {
symbolType = UDAT_STANDALONE_SHORT_MONTHS;
} else {
MOZ_ASSERT(equal(style, "long"));
symbolType = UDAT_STANDALONE_MONTHS;
}
if (MatchPart(&pat, "january")) {
index = UCAL_JANUARY;
} else if (MatchPart(&pat, "february")) {
index = UCAL_FEBRUARY;
} else if (MatchPart(&pat, "march")) {
index = UCAL_MARCH;
} else if (MatchPart(&pat, "april")) {
index = UCAL_APRIL;
} else if (MatchPart(&pat, "may")) {
index = UCAL_MAY;
} else if (MatchPart(&pat, "june")) {
index = UCAL_JUNE;
} else if (MatchPart(&pat, "july")) {
index = UCAL_JULY;
} else if (MatchPart(&pat, "august")) {
index = UCAL_AUGUST;
} else if (MatchPart(&pat, "september")) {
index = UCAL_SEPTEMBER;
} else if (MatchPart(&pat, "october")) {
index = UCAL_OCTOBER;
} else if (MatchPart(&pat, "november")) {
index = UCAL_NOVEMBER;
} else if (MatchPart(&pat, "december")) {
index = UCAL_DECEMBER;
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
} else if (MatchPart(&pat, "weekdays")) {
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
if (equal(style, "narrow")) {
symbolType = UDAT_STANDALONE_NARROW_WEEKDAYS;
} else if (equal(style, "short")) {
symbolType = UDAT_STANDALONE_SHORT_WEEKDAYS;
} else {
MOZ_ASSERT(equal(style, "long"));
symbolType = UDAT_STANDALONE_WEEKDAYS;
}
if (MatchPart(&pat, "monday")) {
index = UCAL_MONDAY;
} else if (MatchPart(&pat, "tuesday")) {
index = UCAL_TUESDAY;
} else if (MatchPart(&pat, "wednesday")) {
index = UCAL_WEDNESDAY;
} else if (MatchPart(&pat, "thursday")) {
index = UCAL_THURSDAY;
} else if (MatchPart(&pat, "friday")) {
index = UCAL_FRIDAY;
} else if (MatchPart(&pat, "saturday")) {
index = UCAL_SATURDAY;
} else if (MatchPart(&pat, "sunday")) {
index = UCAL_SUNDAY;
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
} else if (MatchPart(&pat, "dayperiods")) {
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
symbolType = UDAT_AM_PMS;
if (MatchPart(&pat, "am")) {
index = UCAL_AM;
} else if (MatchPart(&pat, "pm")) {
index = UCAL_PM;
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
// This part must be the final part with no trailing data.
if (*pat != '\0') {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
int32_t resultSize =
udat_getSymbols(fmt, symbolType, index, Char16ToUChar(chars.begin()),
INITIAL_CHAR_BUFFER_SIZE, &status);
if (status == U_BUFFER_OVERFLOW_ERROR) {
if (!chars.resize(resultSize))
return false;
status = U_ZERO_ERROR;
udat_getSymbols(fmt, symbolType, index, Char16ToUChar(chars.begin()),
resultSize, &status);
}
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
JSString* word = NewStringCopyN<CanGC>(cx, chars.begin(), resultSize);
if (!word)
return false;
wordVal.setString(word);
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
MOZ_ASSERT(wordVal.isString());
// 5.b. Append the result string to result.
if (!DefineElement(cx, result, i, wordVal))
return false;
}
// 6. Return result.
args.rval().setObject(*result);
return true;
}
/******************** Intl ********************/
const Class js::IntlClass = {

Просмотреть файл

@ -389,6 +389,48 @@ intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp);
extern MOZ_MUST_USE bool
intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp);
/**
* Returns an Array with CLDR-based fields display names.
* The function takes three arguments:
*
* locale
* BCP47 compliant locale string
* style
* A string with values: long or short or narrow
* keys
* An array or path-like strings that identify keys to be returned
* At the moment the following types of keys are supported:
*
* 'dates/fields/{year|month|week|day}'
* 'dates/gregorian/months/{january|...|december}'
* 'dates/gregorian/weekdays/{sunday|...|saturday}'
* 'dates/gregorian/dayperiods/{am|pm}'
*
* Example:
*
* let info = intl_ComputeDisplayNames(
* 'en-US',
* 'long',
* [
* 'dates/fields/year',
* 'dates/gregorian/months/january',
* 'dates/gregorian/weekdays/monday',
* 'dates/gregorian/dayperiods/am',
* ]
* );
*
* Returned value:
*
* [
* 'year',
* 'January',
* 'Monday',
* 'AM'
* ]
*/
extern MOZ_MUST_USE bool
intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp);
#if ENABLE_INTL_API
/**
* Cast char16_t* strings to UChar* strings used by ICU.
@ -404,6 +446,18 @@ Char16ToUChar(char16_t* chars)
{
return reinterpret_cast<UChar*>(chars);
}
inline char16_t*
UCharToChar16(UChar* chars)
{
return reinterpret_cast<char16_t*>(chars);
}
inline const char16_t*
UCharToChar16(const UChar* chars)
{
return reinterpret_cast<const char16_t*>(chars);
}
#endif // ENABLE_INTL_API
} // namespace js

Просмотреть файл

@ -9,7 +9,8 @@
JSMSG_INVALID_OPTION_VALUE: false, JSMSG_INVALID_DIGITS_VALUE: false,
JSMSG_INTL_OBJECT_REINITED: false, JSMSG_INVALID_CURRENCY_CODE: false,
JSMSG_UNDEFINED_CURRENCY: false, JSMSG_INVALID_TIME_ZONE: false,
JSMSG_DATE_NOT_FINITE: false,
JSMSG_DATE_NOT_FINITE: false, JSMSG_INVALID_KEYS_TYPE: false,
JSMSG_INVALID_KEY: false,
intl_Collator_availableLocales: false,
intl_availableCollations: false,
intl_CompareStrings: false,
@ -3004,3 +3005,126 @@ function Intl_getCalendarInfo(locales) {
return result;
}
/**
* This function is a custom method designed after Intl API, but currently
* not part of the spec or spec proposal.
* We want to use it internally to retrieve translated values from CLDR in
* order to ensure they're aligned with what Intl API returns.
*
* This API may one day be a foundation for an ECMA402 API spec proposal.
*
* The function takes two arguments - locales which is a list of locale strings
* and options which is an object with two optional properties:
*
* keys:
* an Array of string values that are paths to individual terms
*
* style:
* a String with a value "long", "short" or "narrow"
*
* It returns an object with properties:
*
* locale:
* a negotiated locale string
*
* style:
* negotiated style
*
* values:
* A key-value pair list of requested keys and corresponding
* translated values
*
*/
function Intl_getDisplayNames(locales, options) {
// 1. Let requestLocales be ? CanonicalizeLocaleList(locales).
const requestedLocales = CanonicalizeLocaleList(locales);
// 2. If options is undefined, then
if (options === undefined)
// a. Let options be ObjectCreate(%ObjectPrototype%).
options = {};
// 3. Else,
else
// a. Let options be ? ToObject(options).
options = ToObject(options);
const DateTimeFormat = dateTimeFormatInternalProperties;
// 4. Let localeData be %DateTimeFormat%.[[localeData]].
const localeData = DateTimeFormat.localeData;
// 5. Let opt be a new Record.
const localeOpt = new Record();
// 6. Set localeOpt.[[localeMatcher]] to "best fit".
localeOpt.localeMatcher = "best fit";
// 7. Let r be ResolveLocale(%DateTimeFormat%.[[availableLocales]], requestedLocales, localeOpt,
// %DateTimeFormat%.[[relevantExtensionKeys]], localeData).
const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
requestedLocales,
localeOpt,
DateTimeFormat.relevantExtensionKeys,
localeData);
// 8. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow" », "long").
const style = GetOption(options, "style", "string", ["long", "short", "narrow"], "long");
// 9. Let keys be ? Get(options, "keys").
let keys = options.keys;
// 10. If keys is undefined,
if (keys === undefined) {
// a. Let keys be ArrayCreate(0).
keys = [];
} else if (!IsObject(keys)) {
// 11. Else,
// a. If Type(keys) is not Object, throw a TypeError exception.
ThrowTypeError(JSMSG_INVALID_KEYS_TYPE);
}
// 12. Let processedKeys be ArrayCreate(0).
// (This really should be a List, but we use an Array here in order that
// |intl_ComputeDisplayNames| may infallibly access the list's length via
// |ArrayObject::length|.)
let processedKeys = [];
// 13. Let len be ? ToLength(? Get(keys, "length")).
let len = ToLength(keys.length);
// 14. Let i be 0.
// 15. Repeat, while i < len
for (let i = 0; i < len; i++) {
// a. Let processedKey be ? ToString(? Get(keys, i)).
// b. Perform ? CreateDataPropertyOrThrow(processedKeys, i, processedKey).
callFunction(std_Array_push, processedKeys, ToString(keys[i]));
}
// 16. Let names be ? ComputeDisplayNames(r.[[locale]], style, processedKeys).
const names = intl_ComputeDisplayNames(r.locale, style, processedKeys);
// 17. Let values be ObjectCreate(%ObjectPrototype%).
const values = {};
// 18. Set i to 0.
// 19. Repeat, while i < len
for (let i = 0; i < len; i++) {
// a. Let key be ? Get(processedKeys, i).
const key = processedKeys[i];
// b. Let name be ? Get(names, i).
const name = names[i];
// c. Assert: Type(name) is string.
assert(typeof name === "string", "unexpected non-string value");
// d. Assert: the length of name is greater than zero.
assert(name.length > 0, "empty string value");
// e. Perform ? DefinePropertyOrThrow(values, key, name).
_DefineDataProperty(values, key, name);
}
// 20. Let options be ObjectCreate(%ObjectPrototype%).
// 21. Perform ! DefinePropertyOrThrow(result, "locale", r.[[locale]]).
// 22. Perform ! DefinePropertyOrThrow(result, "style", style).
// 23. Perform ! DefinePropertyOrThrow(result, "values", values).
const result = { locale: r.locale, style, values };
// 24. Return result.
return result;
}

Просмотреть файл

@ -477,6 +477,8 @@ MSG_DEF(JSMSG_INTL_OBJECT_NOT_INITED, 3, JSEXN_TYPEERR, "Intl.{0}.prototype.{1}
MSG_DEF(JSMSG_INTL_OBJECT_REINITED, 0, JSEXN_TYPEERR, "can't initialize object twice as an object of an Intl constructor")
MSG_DEF(JSMSG_INVALID_CURRENCY_CODE, 1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}")
MSG_DEF(JSMSG_INVALID_DIGITS_VALUE, 1, JSEXN_RANGEERR, "invalid digits value: {0}")
MSG_DEF(JSMSG_INVALID_KEYS_TYPE, 0, JSEXN_TYPEERR, "calendar info keys must be an object or undefined")
MSG_DEF(JSMSG_INVALID_KEY, 1, JSEXN_RANGEERR, "invalid key: {0}")
MSG_DEF(JSMSG_INVALID_LANGUAGE_TAG, 1, JSEXN_RANGEERR, "invalid language tag: {0}")
MSG_DEF(JSMSG_INVALID_LOCALES_ELEMENT, 0, JSEXN_TYPEERR, "invalid element in locales argument")
MSG_DEF(JSMSG_INVALID_LOCALE_MATCHER, 1, JSEXN_RANGEERR, "invalid locale matcher in supportedLocalesOf(): {0}")

Просмотреть файл

@ -907,6 +907,7 @@ AddIntlExtras(JSContext* cx, unsigned argc, Value* vp)
static const JSFunctionSpec funcs[] = {
JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0),
JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0),
JS_FS_END
};

Просмотреть файл

@ -0,0 +1,238 @@
// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras'))
/* 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/. */
// Tests the getCalendarInfo function with a diverse set of arguments.
/*
* Test if getDisplayNames return value matches expected values.
*/
function checkDisplayNames(names, expected)
{
assertEq(Object.getPrototypeOf(names), Object.prototype);
assertEq(names.locale, expected.locale);
assertEq(names.style, expected.style);
const nameValues = names.values;
const expectedValues = expected.values;
const nameValuesKeys = Object.getOwnPropertyNames(nameValues).sort();
const expectedValuesKeys = Object.getOwnPropertyNames(expectedValues).sort();
assertEqArray(nameValuesKeys, expectedValuesKeys);
for (let key of expectedValuesKeys)
assertEq(nameValues[key], expectedValues[key]);
}
addIntlExtras(Intl);
let gDN = Intl.getDisplayNames;
assertEq(gDN.length, 2);
checkDisplayNames(gDN('en-US', {
}), {
locale: 'en-US',
style: 'long',
values: {}
});
checkDisplayNames(gDN('en-US', {
keys: [
'dates/gregorian/weekdays/wednesday'
],
style: 'narrow'
}), {
locale: 'en-US',
style: 'narrow',
values: {
'dates/gregorian/weekdays/wednesday': 'W'
}
});
checkDisplayNames(gDN('en-US', {
keys: [
'dates/fields/year',
'dates/fields/month',
'dates/fields/week',
'dates/fields/day',
'dates/gregorian/months/january',
'dates/gregorian/months/february',
'dates/gregorian/months/march',
'dates/gregorian/weekdays/tuesday'
]
}), {
locale: 'en-US',
style: 'long',
values: {
'dates/fields/year': 'year',
'dates/fields/month': 'month',
'dates/fields/week': 'week',
'dates/fields/day': 'day',
'dates/gregorian/months/january': 'January',
'dates/gregorian/months/february': 'February',
'dates/gregorian/months/march': 'March',
'dates/gregorian/weekdays/tuesday': 'Tuesday',
}
});
checkDisplayNames(gDN('fr', {
keys: [
'dates/fields/year',
'dates/fields/day',
'dates/gregorian/months/october',
'dates/gregorian/weekdays/saturday',
'dates/gregorian/dayperiods/pm'
]
}), {
locale: 'fr',
style: 'long',
values: {
'dates/fields/year': 'année',
'dates/fields/day': 'jour',
'dates/gregorian/months/october': 'octobre',
'dates/gregorian/weekdays/saturday': 'samedi',
'dates/gregorian/dayperiods/pm': 'PM'
}
});
checkDisplayNames(gDN('it', {
style: 'short',
keys: [
'dates/gregorian/weekdays/thursday',
'dates/gregorian/months/august',
'dates/gregorian/dayperiods/am',
'dates/fields/month',
]
}), {
locale: 'it',
style: 'short',
values: {
'dates/gregorian/weekdays/thursday': 'gio',
'dates/gregorian/months/august': 'ago',
'dates/gregorian/dayperiods/am': 'AM',
'dates/fields/month': 'mese'
}
});
checkDisplayNames(gDN('ar', {
style: 'long',
keys: [
'dates/gregorian/weekdays/thursday',
'dates/gregorian/months/august',
'dates/gregorian/dayperiods/am',
'dates/fields/month',
]
}), {
locale: 'ar',
style: 'long',
values: {
'dates/gregorian/weekdays/thursday': 'الخميس',
'dates/gregorian/months/august': 'أغسطس',
'dates/gregorian/dayperiods/am': 'ص',
'dates/fields/month': 'الشهر'
}
});
/* Invalid input */
assertThrowsInstanceOf(() => {
gDN('en-US', {
style: '',
keys: [
'dates/gregorian/weekdays/thursday',
]
});
}, RangeError);
assertThrowsInstanceOf(() => {
gDN('en-US', {
style: 'bogus',
keys: [
'dates/gregorian/weekdays/thursday',
]
});
}, RangeError);
assertThrowsInstanceOf(() => {
gDN('foo-X', {
keys: [
'dates/gregorian/weekdays/thursday',
]
});
}, RangeError);
const typeErrorKeys = [
null,
'string',
Symbol.iterator,
15,
1,
3.7,
NaN,
Infinity
];
for (let keys of typeErrorKeys) {
assertThrowsInstanceOf(() => {
gDN('en-US', {
keys
});
}, TypeError);
}
const rangeErrorKeys = [
[''],
['foo'],
['dates/foo'],
['/dates/foo'],
['dates/foo/foo'],
['dates/fields'],
['dates/fields/'],
['dates/fields/foo'],
['dates/fields/foo/month'],
['/dates/foo/faa/bar/baz'],
['dates///bar/baz'],
['dates/gregorian'],
['dates/gregorian/'],
['dates/gregorian/foo'],
['dates/gregorian/months'],
['dates/gregorian/months/foo'],
['dates/gregorian/weekdays'],
['dates/gregorian/weekdays/foo'],
['dates/gregorian/dayperiods'],
['dates/gregorian/dayperiods/foo'],
['dates/gregorian/months/الشهر'],
[3],
[null],
['d', 'a', 't', 'e', 's'],
['datesEXTRA'],
['dates/fieldsEXTRA'],
['dates/gregorianEXTRA'],
['dates/gregorian/monthsEXTRA'],
['dates/gregorian/weekdaysEXTRA'],
['dates/fields/dayperiods/amEXTRA'],
['dates/gregori\u1161n/months/january'],
["dates/fields/year/"],
["dates/fields/month/"],
["dates/fields/week/"],
["dates/fields/day/"],
["dates/gregorian/months/january/"],
["dates/gregorian/weekdays/saturday/"],
["dates/gregorian/dayperiods/am/"],
["dates/fields/months/january/"],
];
for (let keys of rangeErrorKeys) {
assertThrowsInstanceOf(() => {
gDN('en-US', {
keys
});
}, RangeError);
}
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -2477,6 +2477,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0),
JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0),
JS_FN("intl_GetCalendarInfo", intl_GetCalendarInfo, 1,0),
JS_FN("intl_ComputeDisplayNames", intl_ComputeDisplayNames, 3,0),
JS_FN("intl_IsValidTimeZoneName", intl_IsValidTimeZoneName, 1,0),
JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0),
JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0),

Просмотреть файл

@ -344,7 +344,7 @@ public:
if (mFrame->IsSVGText()) {
return 0.0f;
}
nscoord coord = mFrame->StyleText()->mWebkitTextStrokeWidth.GetCoordValue();
nscoord coord = mFrame->StyleText()->mWebkitTextStrokeWidth;
return mFrame->PresContext()->AppUnitsToFloatDevPixels(coord);
}
@ -5701,7 +5701,7 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
}
// text-stroke overflows
nscoord textStrokeWidth = StyleText()->mWebkitTextStrokeWidth.GetCoordValue();
nscoord textStrokeWidth = StyleText()->mWebkitTextStrokeWidth;
if (textStrokeWidth > 0) {
nsRect strokeRect = *aVisualOverflowRect;
strokeRect.x -= textStrokeWidth;

Просмотреть файл

@ -3194,7 +3194,7 @@ CSS_PROP_OUTLINE(
VARIANT_HKL | VARIANT_CALC,
kBorderWidthKTable,
offsetof(nsStyleOutline, mOutlineWidth),
eStyleAnimType_Coord)
eStyleAnimType_nscoord)
CSS_PROP_SHORTHAND(
overflow,
overflow,

Просмотреть файл

@ -4064,7 +4064,7 @@ already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetWebkitTextStrokeWidth()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetAppUnits(StyleText()->mWebkitTextStrokeWidth.GetCoordValue());
val->SetAppUnits(StyleText()->mWebkitTextStrokeWidth);
return val.forget();
}

Просмотреть файл

@ -18,6 +18,7 @@
#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for PlaybackDirection
#include "mozilla/Likely.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/Maybe.h"
#include "mozilla/OperatorNewExtensions.h"
#include "mozilla/Unused.h"
@ -1156,6 +1157,46 @@ SetComplexColor(const nsCSSValue& aValue,
}
}
template<UnsetAction UnsetTo>
static Maybe<nscoord>
ComputeLineWidthValue(const nsCSSValue& aValue,
const nscoord aParentCoord,
const nscoord aInitialCoord,
nsStyleContext* aStyleContext,
nsPresContext* aPresContext,
RuleNodeCacheConditions& aConditions)
{
nsCSSUnit unit = aValue.GetUnit();
if (unit == eCSSUnit_Initial ||
(UnsetTo == eUnsetInitial && unit == eCSSUnit_Unset)) {
return Some(aInitialCoord);
} else if (unit == eCSSUnit_Inherit ||
(UnsetTo == eUnsetInherit && unit == eCSSUnit_Unset)) {
aConditions.SetUncacheable();
return Some(aParentCoord);
} else if (unit == eCSSUnit_Enumerated) {
NS_ASSERTION(aValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN ||
aValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM ||
aValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK,
"Unexpected line-width keyword!");
return Some(nsPresContext::GetBorderWidthForKeyword(aValue.GetIntValue()));
} else if (aValue.IsLengthUnit() ||
aValue.IsCalcUnit()) {
nscoord len =
CalcLength(aValue, aStyleContext, aPresContext, aConditions);
if (len < 0) {
NS_ASSERTION(aValue.IsCalcUnit(),
"Parser should have rejected negative length!");
len = 0;
}
return Some(len);
} else {
NS_ASSERTION(unit == eCSSUnit_Null,
"Missing case handling for line-width computing!");
return Maybe<nscoord>(Nothing());
}
}
static void SetGradientCoord(const nsCSSValue& aValue, nsPresContext* aPresContext,
nsStyleContext* aContext, nsStyleCoord& aResult,
RuleNodeCacheConditions& aConditions)
@ -4924,22 +4965,13 @@ nsRuleNode::ComputeTextData(void* aStartStruct,
&nsStyleText::mWebkitTextStrokeColor);
// -webkit-text-stroke-width: length, inherit, initial, enum
const nsCSSValue*
webkitTextStrokeWidthValue = aRuleData->ValueForWebkitTextStrokeWidth();
if (webkitTextStrokeWidthValue->GetUnit() == eCSSUnit_Enumerated) {
NS_ASSERTION(webkitTextStrokeWidthValue->GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN ||
webkitTextStrokeWidthValue->GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM ||
webkitTextStrokeWidthValue->GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK,
"Unexpected enum value");
text->mWebkitTextStrokeWidth.SetCoordValue(
nsPresContext::GetBorderWidthForKeyword(webkitTextStrokeWidthValue->GetIntValue()));
} else {
SetCoord(*webkitTextStrokeWidthValue, text->mWebkitTextStrokeWidth,
parentText->mWebkitTextStrokeWidth,
SETCOORD_LH | SETCOORD_CALC_LENGTH_ONLY |
SETCOORD_CALC_CLAMP_NONNEGATIVE |
SETCOORD_INITIAL_ZERO | SETCOORD_UNSET_INHERIT,
aContext, mPresContext, conditions);
Maybe<nscoord> coord =
ComputeLineWidthValue<eUnsetInherit>(
*aRuleData->ValueForWebkitTextStrokeWidth(),
parentText->mWebkitTextStrokeWidth, 0,
aContext, mPresContext, conditions);
if (coord.isSome()) {
text->mWebkitTextStrokeWidth = *coord;
}
// -moz-control-character-visibility: enum, inherit, initial
@ -7531,35 +7563,13 @@ nsRuleNode::ComputeBorderData(void* aStartStruct,
"method, the "
"nsLineLayout::IsPercentageAwareReplacedElement method "
"and probably some other places");
if (eCSSUnit_Enumerated == value.GetUnit()) {
NS_ASSERTION(value.GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN ||
value.GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM ||
value.GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK,
"Unexpected enum value");
border->SetBorderWidth(side,
nsPresContext::GetBorderWidthForKeyword(value.GetIntValue()));
}
// OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT
else if (SetCoord(value, coord, nsStyleCoord(),
SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY,
aContext, mPresContext, conditions)) {
NS_ASSERTION(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
// clamp negative calc() to 0.
border->SetBorderWidth(side, std::max(coord.GetCoordValue(), 0));
}
else if (eCSSUnit_Inherit == value.GetUnit()) {
conditions.SetUncacheable();
border->SetBorderWidth(side,
parentBorder->GetComputedBorder().Side(side));
}
else if (eCSSUnit_Initial == value.GetUnit() ||
eCSSUnit_Unset == value.GetUnit()) {
border->SetBorderWidth(side,
nsPresContext::GetBorderWidthForKeyword(NS_STYLE_BORDER_WIDTH_MEDIUM));
}
else {
NS_ASSERTION(eCSSUnit_Null == value.GetUnit(),
"missing case handling border width");
Maybe<nscoord> coord =
ComputeLineWidthValue<eUnsetInitial>(
value, parentBorder->GetComputedBorder().Side(side),
nsPresContext::GetBorderWidthForKeyword(NS_STYLE_BORDER_WIDTH_MEDIUM),
aContext, mPresContext, conditions);
if (coord.isSome()) {
border->SetBorderWidth(side, *coord);
}
}
}
@ -7819,17 +7829,13 @@ nsRuleNode::ComputeOutlineData(void* aStartStruct,
COMPUTE_START_RESET(Outline, outline, parentOutline)
// outline-width: length, enum, inherit
const nsCSSValue* outlineWidthValue = aRuleData->ValueForOutlineWidth();
if (eCSSUnit_Initial == outlineWidthValue->GetUnit() ||
eCSSUnit_Unset == outlineWidthValue->GetUnit()) {
outline->mOutlineWidth =
nsStyleCoord(NS_STYLE_BORDER_WIDTH_MEDIUM, eStyleUnit_Enumerated);
}
else {
SetCoord(*outlineWidthValue, outline->mOutlineWidth,
parentOutline->mOutlineWidth,
SETCOORD_LEH | SETCOORD_CALC_LENGTH_ONLY, aContext,
mPresContext, conditions);
Maybe<nscoord> coord =
ComputeLineWidthValue<eUnsetInitial>(
*aRuleData->ValueForOutlineWidth(), parentOutline->mOutlineWidth,
nsPresContext::GetBorderWidthForKeyword(NS_STYLE_BORDER_WIDTH_MEDIUM),
aContext, mPresContext, conditions);
if (coord.isSome()) {
outline->mOutlineWidth = *coord;
}
// outline-offset: length, inherit
@ -9157,36 +9163,14 @@ nsRuleNode::ComputeColumnData(void* aStartStruct,
}
// column-rule-width: length, enum, inherit
const nsCSSValue& widthValue = *aRuleData->ValueForColumnRuleWidth();
if (eCSSUnit_Initial == widthValue.GetUnit() ||
eCSSUnit_Unset == widthValue.GetUnit()) {
column->SetColumnRuleWidth(
nsPresContext::GetBorderWidthForKeyword(NS_STYLE_BORDER_WIDTH_MEDIUM));
}
else if (eCSSUnit_Enumerated == widthValue.GetUnit()) {
NS_ASSERTION(widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN ||
widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM ||
widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK,
"Unexpected enum value");
column->SetColumnRuleWidth(
nsPresContext::GetBorderWidthForKeyword(widthValue.GetIntValue()));
}
else if (eCSSUnit_Inherit == widthValue.GetUnit()) {
column->SetColumnRuleWidth(parent->GetComputedColumnRuleWidth());
conditions.SetUncacheable();
}
else if (widthValue.IsLengthUnit() || widthValue.IsCalcUnit()) {
nscoord len =
CalcLength(widthValue, aContext, mPresContext, conditions);
if (len < 0) {
// FIXME: This is untested (by test_value_storage.html) for
// column-rule-width since it gets covered up by the border
// rounding code.
NS_ASSERTION(widthValue.IsCalcUnit(),
"parser should have rejected negative length");
len = 0;
}
column->SetColumnRuleWidth(len);
Maybe<nscoord> coord =
ComputeLineWidthValue<eUnsetInitial>(
*aRuleData->ValueForColumnRuleWidth(),
parent->GetComputedColumnRuleWidth(),
nsPresContext::GetBorderWidthForKeyword(NS_STYLE_BORDER_WIDTH_MEDIUM),
aContext, mPresContext, conditions);
if (coord.isSome()) {
column->SetColumnRuleWidth(*coord);
}
// column-rule-style: enum, inherit

Просмотреть файл

@ -239,22 +239,6 @@ nsStyleFont::GetLanguage(StyleStructContext aContext)
return language.forget();
}
static nscoord
CalcCoord(const nsStyleCoord& aCoord, const nscoord* aEnumTable, int32_t aNumEnums)
{
if (aCoord.GetUnit() == eStyleUnit_Enumerated) {
MOZ_ASSERT(aEnumTable, "must have enum table");
int32_t value = aCoord.GetIntValue();
if (0 <= value && value < aNumEnums) {
return aEnumTable[aCoord.GetIntValue()];
}
NS_NOTREACHED("unexpected enum value");
return 0;
}
MOZ_ASSERT(aCoord.ConvertsToLength(), "unexpected unit");
return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0);
}
nsStyleMargin::nsStyleMargin(StyleStructContext aContext)
{
MOZ_COUNT_CTOR(nsStyleMargin);
@ -547,7 +531,8 @@ nsStyleBorder::CalcDifference(const nsStyleBorder& aNewData) const
}
nsStyleOutline::nsStyleOutline(StyleStructContext aContext)
: mOutlineWidth(NS_STYLE_BORDER_WIDTH_MEDIUM, eStyleUnit_Enumerated)
: mOutlineWidth((StaticPresData::Get()
->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM])
, mOutlineOffset(0)
, mOutlineColor(StyleComplexColor::CurrentColor())
, mOutlineStyle(NS_STYLE_BORDER_STYLE_NONE)
@ -580,14 +565,8 @@ nsStyleOutline::RecalcData()
if (NS_STYLE_BORDER_STYLE_NONE == mOutlineStyle) {
mActualOutlineWidth = 0;
} else {
MOZ_ASSERT(mOutlineWidth.ConvertsToLength() ||
mOutlineWidth.GetUnit() == eStyleUnit_Enumerated);
// Clamp negative calc() to 0.
mActualOutlineWidth =
std::max(CalcCoord(mOutlineWidth,
StaticPresData::Get()->GetBorderWidthTable(), 3), 0);
mActualOutlineWidth =
NS_ROUND_BORDER_TO_PIXELS(mActualOutlineWidth, mTwipsPerPixel);
NS_ROUND_BORDER_TO_PIXELS(mOutlineWidth, mTwipsPerPixel);
}
}
@ -3791,7 +3770,7 @@ nsStyleText::nsStyleText(StyleStructContext aContext)
, mLetterSpacing(eStyleUnit_Normal)
, mLineHeight(eStyleUnit_Normal)
, mTextIndent(0, nsStyleCoord::CoordConstructor)
, mWebkitTextStrokeWidth(0, nsStyleCoord::CoordConstructor)
, mWebkitTextStrokeWidth(0)
, mTextShadow(nullptr)
{
MOZ_COUNT_CTOR(nsStyleText);

Просмотреть файл

@ -1486,7 +1486,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleOutline
// value used by layout. (We must store mOutlineWidth for the same
// style struct resolution reasons that we do nsStyleBorder::mBorder;
// see that field's comment.)
nsStyleCoord mOutlineWidth; // [reset] coord, enum (see nsStyleConsts.h)
nscoord mOutlineWidth; // [reset] coord, enum (see nsStyleConsts.h)
nscoord mOutlineOffset; // [reset]
mozilla::StyleComplexColor mOutlineColor; // [reset]
uint8_t mOutlineStyle; // [reset] See nsStyleConsts.h
@ -2092,7 +2092,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText
nsStyleCoord mLetterSpacing; // [inherited] coord, normal
nsStyleCoord mLineHeight; // [inherited] coord, factor, normal
nsStyleCoord mTextIndent; // [inherited] coord, percent, calc
nsStyleCoord mWebkitTextStrokeWidth; // [inherited] coord
nscoord mWebkitTextStrokeWidth; // [inherited] coord
RefPtr<nsCSSShadowArray> mTextShadow; // [inherited] nullptr in case of a zero-length
@ -2138,7 +2138,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText
}
bool HasWebkitTextStroke() const {
return mWebkitTextStrokeWidth.GetCoordValue() > 0;
return mWebkitTextStrokeWidth > 0;
}
// These are defined in nsStyleStructInlines.h.

Просмотреть файл

@ -550,14 +550,16 @@ class TransportConduitTest : public ::testing::Test
mozilla::SyncRunnable::DispatchToThread(gMainThread,
WrapRunnableNMRet(&mAudioSession,
&mozilla::AudioSessionConduit::Create));
if( !mAudioSession )
if( !mAudioSession ) {
ASSERT_NE(mAudioSession, (void*)nullptr);
}
mozilla::SyncRunnable::DispatchToThread(gMainThread,
WrapRunnableNMRet(&mAudioSession2,
&mozilla::AudioSessionConduit::Create));
if( !mAudioSession2 )
if( !mAudioSession2 ) {
ASSERT_NE(mAudioSession2, (void*)nullptr);
}
WebrtcMediaTransport* xport = new WebrtcMediaTransport();
ASSERT_NE(xport, (void*)nullptr);
@ -615,15 +617,17 @@ class TransportConduitTest : public ::testing::Test
mozilla::SyncRunnable::DispatchToThread(gMainThread,
WrapRunnableNMRet(&mVideoSession,
&mozilla::VideoSessionConduit::Create));
if( !mVideoSession )
if( !mVideoSession ) {
ASSERT_NE(mVideoSession, (void*)nullptr);
}
// This session is for other one
mozilla::SyncRunnable::DispatchToThread(gMainThread,
WrapRunnableNMRet(&mVideoSession2,
&mozilla::VideoSessionConduit::Create));
if( !mVideoSession2 )
if( !mVideoSession2 ) {
ASSERT_NE(mVideoSession2,(void*)nullptr);
}
if (!send_vp8) {
SetGmpCodecs();
@ -716,8 +720,9 @@ class TransportConduitTest : public ::testing::Test
mozilla::SyncRunnable::DispatchToThread(gMainThread,
WrapRunnableNMRet(&videoSession,
&mozilla::VideoSessionConduit::Create));
if( !videoSession )
if( !videoSession ) {
ASSERT_NE(videoSession, (void*)nullptr);
}
//Test Configure Recv Codec APIS
cerr << " *************************************************" << endl;
@ -831,8 +836,9 @@ class TransportConduitTest : public ::testing::Test
mozilla::SyncRunnable::DispatchToThread(gMainThread,
WrapRunnableNMRet(&mVideoSession,
&mozilla::VideoSessionConduit::Create));
if( !mVideoSession )
if( !mVideoSession ) {
ASSERT_NE(mVideoSession, (void*)nullptr);
}
mozilla::EncodingConstraints constraints;
constraints.maxFs = max_fs;

Просмотреть файл

@ -1475,8 +1475,8 @@ class SignalingAgent {
pObserver->addIceCandidateState = TestObserver::stateNoResponse;
pc->AddIceCandidate(candidate.c_str(), mid.c_str(), level);
ASSERT_TRUE(pObserver->addIceCandidateState ==
expectSuccess ? TestObserver::stateSuccess :
TestObserver::stateError
(expectSuccess ? TestObserver::stateSuccess :
TestObserver::stateError)
);
// Verify that adding ICE candidates does not change the signaling state

Просмотреть файл

@ -61,11 +61,6 @@ public class DataReportingNotification {
}
return;
}
if (currentVersion >= DATA_REPORTING_VERSION) {
// Do nothing, we're at a current (or future) version.
return;
}
}
/**

Просмотреть файл

@ -33,7 +33,7 @@ public class Fetched {
public static @NonNull Fetched fromJSONObject(@NonNull JSONObject json) {
final String value = json.optString("value", null);
final String timestampString = json.optString("timestamp", null);
final long timestamp = timestampString != null ? Long.valueOf(timestampString) : 0L;
final long timestamp = timestampString != null ? Long.parseLong(timestampString) : 0L;
return new Fetched(value, timestamp);
}

Просмотреть файл

@ -149,7 +149,9 @@
<dimen name="tab_thumbnail_height">90dp</dimen>
<dimen name="tab_panel_item_width">129dp</dimen>
<dimen name="tab_panel_grid_hpadding">20dp</dimen>
<dimen name="tab_panel_grid_vpadding">19dp</dimen>
<!-- Top and bottom tab panel grid padding is split between tab_panel_grid_vpadding (the
RecyclerView padding) and tab_panel_grid_item_vpadding (individual item padding). -->
<dimen name="tab_panel_grid_vpadding">9dp</dimen>
<dimen name="tab_panel_grid_ideal_item_hspacing">20dp</dimen>
<dimen name="tab_panel_grid_min_item_hspacing">2dp</dimen>
<dimen name="tab_panel_grid_item_vpadding">10dp</dimen>

Просмотреть файл

@ -48,6 +48,14 @@
"unpack": true
},
{
"version": "rustc 1.14.0-beta.2 (e627a2e6e 2016-11-16) repack",
"size": 75964276,
"digest": "3a83a42330cdc42fbffcd91aa80f8e33749c716068047699fbd0ee5b5c51ec350f60285b34fd45d9bb038a5af9118e16ab66cbf0a7167eed5a6d239f50e32462",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "jcentral.tar.xz",

Просмотреть файл

@ -74,8 +74,8 @@
},
{
"version": "rustc 1.14.0-beta.2 (e627a2e6e 2016-11-16) repack",
"size": 96360828,
"digest": "6f9f6c5fde5366fe7c67f9a7699b8c64c2e14940d57e9cf87026b61a9cbf4aabe0d35933c1365034980031164817fd51c3bc02d6449bd9e8f2225981b053e47e",
"size": 75964276,
"digest": "3a83a42330cdc42fbffcd91aa80f8e33749c716068047699fbd0ee5b5c51ec350f60285b34fd45d9bb038a5af9118e16ab66cbf0a7167eed5a6d239f50e32462",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

Просмотреть файл

@ -617,10 +617,6 @@ public class FormHistoryRepositorySession extends
}
Logger.trace(LOG_TAG, "Remote is older, local is not deleted. Ignoring.");
if (!locallyModified) {
Logger.warn(LOG_TAG, "Inconsistency: old remote record is deleted, but local record not modified!");
// Ensure that this is tracked for upload.
}
return;
}
// End deletion logic.
@ -672,10 +668,6 @@ public class FormHistoryRepositorySession extends
}
Logger.trace(LOG_TAG, "Remote is older, local is not deleted. Ignoring.");
if (!locallyModified) {
Logger.warn(LOG_TAG, "Inconsistency: old remote record is not deleted, but local record not modified!");
}
return;
} catch (Exception e) {
Logger.error(LOG_TAG, "Store failed for " + record.guid, e);
delegate.onRecordStoreFailed(e, record.guid);

Просмотреть файл

@ -316,10 +316,7 @@ public class PasswordsRepositorySession extends
}
trace("Remote is older, local is not deleted. Ignoring.");
if (!locallyModified) {
Logger.warn(LOG_TAG, "Inconsistency: old remote record is deleted, but local record not modified!");
// Ensure that this is tracked for upload.
}
return;
}
// End deletion logic.

Просмотреть файл

@ -44,6 +44,32 @@ MozIntl::AddGetCalendarInfo(JS::Handle<JS::Value> val, JSContext* cx)
return NS_OK;
}
NS_IMETHODIMP
MozIntl::AddGetDisplayNames(JS::Handle<JS::Value> val, JSContext* cx)
{
if (!val.isObject()) {
return NS_ERROR_INVALID_ARG;
}
JS::Rooted<JSObject*> realIntlObj(cx, js::CheckedUnwrap(&val.toObject()));
if (!realIntlObj) {
return NS_ERROR_INVALID_ARG;
}
JSAutoCompartment ac(cx, realIntlObj);
static const JSFunctionSpec funcs[] = {
JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0),
JS_FS_END
};
if (!JS_DefineFunctions(cx, realIntlObj, funcs)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_GENERIC_FACTORY_CONSTRUCTOR(MozIntl)
NS_DEFINE_NAMED_CID(MOZ_MOZINTL_CID);

Просмотреть файл

@ -9,4 +9,5 @@
interface mozIMozIntl : nsISupports
{
[implicit_jscontext] void addGetCalendarInfo(in jsval intlObject);
[implicit_jscontext] void addGetDisplayNames(in jsval intlObject);
};

Просмотреть файл

@ -7,6 +7,7 @@ function run_test() {
test_this_global(mozIntl);
test_cross_global(mozIntl);
test_methods_presence(mozIntl);
ok(true);
}
@ -30,3 +31,16 @@ function test_cross_global(mozIntl) {
equal(waivedX.getCalendarInfo() instanceof Object, false);
equal(waivedX.getCalendarInfo() instanceof global.Object, true);
}
function test_methods_presence(mozIntl) {
equal(mozIntl.addGetCalendarInfo instanceof Function, true);
equal(mozIntl.addGetDisplayNames instanceof Function, true);
let x = {};
mozIntl.addGetCalendarInfo(x);
equal(x.getCalendarInfo instanceof Function, true);
mozIntl.addGetDisplayNames(x);
equal(x.getDisplayNames instanceof Function, true);
}