Merge the last PGO-green inbound changeset to m-c.

This commit is contained in:
Ryan VanderMeulen 2012-12-03 19:03:01 -05:00
commit 5796ccb3d6
104 changed files with 2155 additions and 871 deletions

View File

@ -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);
},
}

View File

@ -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];
}
}
};

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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);
},
/**

View File

@ -9,6 +9,9 @@ VPATH = @srcdir@
relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
XPCSHELL_TESTS = unit
include $(topsrcdir)/config/rules.mk
_BROWSER_FILES = \

View File

@ -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() {

Binary file not shown.

View File

@ -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);
}

View File

@ -0,0 +1,5 @@
[DEFAULT]
head =
tail =
[test_newtab-migrate-v1.js]

View File

@ -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");
}

View File

@ -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");
}
}

View File

@ -7,7 +7,7 @@
%undef WINSTRIPE_AERO
@media (-moz-windows-default-theme) {
#downloadsPanel[hasdownloads] #downloadsFooter {
#downloadsPanel[hasdownloads] > #downloadsFooter {
background-color: #f1f5fb;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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 \

View File

@ -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" }
];

Binary file not shown.

View File

@ -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;
},

View File

@ -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;
}

View File

@ -63,6 +63,7 @@ nsScreen::Create(nsPIDOMWindow* aWindow)
nsScreen::nsScreen()
: mEventListener(nullptr)
{
SetIsDOMBinding();
}
void

View File

@ -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',

View File

@ -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.

View File

@ -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,

View File

@ -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
});
},

View File

@ -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;
};

View File

@ -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))

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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,

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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)

View File

@ -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;

View File

@ -748,7 +748,6 @@ XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
}
}
}
self->GetXPConnect()->ClearGCBeforeCC();
break;
}
}

View File

@ -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)

View File

@ -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.

View File

@ -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);
}

View File

@ -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)

View File

@ -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.

View File

@ -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)));
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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"))

View File

@ -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 \

View File

@ -71,7 +71,7 @@ public class RemoteTabs extends LinearLayout
public void hide() {
}
void autoHidePanel() {
private void autoHidePanel() {
mTabsPanel.autoHidePanel();
}

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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);
}
});
}

View File

@ -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();
}

View File

@ -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());
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

@ -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"

View File

@ -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>

View File

@ -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

View File

@ -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);

View File

@ -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()

View File

@ -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]

View File

@ -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:

View File

@ -31,6 +31,7 @@ EXPORTS_mozilla = \
XPIDLSRCS = \
nsITelemetry.idl \
nsITelemetryPing.idl \
$(NULL)
EXTRA_PP_COMPONENTS = \

View File

@ -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",

View File

@ -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]);

View File

@ -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();
};

View File

@ -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());

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);
},

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -63,6 +63,8 @@ CPPSRCS = \
nsProfilerFactory.cpp \
nsProfiler.cpp \
TableTicker.cpp \
JSObjectBuilder.cpp \
JSCustomObjectBuilder.cpp \
$(NULL)
XPIDLSRCS = \

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