Merge the last PGO-green inbound changeset to m-c.
|
@ -73,6 +73,10 @@ function getContentWindow() {
|
|||
return shell.contentBrowser.contentWindow;
|
||||
}
|
||||
|
||||
function debug(str) {
|
||||
dump(' -*- Shell.js: ' + str + '\n');
|
||||
}
|
||||
|
||||
var shell = {
|
||||
|
||||
get CrashSubmit() {
|
||||
|
@ -611,41 +615,62 @@ var AlertsHelper = {
|
|||
if (!detail || !detail.id)
|
||||
return;
|
||||
|
||||
let listener = this._listeners[detail.id];
|
||||
let uid = detail.id;
|
||||
let listener = this._listeners[uid];
|
||||
if (!listener)
|
||||
return;
|
||||
|
||||
let topic = detail.type == "desktop-notification-click" ? "alertclickcallback"
|
||||
: "alertfinished";
|
||||
|
||||
if (detail.id.startsWith("alert")) {
|
||||
listener.observer.observe(null, topic, listener.cookie);
|
||||
} else {
|
||||
listener.mm.sendAsyncMessage("app-notification-return",
|
||||
{ id: detail.id,
|
||||
type: detail.type });
|
||||
if (uid.startsWith("app-notif")) {
|
||||
listener.mm.sendAsyncMessage("app-notification-return", {
|
||||
uid: uid,
|
||||
topic: topic,
|
||||
target: listener.target
|
||||
});
|
||||
} else if (uid.startsWith("alert")) {
|
||||
try {
|
||||
listener.observer.observe(null, topic, listener.cookie);
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
// we're done with this notification
|
||||
if (topic === "alertfinished")
|
||||
delete this._listeners[detail.id];
|
||||
if (topic === "alertfinished") {
|
||||
delete this._listeners[uid];
|
||||
}
|
||||
},
|
||||
|
||||
registerListener: function alert_registerListener(cookie, alertListener) {
|
||||
let id = "alert" + this._count++;
|
||||
this._listeners[id] = { observer: alertListener, cookie: cookie };
|
||||
return id;
|
||||
let uid = "alert" + this._count++;
|
||||
this._listeners[uid] = { observer: alertListener, cookie: cookie };
|
||||
return uid;
|
||||
},
|
||||
|
||||
registerAppListener: function alertRegisterAppListener(id, mm, title, text,
|
||||
manifestURL, imageURL) {
|
||||
this._listeners[id] = {
|
||||
mm: mm,
|
||||
title: title,
|
||||
text: text,
|
||||
manifestURL: manifestURL,
|
||||
imageURL: imageURL
|
||||
};
|
||||
registerAppListener: function alert_registerAppListener(uid, listener) {
|
||||
this._listeners[uid] = listener;
|
||||
|
||||
let app = DOMApplicationRegistry.getAppByManifestURL(listener.manifestURL);
|
||||
DOMApplicationRegistry.getManifestFor(app.origin, function(manifest) {
|
||||
let helper = new ManifestHelper(manifest, app.origin);
|
||||
let getNotificationURLFor = function(messages) {
|
||||
if (!messages)
|
||||
return null;
|
||||
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
let message = messages[i];
|
||||
if (message === "notification") {
|
||||
return helper.fullLaunchPath();
|
||||
} else if ("notification" in message) {
|
||||
return helper.resolveFromOrigin(message["notification"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listener.target = getNotificationURLFor(manifest.messages);
|
||||
|
||||
// Bug 816944 - Support notification messages for entry_points.
|
||||
});
|
||||
},
|
||||
|
||||
showNotification: function alert_showNotification(imageUrl,
|
||||
|
@ -653,13 +678,13 @@ var AlertsHelper = {
|
|||
text,
|
||||
textClickable,
|
||||
cookie,
|
||||
id,
|
||||
uid,
|
||||
name,
|
||||
manifestUrl) {
|
||||
function send(appName, appIcon) {
|
||||
shell.sendChromeEvent({
|
||||
type: "desktop-notification",
|
||||
id: id,
|
||||
id: uid,
|
||||
icon: imageUrl,
|
||||
title: title,
|
||||
text: text,
|
||||
|
@ -668,17 +693,17 @@ var AlertsHelper = {
|
|||
});
|
||||
}
|
||||
|
||||
// If we have a manifest URL, get the icon and title from the manifest
|
||||
// to prevent spoofing.
|
||||
if (manifestUrl && manifestUrl.length) {
|
||||
let app = DOMApplicationRegistry.getAppByManifestURL(manifestUrl);
|
||||
DOMApplicationRegistry.getManifestFor(app.origin, function(aManifest) {
|
||||
let helper = new ManifestHelper(aManifest, app.origin);
|
||||
send(helper.name, helper.iconURLForSize(128));
|
||||
});
|
||||
} else {
|
||||
if (!manifestUrl || !manifestUrl.length) {
|
||||
send(null, null);
|
||||
}
|
||||
|
||||
// If we have a manifest URL, get the icon and title from the manifest
|
||||
// to prevent spoofing.
|
||||
let app = DOMApplicationRegistry.getAppByManifestURL(manifestUrl);
|
||||
DOMApplicationRegistry.getManifestFor(app.origin, function(aManifest) {
|
||||
let helper = new ManifestHelper(aManifest, app.origin);
|
||||
send(helper.name, helper.iconURLForSize(128));
|
||||
});
|
||||
},
|
||||
|
||||
showAlertNotification: function alert_showAlertNotification(imageUrl,
|
||||
|
@ -688,19 +713,25 @@ var AlertsHelper = {
|
|||
cookie,
|
||||
alertListener,
|
||||
name) {
|
||||
let id = this.registerListener(null, alertListener);
|
||||
let uid = this.registerListener(null, alertListener);
|
||||
this.showNotification(imageUrl, title, text, textClickable, cookie,
|
||||
id, name, null);
|
||||
uid, name, null);
|
||||
},
|
||||
|
||||
receiveMessage: function alert_receiveMessage(message) {
|
||||
let data = message.data;
|
||||
let listener = {
|
||||
mm: message.target,
|
||||
title: data.title,
|
||||
text: data.text,
|
||||
manifestURL: data.manifestURL,
|
||||
imageURL: data.imageURL
|
||||
}
|
||||
this.registerAppListener(data.uid, listener);
|
||||
|
||||
this.registerAppListener(data.id, message.target, data.title, data.text,
|
||||
data.manifestURL, data.imageURL);
|
||||
this.showNotification(data.imageURL, data.title, data.text,
|
||||
data.textClickable, null,
|
||||
data.id, null, data.manifestURL);
|
||||
data.uid, null, data.manifestURL);
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -9,18 +9,29 @@ const Cc = Components.classes;
|
|||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidGenerator",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
|
||||
return Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(Ci.nsIMessageSender);
|
||||
});
|
||||
|
||||
function debug(str) {
|
||||
dump("=*= AlertsService.js : " + str + "\n");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Alerts Service
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
function AlertsService() {
|
||||
cpmm.addMessageListener("app-notification-return", this);
|
||||
this._id = 0;
|
||||
}
|
||||
|
||||
AlertsService.prototype = {
|
||||
|
@ -43,43 +54,67 @@ AlertsService.prototype = {
|
|||
},
|
||||
|
||||
// nsIAppNotificationService
|
||||
_listeners: [],
|
||||
|
||||
receiveMessage: function receiveMessage(aMessage) {
|
||||
let data = aMessage.data;
|
||||
if (aMessage.name !== "app-notification-return" ||
|
||||
!this._listeners[data.id]) {
|
||||
return;
|
||||
}
|
||||
|
||||
let obs = this._listeners[data.id];
|
||||
let topic = data.type == "desktop-notification-click" ? "alertclickcallback"
|
||||
: "alertfinished";
|
||||
obs.observe(null, topic, null);
|
||||
|
||||
// we're done with this notification
|
||||
if (topic === "alertfinished")
|
||||
delete this._listeners[data.id];
|
||||
},
|
||||
|
||||
// This method is called in the content process, so we remote the call
|
||||
// to shell.js
|
||||
showAppNotification: function showAppNotification(aImageURL,
|
||||
aTitle,
|
||||
aText,
|
||||
aTextClickable,
|
||||
aManifestURL,
|
||||
aAlertListener) {
|
||||
let id = "app-notif" + this._id++;
|
||||
this._listeners[id] = aAlertListener;
|
||||
let uid = "app-notif-" + uuidGenerator.generateUUID();
|
||||
|
||||
this._listeners[uid] = {
|
||||
observer: aAlertListener,
|
||||
title: aTitle,
|
||||
text: aText,
|
||||
manifestURL: aManifestURL,
|
||||
imageURL: aImageURL
|
||||
};
|
||||
|
||||
cpmm.sendAsyncMessage("app-notification-send", {
|
||||
imageURL: aImageURL,
|
||||
title: aTitle,
|
||||
text: aText,
|
||||
textClickable: aTextClickable,
|
||||
manifestURL: aManifestURL,
|
||||
id: id
|
||||
uid: uid
|
||||
});
|
||||
},
|
||||
|
||||
// AlertsService.js custom implementation
|
||||
_listeners: [],
|
||||
|
||||
receiveMessage: function receiveMessage(aMessage) {
|
||||
let data = aMessage.data;
|
||||
let listener = this._listeners[data.uid];
|
||||
if (aMessage.name !== "app-notification-return" || !listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
let topic = data.topic;
|
||||
|
||||
try {
|
||||
listener.observer.observe(null, topic, null);
|
||||
} catch (e) {
|
||||
// It seems like there is no callbacks anymore, forward the click on
|
||||
// notification via a system message containing the title/text/icon of
|
||||
// the notification so the app get a change to react.
|
||||
if (data.target) {
|
||||
gSystemMessenger.sendMessage("notification", {
|
||||
title: listener.title,
|
||||
body: listener.text,
|
||||
imageURL: listener.imageURL
|
||||
},
|
||||
Services.io.newURI(data.target, null, null),
|
||||
Services.io.newURI(listener.manifestURL, null, null));
|
||||
}
|
||||
|
||||
cpmm.sendAsyncMessage("app-notification-sysmsg-request", listener);
|
||||
}
|
||||
|
||||
// we're done with this notification
|
||||
if (topic === "alertfinished") {
|
||||
delete this._listeners[data.uid];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -16,9 +16,6 @@ let {NewTabUtils, Sanitizer} = tmp;
|
|||
let uri = Services.io.newURI("about:newtab", null, null);
|
||||
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||
|
||||
let sm = Services.domStorageManager;
|
||||
let storage = sm.getLocalStorageForPrincipal(principal, "");
|
||||
|
||||
let gWindow = window;
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
|
@ -185,7 +182,12 @@ function setPinnedLinks(aLinks) {
|
|||
});
|
||||
}
|
||||
|
||||
storage.setItem("pinnedLinks", JSON.stringify(links));
|
||||
let string = Cc["@mozilla.org/supports-string;1"]
|
||||
.createInstance(Ci.nsISupportsString);
|
||||
string.data = JSON.stringify(links);
|
||||
Services.prefs.setComplexValue("browser.newtabpage.pinned",
|
||||
Ci.nsISupportsString, string);
|
||||
|
||||
NewTabUtils.pinnedLinks.resetCache();
|
||||
NewTabUtils.allPages.update();
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ richlistitem[type="download"]:not([selected]) button {
|
|||
visibility: hidden;
|
||||
}
|
||||
|
||||
#downloadsSummary:not([inprogress="true"]) #downloadsSummaryProgress,
|
||||
#downloadsSummary:not([inprogress="true"]) #downloadsSummaryDetails {
|
||||
#downloadsSummary:not([inprogress="true"]) > vbox > #downloadsSummaryProgress,
|
||||
#downloadsSummary:not([inprogress="true"]) > vbox > #downloadsSummaryDetails {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -19,11 +19,25 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
|
||||
"resource:///modules/PageThumbs.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gPrincipal", function () {
|
||||
let uri = Services.io.newURI("about:newtab", null, null);
|
||||
return Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function () {
|
||||
return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gUnicodeConverter", function () {
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = 'utf8';
|
||||
return converter;
|
||||
});
|
||||
|
||||
// The preference that tells whether this feature is enabled.
|
||||
const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
|
||||
|
||||
|
@ -39,22 +53,117 @@ const HISTORY_RESULTS_LIMIT = 100;
|
|||
// The gather telemetry topic.
|
||||
const TOPIC_GATHER_TELEMETRY = "gather-telemetry";
|
||||
|
||||
/**
|
||||
* Calculate the MD5 hash for a string.
|
||||
* @param aValue
|
||||
* The string to convert.
|
||||
* @return The base64 representation of the MD5 hash.
|
||||
*/
|
||||
function toHash(aValue) {
|
||||
let value = gUnicodeConverter.convertToByteArray(aValue);
|
||||
gCryptoHash.init(gCryptoHash.MD5);
|
||||
gCryptoHash.update(value, value.length);
|
||||
return gCryptoHash.finish(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton that provides storage functionality.
|
||||
*/
|
||||
let Storage = {
|
||||
XPCOMUtils.defineLazyGetter(this, "Storage", function() {
|
||||
return new LinksStorage();
|
||||
});
|
||||
|
||||
function LinksStorage() {
|
||||
// Handle migration of data across versions.
|
||||
try {
|
||||
if (this._storedVersion < this._version) {
|
||||
// This is either an upgrade, or version information is missing.
|
||||
if (this._storedVersion < 1) {
|
||||
this._migrateToV1();
|
||||
}
|
||||
// Add further migration steps here.
|
||||
}
|
||||
else {
|
||||
// This is a downgrade. Since we cannot predict future, upgrades should
|
||||
// be backwards compatible. We will set the version to the old value
|
||||
// regardless, so, on next upgrade, the migration steps will run again.
|
||||
// For this reason, they should also be able to run multiple times, even
|
||||
// on top of an already up-to-date storage.
|
||||
}
|
||||
} catch (ex) {
|
||||
// Something went wrong in the update process, we can't recover from here,
|
||||
// so just clear the storage and start from scratch (dataloss!).
|
||||
Components.utils.reportError(
|
||||
"Unable to migrate the newTab storage to the current version. "+
|
||||
"Restarting from scratch.\n" + ex);
|
||||
this.clear();
|
||||
}
|
||||
|
||||
// Set the version to the current one.
|
||||
this._storedVersion = this._version;
|
||||
}
|
||||
|
||||
LinksStorage.prototype = {
|
||||
get _version() 1,
|
||||
|
||||
get _prefs() Object.freeze({
|
||||
pinnedLinks: "browser.newtabpage.pinned",
|
||||
blockedLinks: "browser.newtabpage.blocked",
|
||||
}),
|
||||
|
||||
get _storedVersion() {
|
||||
if (this.__storedVersion === undefined) {
|
||||
try {
|
||||
this.__storedVersion =
|
||||
Services.prefs.getIntPref("browser.newtabpage.storageVersion");
|
||||
} catch (ex) {
|
||||
this.__storedVersion = 0;
|
||||
}
|
||||
}
|
||||
return this.__storedVersion;
|
||||
},
|
||||
set _storedVersion(aValue) {
|
||||
Services.prefs.setIntPref("browser.newtabpage.storageVersion", aValue);
|
||||
this.__storedVersion = aValue;
|
||||
return aValue;
|
||||
},
|
||||
|
||||
/**
|
||||
* The dom storage instance used to persist data belonging to the New Tab Page.
|
||||
* V1 changes storage from chromeappsstore.sqlite to prefs.
|
||||
*/
|
||||
get domStorage() {
|
||||
let sm = Services.domStorageManager;
|
||||
let storage = sm.getLocalStorageForPrincipal(gPrincipal, "");
|
||||
|
||||
// Cache this value, overwrite the getter.
|
||||
let descriptor = {value: storage, enumerable: true};
|
||||
Object.defineProperty(this, "domStorage", descriptor);
|
||||
|
||||
return storage;
|
||||
_migrateToV1: function Storage__migrateToV1() {
|
||||
// Import data from the old chromeappsstore.sqlite file, if exists.
|
||||
let file = FileUtils.getFile("ProfD", ["chromeappsstore.sqlite"]);
|
||||
if (!file.exists())
|
||||
return;
|
||||
let db = Services.storage.openUnsharedDatabase(file);
|
||||
let stmt = db.createStatement(
|
||||
"SELECT key, value FROM webappsstore2 WHERE scope = 'batwen.:about'");
|
||||
try {
|
||||
while (stmt.executeStep()) {
|
||||
let key = stmt.row.key;
|
||||
let value = JSON.parse(stmt.row.value);
|
||||
switch (key) {
|
||||
case "pinnedLinks":
|
||||
this.set(key, value);
|
||||
break;
|
||||
case "blockedLinks":
|
||||
// Convert urls to hashes.
|
||||
let hashes = {};
|
||||
for (let url in value) {
|
||||
hashes[toHash(url)] = 1;
|
||||
}
|
||||
this.set(key, hashes);
|
||||
break;
|
||||
default:
|
||||
// Ignore unknown keys.
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
stmt.finalize();
|
||||
db.close();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -65,11 +174,11 @@ let Storage = {
|
|||
*/
|
||||
get: function Storage_get(aKey, aDefault) {
|
||||
let value;
|
||||
|
||||
try {
|
||||
value = JSON.parse(this.domStorage.getItem(aKey));
|
||||
let prefValue = Services.prefs.getComplexValue(this._prefs[aKey],
|
||||
Ci.nsISupportsString).data;
|
||||
value = JSON.parse(prefValue);
|
||||
} catch (e) {}
|
||||
|
||||
return value || aDefault;
|
||||
},
|
||||
|
||||
|
@ -79,18 +188,22 @@ let Storage = {
|
|||
* @param aValue The value to set.
|
||||
*/
|
||||
set: function Storage_set(aKey, aValue) {
|
||||
this.domStorage.setItem(aKey, JSON.stringify(aValue));
|
||||
// Page titles may contain unicode, thus use complex values.
|
||||
let string = Cc["@mozilla.org/supports-string;1"]
|
||||
.createInstance(Ci.nsISupportsString);
|
||||
string.data = JSON.stringify(aValue);
|
||||
Services.prefs.setComplexValue(this._prefs[aKey], Ci.nsISupportsString,
|
||||
string);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the storage and removes all values.
|
||||
*/
|
||||
clear: function Storage_clear() {
|
||||
this.domStorage.clear();
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference])
|
||||
for each (let pref in this._prefs) {
|
||||
Services.prefs.clearUserPref(pref);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -355,7 +468,7 @@ let BlockedLinks = {
|
|||
* @param aLink The link to block.
|
||||
*/
|
||||
block: function BlockedLinks_block(aLink) {
|
||||
this.links[aLink.url] = 1;
|
||||
this.links[toHash(aLink.url)] = 1;
|
||||
this.save();
|
||||
|
||||
// Make sure we unpin blocked links.
|
||||
|
@ -368,7 +481,7 @@ let BlockedLinks = {
|
|||
*/
|
||||
unblock: function BlockedLinks_unblock(aLink) {
|
||||
if (this.isBlocked(aLink)) {
|
||||
delete this.links[aLink.url];
|
||||
delete this.links[toHash(aLink.url)];
|
||||
this.save();
|
||||
}
|
||||
},
|
||||
|
@ -385,7 +498,7 @@ let BlockedLinks = {
|
|||
* @param aLink The link to check.
|
||||
*/
|
||||
isBlocked: function BlockedLinks_isBlocked(aLink) {
|
||||
return (aLink.url in this.links);
|
||||
return (toHash(aLink.url) in this.links);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,9 @@ VPATH = @srcdir@
|
|||
relativesrcdir = @relativesrcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
XPCSHELL_TESTS = unit
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_BROWSER_FILES = \
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function getSimpleMeasurementsFromTelemetryPing() {
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
|
||||
let str = Cc['@mozilla.org/supports-string;1'].createInstance(Ci.nsISupportsString);
|
||||
TelemetryPing.observe(str, "get-payload", "");
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
let ping = TelemetryPing.getPayload();
|
||||
|
||||
return JSON.parse(str.data).simpleMeasurements;
|
||||
return ping.simpleMeasurements;
|
||||
}
|
||||
|
||||
function test() {
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/* 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/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource:///modules/NewTabUtils.jsm");
|
||||
Cu.import("resource://gre/modules/commonjs/promise/core.js");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
/**
|
||||
* Asynchronously load test data from chromeappstore.sqlite.
|
||||
*
|
||||
* @param aDBFile
|
||||
* the database file to load
|
||||
* @return {Promise} resolved when the load is complete
|
||||
*/
|
||||
function promiseLoadChromeAppsStore(aDBFile) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let pinnedLinks = [];
|
||||
let blockedLinks = [];
|
||||
|
||||
let db = Services.storage.openUnsharedDatabase(aDBFile);
|
||||
let stmt = db.createAsyncStatement(
|
||||
"SELECT key, value FROM webappsstore2 WHERE scope = 'batwen.:about'");
|
||||
try {
|
||||
stmt.executeAsync({
|
||||
handleResult: function(aResultSet) {
|
||||
for (let row = aResultSet.getNextRow(); row;
|
||||
row = aResultSet.getNextRow()) {
|
||||
let value = JSON.parse(row.getResultByName("value"));
|
||||
if (row.getResultByName("key") == "pinnedLinks") {
|
||||
pinnedLinks = value;
|
||||
} else {
|
||||
for (let url of Object.keys(value)) {
|
||||
blockedLinks.push({ url: url, title: "" });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
handleError: function(aError) {
|
||||
deferred.reject(new Components.Exception("Error", Cr.NS_ERROR_FAILURE));
|
||||
},
|
||||
handleCompletion: function(aReason) {
|
||||
if (aReason === Ci.mozIStorageStatementCallback.REASON_FINISHED) {
|
||||
deferred.resolve([pinnedLinks, blockedLinks]);
|
||||
}
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
stmt.finalize();
|
||||
db.asyncClose();
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
|
||||
// First of all copy the chromeappsstore.sqlite file to the profile folder.
|
||||
let dbFile = do_get_file("chromeappsstore.sqlite");
|
||||
let profileDBFile = do_get_profile();
|
||||
dbFile.copyTo(profileDBFile, "chromeappsstore.sqlite");
|
||||
profileDBFile.append("chromeappsstore.sqlite");
|
||||
do_check_true(profileDBFile.exists());
|
||||
|
||||
// Load test data from the database.
|
||||
promiseLoadChromeAppsStore(dbFile).then(function success(aResults) {
|
||||
let [pinnedLinks, blockedLinks] = aResults;
|
||||
|
||||
do_check_true(pinnedLinks.length > 0);
|
||||
do_check_eq(pinnedLinks.length, NewTabUtils.pinnedLinks.links.length);
|
||||
do_check_true(pinnedLinks.every(
|
||||
function(aLink) NewTabUtils.pinnedLinks.isPinned(aLink)
|
||||
));
|
||||
|
||||
do_check_true(blockedLinks.length > 0);
|
||||
do_check_eq(blockedLinks.length,
|
||||
Object.keys(NewTabUtils.blockedLinks.links).length);
|
||||
do_check_true(blockedLinks.every(
|
||||
function(aLink) NewTabUtils.blockedLinks.isBlocked(aLink)
|
||||
));
|
||||
|
||||
try {
|
||||
profileDBFile.remove(true);
|
||||
} catch (ex) {
|
||||
// May fail due to OS file locking, not a blocking error though.
|
||||
do_print("Unable to remove chromeappsstore.sqlite file.");
|
||||
}
|
||||
|
||||
do_test_finished();
|
||||
}, do_report_unexpected_exception);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
|
||||
[test_newtab-migrate-v1.js]
|
|
@ -24,7 +24,7 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
#downloadsPanel[hasdownloads] #downloadsFooter {
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter {
|
||||
border-top: 1px solid ThreeDShadow;
|
||||
background-image: -moz-linear-gradient(hsla(0,0%,0%,.15), hsla(0,0%,0%,.08) 6px);
|
||||
}
|
||||
|
@ -173,13 +173,13 @@ richlistitem[type="download"][state="1"]:hover {
|
|||
-moz-image-region: rect(32px, 48px, 48px, 32px);
|
||||
}
|
||||
|
||||
richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow {
|
||||
richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow {
|
||||
-moz-image-region: rect(48px, 16px, 64px, 0px);
|
||||
}
|
||||
richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:hover {
|
||||
richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:hover {
|
||||
-moz-image-region: rect(48px, 32px, 64px, 16px);
|
||||
}
|
||||
richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:active {
|
||||
richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:active {
|
||||
-moz-image-region: rect(48px, 48px, 64px, 32px);
|
||||
}
|
||||
|
||||
|
@ -192,33 +192,33 @@ richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:acti
|
|||
position: relative;
|
||||
}
|
||||
|
||||
toolbar[iconsize="small"] #downloads-indicator-anchor {
|
||||
toolbar[iconsize="small"] > #downloads-indicator > #downloads-indicator-anchor {
|
||||
min-width: 16px;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
toolbar[iconsize="large"] #downloads-indicator-anchor {
|
||||
toolbar[iconsize="large"] > #downloads-indicator > #downloads-indicator-anchor {
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
/*** Main indicator icon ***/
|
||||
|
||||
toolbar[iconsize="small"] #downloads-indicator-icon {
|
||||
toolbar[iconsize="small"] > #downloads-indicator > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar-small.png"),
|
||||
0, 16, 16, 0) center no-repeat;
|
||||
}
|
||||
|
||||
toolbar[iconsize="large"] #downloads-indicator-icon {
|
||||
toolbar[iconsize="large"] > #downloads-indicator > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
|
||||
0, 24, 24, 0) center no-repeat;
|
||||
}
|
||||
|
||||
toolbar[iconsize="small"] #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
toolbar[iconsize="small"] > #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow-small.png");
|
||||
}
|
||||
|
||||
toolbar[iconsize="large"] #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
toolbar[iconsize="large"] > #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow.png");
|
||||
}
|
||||
|
||||
|
|
|
@ -26,12 +26,12 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
#downloadsPanel:not([hasdownloads]) #downloadsHistory {
|
||||
#downloadsPanel:not([hasdownloads]) > #downloadsFooter > #downloadsHistory {
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
}
|
||||
|
||||
#downloadsPanel[hasdownloads] #downloadsFooter {
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter {
|
||||
background: #e5e5e5;
|
||||
border-top: 1px solid hsla(0,0%,0%,.1);
|
||||
box-shadow: 0 -1px hsla(0,0%,100%,.5) inset, 0 1px 1px hsla(0,0%,0%,.03) inset;
|
||||
|
@ -48,7 +48,7 @@
|
|||
border-top-right-radius: 6px;
|
||||
}
|
||||
|
||||
#downloadsPanel:not([hasdownloads]) #downloadsHistory:-moz-focusring > .button-box {
|
||||
#downloadsPanel:not([hasdownloads]) > #downloadsFooter > #downloadsHistory:-moz-focusring > .button-box {
|
||||
border-bottom-left-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
|
@ -164,13 +164,13 @@ richlistitem[type="download"][state="1"]:hover {
|
|||
.downloadButton.downloadShow {
|
||||
-moz-image-region: rect(16px, 16px, 32px, 0px);
|
||||
}
|
||||
richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow {
|
||||
richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow {
|
||||
-moz-image-region: rect(16px, 32px, 32px, 16px);
|
||||
}
|
||||
richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:hover {
|
||||
richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:hover {
|
||||
-moz-image-region: rect(16px, 48px, 32px, 32px);
|
||||
}
|
||||
richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:active {
|
||||
richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:active {
|
||||
-moz-image-region: rect(16px, 64px, 32px, 48px);
|
||||
}
|
||||
|
||||
|
@ -219,17 +219,18 @@ richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:acti
|
|||
background-size: 20px;
|
||||
}
|
||||
|
||||
#downloads-indicator:not([counter]) #downloads-indicator-counter {
|
||||
#downloads-indicator:not([counter]) > #downloads-indicator-anchor >
|
||||
#downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 280, 40, 240);
|
||||
}
|
||||
|
||||
#downloads-indicator[attention]
|
||||
#downloads-indicator[attention] > #downloads-indicator-anchor >
|
||||
#downloads-indicator-icon {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow@2x.png");
|
||||
}
|
||||
|
||||
#downloads-indicator:not([counter])[attention]
|
||||
#downloads-indicator-counter {
|
||||
#downloads-indicator:not([counter])[attention] > #downloads-indicator-anchor >
|
||||
#downloads-indicator-progress-area > #downloads-indicator-counter {
|
||||
background-image: url("chrome://browser/skin/downloads/download-glow@2x.png");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
%undef WINSTRIPE_AERO
|
||||
|
||||
@media (-moz-windows-default-theme) {
|
||||
#downloadsPanel[hasdownloads] #downloadsFooter {
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter {
|
||||
background-color: #f1f5fb;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
}
|
||||
|
||||
@media (-moz-windows-default-theme) {
|
||||
#downloadsPanel[hasdownloads] #downloadsFooter {
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter {
|
||||
background-color: hsla(216,45%,88%,.98);
|
||||
box-shadow: 0px 1px 2px rgb(204,214,234) inset;
|
||||
}
|
||||
|
@ -172,13 +172,13 @@ richlistitem[type="download"][state="1"]:hover {
|
|||
@media not all and (-moz-windows-default-theme) {
|
||||
%endif
|
||||
|
||||
richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow {
|
||||
richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow {
|
||||
-moz-image-region: rect(48px, 16px, 64px, 0px);
|
||||
}
|
||||
richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:hover {
|
||||
richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:hover {
|
||||
-moz-image-region: rect(48px, 32px, 64px, 16px);
|
||||
}
|
||||
richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:active {
|
||||
richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:active {
|
||||
-moz-image-region: rect(48px, 48px, 64px, 32px);
|
||||
}
|
||||
|
||||
|
|
|
@ -686,8 +686,7 @@ bool OggReader::ReadOggChain()
|
|||
#ifdef MOZ_OPUS
|
||||
if ((newOpusState && ReadHeaders(newOpusState)) &&
|
||||
(mOpusState->mRate == newOpusState->mRate) &&
|
||||
(mOpusState->mChannels == newOpusState->mChannels) &&
|
||||
(mOpusState->mPreSkip == newOpusState->mPreSkip)) {
|
||||
(mOpusState->mChannels == newOpusState->mChannels)) {
|
||||
mOpusState->Reset();
|
||||
mOpusState = newOpusState;
|
||||
mOpusSerial = mOpusState->mSerial;
|
||||
|
|
|
@ -215,6 +215,7 @@ MOCHITEST_FILES += \
|
|||
variable-channel.opus \
|
||||
chained-video.ogv \
|
||||
chained-audio-video.ogg \
|
||||
variable-preskip.opus \
|
||||
dirac.ogg \
|
||||
multiple-bos.ogg \
|
||||
split.webm \
|
||||
|
|
|
@ -355,6 +355,9 @@ var gChainingTests = [
|
|||
// A file that consist in 4 links of audio, then another link that has video.
|
||||
// We should stop right after the 4 audio links.
|
||||
{ name:"chained-audio-video.ogg", type:"video/ogg", links: 4 },
|
||||
// An opus file that has two links, with a different preskip value for each
|
||||
// link. We should be able to play both links.
|
||||
{ name:"variable-preskip.opus", type:"audio/ogg; codec=opus", links: 2 },
|
||||
{ name:"bogus.duh", type:"bogus/duh" }
|
||||
];
|
||||
|
||||
|
|
|
@ -465,10 +465,20 @@ WebappsApplication.prototype = {
|
|||
|
||||
checkForUpdate: function() {
|
||||
let request = this.createRequest();
|
||||
cpmm.sendAsyncMessage("Webapps:CheckForUpdate",
|
||||
{ manifestURL: this.manifestURL,
|
||||
oid: this._id,
|
||||
requestID: this.getRequestId(request) });
|
||||
|
||||
// We can't update apps that are not removable.
|
||||
if (!this.removable) {
|
||||
Services.tm.currentThread.dispatch({
|
||||
run: function checkUpdateFail() {
|
||||
Services.DOMRequest.fireError(request, "NOT_UPDATABLE");
|
||||
}
|
||||
}, Ci.nsIEventTarget.DISPATCH_NORMAL)
|
||||
} else {
|
||||
cpmm.sendAsyncMessage("Webapps:CheckForUpdate",
|
||||
{ manifestURL: this.manifestURL,
|
||||
oid: this._id,
|
||||
requestID: this.getRequestId(request) });
|
||||
}
|
||||
return request;
|
||||
},
|
||||
|
||||
|
|
|
@ -805,6 +805,8 @@ this.DOMApplicationRegistry = {
|
|||
} else {
|
||||
// hosted app with no appcache, nothing to do, but we fire a
|
||||
// downloaded event
|
||||
debug("No appcache found, sending 'downloaded' for " + aManifestURL);
|
||||
app.downloadAvailable = false;
|
||||
DOMApplicationRegistry.broadcastMessage("Webapps:PackageEvent",
|
||||
{ type: "downloaded",
|
||||
manifestURL: aManifestURL,
|
||||
|
@ -979,7 +981,7 @@ this.DOMApplicationRegistry = {
|
|||
}
|
||||
|
||||
function updateHostedApp(aManifest) {
|
||||
debug("updateHostedApp");
|
||||
debug("updateHostedApp " + aData.manifestURL);
|
||||
let id = this._appId(app.origin);
|
||||
|
||||
if (id in this._manifestCache) {
|
||||
|
@ -1007,18 +1009,11 @@ this.DOMApplicationRegistry = {
|
|||
|
||||
let manifest = new ManifestHelper(aManifest, app.origin);
|
||||
|
||||
if (manifest.appcache_path) {
|
||||
app.installState = "updating";
|
||||
app.downloadAvailable = true;
|
||||
app.downloading = true;
|
||||
app.downloadsize = 0;
|
||||
app.readyToApplyDownload = false;
|
||||
} else {
|
||||
app.installState = "installed";
|
||||
app.downloadAvailable = false;
|
||||
app.downloading = false;
|
||||
app.readyToApplyDownload = false;
|
||||
}
|
||||
app.installState = "installed";
|
||||
app.downloading = false;
|
||||
app.downloadsize = 0;
|
||||
app.readyToApplyDownload = false;
|
||||
app.downloadAvailable = !!manifest.appcache_path;
|
||||
|
||||
app.name = aManifest.name;
|
||||
app.csp = aManifest.csp || "";
|
||||
|
@ -1028,13 +1023,12 @@ this.DOMApplicationRegistry = {
|
|||
this.webapps[id] = app;
|
||||
|
||||
this._saveApps(function() {
|
||||
aData.event = "downloadapplied";
|
||||
aData.app = app;
|
||||
aData.event = manifest.appcache_path ? "downloadavailable"
|
||||
: "downloadapplied";
|
||||
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
|
||||
});
|
||||
|
||||
// Preload the appcache if needed.
|
||||
this.startOfflineCacheDownload(manifest, app);
|
||||
|
||||
// Update the permissions for this app.
|
||||
PermissionsInstaller.installPermissions({ manifest: aManifest,
|
||||
origin: app.origin,
|
||||
|
@ -1046,16 +1040,15 @@ this.DOMApplicationRegistry = {
|
|||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("GET", aData.manifestURL, true);
|
||||
xhr.responseType = "json";
|
||||
if (app.etag) {
|
||||
xhr.setRequestHeader("If-None-Match", app.etag);
|
||||
}
|
||||
|
||||
xhr.addEventListener("load", (function() {
|
||||
if (xhr.status == 200) {
|
||||
let manifest;
|
||||
try {
|
||||
manifest = JSON.parse(xhr.responseText);
|
||||
} catch(e) {
|
||||
let manifest = xhr.response;
|
||||
if (manifest == null) {
|
||||
sendError("MANIFEST_PARSE_ERROR");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ nsScreen::Create(nsPIDOMWindow* aWindow)
|
|||
nsScreen::nsScreen()
|
||||
: mEventListener(nullptr)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -195,11 +195,8 @@ DOMInterfaces = {
|
|||
}],
|
||||
|
||||
'Element': {
|
||||
# 'prefable' is True because some nodes are not on new bindings yet, so the
|
||||
# wrapping code for Element return values needs to fall back to XPConnect as
|
||||
# needed.
|
||||
'prefable': True,
|
||||
'hasXPConnectImpls': True,
|
||||
'register': False,
|
||||
'hasInstanceInterface': 'nsIDOMElement',
|
||||
'resultNotAddRefed': [
|
||||
'classList', 'attributes', 'children', 'firstElementChild',
|
||||
|
@ -314,13 +311,6 @@ DOMInterfaces = {
|
|||
|
||||
'Node': {
|
||||
'nativeType': 'nsINode',
|
||||
# 'prefable' is True because some nodes are not on new bindings yet, so the
|
||||
# wrapping code for Node return values needs to fall back to XPConnect as
|
||||
# needed.
|
||||
'prefable': True,
|
||||
# 'castable' is False because we don't support prefable castable arguments
|
||||
# and we have Node arguments.
|
||||
'castable': False,
|
||||
'hasXPConnectImpls': True,
|
||||
'hasInstanceInterface': 'nsIDOMNode',
|
||||
'resultNotAddRefed': [ 'ownerDocument', 'parentNode', 'parentElement',
|
||||
|
|
|
@ -2492,7 +2492,7 @@ for (uint32_t i = 0; i < length; ++i) {
|
|||
|
||||
templateBody = ""
|
||||
if descriptor.castable:
|
||||
if descriptor.prefable:
|
||||
if descriptor.prefable and not descriptor.hasXPConnectImpls:
|
||||
raise TypeError("We don't support prefable castable object "
|
||||
"arguments (like %s), because we don't know "
|
||||
"how to handle them being preffed off" %
|
||||
|
@ -3205,7 +3205,7 @@ if (!returnArray) {
|
|||
wrap = "%s(cx, ${obj}, %s, ${jsvalPtr})" % (wrapMethod, result)
|
||||
# We don't support prefable stuff in workers.
|
||||
assert(not descriptor.prefable or not descriptor.workers)
|
||||
if not descriptor.prefable:
|
||||
if not descriptor.prefable and not descriptor.hasXPConnectImpls:
|
||||
# Non-prefable bindings can only fail to wrap as a new-binding object
|
||||
# if they already threw an exception. Same thing for
|
||||
# non-prefable bindings.
|
||||
|
|
|
@ -155,13 +155,11 @@
|
|||
"EventTarget interface: calling addEventListener(DOMString,EventListener,boolean) on document.doctype with too few arguments must throw TypeError": true,
|
||||
"EventTarget interface: calling removeEventListener(DOMString,EventListener,boolean) on document.doctype with too few arguments must throw TypeError": true,
|
||||
"EventTarget interface: calling dispatchEvent(Event) on document.doctype with too few arguments must throw TypeError": true,
|
||||
"Element interface: attribute namespaceURI": true,
|
||||
"Element interface: attribute prefix": true,
|
||||
"Element interface: attribute localName": true,
|
||||
"Element interface: existence and properties of interface object": true,
|
||||
"Element interface: existence and properties of interface prototype object": true,
|
||||
"Element interface: existence and properties of interface prototype object's \"constructor\" property": true,
|
||||
"Element interface: attribute className": true,
|
||||
"Element interface: attribute attributes": true,
|
||||
"Element interface: operation remove()": true,
|
||||
"Element must be primary interface of element": true,
|
||||
"Stringification of element": "debug",
|
||||
"Element interface: element must inherit property \"className\" with the proper type (5)": true,
|
||||
"Element interface: element must inherit property \"remove\" with the proper type (25)": true,
|
||||
|
|
|
@ -444,15 +444,16 @@ PeerConnection.prototype = {
|
|||
|
||||
addIceCandidate: function(cand) {
|
||||
if (!cand) {
|
||||
throw "Invalid candidate passed to addIceCandidate!";
|
||||
throw "NULL candidate passed to addIceCandidate!";
|
||||
}
|
||||
if (!cand.candidate || !cand.sdpMid || !cand.sdpMLineIndex) {
|
||||
|
||||
if (!cand.candidate || !cand.sdpMLineIndex) {
|
||||
throw "Invalid candidate passed to addIceCandidate!";
|
||||
}
|
||||
|
||||
this._queueOrRun({
|
||||
func: this._pc.addIceCandidate,
|
||||
args: [cand.candidate, cand.sdpMid, cand.sdpMLineIndex],
|
||||
args: [cand.candidate, cand.sdpMid || "", cand.sdpMLineIndex],
|
||||
wait: false
|
||||
});
|
||||
},
|
||||
|
|
|
@ -170,12 +170,18 @@ class AlertServiceObserver: public nsIObserver
|
|||
const char *aTopic,
|
||||
const PRUnichar *aData)
|
||||
{
|
||||
|
||||
// forward to parent
|
||||
if (mNotification)
|
||||
if (mNotification) {
|
||||
#ifdef MOZ_B2G
|
||||
if (NS_FAILED(mNotification->CheckInnerWindowCorrectness()))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
#endif
|
||||
mNotification->HandleAlertServiceNotification(aTopic);
|
||||
}
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
nsDOMDesktopNotification* mNotification;
|
||||
};
|
||||
|
|
|
@ -136,6 +136,8 @@ MOCHITEST_FILES = \
|
|||
file_bug809290_c.html \
|
||||
file_empty.html \
|
||||
test_sizetocontent_clamp.html \
|
||||
test_protochains.html \
|
||||
test_bug817476.html \
|
||||
$(NULL)
|
||||
|
||||
ifneq (Linux,$(OS_ARCH))
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=817476
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 817476</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=817476">Mozilla Bug 817476</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 817476 **/
|
||||
function getNewWindow() {
|
||||
var ifr = document.createElement("iframe");
|
||||
$("content").appendChild(ifr);
|
||||
return ifr.contentWindow;
|
||||
}
|
||||
|
||||
// Test getting .screen before .Screen
|
||||
var win = getNewWindow();
|
||||
is(Object.getPrototypeOf(win.screen), win.Screen.prototype,
|
||||
"protos must match (1)");
|
||||
is(win.Screen.prototype.toString(), "[object ScreenPrototype]",
|
||||
"proto must be WebIDL (1)");
|
||||
|
||||
// Test getting Screen before .screen
|
||||
var win = getNewWindow();
|
||||
is(win.Screen.prototype, Object.getPrototypeOf(win.screen),
|
||||
"protos must match (2)");
|
||||
is(win.Screen.prototype.toString(), "[object ScreenPrototype]",
|
||||
"proto must be WebIDL (2)");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,60 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=817420
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 817420</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=817420">Mozilla Bug 817420</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 817420 **/
|
||||
is(Object.getPrototypeOf(HTMLElement.prototype), Element.prototype,
|
||||
"Must have correct proto chain for HTMLElement.prototype");
|
||||
is(Object.getPrototypeOf(document.createElementNS(null, "x")),
|
||||
Element.prototype,
|
||||
"Must have correct proto chain for random element");
|
||||
// Todo because of HTMLUnknownElement
|
||||
todo_is(Object.getPrototypeOf(document.createElement("noSuchElementName")),
|
||||
HTMLElement.prototype,
|
||||
"Must have correct proto chain for random HTML element");
|
||||
|
||||
// And check that it's really working as it should
|
||||
function checkPropPresent(propName, objList, expected)
|
||||
{
|
||||
for (obj of objList) {
|
||||
is(propName in obj,
|
||||
expected,
|
||||
obj + " should " + (expected ? "" : "not ") + "have the property");
|
||||
}
|
||||
}
|
||||
var objList = [ Element.prototype,
|
||||
HTMLElement.prototype,
|
||||
document.createElementNS(null, "x"),
|
||||
document.createElement("noSuchElementName"),
|
||||
document.body ]
|
||||
checkPropPresent("somePropertyThatBetterNeverExist", objList, false);
|
||||
Element.prototype.somePropertyThatBetterNeverExist = 1;
|
||||
checkPropPresent("somePropertyThatBetterNeverExist", objList, true);
|
||||
|
||||
objList = [ HTMLElement.prototype,
|
||||
document.createElement("noSuchElementName"),
|
||||
document.body ]
|
||||
checkPropPresent("someOtherPropertyThatBetterNeverExist", objList, false);
|
||||
HTMLElement.prototype.someOtherPropertyThatBetterNeverExist = 1;
|
||||
checkPropPresent("someOtherPropertyThatBetterNeverExist", objList, true);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -11,6 +11,7 @@
|
|||
#include "zlib.h"
|
||||
|
||||
#ifdef WOFF_MOZILLA_CLIENT /* define this when building as part of Gecko */
|
||||
# include "mozilla/mozalloc.h"
|
||||
# define malloc moz_malloc
|
||||
# define realloc moz_realloc
|
||||
# define free moz_free
|
||||
|
|
|
@ -106,7 +106,7 @@ js::IterateCells(JSRuntime *rt, JSCompartment *compartment, AllocKind thingKind,
|
|||
}
|
||||
|
||||
void
|
||||
js::IterateGrayObjects(JSCompartment *compartment, GCThingCallback *cellCallback, void *data)
|
||||
js::IterateGrayObjects(JSCompartment *compartment, GCThingCallback cellCallback, void *data)
|
||||
{
|
||||
JS_ASSERT(compartment);
|
||||
AutoPrepareForTracing prep(compartment->rt);
|
||||
|
|
|
@ -592,6 +592,17 @@ ShouldMarkCrossCompartment(JSTracer *trc, RawObject src, Cell *cell)
|
|||
|
||||
JS_ASSERT(color == BLACK || color == GRAY);
|
||||
if (color == BLACK) {
|
||||
/*
|
||||
* Having black->gray edges violates our promise to the cycle
|
||||
* collector. This can happen if we're collecting a compartment and it
|
||||
* has an edge to an uncollected compartment: it's possible that the
|
||||
* source and destination of the cross-compartment edge should be gray,
|
||||
* but the source was marked black by the conservative scanner.
|
||||
*/
|
||||
if (cell->isMarked(GRAY)) {
|
||||
JS_ASSERT(!cell->compartment()->isCollecting());
|
||||
trc->runtime->gcFoundBlackGrayEdges = true;
|
||||
}
|
||||
return c->isGCMarking();
|
||||
} else {
|
||||
if (c->isGCMarkingBlack()) {
|
||||
|
@ -1477,3 +1488,105 @@ js::CallTracer(JSTracer *trc, void *thing, JSGCTraceKind kind)
|
|||
MarkKind(trc, &tmp, kind);
|
||||
JS_ASSERT(tmp == thing);
|
||||
}
|
||||
|
||||
static void
|
||||
UnmarkGrayGCThing(void *thing)
|
||||
{
|
||||
static_cast<js::gc::Cell *>(thing)->unmark(js::gc::GRAY);
|
||||
}
|
||||
|
||||
struct UnmarkGrayTracer : public JSTracer
|
||||
{
|
||||
UnmarkGrayTracer() : tracingShape(false), previousShape(NULL) {}
|
||||
UnmarkGrayTracer(JSTracer *trc, bool tracingShape)
|
||||
: tracingShape(tracingShape), previousShape(NULL)
|
||||
{
|
||||
JS_TracerInit(this, trc->runtime, trc->callback);
|
||||
}
|
||||
|
||||
/* True iff we are tracing the immediate children of a shape. */
|
||||
bool tracingShape;
|
||||
|
||||
/* If tracingShape, shape child or NULL. Otherwise, NULL. */
|
||||
void *previousShape;
|
||||
};
|
||||
|
||||
/*
|
||||
* The GC and CC are run independently. Consequently, the following sequence of
|
||||
* events can occur:
|
||||
* 1. GC runs and marks an object gray.
|
||||
* 2. Some JS code runs that creates a pointer from a JS root to the gray
|
||||
* object. If we re-ran a GC at this point, the object would now be black.
|
||||
* 3. Now we run the CC. It may think it can collect the gray object, even
|
||||
* though it's reachable from the JS heap.
|
||||
*
|
||||
* To prevent this badness, we unmark the gray bit of an object when it is
|
||||
* accessed by callers outside XPConnect. This would cause the object to go
|
||||
* black in step 2 above. This must be done on everything reachable from the
|
||||
* object being returned. The following code takes care of the recursive
|
||||
* re-coloring.
|
||||
*/
|
||||
static void
|
||||
UnmarkGrayChildren(JSTracer *trc, void **thingp, JSGCTraceKind kind)
|
||||
{
|
||||
void *thing = *thingp;
|
||||
int stackDummy;
|
||||
if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(trc->runtime), &stackDummy)) {
|
||||
/*
|
||||
* If we run out of stack, we take a more drastic measure: require that
|
||||
* we GC again before the next CC.
|
||||
*/
|
||||
trc->runtime->gcGrayBitsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GCThingIsMarkedGray(thing))
|
||||
return;
|
||||
|
||||
UnmarkGrayGCThing(thing);
|
||||
|
||||
/*
|
||||
* Trace children of |thing|. If |thing| and its parent are both shapes,
|
||||
* |thing| will get saved to mPreviousShape without being traced. The parent
|
||||
* will later trace |thing|. This is done to avoid increasing the stack
|
||||
* depth during shape tracing. It is safe to do because a shape can only
|
||||
* have one child that is a shape.
|
||||
*/
|
||||
UnmarkGrayTracer *tracer = static_cast<UnmarkGrayTracer *>(trc);
|
||||
UnmarkGrayTracer childTracer(tracer, kind == JSTRACE_SHAPE);
|
||||
|
||||
if (kind != JSTRACE_SHAPE) {
|
||||
JS_TraceChildren(&childTracer, thing, kind);
|
||||
JS_ASSERT(!childTracer.previousShape);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tracer->tracingShape) {
|
||||
JS_ASSERT(!tracer->previousShape);
|
||||
tracer->previousShape = thing;
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
JS_ASSERT(!GCThingIsMarkedGray(thing));
|
||||
JS_TraceChildren(&childTracer, thing, JSTRACE_SHAPE);
|
||||
thing = childTracer.previousShape;
|
||||
childTracer.previousShape = NULL;
|
||||
} while (thing);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js::UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind)
|
||||
{
|
||||
JS_ASSERT(kind != JSTRACE_SHAPE);
|
||||
|
||||
if (!GCThingIsMarkedGray(thing))
|
||||
return;
|
||||
|
||||
UnmarkGrayGCThing(thing);
|
||||
|
||||
JSRuntime *rt = static_cast<Cell *>(thing)->compartment()->rt;
|
||||
UnmarkGrayTracer trc;
|
||||
JS_TracerInit(&trc, rt, UnmarkGrayChildren);
|
||||
JS_TraceChildren(&trc, thing, kind);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ enum Phase {
|
|||
PHASE_SWEEP_MARK_INCOMING_GRAY,
|
||||
PHASE_SWEEP_MARK_GRAY,
|
||||
PHASE_SWEEP_MARK_GRAY_WEAK,
|
||||
PHASE_SWEEP_FIND_BLACK_GRAY,
|
||||
PHASE_SWEEP_ATOMS,
|
||||
PHASE_SWEEP_COMPARTMENTS,
|
||||
PHASE_SWEEP_TABLES,
|
||||
|
|
|
@ -1183,7 +1183,7 @@ SequentialCompileContext::compile(IonBuilder *builder, MIRGraph *graph,
|
|||
// incremental read barriers.
|
||||
if (js_IonOptions.parallelCompilation &&
|
||||
OffThreadCompilationAvailable(cx) &&
|
||||
!cx->compartment->needsBarrier())
|
||||
!IsIncrementalGCInProgress(cx->runtime))
|
||||
{
|
||||
builder->script()->ion = ION_COMPILING_SCRIPT;
|
||||
|
||||
|
|
|
@ -143,8 +143,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
branchPtr(cond, Address(scratch, BaseShape::offsetOfClass()), ImmWord(clasp), label);
|
||||
}
|
||||
void branchTestObjShape(Condition cond, Register obj, const Shape *shape, Label *label) {
|
||||
branchPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfShape()),
|
||||
ImmGCPtr(shape), label);
|
||||
branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape), label);
|
||||
}
|
||||
|
||||
void loadObjPrivate(Register obj, uint32_t nfixed, Register dest) {
|
||||
|
|
|
@ -786,6 +786,7 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
|
|||
gcDynamicHeapGrowth(false),
|
||||
gcDynamicMarkSlice(false),
|
||||
gcShouldCleanUpEverything(false),
|
||||
gcGrayBitsValid(false),
|
||||
gcIsNeeded(0),
|
||||
gcStats(thisFromCtor()),
|
||||
gcNumber(0),
|
||||
|
@ -1571,7 +1572,6 @@ JS_TransplantObject(JSContext *cx, JSObject *origobjArg, JSObject *targetArg)
|
|||
AutoMaybeTouchDeadCompartments agc(cx);
|
||||
|
||||
JSCompartment *destination = target->compartment();
|
||||
WrapperMap &map = destination->crossCompartmentWrappers;
|
||||
Value origv = ObjectValue(*origobj);
|
||||
JSObject *newIdentity;
|
||||
|
||||
|
@ -1583,7 +1583,7 @@ JS_TransplantObject(JSContext *cx, JSObject *origobjArg, JSObject *targetArg)
|
|||
if (!origobj->swap(cx, target))
|
||||
MOZ_CRASH();
|
||||
newIdentity = origobj;
|
||||
} else if (WrapperMap::Ptr p = map.lookup(origv)) {
|
||||
} else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
|
||||
// There might already be a wrapper for the original object in
|
||||
// the new compartment. If there is, we use its identity and swap
|
||||
// in the contents of |target|.
|
||||
|
@ -1591,7 +1591,7 @@ JS_TransplantObject(JSContext *cx, JSObject *origobjArg, JSObject *targetArg)
|
|||
|
||||
// When we remove origv from the wrapper map, its wrapper, newIdentity,
|
||||
// must immediately cease to be a cross-compartment wrapper. Neuter it.
|
||||
map.remove(p);
|
||||
destination->removeWrapper(p);
|
||||
NukeCrossCompartmentWrapper(cx, newIdentity);
|
||||
|
||||
if (!newIdentity->swap(cx, target))
|
||||
|
@ -1615,7 +1615,7 @@ JS_TransplantObject(JSContext *cx, JSObject *origobjArg, JSObject *targetArg)
|
|||
JS_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity);
|
||||
if (!origobj->swap(cx, newIdentityWrapper))
|
||||
MOZ_CRASH();
|
||||
origobj->compartment()->crossCompartmentWrappers.put(ObjectValue(*newIdentity), origv);
|
||||
origobj->compartment()->putWrapper(ObjectValue(*newIdentity), origv);
|
||||
}
|
||||
|
||||
// The new identity object might be one of several things. Return it to avoid
|
||||
|
@ -1652,7 +1652,6 @@ js_TransplantObjectWithWrapper(JSContext *cx,
|
|||
|
||||
JSObject *newWrapper;
|
||||
JSCompartment *destination = targetobj->compartment();
|
||||
WrapperMap &map = destination->crossCompartmentWrappers;
|
||||
|
||||
// |origv| is the map entry we're looking up. The map entries are going to
|
||||
// be for |origobj|, not |origwrapper|.
|
||||
|
@ -1660,14 +1659,14 @@ js_TransplantObjectWithWrapper(JSContext *cx,
|
|||
|
||||
// There might already be a wrapper for the original object in the new
|
||||
// compartment.
|
||||
if (WrapperMap::Ptr p = map.lookup(origv)) {
|
||||
if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
|
||||
// There is. Make the existing cross-compartment wrapper a same-
|
||||
// compartment wrapper.
|
||||
newWrapper = &p->value.toObject();
|
||||
|
||||
// When we remove origv from the wrapper map, its wrapper, newWrapper,
|
||||
// must immediately cease to be a cross-compartment wrapper. Neuter it.
|
||||
map.remove(p);
|
||||
destination->removeWrapper(p);
|
||||
NukeCrossCompartmentWrapper(cx, newWrapper);
|
||||
|
||||
if (!newWrapper->swap(cx, targetwrapper))
|
||||
|
@ -1707,8 +1706,8 @@ js_TransplantObjectWithWrapper(JSContext *cx,
|
|||
JS_ASSERT(Wrapper::wrappedObject(wrapperGuts) == targetobj);
|
||||
if (!origwrapper->swap(cx, wrapperGuts))
|
||||
MOZ_CRASH();
|
||||
origwrapper->compartment()->crossCompartmentWrappers.put(ObjectValue(*targetobj),
|
||||
ObjectValue(*origwrapper));
|
||||
origwrapper->compartment()->putWrapper(ObjectValue(*targetobj),
|
||||
ObjectValue(*origwrapper));
|
||||
}
|
||||
|
||||
return newWrapper;
|
||||
|
|
|
@ -649,6 +649,12 @@ struct JSRuntime : js::RuntimeFriendFields
|
|||
/* During shutdown, the GC needs to clean up every possible object. */
|
||||
bool gcShouldCleanUpEverything;
|
||||
|
||||
/*
|
||||
* The gray bits can become invalid if UnmarkGray overflows the stack. A
|
||||
* full GC will reset this bit, since it fills in all the gray bits.
|
||||
*/
|
||||
bool gcGrayBitsValid;
|
||||
|
||||
/*
|
||||
* These flags must be kept separate so that a thread requesting a
|
||||
* compartment GC doesn't cancel another thread's concurrent request for a
|
||||
|
|
|
@ -227,6 +227,17 @@ WrapForSameCompartment(JSContext *cx, HandleObject obj, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::putWrapper(const CrossCompartmentKey &wrapped, const js::Value &wrapper)
|
||||
{
|
||||
JS_ASSERT(wrapped.wrapped);
|
||||
JS_ASSERT_IF(wrapped.kind == CrossCompartmentKey::StringWrapper, wrapper.isString());
|
||||
JS_ASSERT_IF(wrapped.kind != CrossCompartmentKey::StringWrapper, wrapper.isObject());
|
||||
// todo: uncomment when bug 815999 is fixed:
|
||||
// JS_ASSERT(!wrapped.wrapped->isMarked(gc::GRAY));
|
||||
return crossCompartmentWrappers.put(wrapped, wrapper);
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::wrap(JSContext *cx, Value *vp, JSObject *existing)
|
||||
{
|
||||
|
@ -337,7 +348,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp, JSObject *existing)
|
|||
if (!wrapped)
|
||||
return false;
|
||||
vp->setString(wrapped);
|
||||
if (!crossCompartmentWrappers.put(orig, *vp))
|
||||
if (!putWrapper(orig, *vp))
|
||||
return false;
|
||||
|
||||
if (str->compartment()->isGCMarking()) {
|
||||
|
@ -385,7 +396,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp, JSObject *existing)
|
|||
|
||||
vp->setObject(*wrapper);
|
||||
|
||||
if (!crossCompartmentWrappers.put(key, *vp))
|
||||
if (!putWrapper(key, *vp))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -115,7 +115,7 @@ struct TypeInferenceSizes;
|
|||
|
||||
namespace js {
|
||||
class AutoDebugModeGC;
|
||||
struct DebugScopes;
|
||||
class DebugScopes;
|
||||
}
|
||||
|
||||
struct JSCompartment : public js::gc::GraphNodeBase
|
||||
|
@ -273,7 +273,6 @@ struct JSCompartment : public js::gc::GraphNodeBase
|
|||
return gcState == Finished;
|
||||
}
|
||||
|
||||
|
||||
size_t gcBytes;
|
||||
size_t gcTriggerBytes;
|
||||
size_t gcMaxMallocBytes;
|
||||
|
@ -297,8 +296,11 @@ struct JSCompartment : public js::gc::GraphNodeBase
|
|||
|
||||
void *data;
|
||||
bool active; // GC flag, whether there are active frames
|
||||
|
||||
private:
|
||||
js::WrapperMap crossCompartmentWrappers;
|
||||
|
||||
public:
|
||||
/*
|
||||
* These flags help us to discover if a compartment that shouldn't be alive
|
||||
* manages to outlive a GC.
|
||||
|
@ -401,6 +403,20 @@ struct JSCompartment : public js::gc::GraphNodeBase
|
|||
bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
|
||||
bool wrap(JSContext *cx, js::AutoIdVector &props);
|
||||
|
||||
bool putWrapper(const js::CrossCompartmentKey& wrapped, const js::Value& wrapper);
|
||||
|
||||
js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) {
|
||||
return crossCompartmentWrappers.lookup(wrapped);
|
||||
}
|
||||
|
||||
void removeWrapper(js::WrapperMap::Ptr p) {
|
||||
crossCompartmentWrappers.remove(p);
|
||||
}
|
||||
|
||||
struct WrapperEnum : public js::WrapperMap::Enum {
|
||||
WrapperEnum(JSCompartment *c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
|
||||
};
|
||||
|
||||
void mark(JSTracer *trc);
|
||||
void markTypes(JSTracer *trc);
|
||||
void discardJitCode(js::FreeOp *fop, bool discardConstraints);
|
||||
|
|
|
@ -564,6 +564,12 @@ js::GCThingIsMarkedGray(void *thing)
|
|||
return reinterpret_cast<gc::Cell *>(thing)->isMarked(gc::GRAY);
|
||||
}
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
js::AreGCGrayBitsValid(JSRuntime *rt)
|
||||
{
|
||||
return rt->gcGrayBitsValid;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSGCTraceKind)
|
||||
js::GCThingTraceKind(void *thing)
|
||||
{
|
||||
|
@ -572,15 +578,9 @@ js::GCThingTraceKind(void *thing)
|
|||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js::UnmarkGrayGCThing(void *thing)
|
||||
js::VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback callback, void *closure)
|
||||
{
|
||||
static_cast<js::gc::Cell *>(thing)->unmark(js::gc::GRAY);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js::VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback *callback, void *closure)
|
||||
{
|
||||
for (WrapperMap::Enum e(comp->crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
|
||||
gc::Cell *thing = e.front().key.wrapped;
|
||||
if (thing->isMarked(gc::GRAY))
|
||||
callback(closure, thing);
|
||||
|
|
|
@ -269,14 +269,21 @@ TraceWeakMaps(WeakMapTracer *trc);
|
|||
extern JS_FRIEND_API(bool)
|
||||
GCThingIsMarkedGray(void *thing);
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
UnmarkGrayGCThing(void *thing);
|
||||
extern JS_FRIEND_API(bool)
|
||||
AreGCGrayBitsValid(JSRuntime *rt);
|
||||
|
||||
/*
|
||||
* Unsets the gray bit for anything reachable from |thing|. |kind| should not be
|
||||
* JSTRACE_SHAPE. |thing| should be non-null.
|
||||
*/
|
||||
extern JS_FRIEND_API(void)
|
||||
UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind);
|
||||
|
||||
typedef void
|
||||
(GCThingCallback)(void *closure, void *gcthing);
|
||||
(*GCThingCallback)(void *closure, void *gcthing);
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback *callback, void *closure);
|
||||
VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback callback, void *closure);
|
||||
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
GetWeakmapKeyDelegate(JSObject *key);
|
||||
|
@ -288,7 +295,7 @@ GCThingTraceKind(void *thing);
|
|||
* Invoke cellCallback on every gray JS_OBJECT in the given compartment.
|
||||
*/
|
||||
extern JS_FRIEND_API(void)
|
||||
IterateGrayObjects(JSCompartment *compartment, GCThingCallback *cellCallback, void *data);
|
||||
IterateGrayObjects(JSCompartment *compartment, GCThingCallback cellCallback, void *data);
|
||||
|
||||
/*
|
||||
* Shadow declarations of JS internal structures, for access by inline access
|
||||
|
|
|
@ -2489,7 +2489,7 @@ InCrossCompartmentMap(JSObject *src, Cell *dst, JSGCTraceKind dstKind)
|
|||
|
||||
if (dstKind == JSTRACE_OBJECT) {
|
||||
Value key = ObjectValue(*static_cast<JSObject *>(dst));
|
||||
if (WrapperMap::Ptr p = srccomp->crossCompartmentWrappers.lookup(key)) {
|
||||
if (WrapperMap::Ptr p = srccomp->lookupWrapper(key)) {
|
||||
if (*p->value.unsafeGet() == ObjectValue(*src))
|
||||
return true;
|
||||
}
|
||||
|
@ -2499,7 +2499,7 @@ InCrossCompartmentMap(JSObject *src, Cell *dst, JSGCTraceKind dstKind)
|
|||
* If the cross-compartment edge is caused by the debugger, then we don't
|
||||
* know the right hashtable key, so we have to iterate.
|
||||
*/
|
||||
for (WrapperMap::Enum e(srccomp->crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
for (JSCompartment::WrapperEnum e(srccomp); !e.empty(); e.popFront()) {
|
||||
if (e.front().key.wrapped == dst && ToMarkable(e.front().value) == src)
|
||||
return true;
|
||||
}
|
||||
|
@ -2540,7 +2540,7 @@ CheckForCompartmentMismatches(JSRuntime *rt)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
static bool
|
||||
BeginMarkPhase(JSRuntime *rt)
|
||||
{
|
||||
int64_t currentTime = PRMJ_Now();
|
||||
|
@ -2550,7 +2550,7 @@ BeginMarkPhase(JSRuntime *rt)
|
|||
#endif
|
||||
|
||||
rt->gcIsFull = true;
|
||||
DebugOnly<bool> any = false;
|
||||
bool any = false;
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
/* Assert that compartment state is as we expect */
|
||||
JS_ASSERT(!c->isCollecting());
|
||||
|
@ -2560,9 +2560,10 @@ BeginMarkPhase(JSRuntime *rt)
|
|||
|
||||
/* Set up which compartments will be collected. */
|
||||
if (c->isGCScheduled()) {
|
||||
any = true;
|
||||
if (c != rt->atomsCompartment)
|
||||
if (c != rt->atomsCompartment) {
|
||||
any = true;
|
||||
c->setGCState(JSCompartment::Mark);
|
||||
}
|
||||
} else {
|
||||
rt->gcIsFull = false;
|
||||
}
|
||||
|
@ -2574,7 +2575,8 @@ BeginMarkPhase(JSRuntime *rt)
|
|||
}
|
||||
|
||||
/* Check that at least one compartment is scheduled for collection. */
|
||||
JS_ASSERT(any);
|
||||
if (!any)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Atoms are not in the cross-compartment map. So if there are any
|
||||
|
@ -2676,7 +2678,7 @@ BeginMarkPhase(JSRuntime *rt)
|
|||
|
||||
/* Set the maybeAlive flag based on cross-compartment edges. */
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
for (WrapperMap::Enum e(c->crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
|
||||
Cell *dst = e.front().key.wrapped;
|
||||
dst->compartment()->maybeAlive = true;
|
||||
}
|
||||
|
@ -2698,6 +2700,8 @@ BeginMarkPhase(JSRuntime *rt)
|
|||
c->scheduledForDestruction = true;
|
||||
}
|
||||
rt->gcFoundBlackGrayEdges = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2876,7 +2880,7 @@ DropStringWrappers(JSRuntime *rt)
|
|||
* compartment group.
|
||||
*/
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
for (WrapperMap::Enum e(c->crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
|
||||
if (e.front().key.kind == CrossCompartmentKey::StringWrapper)
|
||||
e.removeFront();
|
||||
}
|
||||
|
@ -2909,9 +2913,29 @@ JSCompartment::findOutgoingEdges(ComponentFinder& finder)
|
|||
finder.addEdgeTo(rt->atomsCompartment);
|
||||
|
||||
for (js::WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
JS_ASSERT(e.front().key.kind != CrossCompartmentKey::StringWrapper);
|
||||
CrossCompartmentKey::Kind kind = e.front().key.kind;
|
||||
JS_ASSERT(kind != CrossCompartmentKey::StringWrapper);
|
||||
Cell *other = e.front().key.wrapped;
|
||||
if (!other->isMarked(BLACK) || other->isMarked(GRAY)) {
|
||||
if (kind == CrossCompartmentKey::ObjectWrapper) {
|
||||
/*
|
||||
* Add edge to wrapped object compartment if wrapped object is not
|
||||
* marked black to indicate that wrapper compartment not be swept
|
||||
* after wrapped compartment.
|
||||
*/
|
||||
if (!other->isMarked(BLACK) || other->isMarked(GRAY)) {
|
||||
JSCompartment *w = other->compartment();
|
||||
if (w->isGCMarking())
|
||||
finder.addEdgeTo(w);
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(kind == CrossCompartmentKey::DebuggerScript ||
|
||||
kind == CrossCompartmentKey::DebuggerObject ||
|
||||
kind == CrossCompartmentKey::DebuggerEnvironment);
|
||||
/*
|
||||
* Add edge for debugger object wrappers, to ensure (in conjuction
|
||||
* with call to Debugger::findCompartmentEdges below) that debugger
|
||||
* and debuggee objects are always swept in the same group.
|
||||
*/
|
||||
JSCompartment *w = other->compartment();
|
||||
if (w->isGCMarking())
|
||||
finder.addEdgeTo(w);
|
||||
|
@ -3002,11 +3026,13 @@ GrayLinkSlot(RawObject o)
|
|||
return IsCrossCompartmentWrapper(o) ? JSSLOT_GC_GRAY_LINK : Debugger::gcGrayLinkSlot();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
AssertNotOnGrayList(RawObject o)
|
||||
{
|
||||
JS_ASSERT_IF(IsGrayListObject(o), o->getReservedSlot(GrayLinkSlot(o)).isUndefined());
|
||||
}
|
||||
#endif
|
||||
|
||||
static Cell *
|
||||
CrossCompartmentPointerReferent(RawObject o)
|
||||
|
@ -3225,29 +3251,6 @@ EndMarkingCompartmentGroup(JSRuntime *rt)
|
|||
#endif
|
||||
|
||||
JS_ASSERT(rt->gcMarker.isDrained());
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_SWEEP_FIND_BLACK_GRAY);
|
||||
|
||||
/*
|
||||
* Having black->gray edges violates our promise to the cycle
|
||||
* collector. This can happen if we're collecting a compartment and it has
|
||||
* an edge to an uncollected compartment: it's possible that the source and
|
||||
* destination of the cross-compartment edge should be gray, but the source
|
||||
* was marked black by the conservative scanner.
|
||||
*/
|
||||
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
|
||||
for (WrapperMap::Enum e(c->crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
Cell *dst = e.front().key.wrapped;
|
||||
Cell *src = ToMarkable(e.front().value);
|
||||
JS_ASSERT(src->compartment() == c);
|
||||
if (IsCellMarked(&src) && !src->isMarked(GRAY) && dst->isMarked(GRAY)) {
|
||||
JS_ASSERT(!dst->compartment()->isCollecting());
|
||||
rt->gcFoundBlackGrayEdges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -3382,7 +3385,7 @@ BeginSweepPhase(JSRuntime *rt)
|
|||
JS_ASSERT(!rt->gcCompartmentGroup);
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
JS_ASSERT(!c->gcIncomingGrayPointers);
|
||||
for (WrapperMap::Enum e(c->crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
|
||||
if (e.front().key.kind != CrossCompartmentKey::StringWrapper)
|
||||
AssertNotOnGrayList(&e.front().value.get().toObject());
|
||||
}
|
||||
|
@ -3515,8 +3518,13 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rt->gcFinalizeCallback)
|
||||
rt->gcFinalizeCallback(&fop, JSFINALIZE_COLLECTION_END, !isFull);
|
||||
|
||||
/* If we finished a full GC, then the gray bits are correct. */
|
||||
if (isFull)
|
||||
rt->gcGrayBitsValid = true;
|
||||
}
|
||||
|
||||
/* Set up list of compartments for sweeping of background things. */
|
||||
|
@ -3551,7 +3559,7 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC)
|
|||
JS_ASSERT(!c->gcIncomingGrayPointers);
|
||||
JS_ASSERT(!c->gcLiveArrayBuffers);
|
||||
|
||||
for (WrapperMap::Enum e(c->crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
|
||||
if (e.front().key.kind != CrossCompartmentKey::StringWrapper)
|
||||
AssertNotOnGrayList(&e.front().value.get().toObject());
|
||||
}
|
||||
|
@ -3816,7 +3824,11 @@ IncrementalCollectSlice(JSRuntime *rt,
|
|||
switch (rt->gcIncrementalState) {
|
||||
|
||||
case MARK_ROOTS:
|
||||
BeginMarkPhase(rt);
|
||||
if (!BeginMarkPhase(rt)) {
|
||||
rt->gcIncrementalState = NO_INCREMENTAL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rt->hasContexts())
|
||||
PushZealSelectedObjects(rt);
|
||||
|
||||
|
|
|
@ -2834,7 +2834,7 @@ proxy_TraceObject(JSTracer *trc, RawObject obj)
|
|||
* the invariant that the wrapped object is the key in the wrapper map.
|
||||
*/
|
||||
Value key = ObjectValue(*referent);
|
||||
WrapperMap::Ptr p = obj->compartment()->crossCompartmentWrappers.lookup(key);
|
||||
WrapperMap::Ptr p = obj->compartment()->lookupWrapper(key);
|
||||
JS_ASSERT(*p->value.unsafeGet() == ObjectValue(*obj));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1037,8 +1037,7 @@ js::NukeCrossCompartmentWrappers(JSContext* cx,
|
|||
continue;
|
||||
|
||||
// Iterate the wrappers looking for anything interesting.
|
||||
WrapperMap &pmap = c->crossCompartmentWrappers;
|
||||
for (WrapperMap::Enum e(pmap); !e.empty(); e.popFront()) {
|
||||
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
|
||||
// Some cross-compartment wrappers are for strings. We're not
|
||||
// interested in those.
|
||||
const CrossCompartmentKey &k = e.front().key;
|
||||
|
@ -1075,17 +1074,18 @@ js::RemapWrapper(JSContext *cx, JSObject *wobj, JSObject *newTarget)
|
|||
JS_ASSERT(origTarget);
|
||||
Value origv = ObjectValue(*origTarget);
|
||||
JSCompartment *wcompartment = wobj->compartment();
|
||||
WrapperMap &pmap = wcompartment->crossCompartmentWrappers;
|
||||
|
||||
// If we're mapping to a different target (as opposed to just recomputing
|
||||
// for the same target), we must not have an existing wrapper for the new
|
||||
// target, otherwise this will break.
|
||||
JS_ASSERT_IF(origTarget != newTarget, !pmap.has(ObjectValue(*newTarget)));
|
||||
JS_ASSERT_IF(origTarget != newTarget,
|
||||
!wcompartment->lookupWrapper(ObjectValue(*newTarget)));
|
||||
|
||||
// The old value should still be in the cross-compartment wrapper map, and
|
||||
// the lookup should return wobj.
|
||||
JS_ASSERT(&pmap.lookup(origv)->value.unsafeGet()->toObject() == wobj);
|
||||
pmap.remove(origv);
|
||||
WrapperMap::Ptr p = wcompartment->lookupWrapper(origv);
|
||||
JS_ASSERT(&p->value.unsafeGet()->toObject() == wobj);
|
||||
wcompartment->removeWrapper(p);
|
||||
|
||||
// When we remove origv from the wrapper map, its wrapper, wobj, must
|
||||
// immediately cease to be a cross-compartment wrapper. Neuter it.
|
||||
|
@ -1117,7 +1117,7 @@ js::RemapWrapper(JSContext *cx, JSObject *wobj, JSObject *newTarget)
|
|||
|
||||
// Update the entry in the compartment's wrapper map to point to the old
|
||||
// wrapper, which has now been updated (via reuse or swap).
|
||||
pmap.put(ObjectValue(*newTarget), ObjectValue(*wobj));
|
||||
wcompartment->putWrapper(ObjectValue(*newTarget), ObjectValue(*wobj));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1134,8 +1134,7 @@ js::RemapAllWrappersForObject(JSContext *cx, JSObject *oldTarget,
|
|||
return false;
|
||||
|
||||
for (CompartmentsIter c(cx->runtime); !c.done(); c.next()) {
|
||||
WrapperMap &pmap = c->crossCompartmentWrappers;
|
||||
if (WrapperMap::Ptr wp = pmap.lookup(origv)) {
|
||||
if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
|
||||
// We found a wrapper. Remember and root it.
|
||||
toTransplant.infallibleAppend(WrapperValue(wp));
|
||||
}
|
||||
|
@ -1165,8 +1164,7 @@ js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter,
|
|||
continue;
|
||||
|
||||
// Iterate over the wrappers, filtering appropriately.
|
||||
WrapperMap &pmap = c->crossCompartmentWrappers;
|
||||
for (WrapperMap::Enum e(pmap); !e.empty(); e.popFront()) {
|
||||
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
|
||||
// Filter out non-objects.
|
||||
const CrossCompartmentKey &k = e.front().key;
|
||||
if (k.kind != CrossCompartmentKey::ObjectWrapper)
|
||||
|
|
|
@ -651,7 +651,7 @@ Debugger::wrapEnvironment(JSContext *cx, Handle<Env*> env, Value *rval)
|
|||
}
|
||||
|
||||
CrossCompartmentKey key(CrossCompartmentKey::DebuggerEnvironment, object, env);
|
||||
if (!object->compartment()->crossCompartmentWrappers.put(key, ObjectValue(*envobj))) {
|
||||
if (!object->compartment()->putWrapper(key, ObjectValue(*envobj))) {
|
||||
environments.remove(env);
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
|
@ -689,7 +689,7 @@ Debugger::wrapDebuggeeValue(JSContext *cx, Value *vp)
|
|||
|
||||
if (obj->compartment() != object->compartment()) {
|
||||
CrossCompartmentKey key(CrossCompartmentKey::DebuggerObject, object, obj);
|
||||
if (!object->compartment()->crossCompartmentWrappers.put(key, ObjectValue(*dobj))) {
|
||||
if (!object->compartment()->putWrapper(key, ObjectValue(*dobj))) {
|
||||
objects.remove(obj);
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
|
@ -2612,7 +2612,7 @@ Debugger::wrapScript(JSContext *cx, HandleScript script)
|
|||
}
|
||||
|
||||
CrossCompartmentKey key(CrossCompartmentKey::DebuggerScript, object, script);
|
||||
if (!object->compartment()->crossCompartmentWrappers.put(key, ObjectValue(*scriptobj))) {
|
||||
if (!object->compartment()->putWrapper(key, ObjectValue(*scriptobj))) {
|
||||
scripts.remove(script);
|
||||
js_ReportOutOfMemory(cx);
|
||||
return NULL;
|
||||
|
|
|
@ -748,7 +748,6 @@ XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
|
|||
}
|
||||
}
|
||||
}
|
||||
self->GetXPConnect()->ClearGCBeforeCC();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,6 @@ nsXPConnect::nsXPConnect()
|
|||
mDefaultSecurityManager(nullptr),
|
||||
mDefaultSecurityManagerFlags(0),
|
||||
mShuttingDown(false),
|
||||
mNeedGCBeforeCC(true),
|
||||
mEventDepth(0),
|
||||
mCycleCollectionContext(nullptr)
|
||||
{
|
||||
|
@ -317,7 +316,7 @@ nsXPConnect::GetInfoForName(const char * name, nsIInterfaceInfo** info)
|
|||
bool
|
||||
nsXPConnect::NeedCollect()
|
||||
{
|
||||
return !!mNeedGCBeforeCC;
|
||||
return !js::AreGCGrayBitsValid(GetRuntime()->GetJSRuntime());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -388,6 +387,7 @@ struct NoteWeakMapChildrenTracer : public JSTracer
|
|||
{
|
||||
}
|
||||
nsCycleCollectionTraversalCallback &mCb;
|
||||
bool mTracedAny;
|
||||
JSObject *mMap;
|
||||
void *mKey;
|
||||
void *mKeyDelegate;
|
||||
|
@ -406,6 +406,7 @@ TraceWeakMappingChild(JSTracer *trc, void **thingp, JSGCTraceKind kind)
|
|||
return;
|
||||
if (AddToCCKind(kind)) {
|
||||
tracer->mCb.NoteWeakMapping(tracer->mMap, tracer->mKey, tracer->mKeyDelegate, thing);
|
||||
tracer->mTracedAny = true;
|
||||
} else {
|
||||
JS_TraceChildren(trc, thing, kind);
|
||||
}
|
||||
|
@ -430,10 +431,12 @@ TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m,
|
|||
{
|
||||
MOZ_ASSERT(trc->callback == TraceWeakMapping);
|
||||
NoteWeakMapsTracer *tracer = static_cast<NoteWeakMapsTracer *>(trc);
|
||||
if (vkind == JSTRACE_STRING)
|
||||
return;
|
||||
if (!xpc_IsGrayGCThing(v) && !tracer->mCb.WantAllTraces())
|
||||
return;
|
||||
|
||||
// If nothing that could be held alive by this entry is marked gray, return.
|
||||
if ((!k || !xpc_IsGrayGCThing(k)) && MOZ_LIKELY(!tracer->mCb.WantAllTraces())) {
|
||||
if (!v || !xpc_IsGrayGCThing(v) || vkind == JSTRACE_STRING)
|
||||
return;
|
||||
}
|
||||
|
||||
// The cycle collector can only properly reason about weak maps if it can
|
||||
// reason about the liveness of their keys, which in turn requires that
|
||||
|
@ -448,17 +451,25 @@ TraceWeakMapping(js::WeakMapTracer *trc, JSObject *m,
|
|||
if (!AddToCCKind(kkind))
|
||||
k = nullptr;
|
||||
|
||||
JSObject *kdelegate = NULL;
|
||||
if (kkind == JSTRACE_OBJECT)
|
||||
JSObject *kdelegate = nullptr;
|
||||
if (k && kkind == JSTRACE_OBJECT)
|
||||
kdelegate = js::GetWeakmapKeyDelegate((JSObject *)k);
|
||||
|
||||
if (AddToCCKind(vkind)) {
|
||||
tracer->mCb.NoteWeakMapping(m, k, kdelegate, v);
|
||||
} else {
|
||||
tracer->mChildTracer.mTracedAny = false;
|
||||
tracer->mChildTracer.mMap = m;
|
||||
tracer->mChildTracer.mKey = k;
|
||||
tracer->mChildTracer.mKeyDelegate = kdelegate;
|
||||
JS_TraceChildren(&tracer->mChildTracer, v, vkind);
|
||||
|
||||
if (v && vkind != JSTRACE_STRING)
|
||||
JS_TraceChildren(&tracer->mChildTracer, v, vkind);
|
||||
|
||||
// The delegate could hold alive the key, so report something to the CC
|
||||
// if we haven't already.
|
||||
if (!tracer->mChildTracer.mTracedAny && k && xpc_IsGrayGCThing(k) && kdelegate)
|
||||
tracer->mCb.NoteWeakMapping(m, k, kdelegate, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -578,98 +589,6 @@ xpc_GCThingIsGrayCCThing(void *thing)
|
|||
xpc_IsGrayGCThing(thing);
|
||||
}
|
||||
|
||||
struct UnmarkGrayTracer : public JSTracer
|
||||
{
|
||||
UnmarkGrayTracer() : mTracingShape(false), mPreviousShape(nullptr) {}
|
||||
UnmarkGrayTracer(JSTracer *trc, bool aTracingShape)
|
||||
: mTracingShape(aTracingShape), mPreviousShape(nullptr)
|
||||
{
|
||||
JS_TracerInit(this, trc->runtime, trc->callback);
|
||||
}
|
||||
bool mTracingShape; // true iff we are tracing the immediate children of a shape
|
||||
void *mPreviousShape; // If mTracingShape, shape child or NULL. Otherwise, NULL.
|
||||
};
|
||||
|
||||
/*
|
||||
* The GC and CC are run independently. Consequently, the following sequence of
|
||||
* events can occur:
|
||||
* 1. GC runs and marks an object gray.
|
||||
* 2. Some JS code runs that creates a pointer from a JS root to the gray
|
||||
* object. If we re-ran a GC at this point, the object would now be black.
|
||||
* 3. Now we run the CC. It may think it can collect the gray object, even
|
||||
* though it's reachable from the JS heap.
|
||||
*
|
||||
* To prevent this badness, we unmark the gray bit of an object when it is
|
||||
* accessed by callers outside XPConnect. This would cause the object to go
|
||||
* black in step 2 above. This must be done on everything reachable from the
|
||||
* object being returned. The following code takes care of the recursive
|
||||
* re-coloring.
|
||||
*/
|
||||
static void
|
||||
UnmarkGrayChildren(JSTracer *trc, void **thingp, JSGCTraceKind kind)
|
||||
{
|
||||
void *thing = *thingp;
|
||||
int stackDummy;
|
||||
if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(trc->runtime), &stackDummy)) {
|
||||
/*
|
||||
* If we run out of stack, we take a more drastic measure: require that
|
||||
* we GC again before the next CC.
|
||||
*/
|
||||
nsXPConnect* xpc = nsXPConnect::GetXPConnect();
|
||||
xpc->EnsureGCBeforeCC();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!xpc_IsGrayGCThing(thing))
|
||||
return;
|
||||
|
||||
js::UnmarkGrayGCThing(thing);
|
||||
|
||||
/*
|
||||
* Trace children of |thing|. If |thing| and its parent are both shapes, |thing| will
|
||||
* get saved to mPreviousShape without being traced. The parent will later
|
||||
* trace |thing|. This is done to avoid increasing the stack depth during shape
|
||||
* tracing. It is safe to do because a shape can only have one child that is a shape.
|
||||
*/
|
||||
UnmarkGrayTracer *tracer = static_cast<UnmarkGrayTracer*>(trc);
|
||||
UnmarkGrayTracer childTracer(tracer, kind == JSTRACE_SHAPE);
|
||||
|
||||
if (kind != JSTRACE_SHAPE) {
|
||||
JS_TraceChildren(&childTracer, thing, kind);
|
||||
MOZ_ASSERT(!childTracer.mPreviousShape);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tracer->mTracingShape) {
|
||||
MOZ_ASSERT(!tracer->mPreviousShape);
|
||||
tracer->mPreviousShape = thing;
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
MOZ_ASSERT(!xpc_IsGrayGCThing(thing));
|
||||
JS_TraceChildren(&childTracer, thing, JSTRACE_SHAPE);
|
||||
thing = childTracer.mPreviousShape;
|
||||
childTracer.mPreviousShape = nullptr;
|
||||
} while (thing);
|
||||
}
|
||||
|
||||
void
|
||||
xpc_UnmarkGrayGCThingRecursive(void *thing, JSGCTraceKind kind)
|
||||
{
|
||||
MOZ_ASSERT(thing, "Don't pass me null!");
|
||||
MOZ_ASSERT(kind != JSTRACE_SHAPE, "UnmarkGrayGCThingRecursive not intended for Shapes");
|
||||
|
||||
// Unmark.
|
||||
js::UnmarkGrayGCThing(thing);
|
||||
|
||||
// Trace children.
|
||||
UnmarkGrayTracer trc;
|
||||
JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
|
||||
JS_TracerInit(&trc, rt, UnmarkGrayChildren);
|
||||
JS_TraceChildren(&trc, thing, kind);
|
||||
}
|
||||
|
||||
struct TraversalTracer : public JSTracer
|
||||
{
|
||||
TraversalTracer(nsCycleCollectionTraversalCallback &aCb) : cb(aCb)
|
||||
|
|
|
@ -522,9 +522,6 @@ public:
|
|||
|
||||
JSBool IsShuttingDown() const {return mShuttingDown;}
|
||||
|
||||
void EnsureGCBeforeCC() { mNeedGCBeforeCC = true; }
|
||||
void ClearGCBeforeCC() { mNeedGCBeforeCC = false; }
|
||||
|
||||
nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
|
||||
nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
|
||||
|
||||
|
@ -579,7 +576,6 @@ private:
|
|||
nsIXPCSecurityManager* mDefaultSecurityManager;
|
||||
uint16_t mDefaultSecurityManagerFlags;
|
||||
JSBool mShuttingDown;
|
||||
JSBool mNeedGCBeforeCC;
|
||||
|
||||
// nsIThreadInternal doesn't remember which observers it called
|
||||
// OnProcessNextEvent on when it gets around to calling AfterProcessNextEvent.
|
||||
|
|
|
@ -136,16 +136,12 @@ xpc_IsGrayGCThing(void *thing)
|
|||
extern JSBool
|
||||
xpc_GCThingIsGrayCCThing(void *thing);
|
||||
|
||||
// Implemented in nsXPConnect.cpp.
|
||||
extern void
|
||||
xpc_UnmarkGrayGCThingRecursive(void *thing, JSGCTraceKind kind);
|
||||
|
||||
// Unmark gray for known-nonnull cases
|
||||
MOZ_ALWAYS_INLINE void
|
||||
xpc_UnmarkNonNullGrayObject(JSObject *obj)
|
||||
{
|
||||
if (xpc_IsGrayGCThing(obj))
|
||||
xpc_UnmarkGrayGCThingRecursive(obj, JSTRACE_OBJECT);
|
||||
js::UnmarkGrayGCThingRecursively(obj, JSTRACE_OBJECT);
|
||||
else if (js::IsIncrementalBarrierNeededOnObject(obj))
|
||||
js::IncrementalReferenceBarrier(obj);
|
||||
}
|
||||
|
@ -165,7 +161,7 @@ xpc_UnmarkGrayScript(JSScript *script)
|
|||
{
|
||||
if (script) {
|
||||
if (xpc_IsGrayGCThing(script))
|
||||
xpc_UnmarkGrayGCThingRecursive(script, JSTRACE_SCRIPT);
|
||||
js::UnmarkGrayGCThingRecursively(script, JSTRACE_SCRIPT);
|
||||
else if (js::IsIncrementalBarrierNeededOnScript(script))
|
||||
js::IncrementalReferenceBarrier(script);
|
||||
}
|
||||
|
|
|
@ -57,15 +57,15 @@ using namespace mozilla;
|
|||
/*******************************************************************************
|
||||
* nsFramesetDrag
|
||||
******************************************************************************/
|
||||
nsFramesetDrag::nsFramesetDrag()
|
||||
nsFramesetDrag::nsFramesetDrag()
|
||||
{
|
||||
UnSet();
|
||||
}
|
||||
|
||||
void nsFramesetDrag::Reset(bool aVertical,
|
||||
int32_t aIndex,
|
||||
int32_t aChange,
|
||||
nsHTMLFramesetFrame* aSource)
|
||||
void nsFramesetDrag::Reset(bool aVertical,
|
||||
int32_t aIndex,
|
||||
int32_t aChange,
|
||||
nsHTMLFramesetFrame* aSource)
|
||||
{
|
||||
mVertical = aVertical;
|
||||
mIndex = aIndex;
|
||||
|
@ -93,18 +93,18 @@ public:
|
|||
NS_IMETHOD GetFrameName(nsAString& aResult) const;
|
||||
#endif
|
||||
|
||||
NS_IMETHOD HandleEvent(nsPresContext* aPresContext,
|
||||
NS_IMETHOD HandleEvent(nsPresContext* aPresContext,
|
||||
nsGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus);
|
||||
|
||||
NS_IMETHOD GetCursor(const nsPoint& aPoint,
|
||||
nsIFrame::Cursor& aCursor);
|
||||
|
||||
|
||||
NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsRect& aDirtyRect,
|
||||
const nsDisplayListSet& aLists);
|
||||
|
||||
NS_IMETHOD Reflow(nsPresContext* aPresContext,
|
||||
NS_IMETHOD Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus);
|
||||
|
@ -151,7 +151,7 @@ public:
|
|||
const nsRect& aDirtyRect,
|
||||
const nsDisplayListSet& aLists);
|
||||
|
||||
NS_IMETHOD Reflow(nsPresContext* aPresContext,
|
||||
NS_IMETHOD Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus);
|
||||
|
@ -272,7 +272,7 @@ nsHTMLFramesetFrame::Init(nsIContent* aContent,
|
|||
nsFrameborder frameborder = GetFrameBorder();
|
||||
int32_t borderWidth = GetBorderWidth(presContext, false);
|
||||
nscolor borderColor = GetBorderColor();
|
||||
|
||||
|
||||
// Get the rows= cols= data
|
||||
nsHTMLFrameSetElement* ourContent = nsHTMLFrameSetElement::FromContent(mContent);
|
||||
NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
|
||||
|
@ -288,7 +288,7 @@ nsHTMLFramesetFrame::Init(nsIContent* aContent,
|
|||
mRowSizes = new nscoord[mNumRows];
|
||||
mColSizes = new nscoord[mNumCols];
|
||||
if (!mRowSizes || !mColSizes)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
// Ensure we can't overflow numCells
|
||||
PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT < INT32_MAX / NS_MAX_FRAMESET_SPEC_COUNT);
|
||||
|
@ -308,15 +308,15 @@ nsHTMLFramesetFrame::Init(nsIContent* aContent,
|
|||
|
||||
for (int horX = 0; horX < mNumRows; horX++)
|
||||
mHorBorders[horX] = nullptr;
|
||||
|
||||
|
||||
PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT
|
||||
< UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT);
|
||||
< UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT);
|
||||
PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT
|
||||
< UINT_MAX / sizeof(nsFrameborder) / NS_MAX_FRAMESET_SPEC_COUNT);
|
||||
< UINT_MAX / sizeof(nsFrameborder) / NS_MAX_FRAMESET_SPEC_COUNT);
|
||||
PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT
|
||||
< UINT_MAX / sizeof(nsBorderColor) / NS_MAX_FRAMESET_SPEC_COUNT);
|
||||
< UINT_MAX / sizeof(nsBorderColor) / NS_MAX_FRAMESET_SPEC_COUNT);
|
||||
mChildFrameborder = new nsFrameborder[numCells];
|
||||
mChildBorderColors = new nsBorderColor[numCells];
|
||||
mChildBorderColors = new nsBorderColor[numCells];
|
||||
if (!mChildFrameborder || !mChildBorderColors)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
|
@ -349,7 +349,7 @@ nsHTMLFramesetFrame::Init(nsIContent* aContent,
|
|||
}
|
||||
|
||||
// IMPORTANT: This must match the conditions in
|
||||
// nsCSSFrameConstructor::ContentAppended/Inserted/Removed
|
||||
// nsCSSFrameConstructor::ContentAppended/Inserted/Removed
|
||||
if (!child->IsHTML())
|
||||
continue;
|
||||
|
||||
|
@ -391,7 +391,7 @@ nsHTMLFramesetFrame::Init(nsIContent* aContent,
|
|||
mChildBorderColors[mChildCount].Set(GetBorderColor(child));
|
||||
}
|
||||
child->SetPrimaryFrame(frame);
|
||||
|
||||
|
||||
if (NS_FAILED(result))
|
||||
return result;
|
||||
|
||||
|
@ -411,8 +411,8 @@ nsHTMLFramesetFrame::Init(nsIContent* aContent,
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// XXX the blank frame is using the content of its parent - at some point it
|
||||
// should just have null content, if we support that
|
||||
// XXX the blank frame is using the content of its parent - at some point it
|
||||
// should just have null content, if we support that
|
||||
nsHTMLFramesetBlankFrame* blankFrame = new (shell) nsHTMLFramesetBlankFrame(pseudoStyleContext);
|
||||
|
||||
result = blankFrame->Init(mContent, this, nullptr);
|
||||
|
@ -420,7 +420,7 @@ nsHTMLFramesetFrame::Init(nsIContent* aContent,
|
|||
blankFrame->Destroy();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
mFrames.AppendFrame(nullptr, blankFrame);
|
||||
|
||||
mChildBorderColors[mChildCount].Set(NO_COLOR);
|
||||
|
@ -447,9 +447,9 @@ nsHTMLFramesetFrame::SetInitialChildList(ChildListID aListID,
|
|||
}
|
||||
|
||||
// XXX should this try to allocate twips based on an even pixel boundary?
|
||||
void nsHTMLFramesetFrame::Scale(nscoord aDesired,
|
||||
int32_t aNumIndicies,
|
||||
int32_t* aIndicies,
|
||||
void nsHTMLFramesetFrame::Scale(nscoord aDesired,
|
||||
int32_t aNumIndicies,
|
||||
int32_t* aIndicies,
|
||||
int32_t aNumItems,
|
||||
int32_t* aItems)
|
||||
{
|
||||
|
@ -491,7 +491,7 @@ void nsHTMLFramesetFrame::Scale(nscoord aDesired,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Translate the rows/cols specs into an array of integer sizes for
|
||||
|
@ -499,10 +499,10 @@ void nsHTMLFramesetFrame::Scale(nscoord aDesired,
|
|||
* specifier - fixed sizes have the highest priority, percentage sizes have the next
|
||||
* highest priority and relative sizes have the lowest.
|
||||
*/
|
||||
void nsHTMLFramesetFrame::CalculateRowCol(nsPresContext* aPresContext,
|
||||
nscoord aSize,
|
||||
int32_t aNumSpecs,
|
||||
const nsFramesetSpec* aSpecs,
|
||||
void nsHTMLFramesetFrame::CalculateRowCol(nsPresContext* aPresContext,
|
||||
nscoord aSize,
|
||||
int32_t aNumSpecs,
|
||||
const nsFramesetSpec* aSpecs,
|
||||
nscoord* aValues)
|
||||
{
|
||||
// aNumSpecs maximum value is NS_MAX_FRAMESET_SPEC_COUNT
|
||||
|
@ -522,9 +522,9 @@ void nsHTMLFramesetFrame::CalculateRowCol(nsPresContext* aPresContext,
|
|||
}
|
||||
|
||||
int32_t i, j;
|
||||
|
||||
|
||||
// initialize the fixed, percent, relative indices, allocate the fixed sizes and zero the others
|
||||
for (i = 0; i < aNumSpecs; i++) {
|
||||
for (i = 0; i < aNumSpecs; i++) {
|
||||
aValues[i] = 0;
|
||||
switch (aSpecs[i].mUnit) {
|
||||
case eFramesetUnit_Fixed:
|
||||
|
@ -546,7 +546,7 @@ void nsHTMLFramesetFrame::CalculateRowCol(nsPresContext* aPresContext,
|
|||
}
|
||||
|
||||
// scale the fixed sizes if they total too much (or too little and there aren't any percent or relative)
|
||||
if ((fixedTotal > aSize) || ((fixedTotal < aSize) && (0 == numPercent) && (0 == numRelative))) {
|
||||
if ((fixedTotal > aSize) || ((fixedTotal < aSize) && (0 == numPercent) && (0 == numRelative))) {
|
||||
Scale(aSize, numFixed, fixed, aNumSpecs, aValues);
|
||||
return;
|
||||
}
|
||||
|
@ -561,7 +561,7 @@ void nsHTMLFramesetFrame::CalculateRowCol(nsPresContext* aPresContext,
|
|||
}
|
||||
|
||||
// scale the percent sizes if they total too much (or too little and there aren't any relative)
|
||||
if ((percentTotal > percentMax) || ((percentTotal < percentMax) && (0 == numRelative))) {
|
||||
if ((percentTotal > percentMax) || ((percentTotal < percentMax) && (0 == numRelative))) {
|
||||
Scale(percentMax, numPercent, percent, aNumSpecs, aValues);
|
||||
return;
|
||||
}
|
||||
|
@ -576,7 +576,7 @@ void nsHTMLFramesetFrame::CalculateRowCol(nsPresContext* aPresContext,
|
|||
}
|
||||
|
||||
// scale the relative sizes if they take up too much or too little
|
||||
if (relativeTotal != relativeMax) {
|
||||
if (relativeTotal != relativeMax) {
|
||||
Scale(relativeMax, numRelative, relative, aNumSpecs, aValues);
|
||||
}
|
||||
}
|
||||
|
@ -587,19 +587,19 @@ void nsHTMLFramesetFrame::CalculateRowCol(nsPresContext* aPresContext,
|
|||
* each cell in the frameset. Reverse of CalculateRowCol() behaviour.
|
||||
* This allows us to maintain the user size info through reflows.
|
||||
*/
|
||||
void nsHTMLFramesetFrame::GenerateRowCol(nsPresContext* aPresContext,
|
||||
nscoord aSize,
|
||||
int32_t aNumSpecs,
|
||||
void nsHTMLFramesetFrame::GenerateRowCol(nsPresContext* aPresContext,
|
||||
nscoord aSize,
|
||||
int32_t aNumSpecs,
|
||||
const nsFramesetSpec* aSpecs,
|
||||
nscoord* aValues,
|
||||
nsString& aNewAttr)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
|
||||
for (i = 0; i < aNumSpecs; i++) {
|
||||
if (!aNewAttr.IsEmpty())
|
||||
aNewAttr.Append(PRUnichar(','));
|
||||
|
||||
|
||||
switch (aSpecs[i].mUnit) {
|
||||
case eFramesetUnit_Fixed:
|
||||
aNewAttr.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(aValues[i]));
|
||||
|
@ -607,7 +607,7 @@ void nsHTMLFramesetFrame::GenerateRowCol(nsPresContext* aPresContext,
|
|||
case eFramesetUnit_Percent: // XXX Only accurate to 1%, need 1 pixel
|
||||
case eFramesetUnit_Relative:
|
||||
// Add 0.5 to the percentage to make rounding work right.
|
||||
aNewAttr.AppendInt(uint32_t((100.0*aValues[i])/aSize + 0.5));
|
||||
aNewAttr.AppendInt(uint32_t((100.0*aValues[i])/aSize + 0.5));
|
||||
aNewAttr.Append(PRUnichar('%'));
|
||||
break;
|
||||
}
|
||||
|
@ -618,7 +618,7 @@ int32_t nsHTMLFramesetFrame::GetBorderWidth(nsPresContext* aPresContext,
|
|||
bool aTakeForcingIntoAccount)
|
||||
{
|
||||
bool forcing = mForceFrameResizability && aTakeForcingIntoAccount;
|
||||
|
||||
|
||||
if (!forcing) {
|
||||
nsFrameborder frameborder = GetFrameBorder();
|
||||
if (frameborder == eFrameborder_No) {
|
||||
|
@ -660,8 +660,8 @@ nsHTMLFramesetFrame::GetSkipSides() const
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLFramesetFrame::GetDesiredSize(nsPresContext* aPresContext,
|
||||
void
|
||||
nsHTMLFramesetFrame::GetDesiredSize(nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsHTMLReflowMetrics& aDesiredSize)
|
||||
{
|
||||
|
@ -682,12 +682,12 @@ nsHTMLFramesetFrame::GetDesiredSize(nsPresContext* aPresContext,
|
|||
framesetParent->GetSizeOfChild(this, size);
|
||||
aDesiredSize.width = size.width;
|
||||
aDesiredSize.height = size.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only valid for non border children
|
||||
void nsHTMLFramesetFrame::GetSizeOfChildAt(int32_t aIndexInParent,
|
||||
nsSize& aSize,
|
||||
void nsHTMLFramesetFrame::GetSizeOfChildAt(int32_t aIndexInParent,
|
||||
nsSize& aSize,
|
||||
nsIntPoint& aCellIndex)
|
||||
{
|
||||
int32_t row = aIndexInParent / mNumCols;
|
||||
|
@ -704,7 +704,7 @@ void nsHTMLFramesetFrame::GetSizeOfChildAt(int32_t aIndexInParent,
|
|||
}
|
||||
|
||||
// only valid for non border children
|
||||
void nsHTMLFramesetFrame::GetSizeOfChild(nsIFrame* aChild,
|
||||
void nsHTMLFramesetFrame::GetSizeOfChild(nsIFrame* aChild,
|
||||
nsSize& aSize)
|
||||
{
|
||||
// Reflow only creates children frames for <frameset> and <frame> content.
|
||||
|
@ -721,12 +721,12 @@ void nsHTMLFramesetFrame::GetSizeOfChild(nsIFrame* aChild,
|
|||
}
|
||||
aSize.width = 0;
|
||||
aSize.height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NS_METHOD nsHTMLFramesetFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
nsGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus)
|
||||
|
||||
NS_METHOD nsHTMLFramesetFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
nsGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEventStatus);
|
||||
if (mDragger) {
|
||||
|
@ -769,7 +769,7 @@ nsHTMLFramesetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
{
|
||||
nsresult rv = BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
if (mDragger && aBuilder->IsForEventDelivery()) {
|
||||
rv = aLists.Content()->AppendNewToTop(
|
||||
new (aBuilder) nsDisplayEventReceiver(aBuilder, this));
|
||||
|
@ -777,9 +777,9 @@ nsHTMLFramesetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
nsHTMLFramesetFrame::ReflowPlaceChild(nsIFrame* aChild,
|
||||
nsPresContext* aPresContext,
|
||||
nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsPoint& aOffset,
|
||||
nsSize& aSize,
|
||||
|
@ -791,11 +791,11 @@ nsHTMLFramesetFrame::ReflowPlaceChild(nsIFrame* aChild,
|
|||
metrics.width = aSize.width;
|
||||
metrics.height= aSize.height;
|
||||
nsReflowStatus status;
|
||||
|
||||
|
||||
ReflowChild(aChild, aPresContext, metrics, reflowState, aOffset.x,
|
||||
aOffset.y, 0, status);
|
||||
NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status");
|
||||
|
||||
|
||||
// Place and size the child
|
||||
metrics.width = aSize.width;
|
||||
metrics.height = aSize.height;
|
||||
|
@ -825,7 +825,7 @@ nsFrameborder GetFrameBorderHelper(nsGenericHTMLElement* aContent)
|
|||
return eFrameborder_Notset;
|
||||
}
|
||||
|
||||
nsFrameborder nsHTMLFramesetFrame::GetFrameBorder()
|
||||
nsFrameborder nsHTMLFramesetFrame::GetFrameBorder()
|
||||
{
|
||||
nsFrameborder result = eFrameborder_Notset;
|
||||
nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
|
||||
|
@ -854,7 +854,7 @@ nsFrameborder nsHTMLFramesetFrame::GetFrameBorder(nsIContent* aContent)
|
|||
return result;
|
||||
}
|
||||
|
||||
nscolor nsHTMLFramesetFrame::GetBorderColor()
|
||||
nscolor nsHTMLFramesetFrame::GetBorderColor()
|
||||
{
|
||||
nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
|
||||
|
||||
|
@ -871,7 +871,7 @@ nscolor nsHTMLFramesetFrame::GetBorderColor()
|
|||
return mParentBorderColor;
|
||||
}
|
||||
|
||||
nscolor nsHTMLFramesetFrame::GetBorderColor(nsIContent* aContent)
|
||||
nscolor nsHTMLFramesetFrame::GetBorderColor(nsIContent* aContent)
|
||||
{
|
||||
nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(aContent);
|
||||
|
||||
|
@ -888,7 +888,7 @@ nscolor nsHTMLFramesetFrame::GetBorderColor(nsIContent* aContent)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
|
@ -899,11 +899,11 @@ nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
|
|||
nsStyleSet *styleSet = shell->StyleSet();
|
||||
|
||||
mParent->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
|
||||
|
||||
//printf("FramesetFrame2::Reflow %X (%d,%d) \n", this, aReflowState.availableWidth, aReflowState.availableHeight);
|
||||
|
||||
//printf("FramesetFrame2::Reflow %X (%d,%d) \n", this, aReflowState.availableWidth, aReflowState.availableHeight);
|
||||
// Always get the size so that the caller knows how big we are
|
||||
GetDesiredSize(aPresContext, aReflowState, aDesiredSize);
|
||||
|
||||
|
||||
nscoord width = (aDesiredSize.width <= aReflowState.availableWidth)
|
||||
? aDesiredSize.width : aReflowState.availableWidth;
|
||||
nscoord height = (aDesiredSize.height <= aReflowState.availableHeight)
|
||||
|
@ -915,7 +915,7 @@ nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
|
|||
kFrameResizePref, this);
|
||||
mForceFrameResizability = Preferences::GetBool(kFrameResizePref);
|
||||
}
|
||||
|
||||
|
||||
// subtract out the width of all of the potential borders. There are
|
||||
// only borders between <frame>s. There are none on the edges (e.g the
|
||||
// leftmost <frame> has no left border).
|
||||
|
@ -1032,13 +1032,13 @@ nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
|
|||
if (cellIndex.x > 0) { // moved to next col in same row
|
||||
if (0 == cellIndex.y) { // in 1st row
|
||||
if (firstTime) { // create vertical border
|
||||
|
||||
|
||||
nsRefPtr<nsStyleContext> pseudoStyleContext;
|
||||
pseudoStyleContext = styleSet->
|
||||
ResolveAnonymousBoxStyle(nsCSSAnonBoxes::verticalFramesetBorder,
|
||||
mStyleContext);
|
||||
|
||||
borderFrame = new (shell) nsHTMLFramesetBorderFrame(pseudoStyleContext,
|
||||
borderFrame = new (shell) nsHTMLFramesetBorderFrame(pseudoStyleContext,
|
||||
borderWidth,
|
||||
true,
|
||||
false);
|
||||
|
@ -1051,7 +1051,7 @@ nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
|
|||
borderFrame->mPrevNeighbor = lastCol;
|
||||
borderFrame->mNextNeighbor = cellIndex.x;
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
borderFrame = (nsHTMLFramesetBorderFrame*)mFrames.FrameAt(borderChildX);
|
||||
if (MOZ_LIKELY(borderFrame != nullptr)) {
|
||||
borderFrame->mWidth = borderWidth;
|
||||
|
@ -1085,7 +1085,7 @@ nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
|
|||
} else { // notset
|
||||
childVis = (eFrameborder_No == frameborder) ? NONE_VIS : ALL_VIS;
|
||||
}
|
||||
} else { // blank
|
||||
} else { // blank
|
||||
DebugOnly<nsHTMLFramesetBlankFrame*> blank;
|
||||
MOZ_ASSERT(blank = do_QueryFrame(child), "unexpected child frame type");
|
||||
childVis = NONE_VIS;
|
||||
|
@ -1181,7 +1181,7 @@ nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext,
|
|||
} else {
|
||||
SetBorderResize(mHorBorders[horX]);
|
||||
}
|
||||
childColor = (NO_COLOR == horBorderColors[horX]) ? borderColor : horBorderColors[horX];
|
||||
childColor = (NO_COLOR == horBorderColors[horX]) ? borderColor : horBorderColors[horX];
|
||||
mHorBorders[horX]->SetColor(childColor);
|
||||
}
|
||||
}
|
||||
|
@ -1223,26 +1223,23 @@ nsHTMLFramesetFrame::IsLeaf() const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsHTMLFramesetFrame::CanResize(bool aVertical,
|
||||
bool aLeft)
|
||||
bool
|
||||
nsHTMLFramesetFrame::CanResize(bool aVertical,
|
||||
bool aLeft)
|
||||
{
|
||||
nsIFrame* child;
|
||||
int32_t childX;
|
||||
int32_t startX;
|
||||
if (aVertical) {
|
||||
startX = (aLeft) ? 0 : mNumCols-1;
|
||||
for (childX = startX; childX < mNonBorderChildCount; childX += mNumCols) {
|
||||
child = mFrames.FrameAt(childX);
|
||||
if (!CanChildResize(aVertical, aLeft, childX)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
startX = (aLeft) ? 0 : (mNumRows - 1) * mNumCols;
|
||||
int32_t endX = startX + mNumCols;
|
||||
for (childX = startX; childX < endX; childX++) {
|
||||
child = mFrames.FrameAt(childX);
|
||||
if (!CanChildResize(aVertical, aLeft, childX)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1252,14 +1249,14 @@ nsHTMLFramesetFrame::CanResize(bool aVertical,
|
|||
}
|
||||
|
||||
bool
|
||||
nsHTMLFramesetFrame::GetNoResize(nsIFrame* aChildFrame)
|
||||
nsHTMLFramesetFrame::GetNoResize(nsIFrame* aChildFrame)
|
||||
{
|
||||
nsIContent* content = aChildFrame->GetContent();
|
||||
|
||||
return content && content->HasAttr(kNameSpaceID_None, nsGkAtoms::noresize);
|
||||
}
|
||||
|
||||
bool
|
||||
bool
|
||||
nsHTMLFramesetFrame::CanChildResize(bool aVertical, bool aLeft, int32_t aChildX)
|
||||
{
|
||||
nsIFrame* child = mFrames.FrameAt(aChildX);
|
||||
|
@ -1278,7 +1275,7 @@ nsHTMLFramesetFrame::RecalculateBorderResize()
|
|||
|
||||
PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT < INT32_MAX / NS_MAX_FRAMESET_SPEC_COUNT);
|
||||
PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT
|
||||
< UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT);
|
||||
< UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT);
|
||||
// set the visibility and mouse sensitivity of borders
|
||||
int32_t verX;
|
||||
for (verX = 0; verX < mNumCols-1; verX++) {
|
||||
|
@ -1306,7 +1303,7 @@ nsHTMLFramesetFrame::RecalculateBorderResize()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
void
|
||||
nsHTMLFramesetFrame::SetBorderResize(nsHTMLFramesetBorderFrame* aBorderFrame)
|
||||
{
|
||||
if (aBorderFrame->mVertical) {
|
||||
|
@ -1333,10 +1330,10 @@ nsHTMLFramesetFrame::SetBorderResize(nsHTMLFramesetBorderFrame* aBorderFrame)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsHTMLFramesetFrame::StartMouseDrag(nsPresContext* aPresContext,
|
||||
nsHTMLFramesetBorderFrame* aBorder,
|
||||
nsHTMLFramesetFrame::StartMouseDrag(nsPresContext* aPresContext,
|
||||
nsHTMLFramesetBorderFrame* aBorder,
|
||||
nsGUIEvent* aEvent)
|
||||
{
|
||||
#if 0
|
||||
|
@ -1362,11 +1359,11 @@ nsHTMLFramesetFrame::StartMouseDrag(nsPresContext* aPresContext,
|
|||
|
||||
gDragInProgress = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
nsHTMLFramesetFrame::MouseDrag(nsPresContext* aPresContext,
|
||||
nsGUIEvent* aEvent)
|
||||
nsHTMLFramesetFrame::MouseDrag(nsPresContext* aPresContext,
|
||||
nsGUIEvent* aEvent)
|
||||
{
|
||||
// if the capture ended, reset the drag state
|
||||
if (nsIPresShell::GetCapturingContent() != GetContent()) {
|
||||
|
@ -1429,7 +1426,7 @@ nsHTMLFramesetFrame::MouseDrag(nsPresContext* aPresContext,
|
|||
if (change != 0) {
|
||||
mDrag.Reset(mDragger->mVertical, mDragger->mPrevNeighbor, change, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLFramesetFrame::EndMouseDrag(nsPresContext* aPresContext)
|
||||
|
@ -1489,18 +1486,18 @@ nscoord nsHTMLFramesetBorderFrame::GetIntrinsicHeight()
|
|||
}
|
||||
|
||||
void nsHTMLFramesetBorderFrame::SetVisibility(bool aVisibility)
|
||||
{
|
||||
mVisibility = aVisibility;
|
||||
{
|
||||
mVisibility = aVisibility;
|
||||
}
|
||||
|
||||
void nsHTMLFramesetBorderFrame::SetColor(nscolor aColor)
|
||||
{
|
||||
{
|
||||
mColor = aColor;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLFramesetBorderFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLFramesetBorderFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
|
@ -1637,9 +1634,9 @@ void nsHTMLFramesetBorderFrame::PaintBorder(nsRenderingContext& aRenderingContex
|
|||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLFramesetBorderFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
nsGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus)
|
||||
nsHTMLFramesetBorderFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
nsGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEventStatus);
|
||||
*aEventStatus = nsEventStatus_eIgnore;
|
||||
|
@ -1667,7 +1664,7 @@ nsHTMLFramesetBorderFrame::GetCursor(const nsPoint& aPoint,
|
|||
{
|
||||
if (!mCanResize) {
|
||||
aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
|
||||
} else {
|
||||
} else {
|
||||
aCursor.mCursor = (mVertical) ? NS_STYLE_CURSOR_EW_RESIZE : NS_STYLE_CURSOR_NS_RESIZE;
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -1704,7 +1701,7 @@ nscoord nsHTMLFramesetBlankFrame::GetIntrinsicHeight()
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLFramesetBlankFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLFramesetBlankFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState,
|
||||
nsReflowStatus& aStatus)
|
||||
|
|
|
@ -406,6 +406,7 @@ user_pref("reftest.browser.iframe.enabled", true);
|
|||
user_pref("reftest.remote", true);
|
||||
user_pref("reftest.uri", "%s");
|
||||
user_pref("toolkit.telemetry.prompted", true);
|
||||
user_pref("marionette.loadearly", true);
|
||||
""" % reftestlist)
|
||||
|
||||
#workaround for jsreftests.
|
||||
|
|
|
@ -742,8 +742,8 @@ public class AboutHomeContent extends ScrollView
|
|||
// Just using getWidth() will use incorrect values during onMeasure when rotating the device
|
||||
// Instead we pass in the measuredWidth, which is correct
|
||||
int w = getColumnWidth(measuredWidth);
|
||||
Tabs.setThumbnailWidth(w);
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int)(w*Tabs.getThumbnailAspectRatio()*numRows) + getPaddingTop() + getPaddingBottom(),
|
||||
ThumbnailHelper.getInstance().setThumbnailWidth(w);
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int)(w*ThumbnailHelper.THUMBNAIL_ASPECT_RATIO*numRows) + getPaddingTop() + getPaddingBottom(),
|
||||
MeasureSpec.EXACTLY);
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
@ -771,7 +771,7 @@ public class AboutHomeContent extends ScrollView
|
|||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
super.bindView(view, context, cursor);
|
||||
view.setLayoutParams(new AbsListView.LayoutParams(mTopSitesGrid.getColumnWidth(),
|
||||
Math.round(mTopSitesGrid.getColumnWidth()*Tabs.getThumbnailAspectRatio())));
|
||||
Math.round(mTopSitesGrid.getColumnWidth()*ThumbnailHelper.THUMBNAIL_ASPECT_RATIO)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/* 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/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
public class BackButton extends ShapedButton {
|
||||
private Path mBorderPath;
|
||||
private Paint mBorderPaint;
|
||||
private Paint mBorderPrivatePaint;
|
||||
|
||||
public BackButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
// Paint to draw the border.
|
||||
mBorderPaint = new Paint();
|
||||
mBorderPaint.setAntiAlias(true);
|
||||
mBorderPaint.setColor(0xFF000000);
|
||||
mBorderPaint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
mBorderPrivatePaint = new Paint(mBorderPaint);
|
||||
|
||||
// Path is masked.
|
||||
mPath = new Path();
|
||||
mBorderPath = new Path();
|
||||
mCanvasDelegate = new CanvasDelegate(this, Mode.DST_IN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
|
||||
super.onSizeChanged(width, height, oldWidth, oldHeight);
|
||||
|
||||
mPath.reset();
|
||||
mPath.addCircle(width/2, height/2, width/2, Path.Direction.CW);
|
||||
|
||||
float borderWidth = getContext().getResources().getDimension(R.dimen.nav_button_border_width);
|
||||
mBorderPaint.setStrokeWidth(borderWidth);
|
||||
mBorderPrivatePaint.setStrokeWidth(borderWidth);
|
||||
|
||||
mBorderPath.reset();
|
||||
mBorderPath.addCircle(width/2, height/2, (width/2) - borderWidth, Path.Direction.CW);
|
||||
|
||||
mBorderPaint.setShader(new LinearGradient(0, 0,
|
||||
0, height,
|
||||
0xFF898D8F, 0xFFFEFEFE,
|
||||
Shader.TileMode.CLAMP));
|
||||
|
||||
mBorderPrivatePaint.setShader(new LinearGradient(0, 0,
|
||||
0, height,
|
||||
0xCC06090D, 0xFF616569,
|
||||
Shader.TileMode.CLAMP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
mCanvasDelegate.draw(canvas, mPath, getWidth(), getHeight());
|
||||
|
||||
// Draw the border on top.
|
||||
canvas.drawPath(mBorderPath, isPrivateMode() ? mBorderPrivatePaint : mBorderPaint);
|
||||
}
|
||||
|
||||
// The drawable is constructed as per @drawable/address_bar_nav_button.
|
||||
@Override
|
||||
public void onLightweightThemeChanged() {
|
||||
Drawable drawable = mActivity.getLightweightTheme().getDrawable(this);
|
||||
if (drawable == null)
|
||||
return;
|
||||
|
||||
Resources resources = getContext().getResources();
|
||||
StateListDrawable stateList = new StateListDrawable();
|
||||
|
||||
stateList.addState(new int[] { android.R.attr.state_pressed }, resources.getDrawable(R.drawable.highlight));
|
||||
stateList.addState(new int[] { R.attr.state_private }, resources.getDrawable(R.drawable.address_bar_bg_private));
|
||||
stateList.addState(new int[] {}, drawable);
|
||||
|
||||
setBackgroundDrawable(stateList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLightweightThemeReset() {
|
||||
setBackgroundResource(R.drawable.address_bar_nav_button);
|
||||
}
|
||||
}
|
|
@ -68,8 +68,8 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
|
|||
private boolean mAnimateSiteSecurity;
|
||||
private GeckoImageButton mTabs;
|
||||
private int mTabsPaneWidth;
|
||||
private ImageView mBack;
|
||||
private ImageView mForward;
|
||||
private ImageButton mBack;
|
||||
private ImageButton mForward;
|
||||
public ImageButton mFavicon;
|
||||
public ImageButton mStop;
|
||||
public ImageButton mSiteSecurity;
|
||||
|
@ -996,6 +996,12 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
|
|||
((GeckoTextView) mTabsCount.getCurrentView()).setPrivateMode(tab.isPrivate());
|
||||
mTitle.setPrivateMode(tab.isPrivate());
|
||||
mMenu.setPrivateMode(tab.isPrivate());
|
||||
|
||||
if (mBack instanceof BackButton)
|
||||
((BackButton) mBack).setPrivateMode(tab.isPrivate());
|
||||
|
||||
if (mForward instanceof ForwardButton)
|
||||
((ForwardButton) mForward).setPrivateMode(tab.isPrivate());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/* 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/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
public class ForwardButton extends ShapedButton {
|
||||
private Path mBorderPath;
|
||||
private Paint mBorderPaint;
|
||||
private Paint mBorderPrivatePaint;
|
||||
|
||||
public ForwardButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
// Paint to draw the border.
|
||||
mBorderPaint = new Paint();
|
||||
mBorderPaint.setAntiAlias(true);
|
||||
mBorderPaint.setColor(0xFF000000);
|
||||
mBorderPaint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
mBorderPrivatePaint = new Paint(mBorderPaint);
|
||||
|
||||
mBorderPath = new Path();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
|
||||
super.onSizeChanged(width, height, oldWidth, oldHeight);
|
||||
|
||||
float borderWidth = getContext().getResources().getDimension(R.dimen.nav_button_border_width);
|
||||
mBorderPaint.setStrokeWidth(borderWidth);
|
||||
mBorderPrivatePaint.setStrokeWidth(borderWidth);
|
||||
|
||||
mBorderPath.reset();
|
||||
mBorderPath.moveTo(width - borderWidth, 0);
|
||||
mBorderPath.lineTo(width - borderWidth, height);
|
||||
|
||||
mBorderPaint.setShader(new LinearGradient(0, 0,
|
||||
0, height,
|
||||
0xFF898D8F, 0xFFFEFEFE,
|
||||
Shader.TileMode.CLAMP));
|
||||
|
||||
mBorderPrivatePaint.setShader(new LinearGradient(0, 0,
|
||||
0, height,
|
||||
0xCC06090D, 0xFF616569,
|
||||
Shader.TileMode.CLAMP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
super.draw(canvas);
|
||||
|
||||
// Draw the border on top.
|
||||
canvas.drawPath(mBorderPath, isPrivateMode() ? mBorderPrivatePaint : mBorderPaint);
|
||||
}
|
||||
|
||||
// The drawable is constructed as per @drawable/address_bar_nav_button.
|
||||
@Override
|
||||
public void onLightweightThemeChanged() {
|
||||
Drawable drawable = mActivity.getLightweightTheme().getDrawable(this);
|
||||
if (drawable == null)
|
||||
return;
|
||||
|
||||
Resources resources = getContext().getResources();
|
||||
StateListDrawable stateList = new StateListDrawable();
|
||||
|
||||
stateList.addState(new int[] { android.R.attr.state_pressed }, resources.getDrawable(R.drawable.highlight));
|
||||
stateList.addState(new int[] { R.attr.state_private }, resources.getDrawable(R.drawable.address_bar_bg_private));
|
||||
stateList.addState(new int[] {}, drawable);
|
||||
|
||||
setBackgroundDrawable(stateList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLightweightThemeReset() {
|
||||
setBackgroundResource(R.drawable.address_bar_nav_button);
|
||||
}
|
||||
}
|
|
@ -43,7 +43,6 @@ import android.content.pm.ServiceInfo;
|
|||
import android.content.pm.Signature;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
|
@ -102,7 +101,6 @@ import java.io.OutputStream;
|
|||
import java.lang.reflect.Method;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -705,54 +703,6 @@ abstract public class GeckoApp
|
|||
outState.putString(SAVED_STATE_PRIVATE_SESSION, mPrivateBrowsingSession);
|
||||
}
|
||||
|
||||
void getAndProcessThumbnailForTab(final Tab tab) {
|
||||
if ("about:home".equals(tab.getURL())) {
|
||||
tab.updateThumbnail(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab.getState() == Tab.STATE_DELAYED) {
|
||||
if (tab.getURL() != null) {
|
||||
byte[] thumbnail = BrowserDB.getThumbnailForUrl(getContentResolver(), tab.getURL());
|
||||
if (thumbnail != null)
|
||||
processThumbnail(tab, null, thumbnail);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int dw = Tabs.getThumbnailWidth();
|
||||
int dh = Tabs.getThumbnailHeight();
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, 0, 0, 0, 0, dw, dh, dw, dh, ScreenshotHandler.SCREENSHOT_THUMBNAIL, tab.getThumbnailBuffer()));
|
||||
}
|
||||
|
||||
void handleThumbnailData(Tab tab, ByteBuffer data) {
|
||||
if (shouldUpdateThumbnail(tab)) {
|
||||
Bitmap b = tab.getThumbnailBitmap();
|
||||
data.position(0);
|
||||
b.copyPixelsFromBuffer(data);
|
||||
processThumbnail(tab, b, null);
|
||||
}
|
||||
}
|
||||
|
||||
void processThumbnail(Tab thumbnailTab, Bitmap bitmap, byte[] compressed) {
|
||||
try {
|
||||
if (bitmap == null) {
|
||||
if (compressed == null) {
|
||||
Log.w(LOGTAG, "processThumbnail: one of bitmap or compressed must be non-null!");
|
||||
return;
|
||||
}
|
||||
bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length);
|
||||
}
|
||||
thumbnailTab.updateThumbnail(bitmap);
|
||||
} catch (OutOfMemoryError ome) {
|
||||
Log.w(LOGTAG, "decoding byte array ran out of memory", ome);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldUpdateThumbnail(Tab tab) {
|
||||
return (Tabs.getInstance().isSelectedTab(tab) || areTabsShown());
|
||||
}
|
||||
|
||||
public void hideFormAssistPopup() {
|
||||
if (mFormAssistPopup != null)
|
||||
mFormAssistPopup.hide();
|
||||
|
@ -1224,7 +1174,7 @@ abstract public class GeckoApp
|
|||
if (!TextUtils.equals(oldURL, tab.getURL()))
|
||||
return;
|
||||
|
||||
getAndProcessThumbnailForTab(tab);
|
||||
ThumbnailHelper.getInstance().getAndProcessThumbnailFor(tab);
|
||||
if (Tabs.getInstance().isSelectedTab(tab)) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createStartPaintListentingEvent(tab.getId()));
|
||||
ScreenshotHandler.screenshotWholePage(tab);
|
||||
|
|
|
@ -27,6 +27,10 @@ public class Gecko@VIEWTYPE@ extends @VIEWTYPE@ {
|
|||
return drawableState;
|
||||
}
|
||||
|
||||
public boolean isPrivateMode() {
|
||||
return mIsPrivate;
|
||||
}
|
||||
|
||||
public void setPrivateMode(boolean isPrivate) {
|
||||
if (mIsPrivate != isPrivate) {
|
||||
mIsPrivate = isPrivate;
|
||||
|
|
|
@ -60,6 +60,8 @@ public final class GeckoViewsFactory implements LayoutInflater.Factory {
|
|||
return new AwesomeBarTabs(context, attrs);
|
||||
else if (TextUtils.equals(viewName, "AwesomeBarTabs.Background"))
|
||||
return new AwesomeBarTabs.Background(context, attrs);
|
||||
else if (TextUtils.equals(viewName, "BackButton"))
|
||||
return new BackButton(context, attrs);
|
||||
else if (TextUtils.equals(viewName, "BrowserToolbarBackground"))
|
||||
return new BrowserToolbarBackground(context, attrs);
|
||||
else if (TextUtils.equals(viewName, "BrowserToolbar$RightEdge"))
|
||||
|
|
|
@ -51,6 +51,7 @@ FENNEC_JAVA_FILES = \
|
|||
awesomebar/AllPagesTab.java \
|
||||
awesomebar/BookmarksTab.java \
|
||||
awesomebar/HistoryTab.java \
|
||||
BackButton.java \
|
||||
BrowserApp.java \
|
||||
BrowserToolbar.java \
|
||||
BrowserToolbarBackground.java \
|
||||
|
@ -71,6 +72,7 @@ FENNEC_JAVA_FILES = \
|
|||
FlowLayout.java \
|
||||
FontSizePreference.java \
|
||||
FormAssistPopup.java \
|
||||
ForwardButton.java \
|
||||
GeckoAccessibility.java \
|
||||
GeckoApplication.java \
|
||||
GeckoApp.java \
|
||||
|
@ -135,6 +137,7 @@ FENNEC_JAVA_FILES = \
|
|||
Telemetry.java \
|
||||
TextSelection.java \
|
||||
TextSelectionHandle.java \
|
||||
ThumbnailHelper.java \
|
||||
WebAppAllocator.java \
|
||||
ZoomConstraints.java \
|
||||
gfx/BitmapUtils.java \
|
||||
|
@ -226,6 +229,7 @@ FENNEC_PP_XML_FILES = \
|
|||
res/color/awesome_bar_title_hint.xml \
|
||||
res/color/tabs_counter_color.xml \
|
||||
res/drawable/address_bar_bg.xml \
|
||||
res/drawable/address_bar_nav_button.xml \
|
||||
res/drawable/address_bar_url.xml \
|
||||
res/drawable/awesomebar_tabs_bg.xml \
|
||||
res/drawable/menu_level.xml \
|
||||
|
@ -538,10 +542,6 @@ RES_DRAWABLE_BASE = \
|
|||
res/drawable/tab_thumbnail_shadow.png \
|
||||
res/drawable/tabs_carat.png \
|
||||
res/drawable/tabs_carat_pb.png \
|
||||
res/drawable/address_bar_back_button.xml \
|
||||
res/drawable/address_bar_back_button_bg.xml \
|
||||
res/drawable/address_bar_back_button_pressed_bg.xml \
|
||||
res/drawable/address_bar_forward_button.xml \
|
||||
res/drawable/address_bar_texture_port.png \
|
||||
res/drawable/address_bar_texture_port_pb.png \
|
||||
res/drawable/address_bar_url_default.9.png \
|
||||
|
@ -896,8 +896,6 @@ RES_DRAWABLE_LARGE_MDPI_V11 = \
|
|||
res/drawable-large-mdpi-v11/address_bar_bg_private.xml \
|
||||
res/drawable-large-mdpi-v11/address_bar_texture_tablet.png \
|
||||
res/drawable-large-mdpi-v11/address_bar_texture_tablet_pb.png \
|
||||
res/drawable-large-mdpi-v11/address_bar_back_button_bg.png \
|
||||
res/drawable-large-mdpi-v11/address_bar_back_button_pressed_bg.png \
|
||||
res/drawable-large-mdpi-v11/address_bar_url_default.9.png \
|
||||
res/drawable-large-mdpi-v11/address_bar_url_default_pb.9.png \
|
||||
res/drawable-large-mdpi-v11/address_bar_url_pressed.9.png \
|
||||
|
@ -924,8 +922,6 @@ RES_DRAWABLE_LARGE_MDPI_V11 = \
|
|||
RES_DRAWABLE_LARGE_HDPI_V11 = \
|
||||
res/drawable-large-hdpi-v11/address_bar_texture_tablet.png \
|
||||
res/drawable-large-hdpi-v11/address_bar_texture_tablet_pb.png \
|
||||
res/drawable-large-hdpi-v11/address_bar_back_button_bg.png \
|
||||
res/drawable-large-hdpi-v11/address_bar_back_button_pressed_bg.png \
|
||||
res/drawable-large-hdpi-v11/address_bar_url_default.9.png \
|
||||
res/drawable-large-hdpi-v11/address_bar_url_default_pb.9.png \
|
||||
res/drawable-large-hdpi-v11/address_bar_url_pressed.9.png \
|
||||
|
@ -952,8 +948,6 @@ RES_DRAWABLE_LARGE_HDPI_V11 = \
|
|||
RES_DRAWABLE_LARGE_XHDPI_V11 = \
|
||||
res/drawable-large-xhdpi-v11/address_bar_texture_tablet.png \
|
||||
res/drawable-large-xhdpi-v11/address_bar_texture_tablet_pb.png \
|
||||
res/drawable-large-xhdpi-v11/address_bar_back_button_bg.png \
|
||||
res/drawable-large-xhdpi-v11/address_bar_back_button_pressed_bg.png \
|
||||
res/drawable-large-xhdpi-v11/address_bar_url_default.9.png \
|
||||
res/drawable-large-xhdpi-v11/address_bar_url_default_pb.9.png \
|
||||
res/drawable-large-xhdpi-v11/address_bar_url_pressed.9.png \
|
||||
|
|
|
@ -71,7 +71,7 @@ public class RemoteTabs extends LinearLayout
|
|||
public void hide() {
|
||||
}
|
||||
|
||||
void autoHidePanel() {
|
||||
private void autoHidePanel() {
|
||||
mTabsPanel.autoHidePanel();
|
||||
}
|
||||
|
||||
|
|
|
@ -367,7 +367,7 @@ public final class ScreenshotHandler implements Runnable {
|
|||
{
|
||||
Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab != null) {
|
||||
GeckoApp.mAppContext.handleThumbnailData(tab, data);
|
||||
ThumbnailHelper.getInstance().handleThumbnailData(tab, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,10 @@ public abstract class ShapedButton extends GeckoImageButton
|
|||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
mCanvasDelegate.draw(canvas, mPath, getWidth(), getHeight());
|
||||
if (mCanvasDelegate != null)
|
||||
mCanvasDelegate.draw(canvas, mPath, getWidth(), getHeight());
|
||||
else
|
||||
defaultDraw(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,7 +7,6 @@ package org.mozilla.gecko;
|
|||
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.gfx.Layer;
|
||||
import org.mozilla.gecko.mozglue.DirectBufferAllocator;
|
||||
import org.mozilla.gecko.util.GeckoAsyncTask;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
@ -25,7 +24,6 @@ import android.text.TextUtils;
|
|||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
@ -64,7 +62,6 @@ public class Tab {
|
|||
private ContentObserver mContentObserver;
|
||||
private int mCheckerboardColor = Color.WHITE;
|
||||
private int mState;
|
||||
private ByteBuffer mThumbnailBuffer;
|
||||
private Bitmap mThumbnailBitmap;
|
||||
private boolean mDesktopMode;
|
||||
private boolean mEnteringReaderMode;
|
||||
|
@ -155,33 +152,25 @@ public class Tab {
|
|||
return mThumbnail;
|
||||
}
|
||||
|
||||
synchronized public ByteBuffer getThumbnailBuffer() {
|
||||
int capacity = Tabs.getThumbnailWidth() * Tabs.getThumbnailHeight() * 2 /* 16 bpp */;
|
||||
if (mThumbnailBuffer != null && mThumbnailBuffer.capacity() == capacity)
|
||||
return mThumbnailBuffer;
|
||||
freeBuffer();
|
||||
mThumbnailBitmap = null;
|
||||
mThumbnailBuffer = DirectBufferAllocator.allocate(capacity);
|
||||
return mThumbnailBuffer;
|
||||
}
|
||||
|
||||
synchronized public Bitmap getThumbnailBitmap() {
|
||||
// Bug 787318 - Honeycomb has a bug with bitmap caching, we can't
|
||||
// reuse the bitmap there.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
|
||||
|| Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2) {
|
||||
if (mThumbnailBitmap != null)
|
||||
return mThumbnailBitmap;
|
||||
} else {
|
||||
if (mThumbnailBitmap != null)
|
||||
public Bitmap getThumbnailBitmap(int width, int height) {
|
||||
if (mThumbnailBitmap != null) {
|
||||
// Bug 787318 - Honeycomb has a bug with bitmap caching, we can't
|
||||
// reuse the bitmap there.
|
||||
boolean honeycomb = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
|
||||
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR2);
|
||||
boolean sizeChange = mThumbnailBitmap.getWidth() != width
|
||||
|| mThumbnailBitmap.getHeight() != height;
|
||||
if (honeycomb || sizeChange) {
|
||||
mThumbnailBitmap.recycle();
|
||||
mThumbnailBitmap = null;
|
||||
}
|
||||
}
|
||||
return mThumbnailBitmap = Bitmap.createBitmap(Tabs.getThumbnailWidth(), Tabs.getThumbnailHeight(), Bitmap.Config.RGB_565);
|
||||
}
|
||||
|
||||
synchronized void freeBuffer() {
|
||||
DirectBufferAllocator.free(mThumbnailBuffer);
|
||||
mThumbnailBuffer = null;
|
||||
if (mThumbnailBitmap == null) {
|
||||
mThumbnailBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
|
||||
}
|
||||
|
||||
return mThumbnailBitmap;
|
||||
}
|
||||
|
||||
public void updateThumbnail(final Bitmap b) {
|
||||
|
|
|
@ -51,8 +51,6 @@ public class Tabs implements GeckoEventListener {
|
|||
|
||||
private GeckoApp mActivity;
|
||||
|
||||
static private int sThumbnailWidth = -1;
|
||||
|
||||
private Tabs() {
|
||||
registerEventListener("SessionHistory:New");
|
||||
registerEventListener("SessionHistory:Back");
|
||||
|
@ -69,24 +67,6 @@ public class Tabs implements GeckoEventListener {
|
|||
registerEventListener("Reader:Share");
|
||||
}
|
||||
|
||||
static public void setThumbnailWidth(int val) {
|
||||
// Round this to the next highest power of two
|
||||
sThumbnailWidth = (int)(Math.pow( 2, Math.ceil(Math.log(val)/Math.log(2) )));
|
||||
}
|
||||
|
||||
static public int getThumbnailWidth() {
|
||||
if (sThumbnailWidth < 0) {
|
||||
sThumbnailWidth = (int) (GeckoApp.mAppContext.getResources().getDimension(R.dimen.tab_thumbnail_width));
|
||||
}
|
||||
return sThumbnailWidth & ~0x1;
|
||||
}
|
||||
|
||||
static public int getThumbnailHeight() {
|
||||
return Math.round(getThumbnailWidth() * getThumbnailAspectRatio()) & ~0x1;
|
||||
}
|
||||
|
||||
static public float getThumbnailAspectRatio() { return 0.714f; }
|
||||
|
||||
public void attachToActivity(GeckoApp activity) {
|
||||
mActivity = activity;
|
||||
}
|
||||
|
@ -114,7 +94,6 @@ public class Tabs implements GeckoEventListener {
|
|||
Tab tab = getTab(id);
|
||||
mOrder.remove(tab);
|
||||
mTabs.remove(id);
|
||||
tab.freeBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,12 +309,13 @@ public class Tabs implements GeckoEventListener {
|
|||
}
|
||||
|
||||
public void refreshThumbnails() {
|
||||
final ThumbnailHelper helper = ThumbnailHelper.getInstance();
|
||||
Iterator<Tab> iterator = mTabs.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final Tab tab = iterator.next();
|
||||
GeckoAppShell.getHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
mActivity.getAndProcessThumbnailForTab(tab);
|
||||
helper.getAndProcessThumbnailFor(tab);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ public class TabsTray extends LinearLayout
|
|||
mTabsAdapter.clear();
|
||||
}
|
||||
|
||||
void autoHidePanel() {
|
||||
private void autoHidePanel() {
|
||||
mTabsPanel.autoHidePanel();
|
||||
}
|
||||
|
||||
|
@ -290,6 +290,9 @@ public class TabsTray extends LinearLayout
|
|||
}
|
||||
});
|
||||
|
||||
if (mTabsAdapter.getCount() == 1)
|
||||
autoHidePanel();
|
||||
|
||||
animator.start();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.gfx.IntSize;
|
||||
import org.mozilla.gecko.mozglue.DirectBufferAllocator;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.util.Log;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Helper class to generate thumbnails for tabs.
|
||||
* Internally, a queue of pending thumbnails is maintained in mPendingThumbnails.
|
||||
* The head of the queue is the thumbnail that is currently being processed; upon
|
||||
* completion of the current thumbnail the next one is automatically processed.
|
||||
* Changes to the thumbnail width are stashed in mPendingWidth and the change is
|
||||
* applied between thumbnail processing. This allows a single thumbnail buffer to
|
||||
* be used for all thumbnails.
|
||||
*/
|
||||
final class ThumbnailHelper {
|
||||
private static final String LOGTAG = "GeckoThumbnailHelper";
|
||||
|
||||
public static final float THUMBNAIL_ASPECT_RATIO = 0.714f; // this is a 5:7 ratio (as per UX decision)
|
||||
|
||||
// static singleton stuff
|
||||
|
||||
private static ThumbnailHelper sInstance;
|
||||
|
||||
public static synchronized ThumbnailHelper getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new ThumbnailHelper();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
// instance stuff
|
||||
|
||||
private final LinkedList<Tab> mPendingThumbnails; // synchronized access only
|
||||
private AtomicInteger mPendingWidth;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
private ByteBuffer mBuffer;
|
||||
|
||||
private ThumbnailHelper() {
|
||||
mPendingThumbnails = new LinkedList<Tab>();
|
||||
mPendingWidth = new AtomicInteger((int)GeckoApp.mAppContext.getResources().getDimension(R.dimen.tab_thumbnail_width));
|
||||
mWidth = -1;
|
||||
mHeight = -1;
|
||||
}
|
||||
|
||||
public void getAndProcessThumbnailFor(Tab tab) {
|
||||
if ("about:home".equals(tab.getURL())) {
|
||||
tab.updateThumbnail(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab.getState() == Tab.STATE_DELAYED) {
|
||||
String url = tab.getURL();
|
||||
if (url != null) {
|
||||
byte[] thumbnail = BrowserDB.getThumbnailForUrl(GeckoApp.mAppContext.getContentResolver(), url);
|
||||
if (thumbnail != null) {
|
||||
setTabThumbnail(tab, null, thumbnail);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mPendingThumbnails) {
|
||||
if (mPendingThumbnails.lastIndexOf(tab) > 0) {
|
||||
// This tab is already in the queue, so don't add it again.
|
||||
// Note that if this tab is only at the *head* of the queue,
|
||||
// (i.e. mPendingThumbnails.lastIndexOf(tab) == 0) then we do
|
||||
// add it again because it may have already been thumbnailed
|
||||
// and now we need to do it again.
|
||||
return;
|
||||
}
|
||||
|
||||
mPendingThumbnails.add(tab);
|
||||
if (mPendingThumbnails.size() > 1) {
|
||||
// Some thumbnail was already being processed, so wait
|
||||
// for that to be done.
|
||||
return;
|
||||
}
|
||||
}
|
||||
requestThumbnailFor(tab);
|
||||
}
|
||||
|
||||
public void setThumbnailWidth(int width) {
|
||||
mPendingWidth.set(IntSize.nextPowerOfTwo(width));
|
||||
}
|
||||
|
||||
private void updateThumbnailSize() {
|
||||
// Apply any pending width updates
|
||||
mWidth = mPendingWidth.get();
|
||||
|
||||
mWidth &= ~0x1; // Ensure the width is always an even number (bug 776906)
|
||||
mHeight = Math.round(mWidth * THUMBNAIL_ASPECT_RATIO);
|
||||
|
||||
int capacity = mWidth * mHeight * 2; // Multiply by 2 for 16bpp
|
||||
if (mBuffer == null || mBuffer.capacity() != capacity) {
|
||||
if (mBuffer != null) {
|
||||
mBuffer = DirectBufferAllocator.free(mBuffer);
|
||||
}
|
||||
try {
|
||||
mBuffer = DirectBufferAllocator.allocate(capacity);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
Log.w(LOGTAG, "Unable to allocate thumbnail buffer of capacity " + capacity);
|
||||
// At this point mBuffer will be pointing to null, so we are in a sane state.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void requestThumbnailFor(Tab tab) {
|
||||
updateThumbnailSize();
|
||||
|
||||
if (mBuffer == null) {
|
||||
// Buffer allocation may have failed. In this case we can't send the
|
||||
// event requesting the screenshot which means we won't get back a response
|
||||
// and so our queue will grow unboundedly. Handle this scenario by clearing
|
||||
// the queue (no point trying more thumbnailing right now since we're likely
|
||||
// low on memory). We will try again normally on the next call to
|
||||
// getAndProcessThumbnailFor which will hopefully be when we have more free memory.
|
||||
synchronized (mPendingThumbnails) {
|
||||
mPendingThumbnails.clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GeckoEvent e = GeckoEvent.createScreenshotEvent(
|
||||
tab.getId(),
|
||||
0, 0, 0, 0, // sx, sy, sw, sh
|
||||
0, 0, mWidth, mHeight, // dx, dy, dw, dh
|
||||
mWidth, mHeight, // bw, bh
|
||||
ScreenshotHandler.SCREENSHOT_THUMBNAIL,
|
||||
mBuffer);
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
|
||||
/* This method is invoked by Gecko once the thumbnail data is ready. */
|
||||
void handleThumbnailData(Tab tab, ByteBuffer data) {
|
||||
if (data != mBuffer) {
|
||||
// This should never happen, but log it and recover gracefully
|
||||
Log.e(LOGTAG, "handleThumbnailData called with an unexpected ByteBuffer!");
|
||||
}
|
||||
|
||||
if (shouldUpdateThumbnail(tab)) {
|
||||
processThumbnailData(tab, data);
|
||||
}
|
||||
Tab nextTab = null;
|
||||
synchronized (mPendingThumbnails) {
|
||||
if (tab != mPendingThumbnails.peek()) {
|
||||
Log.e(LOGTAG, "handleThumbnailData called with unexpected tab's data!");
|
||||
// This should never happen, but recover gracefully by processing the
|
||||
// unexpected tab that we found in the queue
|
||||
} else {
|
||||
mPendingThumbnails.remove();
|
||||
}
|
||||
nextTab = mPendingThumbnails.peek();
|
||||
}
|
||||
if (nextTab != null) {
|
||||
requestThumbnailFor(nextTab);
|
||||
}
|
||||
}
|
||||
|
||||
private void processThumbnailData(Tab tab, ByteBuffer data) {
|
||||
Bitmap b = tab.getThumbnailBitmap(mWidth, mHeight);
|
||||
data.position(0);
|
||||
b.copyPixelsFromBuffer(data);
|
||||
setTabThumbnail(tab, b, null);
|
||||
}
|
||||
|
||||
private void setTabThumbnail(Tab tab, Bitmap bitmap, byte[] compressed) {
|
||||
try {
|
||||
if (bitmap == null) {
|
||||
if (compressed == null) {
|
||||
Log.w(LOGTAG, "setTabThumbnail: one of bitmap or compressed must be non-null!");
|
||||
return;
|
||||
}
|
||||
bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length);
|
||||
}
|
||||
tab.updateThumbnail(bitmap);
|
||||
} catch (OutOfMemoryError ome) {
|
||||
Log.w(LOGTAG, "setTabThumbnail: decoding byte array of length " + compressed.length + " ran out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldUpdateThumbnail(Tab tab) {
|
||||
return (Tabs.getInstance().isSelectedTab(tab) || GeckoApp.mAppContext.areTabsShown());
|
||||
}
|
||||
}
|
До Ширина: | Высота: | Размер: 7.7 KiB |
До Ширина: | Высота: | Размер: 3.9 KiB |
До Ширина: | Высота: | Размер: 4.6 KiB |
До Ширина: | Высота: | Размер: 2.8 KiB |
До Ширина: | Высота: | Размер: 1.9 KiB После Ширина: | Высота: | Размер: 1.7 KiB |
До Ширина: | Высота: | Размер: 12 KiB |
До Ширина: | Высота: | Размер: 5.2 KiB |
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_pressed="true" android:drawable="@drawable/address_bar_back_button_pressed_bg"/>
|
||||
<item android:drawable="@drawable/address_bar_back_button_bg"/>
|
||||
|
||||
</selector>
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
|
||||
</shape>
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
|
||||
</shape>
|
|
@ -1,11 +1,19 @@
|
|||
#filter substitution
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res/@ANDROID_PACKAGE_NAME@">
|
||||
|
||||
<!-- pressed state -->
|
||||
<item android:state_pressed="true" android:drawable="@drawable/highlight"/>
|
||||
<item android:drawable="@drawable/address_bar_bg"/>
|
||||
|
||||
<!-- private browsing mode -->
|
||||
<item gecko:state_private="true" android:drawable="@drawable/address_bar_bg_private"/>
|
||||
|
||||
<!-- normal mode -->
|
||||
<item android:drawable="@drawable/address_bar_bg_normal"/>
|
||||
|
||||
</selector>
|
|
@ -69,26 +69,26 @@
|
|||
|
||||
</Gecko.RelativeLayout>
|
||||
|
||||
<ImageButton android:id="@+id/forward"
|
||||
style="@style/AddressBar.ImageButton"
|
||||
android:layout_width="64dip"
|
||||
android:layout_height="40dip"
|
||||
android:layout_marginLeft="21dp"
|
||||
android:paddingLeft="21dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_menu_forward"
|
||||
android:contentDescription="@string/forward"
|
||||
android:background="@drawable/address_bar_forward_button"/>
|
||||
<org.mozilla.gecko.ForwardButton android:id="@+id/forward"
|
||||
style="@style/AddressBar.ImageButton"
|
||||
android:layout_width="64dip"
|
||||
android:layout_height="42dip"
|
||||
android:layout_marginLeft="21dp"
|
||||
android:paddingLeft="21dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_menu_forward"
|
||||
android:contentDescription="@string/forward"
|
||||
android:background="@drawable/address_bar_nav_button"/>
|
||||
|
||||
<ImageButton android:id="@+id/back"
|
||||
style="@style/AddressBar.ImageButton"
|
||||
android:layout_width="50dip"
|
||||
android:layout_height="50dip"
|
||||
android:layout_marginLeft="1dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_menu_back"
|
||||
android:contentDescription="@string/back"
|
||||
android:background="@drawable/address_bar_back_button"/>
|
||||
<org.mozilla.gecko.BackButton android:id="@+id/back"
|
||||
style="@style/AddressBar.ImageButton"
|
||||
android:layout_width="50dip"
|
||||
android:layout_height="50dip"
|
||||
android:layout_marginLeft="1dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_menu_back"
|
||||
android:contentDescription="@string/back"
|
||||
android:background="@drawable/address_bar_nav_button"/>
|
||||
|
||||
<LinearLayout style="@style/AddressBar.Button"
|
||||
android:layout_marginLeft="90dp"
|
||||
|
|
|
@ -104,25 +104,25 @@
|
|||
|
||||
</Gecko.RelativeLayout>
|
||||
|
||||
<ImageButton android:id="@+id/forward"
|
||||
style="@style/AddressBar.ImageButton"
|
||||
android:layout_width="64dip"
|
||||
android:layout_height="40dip"
|
||||
android:layout_marginLeft="22dp"
|
||||
android:paddingLeft="22dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_menu_forward"
|
||||
android:contentDescription="@string/forward"
|
||||
android:background="@drawable/address_bar_forward_button"/>
|
||||
<org.mozilla.gecko.ForwardButton android:id="@+id/forward"
|
||||
style="@style/AddressBar.ImageButton"
|
||||
android:layout_width="64dip"
|
||||
android:layout_height="42dip"
|
||||
android:layout_marginLeft="22dp"
|
||||
android:paddingLeft="22dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_menu_forward"
|
||||
android:contentDescription="@string/forward"
|
||||
android:background="@drawable/address_bar_nav_button"/>
|
||||
|
||||
<ImageButton android:id="@+id/back"
|
||||
style="@style/AddressBar.ImageButton"
|
||||
android:layout_width="50dip"
|
||||
android:layout_height="50dip"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_menu_back"
|
||||
android:contentDescription="@string/back"
|
||||
android:background="@drawable/address_bar_back_button"/>
|
||||
<org.mozilla.gecko.BackButton android:id="@+id/back"
|
||||
style="@style/AddressBar.ImageButton"
|
||||
android:layout_width="50dip"
|
||||
android:layout_height="50dip"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_menu_back"
|
||||
android:contentDescription="@string/back"
|
||||
android:background="@drawable/address_bar_nav_button"/>
|
||||
|
||||
<LinearLayout style="@style/AddressBar.Button"
|
||||
android:layout_marginLeft="84dp"
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
<dimen name="menu_item_row_width">240dp</dimen>
|
||||
<dimen name="menu_popup_width">256dp</dimen>
|
||||
<dimen name="menu_popup_offset">8dp</dimen>
|
||||
<dimen name="nav_button_border_width">1dp</dimen>
|
||||
<dimen name="prompt_service_group_padding_size">32dp</dimen>
|
||||
<dimen name="prompt_service_icon_size">72dp</dimen>
|
||||
<dimen name="prompt_service_icon_text_padding">10dp</dimen>
|
||||
|
|
|
@ -330,11 +330,11 @@ waitFor(
|
|||
|
||||
def add_prefs_to_profile(self, prefs=None):
|
||||
if not prefs:
|
||||
prefs = ["user_pref('marionette.loadearly', true);"]
|
||||
prefs = ['user_pref("marionette.loadearly", true);']
|
||||
local_user_js = tempfile.mktemp(prefix='localuserjs')
|
||||
self.dm.getFile(self.remote_user_js, local_user_js)
|
||||
with open(local_user_js, 'a') as f:
|
||||
f.write('/n'.join(prefs))
|
||||
f.write('%s\n' % '\n'.join(prefs))
|
||||
self.dm.pushFile(local_user_js, self.remote_user_js)
|
||||
|
||||
def start(self):
|
||||
|
@ -379,10 +379,6 @@ waitFor(
|
|||
self._run_adb(['shell', 'setprop', 'net.dns1', '10.0.2.3'])
|
||||
|
||||
def setup(self, marionette, gecko_path=None, load_early=False):
|
||||
# Wait for the system-message-listener-ready event, otherwise
|
||||
# Bad Things happen.
|
||||
self.wait_for_system_message(marionette)
|
||||
|
||||
if gecko_path:
|
||||
if load_early:
|
||||
# Inject prefs into the profile now, since we have to restart
|
||||
|
@ -393,9 +389,7 @@ waitFor(
|
|||
self.add_prefs_to_profile()
|
||||
self.restart_b2g()
|
||||
|
||||
#if load_early:
|
||||
# Temporarily enable this always until part 2 of bug 815807 lands
|
||||
if True:
|
||||
if load_early:
|
||||
# If we're loading early, we have to wait for the
|
||||
# system-message-listener-ready event again after restarting B2G.
|
||||
# If we're not loading early, we skip this because Marionette
|
||||
|
|
|
@ -8,6 +8,7 @@ const MARIONETTE_CONTRACTID = "@mozilla.org/marionette;1";
|
|||
const MARIONETTE_CID = Components.ID("{786a1369-dca5-4adc-8486-33d23c88010a}");
|
||||
const DEBUGGER_ENABLED_PREF = 'devtools.debugger.remote-enabled';
|
||||
const MARIONETTE_ENABLED_PREF = 'marionette.defaultPrefs.enabled';
|
||||
const MARIONETTE_LOADEARLY_PREF = 'marionette.loadearly';
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
@ -38,22 +39,36 @@ MarionetteComponent.prototype = {
|
|||
let observerService = Services.obs;
|
||||
switch (aTopic) {
|
||||
case "profile-after-change":
|
||||
let appName = Services.appinfo.name;
|
||||
let enabled = false;
|
||||
let loadearly = appName == 'B2G' ? false : true;
|
||||
try {
|
||||
enabled = Services.prefs.getBoolPref(MARIONETTE_ENABLED_PREF);
|
||||
loadearly = Services.prefs.getBoolPref(MARIONETTE_LOADEARLY_PREF);
|
||||
} catch(e) {}
|
||||
if (enabled) {
|
||||
this.logger.info("marionette enabled");
|
||||
this.logger.info("marionette enabled, loadearly: " + loadearly);
|
||||
|
||||
//add observers
|
||||
observerService.addObserver(this, "final-ui-startup", false);
|
||||
if (loadearly) {
|
||||
observerService.addObserver(this, "final-ui-startup", false);
|
||||
}
|
||||
else {
|
||||
observerService.addObserver(this, "system-message-listener-ready", false);
|
||||
}
|
||||
observerService.addObserver(this, "xpcom-shutdown", false);
|
||||
}
|
||||
else {
|
||||
this.logger.info("marionette not enabled");
|
||||
}
|
||||
break;
|
||||
case "system-message-listener-ready":
|
||||
this.logger.info("marionette initializing at system-message-listener-ready");
|
||||
observerService.removeObserver(this, "system-message-listener-ready");
|
||||
this.init();
|
||||
break;
|
||||
case "final-ui-startup":
|
||||
this.logger.info("marionette initializing at final-ui-startup");
|
||||
observerService.removeObserver(this, "final-ui-startup");
|
||||
this.init();
|
||||
break;
|
||||
|
@ -75,7 +90,7 @@ MarionetteComponent.prototype = {
|
|||
port = 2828;
|
||||
}
|
||||
try {
|
||||
Cu.import('resource:///modules/devtools/dbg-server.jsm');
|
||||
Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
|
||||
DebuggerServer.addActors('chrome://marionette/content/marionette-actors.js');
|
||||
// This pref is required for the remote debugger to open a socket,
|
||||
// so force it to true. See bug 761252.
|
||||
|
@ -87,8 +102,9 @@ MarionetteComponent.prototype = {
|
|||
Services.prefs.setBoolPref(DEBUGGER_ENABLED_PREF, true);
|
||||
// Always allow remote connections.
|
||||
DebuggerServer.initTransport(function () { return true; });
|
||||
DebuggerServer.openListener(port, true);
|
||||
DebuggerServer.openListener(port);
|
||||
Services.prefs.setBoolPref(DEBUGGER_ENABLED_PREF, original);
|
||||
this.logger.info("marionette listener opened");
|
||||
}
|
||||
catch(e) {
|
||||
this.logger.error('exception: ' + e.name + ', ' + e.message);
|
||||
|
|
|
@ -429,6 +429,7 @@ user_pref("dom.ipc.tabs.disabled", false);
|
|||
user_pref("dom.ipc.browser_frames.oop_by_default", false);
|
||||
user_pref("dom.mozBrowserFramesWhitelist","app://test-container.gaiamobile.org,http://mochi.test:8888");
|
||||
user_pref("network.dns.localDomains","app://test-container.gaiamobile.org");
|
||||
user_pref("marionette.loadearly", true);
|
||||
""")
|
||||
f.close()
|
||||
|
||||
|
|
|
@ -99,6 +99,7 @@ skip-if = os == "android"
|
|||
[include:browser/components/privatebrowsing/test/unit/xpcshell.ini]
|
||||
[include:browser/components/shell/test/unit/xpcshell.ini]
|
||||
[include:browser/devtools/shared/test/unit/xpcshell.ini]
|
||||
[include:browser/modules/test/unit/xpcshell.ini]
|
||||
[include:extensions/spellcheck/hunspell/tests/unit/xpcshell.ini]
|
||||
[include:toolkit/components/search/tests/xpcshell/xpcshell.ini]
|
||||
[include:toolkit/components/osfile/tests/xpcshell/xpcshell.ini]
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "nsAutoPtr.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "sampler.h"
|
||||
|
||||
#include "prprf.h"
|
||||
#include "nsCRT.h"
|
||||
|
@ -387,6 +388,7 @@ nsAppStartup::Quit(uint32_t aMode)
|
|||
if (mShuttingDown)
|
||||
return NS_OK;
|
||||
|
||||
SAMPLE_MARKER("Shutdown start");
|
||||
RecordShutdownStartTimeStamp();
|
||||
|
||||
// If we're considering quitting, we will only do so if:
|
||||
|
|
|
@ -31,6 +31,7 @@ EXPORTS_mozilla = \
|
|||
|
||||
XPIDLSRCS = \
|
||||
nsITelemetry.idl \
|
||||
nsITelemetryPing.idl \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_PP_COMPONENTS = \
|
||||
|
|
|
@ -676,7 +676,7 @@ mHangReportsMutex("Telemetry::mHangReportsMutex")
|
|||
{
|
||||
// A whitelist to prevent Telemetry reporting on Addon & Thunderbird DBs
|
||||
const char *trackedDBs[] = {
|
||||
"addons.sqlite", "chromeappsstore.sqlite", "content-prefs.sqlite",
|
||||
"addons.sqlite", "content-prefs.sqlite",
|
||||
"cookies.sqlite", "downloads.sqlite", "extensions.sqlite",
|
||||
"formhistory.sqlite", "index.sqlite", "permissions.sqlite", "places.sqlite",
|
||||
"search.sqlite", "signons.sqlite", "urlclassifier3.sqlite",
|
||||
|
|
|
@ -491,9 +491,8 @@ TelemetryPing.prototype = {
|
|||
this._slowSQLStartup = Telemetry.slowSQL;
|
||||
},
|
||||
|
||||
getCurrentSessionPayloadAndSlug: function getCurrentSessionPayloadAndSlug(reason) {
|
||||
getCurrentSessionPayload: function getCurrentSessionPayload(reason) {
|
||||
// use a deterministic url for testing.
|
||||
let isTestPing = (reason == "test-ping");
|
||||
let payloadObj = {
|
||||
ver: PAYLOAD_VERSION,
|
||||
simpleMeasurements: getSimpleMeasurements(),
|
||||
|
@ -520,8 +519,15 @@ TelemetryPing.prototype = {
|
|||
payloadObj.simpleMeasurements.savedPings = this._pingsLoaded;
|
||||
}
|
||||
|
||||
let slug = (isTestPing ? reason : this._uuid);
|
||||
payloadObj.info = this.getMetadata(reason);
|
||||
|
||||
return payloadObj;
|
||||
},
|
||||
|
||||
getCurrentSessionPayloadAndSlug: function getCurrentSessionPayloadAndSlug(reason) {
|
||||
let isTestPing = (reason == "test-ping");
|
||||
let payloadObj = this.getCurrentSessionPayload(reason);
|
||||
let slug = (isTestPing ? reason : this._uuid);
|
||||
return { slug: slug, payload: JSON.stringify(payloadObj) };
|
||||
},
|
||||
|
||||
|
@ -924,6 +930,15 @@ TelemetryPing.prototype = {
|
|||
Services.obs.removeObserver(this, "quit-application-granted");
|
||||
},
|
||||
|
||||
getPayload: function getPayload() {
|
||||
// This function returns the current Telemetry payload to the caller.
|
||||
// We only gather startup info once.
|
||||
if (Object.keys(this._slowSQLStartup).length == 0)
|
||||
this.gatherStartupInformation();
|
||||
this.gatherMemory();
|
||||
return this.getCurrentSessionPayload("gather-payload");
|
||||
},
|
||||
|
||||
/**
|
||||
* This observer drives telemetry.
|
||||
*/
|
||||
|
@ -992,16 +1007,6 @@ TelemetryPing.prototype = {
|
|||
this._isIdleObserver = true;
|
||||
}).bind(this), Ci.nsIThread.DISPATCH_NORMAL);
|
||||
break;
|
||||
case "get-payload":
|
||||
// This handler returns the current Telemetry payload to the caller.
|
||||
// We only gather startup info once.
|
||||
if (Object.keys(this._slowSQLStartup).length == 0)
|
||||
this.gatherStartupInformation();
|
||||
this.gatherMemory();
|
||||
let data = this.getCurrentSessionPayloadAndSlug("gather-payload");
|
||||
|
||||
aSubject.QueryInterface(Ci.nsISupportsString).data = data.payload;
|
||||
break;
|
||||
case "test-save-histograms":
|
||||
this.saveHistograms(aSubject.QueryInterface(Ci.nsIFile), aData != "async");
|
||||
break;
|
||||
|
@ -1037,7 +1042,7 @@ TelemetryPing.prototype = {
|
|||
},
|
||||
|
||||
classID: Components.ID("{55d6a5fa-130e-4ee6-a158-0133af3b86ba}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelemetryPing]),
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelemetryPing]);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* 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 "nsIObserver.idl"
|
||||
|
||||
[scriptable, uuid(077ee790-3a9d-11e2-81c1-0800200c9a66)]
|
||||
interface nsITelemetryPing : nsIObserver {
|
||||
jsval getPayload();
|
||||
};
|
|
@ -44,7 +44,7 @@ var httpserver = new HttpServer();
|
|||
var gFinished = false;
|
||||
|
||||
function telemetry_ping () {
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
TelemetryPing.observe(null, "test-gather-startup", null);
|
||||
TelemetryPing.observe(null, "test-enable-load-save-notifications", null);
|
||||
TelemetryPing.observe(null, "test-ping", SERVER);
|
||||
|
@ -99,7 +99,7 @@ function telemetryObserver(aSubject, aTopic, aData) {
|
|||
let histogramsFile = getSavedHistogramsFile("saved-histograms.dat");
|
||||
setupTestData();
|
||||
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
TelemetryPing.observe(histogramsFile, "test-save-histograms", null);
|
||||
TelemetryPing.observe(histogramsFile, "test-load-histograms", null);
|
||||
telemetry_ping();
|
||||
|
@ -269,7 +269,7 @@ function runAsyncTestObserver(aSubject, aTopic, aData) {
|
|||
httpserver.registerPathHandler(PATH, checkHistogramsAsync);
|
||||
let histogramsFile = getSavedHistogramsFile("saved-histograms2.dat");
|
||||
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
Services.obs.addObserver(function(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(arguments.callee, aTopic);
|
||||
|
||||
|
@ -306,14 +306,14 @@ function runInvalidJSONTest() {
|
|||
writeStringToFile(histogramsFile, "this.is.invalid.JSON");
|
||||
do_check_true(histogramsFile.exists());
|
||||
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
TelemetryPing.observe(histogramsFile, "test-load-histograms", null);
|
||||
do_check_false(histogramsFile.exists());
|
||||
}
|
||||
|
||||
function runOldPingFileTest() {
|
||||
let histogramsFile = getSavedHistogramsFile("old-histograms.dat");
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
TelemetryPing.observe(histogramsFile, "test-save-histograms", null);
|
||||
do_check_true(histogramsFile.exists());
|
||||
|
||||
|
|
|
@ -14,6 +14,6 @@ function run_test() {
|
|||
}, "gather-telemetry", false);
|
||||
|
||||
Components.classes["@mozilla.org/base/telemetry-ping;1"]
|
||||
.getService(Components.interfaces.nsIObserver)
|
||||
.getService(Components.interfaces.nsITelemetryPing)
|
||||
.observe(null, "idle-daily", null);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ const bundle = Services.strings.createBundle(
|
|||
const brandBundle = Services.strings.createBundle(
|
||||
"chrome://branding/locale/brand.properties");
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].
|
||||
getService(Ci.nsIObserver);
|
||||
getService(Ci.nsITelemetryPing);
|
||||
|
||||
// Maximum height of a histogram bar (in em)
|
||||
const MAX_BAR_HEIGHT = 18;
|
||||
|
@ -685,14 +685,7 @@ function onLoad() {
|
|||
}
|
||||
|
||||
// Get the Telemetry Ping payload
|
||||
let pingData = Cc['@mozilla.org/supports-string;1'].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
TelemetryPing.observe(pingData, "get-payload", "");
|
||||
let ping = {};
|
||||
try {
|
||||
ping = JSON.parse(pingData.data);
|
||||
} catch (e) {
|
||||
}
|
||||
let ping = TelemetryPing.getPayload();
|
||||
|
||||
// Show simple measurements
|
||||
if (Object.keys(ping.simpleMeasurements).length) {
|
||||
|
|
|
@ -1802,7 +1802,7 @@ var XPIProvider = {
|
|||
}
|
||||
catch (e) { }
|
||||
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
|
||||
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
|
||||
TelemetryPing.observe(null, "Add-ons", data);
|
||||
},
|
||||
|
||||
|
|
|
@ -1117,6 +1117,7 @@ ScopedXPCOMStartup::~ScopedXPCOMStartup()
|
|||
appStartup->DestroyHiddenWindow();
|
||||
|
||||
gDirServiceProvider->DoShutdown();
|
||||
SAMPLE_MARKER("Shutdown early");
|
||||
|
||||
WriteConsoleLog();
|
||||
|
||||
|
@ -3937,6 +3938,7 @@ XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
|
|||
MOZ_gdk_display_close(mGdkDisplay);
|
||||
#endif
|
||||
|
||||
SAMPLER_SHUTDOWN();
|
||||
rv = LaunchChild(mNativeApp, true);
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
|
@ -3959,6 +3961,8 @@ XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
|
|||
|
||||
XRE_DeinitCommandLine();
|
||||
|
||||
SAMPLER_SHUTDOWN();
|
||||
|
||||
return NS_FAILED(rv) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
@ -4070,6 +4074,7 @@ XRE_mainMetro(int argc, char* argv[], const nsXREAppData* aAppData)
|
|||
// thread that called XRE_metroStartup.
|
||||
NS_ASSERTION(!xreMainPtr->mScopedXPCom,
|
||||
"XPCOM Shutdown hasn't occured, and we are exiting.");
|
||||
SAMPLER_SHUTDOWN();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#ifndef JSAOBJECTBUILDER_H
|
||||
#define JSAOBJECTBUILDER_H
|
||||
|
||||
class JSCustomObject;
|
||||
class JSCustomArray;
|
||||
class nsAString;
|
||||
|
||||
class JSAObjectBuilder
|
||||
{
|
||||
public:
|
||||
virtual ~JSAObjectBuilder() = 0;
|
||||
|
||||
virtual void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue) = 0;
|
||||
virtual void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue) = 0;
|
||||
virtual void DefineProperty(JSCustomObject *aObject, const char *name, int value) = 0;
|
||||
virtual void DefineProperty(JSCustomObject *aObject, const char *name, double value) = 0;
|
||||
virtual void DefineProperty(JSCustomObject *aObject, const char *name, const char *value) = 0;
|
||||
virtual void ArrayPush(JSCustomArray *aArray, int value) = 0;
|
||||
virtual void ArrayPush(JSCustomArray *aArray, const char *value) = 0;
|
||||
virtual void ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject) = 0;
|
||||
virtual JSCustomArray *CreateArray() = 0;
|
||||
virtual JSCustomObject *CreateObject() = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,311 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#include "JSCustomObjectBuilder.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsUTF8Utils.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
// These are owned and deleted by JSCustomObject
|
||||
struct PropertyValue {
|
||||
virtual ~PropertyValue() {}
|
||||
virtual void SendToStream(std::ostream& stream) = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct finalizer_impl
|
||||
{
|
||||
static void run(T) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct finalizer_impl<T*>
|
||||
{
|
||||
static void run(T* p) {
|
||||
delete p;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct finalizer_impl<char *>
|
||||
{
|
||||
static void run(char* p) {
|
||||
free(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class TemplatePropertyValue : public PropertyValue {
|
||||
public:
|
||||
TemplatePropertyValue(T aValue)
|
||||
: mValue(aValue)
|
||||
{}
|
||||
|
||||
~TemplatePropertyValue() {
|
||||
finalizer_impl<T>::run(mValue);
|
||||
}
|
||||
|
||||
virtual void SendToStream(std::ostream& stream);
|
||||
private:
|
||||
T mValue;
|
||||
};
|
||||
|
||||
// Escape a UTF8 string to a stream. When an illegal encoding
|
||||
// is found it will insert "INVALID" and the function will return.
|
||||
void EscapeToStream(std::ostream& stream, const char* str) {
|
||||
stream << "\"";
|
||||
|
||||
size_t len = strlen(str);
|
||||
const char* end = &str[len];
|
||||
while (str < end) {
|
||||
bool err;
|
||||
const char* utf8CharStart = str;
|
||||
uint32_t ucs4Char = UTF8CharEnumerator::NextChar(&str, end, &err);
|
||||
|
||||
if (err) {
|
||||
// Encoding error
|
||||
stream << "INVALID\"";
|
||||
return;
|
||||
}
|
||||
|
||||
// See http://www.ietf.org/rfc/rfc4627.txt?number=4627
|
||||
// characters that must be escaped: quotation mark,
|
||||
// reverse solidus, and the control characters
|
||||
// (U+0000 through U+001F).
|
||||
if (ucs4Char == '\"') {
|
||||
stream << "\\\"";
|
||||
} else if (ucs4Char == '\\') {
|
||||
stream << "\\\\";
|
||||
} else if (ucs4Char > 0xFF) {
|
||||
PRUnichar chr[2];
|
||||
ConvertUTF8toUTF16 encoder(chr);
|
||||
encoder.write(utf8CharStart, str-utf8CharStart);
|
||||
char escChar[13];
|
||||
snprintf(escChar, mozilla::ArrayLength(escChar), "\\u%04X\\u%04X", chr[0], chr[1]);
|
||||
stream << escChar;
|
||||
} else if (ucs4Char < 0x1F || ucs4Char > 0xFF) {
|
||||
char escChar[7];
|
||||
snprintf(escChar, mozilla::ArrayLength(escChar), "\\u%04X", ucs4Char);
|
||||
stream << escChar;
|
||||
} else {
|
||||
stream << char(ucs4Char);
|
||||
}
|
||||
}
|
||||
stream << "\"";
|
||||
}
|
||||
|
||||
class JSCustomObject {
|
||||
public:
|
||||
JSCustomObject() {
|
||||
mProperties.Init();
|
||||
}
|
||||
~JSCustomObject();
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, JSCustomObject* entry);
|
||||
|
||||
template<class T>
|
||||
void AddProperty(const char* aName, T aValue) {
|
||||
mProperties.Put(nsDependentCString(aName), new TemplatePropertyValue<T>(aValue));
|
||||
}
|
||||
|
||||
nsDataHashtable<nsCStringHashKey, PropertyValue*> mProperties;
|
||||
};
|
||||
|
||||
class JSCustomArray {
|
||||
public:
|
||||
nsTArray<PropertyValue*> mValues;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, JSCustomArray* entry);
|
||||
|
||||
template<class T>
|
||||
void AppendElement(T aValue) {
|
||||
mValues.AppendElement(new TemplatePropertyValue<T>(aValue));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct SendToStreamImpl
|
||||
{
|
||||
static void run(std::ostream& stream, const T& t) {
|
||||
stream << t;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SendToStreamImpl<T*>
|
||||
{
|
||||
static void run(std::ostream& stream, T* t) {
|
||||
stream << *t;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SendToStreamImpl<char *>
|
||||
{
|
||||
static void run(std::ostream& stream, char* p) {
|
||||
EscapeToStream(stream, p);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SendToStreamImpl<JSCustomObject*>
|
||||
{
|
||||
static void run(std::ostream& stream, JSCustomObject* p) {
|
||||
stream << p;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SendToStreamImpl<JSCustomArray*>
|
||||
{
|
||||
static void run(std::ostream& stream, JSCustomArray* p) {
|
||||
stream << p;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> void
|
||||
TemplatePropertyValue<T>::SendToStream(std::ostream& stream)
|
||||
{
|
||||
SendToStreamImpl<T>::run(stream, mValue);
|
||||
}
|
||||
|
||||
struct JSONStreamClosure {
|
||||
std::ostream& mStream;
|
||||
bool mNeedsComma;
|
||||
};
|
||||
|
||||
PLDHashOperator HashTableOutput(const nsACString& aKey, PropertyValue* aValue, void* stream)
|
||||
{
|
||||
JSONStreamClosure& streamClosure = *(JSONStreamClosure*)stream;
|
||||
if (streamClosure.mNeedsComma) {
|
||||
streamClosure.mStream << ",";
|
||||
}
|
||||
streamClosure.mNeedsComma = true;
|
||||
EscapeToStream(streamClosure.mStream, (const char*)aKey.BeginReading());
|
||||
streamClosure.mStream << ":";
|
||||
aValue->SendToStream(streamClosure.mStream);
|
||||
return PLDHashOperator::PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& stream, JSCustomObject* entry)
|
||||
{
|
||||
JSONStreamClosure streamClosure = {stream, false};
|
||||
stream << "{";
|
||||
entry->mProperties.EnumerateRead(HashTableOutput, &streamClosure);
|
||||
stream << "}";
|
||||
return stream;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& stream, JSCustomArray* entry)
|
||||
{
|
||||
bool needsComma = false;
|
||||
stream << "[";
|
||||
for (int i = 0; i < entry->mValues.Length(); i++) {
|
||||
if (needsComma) {
|
||||
stream << ",";
|
||||
}
|
||||
entry->mValues[i]->SendToStream(stream);
|
||||
needsComma = true;
|
||||
}
|
||||
stream << "]";
|
||||
return stream;
|
||||
}
|
||||
|
||||
PLDHashOperator HashTableFree(const nsACString& aKey, PropertyValue* aValue, void* stream)
|
||||
{
|
||||
delete aValue;
|
||||
return PLDHashOperator::PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
JSCustomObject::~JSCustomObject()
|
||||
{
|
||||
mProperties.EnumerateRead(HashTableFree, nullptr);
|
||||
}
|
||||
|
||||
JSAObjectBuilder::~JSAObjectBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
JSCustomObjectBuilder::JSCustomObjectBuilder()
|
||||
{}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DeleteObject(JSCustomObject* aObject)
|
||||
{
|
||||
delete aObject;
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::Serialize(JSCustomObject* aObject, std::ostream& stream)
|
||||
{
|
||||
stream << aObject;
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue)
|
||||
{
|
||||
aObject->AddProperty(name, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue)
|
||||
{
|
||||
aObject->AddProperty(name, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, int aValue)
|
||||
{
|
||||
aObject->AddProperty(name, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, double aValue)
|
||||
{
|
||||
aObject->AddProperty(name, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, const char *aValue)
|
||||
{
|
||||
// aValue copy will be freed by the property desctructor (template specialization)
|
||||
aObject->AddProperty(name, strdup(aValue));
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::ArrayPush(JSCustomArray *aArray, int aValue)
|
||||
{
|
||||
aArray->AppendElement(aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::ArrayPush(JSCustomArray *aArray, const char *aValue)
|
||||
{
|
||||
// aValue copy will be freed by the property desctructor (template specialization)
|
||||
aArray->AppendElement(strdup(aValue));
|
||||
}
|
||||
|
||||
void
|
||||
JSCustomObjectBuilder::ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject)
|
||||
{
|
||||
aArray->AppendElement(aObject);
|
||||
}
|
||||
|
||||
JSCustomArray*
|
||||
JSCustomObjectBuilder::CreateArray() {
|
||||
return new JSCustomArray();
|
||||
}
|
||||
|
||||
JSCustomObject*
|
||||
JSCustomObjectBuilder::CreateObject() {
|
||||
return new JSCustomObject();
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#ifndef JSCUSTOMOBJECTBUILDER_H
|
||||
#define JSCUSTOMOBJECTBUILDER_H
|
||||
|
||||
#include <ostream>
|
||||
#include "JSAObjectBuilder.h"
|
||||
|
||||
class JSCustomObject;
|
||||
class JSCustomArray;
|
||||
class JSCustomObjectBuilder;
|
||||
|
||||
class JSCustomObjectBuilder : public JSAObjectBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
// We need to ensure that this object lives on the stack so that GC sees it properly
|
||||
JSCustomObjectBuilder();
|
||||
|
||||
void Serialize(JSCustomObject* aObject, std::ostream& stream);
|
||||
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, int value);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, double value);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, const char *value, size_t valueLength);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, const char *value);
|
||||
void ArrayPush(JSCustomArray *aArray, int value);
|
||||
void ArrayPush(JSCustomArray *aArray, const char *value);
|
||||
void ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject);
|
||||
JSCustomArray *CreateArray();
|
||||
JSCustomObject *CreateObject();
|
||||
|
||||
// Delete this object and all of its descendant
|
||||
void DeleteObject(JSCustomObject* aObject);
|
||||
|
||||
private:
|
||||
// This class can't be copied
|
||||
JSCustomObjectBuilder(const JSCustomObjectBuilder&);
|
||||
JSCustomObjectBuilder& operator=(const JSCustomObjectBuilder&);
|
||||
|
||||
void* operator new(size_t);
|
||||
void* operator new[](size_t);
|
||||
void operator delete(void*) {
|
||||
// Since JSCustomObjectBuilder has a virtual destructor the compiler
|
||||
// has to provide a destructor in the object file that will call
|
||||
// operate delete in case there is a derived class since its
|
||||
// destructor wont know how to free this instance.
|
||||
abort();
|
||||
}
|
||||
void operator delete[](void*);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,153 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "JSObjectBuilder.h"
|
||||
|
||||
JSObjectBuilder::JSObjectBuilder(JSContext *aCx) : mCx(aCx), mOk(JS_TRUE)
|
||||
{}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue)
|
||||
{
|
||||
DefineProperty(aObject, name, (JSCustomObject*)aValue);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, (JSObject*)aObject, name, OBJECT_TO_JSVAL((JSObject*)aValue), nullptr, nullptr, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, int value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, (JSObject*)aObject, name, INT_TO_JSVAL(value), nullptr, nullptr, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, double value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, (JSObject*)aObject, name, DOUBLE_TO_JSVAL(value), nullptr, nullptr, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, nsAString &value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
const nsString &flat = PromiseFlatString(value);
|
||||
JSString *string = JS_NewUCStringCopyN(mCx, static_cast<const jschar*>(flat.get()), flat.Length());
|
||||
if (!string)
|
||||
mOk = JS_FALSE;
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, (JSObject*)aObject, name, STRING_TO_JSVAL(string), nullptr, nullptr, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, const char *value, size_t valueLength)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
JSString *string = JS_InternStringN(mCx, value, valueLength);
|
||||
if (!string) {
|
||||
mOk = JS_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
mOk = JS_DefineProperty(mCx, (JSObject*)aObject, name, STRING_TO_JSVAL(string), nullptr, nullptr, JSPROP_ENUMERATE); }
|
||||
|
||||
void
|
||||
JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, const char *value)
|
||||
{
|
||||
DefineProperty(aObject, name, value, strlen(value));
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::ArrayPush(JSCustomArray *aArray, int value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
jsval objval = INT_TO_JSVAL(value);
|
||||
uint32_t length;
|
||||
mOk = JS_GetArrayLength(mCx, (JSObject*)aArray, &length);
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_SetElement(mCx, (JSObject*)aArray, length, &objval);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::ArrayPush(JSCustomArray *aArray, const char *value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
JSString *string = JS_NewStringCopyN(mCx, value, strlen(value));
|
||||
if (!string) {
|
||||
mOk = JS_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
jsval objval = STRING_TO_JSVAL(string);
|
||||
uint32_t length;
|
||||
mOk = JS_GetArrayLength(mCx, (JSObject*)aArray, &length);
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_SetElement(mCx, (JSObject*)aArray, length, &objval);
|
||||
}
|
||||
|
||||
void
|
||||
JSObjectBuilder::ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
jsval objval = OBJECT_TO_JSVAL((JSObject*)aObject); uint32_t length;
|
||||
mOk = JS_GetArrayLength(mCx, (JSObject*)aArray, &length);
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_SetElement(mCx, (JSObject*)aArray, length, &objval);
|
||||
}
|
||||
|
||||
JSCustomArray*
|
||||
JSObjectBuilder::CreateArray() {
|
||||
JSCustomArray *array = (JSCustomArray*)JS_NewArrayObject(mCx, 0, nullptr);
|
||||
if (!array)
|
||||
mOk = JS_FALSE;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
JSCustomObject*
|
||||
JSObjectBuilder::CreateObject() {
|
||||
JSCustomObject *obj = (JSCustomObject*)JS_NewObject(mCx, nullptr, nullptr, nullptr);
|
||||
if (!obj)
|
||||
mOk = JS_FALSE;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
|
@ -3,151 +3,60 @@
|
|||
* 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 "jsapi.h"
|
||||
#ifndef JSOBJECTBUILDER_H
|
||||
#define JSOBJECTBUILDER_H
|
||||
|
||||
#include "JSAObjectBuilder.h"
|
||||
|
||||
class JSCustomObject;
|
||||
class JSCustomObjectBuilder;
|
||||
class JSContext;
|
||||
class nsAString;
|
||||
|
||||
/* this is handy wrapper around JSAPI to make it more pleasant to use.
|
||||
* We collect the JSAPI errors and so that callers don't need to */
|
||||
class JSObjectBuilder
|
||||
class JSObjectBuilder : public JSAObjectBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
void DefineProperty(JSObject *aObject, const char *name, JSObject *aValue)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, aObject, name, OBJECT_TO_JSVAL(aValue), NULL, NULL, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void DefineProperty(JSObject *aObject, const char *name, int value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, aObject, name, INT_TO_JSVAL(value), NULL, NULL, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void DefineProperty(JSObject *aObject, const char *name, double value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, aObject, name, DOUBLE_TO_JSVAL(value), NULL, NULL, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void DefineProperty(JSObject *aObject, const char *name, nsAString &value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
const nsString &flat = PromiseFlatString(value);
|
||||
JSString *string = JS_NewUCStringCopyN(mCx, static_cast<const jschar*>(flat.get()), flat.Length());
|
||||
if (!string)
|
||||
mOk = JS_FALSE;
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_DefineProperty(mCx, aObject, name, STRING_TO_JSVAL(string), NULL, NULL, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void DefineProperty(JSObject *aObject, const char *name, const char *value, size_t valueLength)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
JSString *string = JS_InternStringN(mCx, value, valueLength);
|
||||
if (!string) {
|
||||
mOk = JS_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
mOk = JS_DefineProperty(mCx, aObject, name, STRING_TO_JSVAL(string), NULL, NULL, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
void DefineProperty(JSObject *aObject, const char *name, const char *value)
|
||||
{
|
||||
DefineProperty(aObject, name, value, strlen(value));
|
||||
}
|
||||
|
||||
void ArrayPush(JSObject *aArray, int value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
jsval objval = INT_TO_JSVAL(value);
|
||||
uint32_t length;
|
||||
mOk = JS_GetArrayLength(mCx, aArray, &length);
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_SetElement(mCx, aArray, length, &objval);
|
||||
}
|
||||
|
||||
void ArrayPush(JSObject *aArray, const char *value)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
JSString *string = JS_NewStringCopyN(mCx, value, strlen(value));
|
||||
if (!string) {
|
||||
mOk = JS_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
jsval objval = STRING_TO_JSVAL(string);
|
||||
uint32_t length;
|
||||
mOk = JS_GetArrayLength(mCx, aArray, &length);
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_SetElement(mCx, aArray, length, &objval);
|
||||
}
|
||||
|
||||
void ArrayPush(JSObject *aArray, JSObject *aObject)
|
||||
{
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
jsval objval = OBJECT_TO_JSVAL(aObject);
|
||||
uint32_t length;
|
||||
mOk = JS_GetArrayLength(mCx, aArray, &length);
|
||||
|
||||
if (!mOk)
|
||||
return;
|
||||
|
||||
mOk = JS_SetElement(mCx, aArray, length, &objval);
|
||||
}
|
||||
|
||||
JSObject *CreateArray() {
|
||||
JSObject *array = JS_NewArrayObject(mCx, 0, NULL);
|
||||
if (!array)
|
||||
mOk = JS_FALSE;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
JSObject *CreateObject() {
|
||||
JSObject *obj = JS_NewObject(mCx, NULL, NULL, NULL);
|
||||
if (!obj)
|
||||
mOk = JS_FALSE;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
// We need to ensure that this object lives on the stack so that GC sees it properly
|
||||
JSObjectBuilder(JSContext *aCx) : mCx(aCx), mOk(JS_TRUE)
|
||||
{
|
||||
explicit JSObjectBuilder(JSContext *aCx);
|
||||
~JSObjectBuilder() {}
|
||||
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, int value);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, double value);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, nsAString &value);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, const char *value, size_t valueLength);
|
||||
void DefineProperty(JSCustomObject *aObject, const char *name, const char *value);
|
||||
void ArrayPush(JSCustomArray *aArray, int value);
|
||||
void ArrayPush(JSCustomArray *aArray, const char *value);
|
||||
void ArrayPush(JSCustomArray *aArray, JSCustomArray *aObject);
|
||||
void ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject);
|
||||
JSCustomArray *CreateArray();
|
||||
JSCustomObject *CreateObject();
|
||||
|
||||
JSObject* GetJSObject(JSCustomObject* aObject) { return (JSObject*)aObject; }
|
||||
|
||||
private:
|
||||
JSObjectBuilder(const JSObjectBuilder&);
|
||||
JSObjectBuilder& operator=(const JSObjectBuilder&);
|
||||
|
||||
void* operator new(size_t);
|
||||
void* operator new[](size_t);
|
||||
void operator delete(void*) {
|
||||
// Since JSObjectBuilder has a virtual destructor the compiler
|
||||
// has to provide a destructor in the object file that will call
|
||||
// operate delete in case there is a derived class since its
|
||||
// destructor wont know how to free this instance.
|
||||
abort();
|
||||
}
|
||||
private:
|
||||
JSObjectBuilder(JSObjectBuilder&);
|
||||
void operator delete[](void*);
|
||||
|
||||
JSContext *mCx;
|
||||
JSObject *mObj;
|
||||
JSBool mOk;
|
||||
int mOk;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -63,6 +63,8 @@ CPPSRCS = \
|
|||
nsProfilerFactory.cpp \
|
||||
nsProfiler.cpp \
|
||||
TableTicker.cpp \
|
||||
JSObjectBuilder.cpp \
|
||||
JSCustomObjectBuilder.cpp \
|
||||
$(NULL)
|
||||
|
||||
XPIDLSRCS = \
|
||||
|
|