Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2013-10-18 12:00:14 +02:00
commit 5310a97558
324 changed files with 5150 additions and 2408 deletions

View File

@ -129,8 +129,9 @@ pref("app.update.cert.maxErrors", 5);
// the |app.update.url.override| preference should ONLY be used for testing.
// IMPORTANT! metro.js should also be updated for updates to certs.X.issuerName
// Nightly builds have switched over to aus4.mozilla.org, but we don't want anything else to yet.
#ifdef NIGHTLY_BUILD
// Non-release builds (Nightly, Aurora, etc.) have been switched over to aus4.mozilla.org.
// This condition protects us against accidentally using it for release builds.
#ifndef RELEASE_BUILD
pref("app.update.certs.1.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US");
pref("app.update.certs.1.commonName", "aus4.mozilla.org");
@ -172,7 +173,7 @@ pref("app.update.silent", false);
pref("app.update.staging.enabled", true);
// Update service URL:
#ifdef NIGHTLY_BUILD
#ifndef RELEASE_BUILD
pref("app.update.url", "https://aus4.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
#else
pref("app.update.url", "https://aus3.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
@ -232,6 +233,12 @@ pref("xpinstall.whitelist.add.180", "marketplace.firefox.com");
pref("lightweightThemes.update.enabled", true);
// UI tour experience.
pref("browser.uitour.enabled", true);
pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/");
pref("browser.uitour.pinnedTabUrl", "https://support.mozilla.org/%LOCALE%/kb/pinned-tabs-keep-favorite-websites-open");
pref("browser.uitour.whitelist.add.260", "www.mozilla.org,support.mozilla.org");
pref("keyword.enabled", true);
pref("general.useragent.locale", "@AB_CD@");
@ -659,6 +666,8 @@ pref("plugins.update.notifyUser", false);
pref("plugins.click_to_play", true);
pref("plugins.clickToActivateInfo.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/clicktoplay");
// let all plugins except Flash default to click-to-play
pref("plugin.default.state", 1);
pref("plugin.state.flash", 2);

View File

@ -704,6 +704,9 @@ var gPluginHandler = {
else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
}
else {
url = Services.urlFormatter.formatURLPref("plugins.clickToActivateInfo.url");
}
pluginInfo.detailsLink = url;
centerActions.push(pluginInfo);

View File

@ -778,27 +778,6 @@ var gBrowserInit = {
window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
new nsBrowserAccess();
// Manually hook up session and global history for the first browser
// so that we don't have to load global history before bringing up a
// window.
// Wire up session and global history before any possible
// progress notifications for back/forward button updating
gBrowser.webNavigation.sessionHistory = Cc["@mozilla.org/browser/shistory;1"].
createInstance(Ci.nsISHistory);
Services.obs.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false);
// remove the disablehistory attribute so the browser cleans up, as
// though it had done this work itself
gBrowser.browsers[0].removeAttribute("disablehistory");
// enable global history
try {
if (!gMultiProcessBrowser)
gBrowser.docShell.useGlobalHistory = true;
} catch(ex) {
Cu.reportError("Places database may be locked: " + ex);
}
// hook up UI through progress listener
gBrowser.addProgressListener(window.XULBrowserWindow);
gBrowser.addTabsProgressListener(window.TabsProgressListener);

View File

@ -22,6 +22,7 @@
<window id="main-window"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="gBrowserInit.onLoad()" onunload="gBrowserInit.onUnload()" onclose="return WindowIsClosing();"
title="&mainWindow.title;@PRE_RELEASE_SUFFIX@"
@ -183,6 +184,22 @@
</hbox>
</panel>
<!-- UI tour experience -->
<panel id="UITourTooltip"
type="arrow"
hidden="true"
consumeoutsideclicks="false"
noautofocus="true"
align="start"
orient="vertical"
role="alert">
<label id="UITourTooltipTitle" flex="1"/>
<description id="UITourTooltipDescription" flex="1"/>
</panel>
<html:div id="UITourHighlightContainer" style="position:relative">
<html:div id="UITourHighlight"></html:div>
</html:div>
<panel id="socialActivatedNotification"
type="arrow"
hidden="true"
@ -1028,7 +1045,7 @@
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
<vbox id="appcontent" flex="1">
<tabbrowser id="content" disablehistory="true"
<tabbrowser id="content"
flex="1" contenttooltip="aHTMLTooltip"
tabcontainer="tabbrowser-tabs"
contentcontextmenu="contentAreaContextMenu"

View File

@ -15,6 +15,8 @@ XPCOMUtils.defineLazyModuleGetter(this,
"InsecurePasswordUtils", "resource://gre/modules/InsecurePasswordUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
"resource:///modules/UITour.jsm");
// Creates a new nsIURI object.
function makeURI(uri, originCharset, baseURI) {
@ -49,6 +51,15 @@ if (Services.prefs.getBoolPref("browser.tabs.remote")) {
addEventListener("blur", function(event) {
LoginManagerContent.onUsernameInput(event);
});
addEventListener("mozUITour", function(event) {
if (!Services.prefs.getBoolPref("browser.uitour.enabled"))
return;
let handled = UITour.onPageEvent(event);
if (handled)
addEventListener("pagehide", UITour);
}, false, true);
}
let AboutHomeListener = {

View File

@ -34,7 +34,7 @@
<xul:hbox flex="1" class="browserSidebarContainer">
<xul:vbox flex="1" class="browserContainer">
<xul:stack flex="1" class="browserStack" anonid="browserStack">
<xul:browser anonid="initialBrowser" type="content-primary" message="true" disablehistory="true"
<xul:browser anonid="initialBrowser" type="content-primary" message="true"
xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectpopup"/>
</xul:stack>
</xul:vbox>

View File

@ -472,6 +472,7 @@ BrowserGlue.prototype = {
ShumwayUtils.init();
webrtcUI.init();
AboutHome.init();
SessionStore.init();
if (Services.prefs.getBoolPref("browser.tabs.remote"))
ContentClick.init();
@ -612,7 +613,6 @@ BrowserGlue.prototype = {
}
#endif
SessionStore.init(aWindow);
this._trackSlowStartup();
// Offer to reset a user's profile if it hasn't been used for 60 days.

View File

@ -156,8 +156,8 @@ this.SessionStore = {
SessionStoreInternal.canRestoreLastSession = val;
},
init: function ss_init(aWindow) {
SessionStoreInternal.init(aWindow);
init: function ss_init() {
SessionStoreInternal.init();
},
getBrowserState: function ss_getBrowserState() {
@ -368,15 +368,11 @@ let SessionStoreInternal = {
/**
* Initialize the sessionstore service.
*/
init: function (aWindow) {
init: function () {
if (this._initialized) {
throw new Error("SessionStore.init() must only be called once!");
}
if (!aWindow) {
throw new Error("SessionStore.init() must be called with a valid window.");
}
this._disabledForMultiProcess = Services.prefs.getBoolPref("browser.tabs.remote");
if (this._disabledForMultiProcess) {
this._deferredInitialized.resolve();
@ -390,20 +386,6 @@ let SessionStoreInternal = {
this._initPrefs();
this._initialized = true;
// Wait until nsISessionStartup has finished reading the session data.
gSessionStartup.onceInitialized.then(() => {
// Parse session data and start restoring.
let initialState = this.initSession();
// Start tracking the given (initial) browser window.
if (!aWindow.closed) {
this.onLoad(aWindow, initialState);
}
// Let everyone know we're done.
this._deferredInitialized.resolve();
}, Cu.reportError);
},
initSession: function ssi_initSession() {
@ -489,7 +471,6 @@ let SessionStoreInternal = {
this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
this._performUpgradeBackup();
this._sessionInitialized = true;
return state;
},
@ -876,7 +857,34 @@ let SessionStoreInternal = {
onOpen: function ssi_onOpen(aWindow) {
let onload = () => {
aWindow.removeEventListener("load", onload);
this.onLoad(aWindow);
if (this._sessionInitialized) {
this.onLoad(aWindow);
return;
}
// We can't call this.onLoad since initialization
// hasn't completed, so we'll wait until it is done.
// Even if additional windows are opened and wait
// for initialization as well, the first opened
// window should execute first, and this.onLoad
// will be called with the initialState.
gSessionStartup.onceInitialized.then(() => {
if (aWindow.closed) {
return;
}
if (this._sessionInitialized) {
this.onLoad(aWindow);
} else {
let initialState = this.initSession();
this._sessionInitialized = true;
this.onLoad(aWindow, initialState);
// Let everyone know we're done.
this._deferredInitialized.resolve();
}
}, Cu.reportError);
};
aWindow.addEventListener("load", onload);
@ -1471,7 +1479,7 @@ let SessionStoreInternal = {
TabStateCache.delete(aTab);
this._setWindowStateBusy(window);
this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0);
this.restoreHistoryPrecursor(window, [aTab], [tabState], 0);
},
duplicateTab: function ssi_duplicateTab(aWindow, aTab, aDelta = 0) {
@ -1491,7 +1499,7 @@ let SessionStoreInternal = {
aWindow.gBrowser.addTab(null, {relatedToCurrent: true, ownerTab: aTab}) :
aWindow.gBrowser.addTab();
this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0, 0, 0,
this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0,
true /* Load this tab right away. */);
return newTab;
@ -1571,7 +1579,7 @@ let SessionStoreInternal = {
let tab = tabbrowser.addTab();
// restore tab content
this.restoreHistoryPrecursor(aWindow, [tab], [closedTabState], 1, 0, 0);
this.restoreHistoryPrecursor(aWindow, [tab], [closedTabState], 1);
// restore the tab's position
tabbrowser.moveTabTo(tab, closedTab.pos);
@ -2344,7 +2352,7 @@ let SessionStoreInternal = {
}
this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
(overwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0);
(overwriteTabs ? (parseInt(winData.selected) || 1) : 0));
if (aState.scratchpads) {
ScratchpadManager.restoreSession(aState.scratchpads);
@ -2448,40 +2456,17 @@ let SessionStoreInternal = {
* Array of tab data
* @param aSelectTab
* Index of selected tab
* @param aIx
* Index of the next tab to check readyness for
* @param aCount
* Counter for number of times delaying b/c browser or history aren't ready
* @param aRestoreImmediately
* Flag to indicate whether the given set of tabs aTabs should be
* restored/loaded immediately even if restore_on_demand = true
*/
restoreHistoryPrecursor:
function ssi_restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab,
aIx, aCount, aRestoreImmediately = false) {
aRestoreImmediately = false)
{
var tabbrowser = aWindow.gBrowser;
// make sure that all browsers and their histories are available
// - if one's not, resume this check in 100ms (repeat at most 10 times)
for (var t = aIx; t < aTabs.length; t++) {
try {
if (!tabbrowser.getBrowserForTab(aTabs[t]).webNavigation.sessionHistory) {
throw new Error();
}
}
catch (ex) { // in case browser or history aren't ready yet
if (aCount < 10) {
var restoreHistoryFunc = function(self) {
self.restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab,
aIx, aCount + 1, aRestoreImmediately);
};
aWindow.setTimeout(restoreHistoryFunc, 100, this);
return;
}
}
}
if (!this._isWindowLoaded(aWindow)) {
// from now on, the data will come from the actual window
delete this._statesToRestore[aWindow.__SS_restoreID];
@ -2507,7 +2492,7 @@ let SessionStoreInternal = {
return;
}
// Sets the tabs restoring order.
// Sets the tabs restoring order.
[aTabs, aTabData] =
this._setTabsRestoringOrder(tabbrowser, aTabs, aTabData, aSelectTab);
@ -2515,7 +2500,7 @@ let SessionStoreInternal = {
// and show/hide tabs as necessary. We'll also set the labels, user typed
// value, and attach a copy of the tab's data in case we close it before
// it's been restored.
for (t = 0; t < aTabs.length; t++) {
for (let t = 0; t < aTabs.length; t++) {
let tab = aTabs[t];
let browser = tabbrowser.getBrowserForTab(tab);
let tabData = aTabData[t];
@ -2587,14 +2572,14 @@ let SessionStoreInternal = {
// helper hashes for ensuring unique frame IDs and unique document
// identifiers.
var idMap = { used: {} };
var docIdentMap = {};
let idMap = { used: {} };
let docIdentMap = {};
this.restoreHistory(aWindow, aTabs, aTabData, idMap, docIdentMap,
aRestoreImmediately);
},
/**
* Restore history for a window
* Restore history for a list of tabs.
* @param aWindow
* Window reference
* @param aTabs

View File

@ -1537,6 +1537,7 @@ NetworkDetailsView.prototype = {
}));
this._json = new VariablesView($("#response-content-json"),
Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
onlyEnumVisible: true,
searchPlaceholder: L10N.getStr("jsonFilterText")
}));
VariablesViewController.attach(this._json);
@ -1874,7 +1875,7 @@ NetworkDetailsView.prototype = {
let sanitizedJSON = aString.replace(jsonpRegex, "");
let callbackPadding = aString.match(jsonpRegex);
// Make sure this is an valid JSON object first. If so, nicely display
// Make sure this is a valid JSON object first. If so, nicely display
// the parsing results in a variables view. Otherwise, simply show
// the contents as plain text.
try {

View File

@ -610,66 +610,23 @@ var Scratchpad = {
deferred.resolve(aError);
}
else {
let reject = aReason => deferred.reject(aReason);
let objectClient = new ObjectClient(this.debuggerClient, aError);
// Because properties on Error objects are lazily added, this roundabout
// way of getting all the properties is required, rather than simply
// using getPrototypeAndProperties. See bug 724768.
let names = ["message", "stack", "fileName", "lineNumber"];
let promises = names.map(aName => {
let deferred = promise.defer();
objectClient.getProperty(aName, aResponse => {
if (aResponse.error) {
deferred.reject(aResponse);
}
else {
deferred.resolve({
name: aName,
descriptor: aResponse.descriptor
});
}
});
return deferred.promise;
});
{
// We also need to use getPrototypeAndProperties to retrieve any
// safeGetterValues in case this is a DOM error.
let deferred = promise.defer();
objectClient.getPrototypeAndProperties(aResponse => {
if (aResponse.error) {
deferred.reject(aResponse);
}
else {
deferred.resolve(aResponse);
}
});
promises.push(deferred.promise);
}
promise.all(promises).then(aProperties => {
let error = {};
let safeGetters;
// Combine all the property descriptor/getter values into one object.
for (let property of aProperties) {
if (property.descriptor) {
error[property.name] = property.descriptor.value;
}
else if (property.safeGetterValues) {
safeGetters = property.safeGetterValues;
}
objectClient.getPrototypeAndProperties(aResponse => {
if (aResponse.error) {
deferred.reject(aResponse);
return;
}
if (safeGetters) {
for (let key of Object.keys(safeGetters)) {
if (!error.hasOwnProperty(key)) {
error[key] = safeGetters[key].getterValue;
}
}
let { ownProperties, safeGetterValues } = aResponse;
let error = Object.create(null);
// Combine all the property descriptor/getter values into one object.
for (let key of Object.keys(safeGetterValues)) {
error[key] = safeGetterValues[key].getterValue;
}
for (let key of Object.keys(ownProperties)) {
error[key] = ownProperties[key].value;
}
// Assemble the best possible stack we can given the properties we have.
@ -693,23 +650,23 @@ var Scratchpad = {
deferred.resolve(error.message + stack);
}
else {
objectClient.getDisplayString(aResult => {
if (aResult.error) {
deferred.reject(aResult);
objectClient.getDisplayString(aResponse => {
if (aResponse.error) {
deferred.reject(aResponse);
}
else if (aResult.displayString.type == "null") {
deferred.resolve(stack);
else if (typeof aResponse.displayString == "string") {
deferred.resolve(aResponse.displayString + stack);
}
else {
deferred.resolve(aResult.displayString + stack);
deferred.resolve(stack);
}
}, reject);
});
}
}, reject);
});
}
return deferred.promise.then(aMessage => {
console.log(aMessage);
console.error(aMessage);
this.writeAsComment("Exception: " + aMessage);
});
},

View File

@ -43,6 +43,7 @@ function testColorUtils() {
let color = new colorUtils.CssColor(authored);
// Check all values.
info("Checking values for " + authored);
is(color.name, name, "color.name === name");
is(color.hex, hex, "color.hex === hex");
is(color.hsl, hsl, "color.hsl === hsl");
@ -291,14 +292,18 @@ function getTestData() {
{authored: "whitesmoke", name: "whitesmoke", hex: "#F5F5F5", hsl: "hsl(0, 0%, 96%)", rgb: "rgb(245, 245, 245)"},
{authored: "yellow", name: "yellow", hex: "#FF0", hsl: "hsl(60, 100%, 50%)", rgb: "rgb(255, 255, 0)"},
{authored: "yellowgreen", name: "yellowgreen", hex: "#9ACD32", hsl: "hsl(79.742, 61%, 50%)", rgb: "rgb(154, 205, 50)"},
{authored: "transparent", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
{authored: "rgba(0, 0, 0, 0)", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
{authored: "hsla(0, 0%, 0%, 0)", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
{authored: "rgba(0, 0, 0, 0)", name: "rgba(0, 0, 0, 0)", hex: "rgba(0, 0, 0, 0)", hsl: "hsla(0, 0%, 0%, 0)", rgb: "rgba(0, 0, 0, 0)"},
{authored: "hsla(0, 0%, 0%, 0)", name: "rgba(0, 0, 0, 0)", hex: "rgba(0, 0, 0, 0)", hsl: "hsla(0, 0%, 0%, 0)", rgb: "rgba(0, 0, 0, 0)"},
{authored: "rgba(50, 60, 70, 0.5)", name: "rgba(50, 60, 70, 0.5)", hex: "rgba(50, 60, 70, 0.5)", hsl: "hsla(210, 17%, 24%, 0.5)", rgb: "rgba(50, 60, 70, 0.5)"},
{authored: "rgba(0, 0, 0, 0.3)", name: "rgba(0, 0, 0, 0.3)", hex: "rgba(0, 0, 0, 0.3)", hsl: "hsla(0, 0%, 0%, 0.3)", rgb: "rgba(0, 0, 0, 0.3)"},
{authored: "rgba(255, 255, 255, 0.6)", name: "rgba(255, 255, 255, 0.6)", hex: "rgba(255, 255, 255, 0.6)", hsl: "hsla(0, 0%, 100%, 0.6)", rgb: "rgba(255, 255, 255, 0.6)"},
{authored: "rgba(127, 89, 45, 1)", name: "#7F592D", hex: "#7F592D", hsl: "hsl(32.195, 48%, 34%)", rgb: "rgb(127, 89, 45)"},
{authored: "hsla(19.304, 56%, 40%, 1)", name: "#9F512C", hex: "#9F512C", hsl: "hsl(19.304, 57%, 40%)", rgb: "rgb(159, 81, 44)"},
{authored: "invalidColor", name: "", hex: "", hsl: "", rgb: ""}
{authored: "currentcolor", name: "currentcolor", hex: "currentcolor", hsl: "currentcolor", rgb: "currentcolor"},
{authored: "inherit", name: "inherit", hex: "inherit", hsl: "inherit", rgb: "inherit"},
{authored: "initial", name: "initial", hex: "initial", hsl: "initial", rgb: "initial"},
{authored: "invalidColor", name: "", hex: "", hsl: "", rgb: ""},
{authored: "transparent", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
{authored: "unset", name: "unset", hex: "unset", hsl: "unset", rgb: "unset"}
];
}

View File

@ -461,7 +461,7 @@ VariablesViewController.prototype = {
scope.expanded = true;
scope.locked = true;
let variable = scope.addItem();
let variable = scope.addItem("", { enumerable: true });
let expanded;
if (aOptions.objectActor) {

View File

@ -449,7 +449,7 @@ pref("app.update.silent", true);
pref("app.update.staging.enabled", true);
// Update service URL:
#ifdef NIGHTLY_BUILD
#ifndef RELEASE_BUILD
pref("app.update.url", "https://aus4.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
#else
pref("app.update.url", "https://aus3.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
@ -522,8 +522,9 @@ pref("app.update.cert.maxErrors", 5);
// the |app.update.url.override| preference should ONLY be used for testing.
// IMPORTANT! firefox.js should also be updated for updates to certs.X.issuerName
// Nightly builds have switched over to aus4.mozilla.org, but we don't want anything else to yet.
#ifdef NIGHTLY_BUILD
// Non-release builds (Nightly, Aurora, etc.) have been switched over to aus4.mozilla.org.
// This condition protects us against accidentally using it for release builds.
#ifndef RELEASE_BUILD
pref("app.update.certs.1.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US");
pref("app.update.certs.1.commonName", "aus4.mozilla.org");
pref("app.update.certs.2.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");

426
browser/modules/UITour.jsm Normal file
View File

@ -0,0 +1,426 @@
// 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/.
this.EXPORTED_SYMBOLS = ["UITour"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
"resource://gre/modules/LightweightThemeManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PermissionsUtils",
"resource://gre/modules/PermissionsUtils.jsm");
const UITOUR_PERMISSION = "uitour";
const PREF_PERM_BRANCH = "browser.uitour.";
this.UITour = {
originTabs: new WeakMap(),
pinnedTabs: new WeakMap(),
urlbarCapture: new WeakMap(),
highlightEffects: ["wobble", "zoom", "color"],
targets: new Map([
["backforward", "#unified-back-forward-button"],
["appmenu", "#appmenu-button"],
["home", "#home-button"],
["urlbar", "#urlbar"],
["bookmarks", "#bookmarks-menu-button"],
["search", "#searchbar"],
["searchprovider", function UITour_target_searchprovider(aDocument) {
let searchbar = aDocument.getElementById("searchbar");
return aDocument.getAnonymousElementByAttribute(searchbar,
"anonid",
"searchbar-engine-button");
}],
]),
onPageEvent: function(aEvent) {
let contentDocument = null;
if (aEvent.target instanceof Ci.nsIDOMHTMLDocument)
contentDocument = aEvent.target;
else if (aEvent.target instanceof Ci.nsIDOMHTMLElement)
contentDocument = aEvent.target.ownerDocument;
else
return false;
// Ignore events if they're not from a trusted origin.
if (!this.ensureTrustedOrigin(contentDocument))
return false;
if (typeof aEvent.detail != "object")
return false;
let action = aEvent.detail.action;
if (typeof action != "string" || !action)
return false;
let data = aEvent.detail.data;
if (typeof data != "object")
return false;
let window = this.getChromeWindow(contentDocument);
switch (action) {
case "showHighlight": {
let target = this.getTarget(window, data.target);
if (!target)
return false;
this.showHighlight(target);
break;
}
case "hideHighlight": {
this.hideHighlight(window);
break;
}
case "showInfo": {
let target = this.getTarget(window, data.target, true);
if (!target)
return false;
this.showInfo(target, data.title, data.text);
break;
}
case "hideInfo": {
this.hideInfo(window);
break;
}
case "previewTheme": {
this.previewTheme(data.theme);
break;
}
case "resetTheme": {
this.resetTheme();
break;
}
case "addPinnedTab": {
this.ensurePinnedTab(window, true);
break;
}
case "removePinnedTab": {
this.removePinnedTab(window);
break;
}
case "showMenu": {
this.showMenu(window, data.name);
break;
}
case "startUrlbarCapture": {
if (typeof data.text != "string" || !data.text ||
typeof data.url != "string" || !data.url) {
return false;
}
let uri = null;
try {
uri = Services.io.newURI(data.url, null, null);
} catch (e) {
return false;
}
let secman = Services.scriptSecurityManager;
let principal = contentDocument.nodePrincipal;
let flags = secman.DISALLOW_INHERIT_PRINCIPAL;
try {
secman.checkLoadURIWithPrincipal(principal, uri, flags);
} catch (e) {
return false;
}
this.startUrlbarCapture(window, data.text, data.url);
break;
}
case "endUrlbarCapture": {
this.endUrlbarCapture(window);
break;
}
}
let tab = window.gBrowser._getTabForContentWindow(contentDocument.defaultView);
if (!this.originTabs.has(window))
this.originTabs.set(window, new Set());
this.originTabs.get(window).add(tab);
tab.addEventListener("TabClose", this);
window.gBrowser.tabContainer.addEventListener("TabSelect", this);
window.addEventListener("SSWindowClosing", this);
return true;
},
handleEvent: function(aEvent) {
switch (aEvent.type) {
case "pagehide": {
let window = this.getChromeWindow(aEvent.target);
this.teardownTour(window);
break;
}
case "TabClose": {
let window = aEvent.target.ownerDocument.defaultView;
this.teardownTour(window);
break;
}
case "TabSelect": {
let window = aEvent.target.ownerDocument.defaultView;
let pinnedTab = this.pinnedTabs.get(window);
if (pinnedTab && pinnedTab.tab == window.gBrowser.selectedTab)
break;
let originTabs = this.originTabs.get(window);
if (originTabs && originTabs.has(window.gBrowser.selectedTab))
break;
this.teardownTour(window);
break;
}
case "SSWindowClosing": {
let window = aEvent.target;
this.teardownTour(window, true);
break;
}
case "input": {
if (aEvent.target.id == "urlbar") {
let window = aEvent.target.ownerDocument.defaultView;
this.handleUrlbarInput(window);
}
break;
}
}
},
teardownTour: function(aWindow, aWindowClosing = false) {
aWindow.gBrowser.tabContainer.removeEventListener("TabSelect", this);
aWindow.removeEventListener("SSWindowClosing", this);
let originTabs = this.originTabs.get(aWindow);
if (originTabs) {
for (let tab of originTabs)
tab.removeEventListener("TabClose", this);
}
this.originTabs.delete(aWindow);
if (!aWindowClosing) {
this.hideHighlight(aWindow);
this.hideInfo(aWindow);
}
this.endUrlbarCapture(aWindow);
this.removePinnedTab(aWindow);
this.resetTheme();
},
getChromeWindow: function(aContentDocument) {
return aContentDocument.defaultView
.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow)
.wrappedJSObject;
},
importPermissions: function() {
try {
PermissionsUtils.importFromPrefs(PREF_PERM_BRANCH, UITOUR_PERMISSION);
} catch (e) {
Cu.reportError(e);
}
},
ensureTrustedOrigin: function(aDocument) {
if (aDocument.defaultView.top != aDocument.defaultView)
return false;
let uri = aDocument.documentURIObject;
if (uri.schemeIs("chrome"))
return true;
if (!uri.schemeIs("https"))
return false;
this.importPermissions();
let permission = Services.perms.testPermission(uri, UITOUR_PERMISSION);
return permission == Services.perms.ALLOW_ACTION;
},
getTarget: function(aWindow, aTargetName, aSticky = false) {
if (typeof aTargetName != "string" || !aTargetName)
return null;
if (aTargetName == "pinnedtab")
return this.ensurePinnedTab(aWindow, aSticky);
let targetQuery = this.targets.get(aTargetName);
if (!targetQuery)
return null;
if (typeof targetQuery == "function")
return targetQuery(aWindow.document);
return aWindow.document.querySelector(targetQuery);
},
previewTheme: function(aTheme) {
let origin = Services.prefs.getCharPref("browser.uitour.themeOrigin");
let data = LightweightThemeManager.parseTheme(aTheme, origin);
if (data)
LightweightThemeManager.previewTheme(data);
},
resetTheme: function() {
LightweightThemeManager.resetPreview();
},
ensurePinnedTab: function(aWindow, aSticky = false) {
let tabInfo = this.pinnedTabs.get(aWindow);
if (tabInfo) {
tabInfo.sticky = tabInfo.sticky || aSticky;
} else {
let url = Services.urlFormatter.formatURLPref("browser.uitour.pinnedTabUrl");
let tab = aWindow.gBrowser.addTab(url);
aWindow.gBrowser.pinTab(tab);
tab.addEventListener("TabClose", () => {
this.pinnedTabs.delete(aWindow);
});
tabInfo = {
tab: tab,
sticky: aSticky
};
this.pinnedTabs.set(aWindow, tabInfo);
}
return tabInfo.tab;
},
removePinnedTab: function(aWindow) {
let tabInfo = this.pinnedTabs.get(aWindow);
if (tabInfo)
aWindow.gBrowser.removeTab(tabInfo.tab);
},
showHighlight: function(aTarget) {
let highlighter = aTarget.ownerDocument.getElementById("UITourHighlight");
let randomEffect = Math.floor(Math.random() * this.highlightEffects.length);
if (randomEffect == this.highlightEffects.length)
randomEffect--; // On the order of 1 in 2^62 chance of this happening.
highlighter.setAttribute("active", this.highlightEffects[randomEffect]);
let targetRect = aTarget.getBoundingClientRect();
highlighter.style.height = targetRect.height + "px";
highlighter.style.width = targetRect.width + "px";
let highlighterRect = highlighter.getBoundingClientRect();
let top = targetRect.top + (targetRect.height / 2) - (highlighterRect.height / 2);
highlighter.style.top = top + "px";
let left = targetRect.left + (targetRect.width / 2) - (highlighterRect.width / 2);
highlighter.style.left = left + "px";
},
hideHighlight: function(aWindow) {
let tabData = this.pinnedTabs.get(aWindow);
if (tabData && !tabData.sticky)
this.removePinnedTab(aWindow);
let highlighter = aWindow.document.getElementById("UITourHighlight");
highlighter.removeAttribute("active");
},
showInfo: function(aAnchor, aTitle, aDescription) {
aAnchor.focus();
let document = aAnchor.ownerDocument;
let tooltip = document.getElementById("UITourTooltip");
let tooltipTitle = document.getElementById("UITourTooltipTitle");
let tooltipDesc = document.getElementById("UITourTooltipDescription");
tooltip.hidePopup();
tooltipTitle.textContent = aTitle;
tooltipDesc.textContent = aDescription;
let alignment = "bottomcenter topright";
let anchorRect = aAnchor.getBoundingClientRect();
tooltip.hidden = false;
tooltip.openPopup(aAnchor, alignment);
},
hideInfo: function(aWindow) {
let tooltip = aWindow.document.getElementById("UITourTooltip");
tooltip.hidePopup();
},
showMenu: function(aWindow, aMenuName) {
function openMenuButton(aId) {
let menuBtn = aWindow.document.getElementById(aId);
if (menuBtn && menuBtn.boxObject)
menuBtn.boxObject.QueryInterface(Ci.nsIMenuBoxObject).openMenu(true);
}
if (aMenuName == "appmenu")
openMenuButton("appmenu-button");
else if (aMenuName == "bookmarks")
openMenuButton("bookmarks-menu-button");
},
startUrlbarCapture: function(aWindow, aExpectedText, aUrl) {
let urlbar = aWindow.document.getElementById("urlbar");
this.urlbarCapture.set(aWindow, {
expected: aExpectedText.toLocaleLowerCase(),
url: aUrl
});
urlbar.addEventListener("input", this);
},
endUrlbarCapture: function(aWindow) {
let urlbar = aWindow.document.getElementById("urlbar");
urlbar.removeEventListener("input", this);
this.urlbarCapture.delete(aWindow);
},
handleUrlbarInput: function(aWindow) {
if (!this.urlbarCapture.has(aWindow))
return;
let urlbar = aWindow.document.getElementById("urlbar");
let {expected, url} = this.urlbarCapture.get(aWindow);
if (urlbar.value.toLocaleLowerCase().localeCompare(expected) != 0)
return;
urlbar.handleRevert();
let tab = aWindow.gBrowser.addTab(url, {
owner: aWindow.gBrowser.selectedTab,
relatedToCurrent: true
});
aWindow.gBrowser.selectedTab = tab;
},
};

View File

@ -15,6 +15,7 @@ EXTRA_JS_MODULES += [
'SitePermissions.jsm',
'Social.jsm',
'TabCrashReporter.jsm',
'UITour.jsm',
'offlineAppCache.jsm',
'openLocationLastURL.jsm',
'webappsUI.jsm',

View File

@ -1,3 +1,5 @@
[DEFAULT]
[browser_NetworkPrioritizer.js]
[browser_UITour.js]
support-files = uitour.*

View File

@ -0,0 +1,212 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gTestTab;
let gContentAPI;
Components.utils.import("resource:///modules/UITour.jsm");
function is_hidden(element) {
var style = element.ownerDocument.defaultView.getComputedStyle(element, "");
if (style.display == "none")
return true;
if (style.visibility != "visible")
return true;
// Hiding a parent element will hide all its children
if (element.parentNode != element.ownerDocument)
return is_hidden(element.parentNode);
return false;
}
function is_element_visible(element, msg) {
isnot(element, null, "Element should not be null, when checking visibility");
ok(!is_hidden(element), msg);
}
function is_element_hidden(element, msg) {
isnot(element, null, "Element should not be null, when checking visibility");
ok(is_hidden(element), msg);
}
function loadTestPage(callback, untrustedHost = false) {
if (gTestTab)
gBrowser.removeTab(gTestTab);
let url = getRootDirectory(gTestPath) + "uitour.html";
if (untrustedHost)
url = url.replace("chrome://mochitests/content/", "http://example.com/");
gTestTab = gBrowser.addTab(url);
gBrowser.selectedTab = gTestTab;
gTestTab.linkedBrowser.addEventListener("load", function onLoad() {
gTestTab.linkedBrowser.removeEventListener("load", onLoad);
let contentWindow = Components.utils.waiveXrays(gTestTab.linkedBrowser.contentDocument.defaultView);
gContentAPI = contentWindow.Mozilla.UITour;
waitForFocus(callback, contentWindow);
}, true);
}
function test() {
Services.prefs.setBoolPref("browser.uitour.enabled", true);
waitForExplicitFinish();
registerCleanupFunction(function() {
delete window.UITour;
delete window.gContentAPI;
if (gTestTab)
gBrowser.removeTab(gTestTab);
delete window.gTestTab;
Services.prefs.clearUserPref("browser.uitour.enabled", true);
});
function done() {
if (gTestTab)
gBrowser.removeTab(gTestTab);
gTestTab = null;
let highlight = document.getElementById("UITourHighlight");
is_element_hidden(highlight, "Highlight should be hidden after UITour tab is closed");
let popup = document.getElementById("UITourTooltip");
isnot(["hidding","closed"].indexOf(popup.state), -1, "Popup should be closed/hidding after UITour tab is closed");
is(UITour.pinnedTabs.get(window), null, "Any pinned tab should be closed after UITour tab is closed");
executeSoon(nextTest);
}
function nextTest() {
if (tests.length == 0) {
finish();
return;
}
let test = tests.shift();
loadTestPage(function() {
test(done);
});
}
nextTest();
}
let tests = [
function test_disabled(done) {
Services.prefs.setBoolPref("browser.uitour.enabled", false);
let highlight = document.getElementById("UITourHighlight");
is_element_hidden(highlight, "Highlight should initially be hidden");
gContentAPI.showHighlight("urlbar");
is_element_hidden(highlight, "Highlight should not be shown when feature is disabled");
Services.prefs.setBoolPref("browser.uitour.enabled", true);
done();
},
function test_untrusted_host(done) {
loadTestPage(function() {
let highlight = document.getElementById("UITourHighlight");
is_element_hidden(highlight, "Highlight should initially be hidden");
gContentAPI.showHighlight("urlbar");
is_element_hidden(highlight, "Highlight should not be shown on a untrusted domain");
done();
}, true);
},
function test_highlight(done) {
let highlight = document.getElementById("UITourHighlight");
is_element_hidden(highlight, "Highlight should initially be hidden");
gContentAPI.showHighlight("urlbar");
is_element_visible(highlight, "Highlight should be shown after showHighlight()");
gContentAPI.hideHighlight();
is_element_hidden(highlight, "Highlight should be hidden after hideHighlight()");
gContentAPI.showHighlight("urlbar");
is_element_visible(highlight, "Highlight should be shown after showHighlight()");
gContentAPI.showHighlight("backforward");
is_element_visible(highlight, "Highlight should be shown after showHighlight()");
done();
},
function test_info_1(done) {
let popup = document.getElementById("UITourTooltip");
let title = document.getElementById("UITourTooltipTitle");
let desc = document.getElementById("UITourTooltipDescription");
popup.addEventListener("popupshown", function onPopupShown() {
popup.removeEventListener("popupshown", onPopupShown);
is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
is(title.textContent, "test title", "Popup should have correct title");
is(desc.textContent, "test text", "Popup should have correct description text");
popup.addEventListener("popuphidden", function onPopupHidden() {
popup.removeEventListener("popuphidden", onPopupHidden);
popup.addEventListener("popupshown", function onPopupShown() {
popup.removeEventListener("popupshown", onPopupShown);
done();
});
gContentAPI.showInfo("urlbar", "test title", "test text");
});
gContentAPI.hideInfo();
});
gContentAPI.showInfo("urlbar", "test title", "test text");
},
function test_info_2(done) {
let popup = document.getElementById("UITourTooltip");
let title = document.getElementById("UITourTooltipTitle");
let desc = document.getElementById("UITourTooltipDescription");
popup.addEventListener("popupshown", function onPopupShown() {
popup.removeEventListener("popupshown", onPopupShown);
is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
is(title.textContent, "urlbar title", "Popup should have correct title");
is(desc.textContent, "urlbar text", "Popup should have correct description text");
gContentAPI.showInfo("search", "search title", "search text");
executeSoon(function() {
is(popup.popupBoxObject.anchorNode, document.getElementById("searchbar"), "Popup should be anchored to the searchbar");
is(title.textContent, "search title", "Popup should have correct title");
is(desc.textContent, "search text", "Popup should have correct description text");
done();
});
});
gContentAPI.showInfo("urlbar", "urlbar title", "urlbar text");
},
function test_pinnedTab(done) {
is(UITour.pinnedTabs.get(window), null, "Should not already have a pinned tab");
gContentAPI.addPinnedTab();
let tabInfo = UITour.pinnedTabs.get(window);
isnot(tabInfo, null, "Should have recorded data about a pinned tab after addPinnedTab()");
isnot(tabInfo.tab, null, "Should have added a pinned tab after addPinnedTab()");
is(tabInfo.tab.pinned, true, "Tab should be marked as pinned");
let tab = tabInfo.tab;
gContentAPI.removePinnedTab();
isnot(gBrowser.tabs[0], tab, "First tab should not be the pinned tab");
let tabInfo = UITour.pinnedTabs.get(window);
is(tabInfo, null, "Should not have any data about the removed pinned tab after removePinnedTab()");
gContentAPI.addPinnedTab();
gContentAPI.addPinnedTab();
gContentAPI.addPinnedTab();
is(gBrowser.tabs[1].pinned, false, "After multiple calls of addPinnedTab, should still only have one pinned tab");
done();
},
];

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>UITour test</title>
<script type="application/javascript" src="uitour.js">
</script>
</head>
<body>
<h1>UITour tests</h1>
<p>Because Firefox is...</p>
<p>Never gonna let you down</p>
<p>Never gonna give you up</p>
</body>
</html>

View File

@ -0,0 +1,115 @@
/* 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/. */
// Copied from the proposed JS library for Bedrock (ie, www.mozilla.org).
// create namespace
if (typeof Mozilla == 'undefined') {
var Mozilla = {};
}
(function($) {
'use strict';
// create namespace
if (typeof Mozilla.UITour == 'undefined') {
Mozilla.UITour = {};
}
var themeIntervalId = null;
function _stopCyclingThemes() {
if (themeIntervalId) {
clearInterval(themeIntervalId);
themeIntervalId = null;
}
}
function _sendEvent(action, data) {
var event = new CustomEvent('mozUITour', {
bubbles: true,
detail: {
action: action,
data: data || {}
}
});
console.log("Sending mozUITour event: ", event);
document.dispatchEvent(event);
}
Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY = 10 * 1000;
Mozilla.UITour.showHighlight = function(target) {
_sendEvent('showHighlight', {
target: target
});
};
Mozilla.UITour.hideHighlight = function() {
_sendEvent('hideHighlight');
};
Mozilla.UITour.showInfo = function(target, title, text) {
_sendEvent('showInfo', {
target: target,
title: title,
text: text
});
};
Mozilla.UITour.hideInfo = function() {
_sendEvent('hideInfo');
};
Mozilla.UITour.previewTheme = function(theme) {
_stopCyclingThemes();
_sendEvent('previewTheme', {
theme: JSON.stringify(theme)
});
};
Mozilla.UITour.resetTheme = function() {
_stopCyclingThemes();
_sendEvent('resetTheme');
};
Mozilla.UITour.cycleThemes = function(themes, delay, callback) {
_stopCyclingThemes();
if (!delay) {
delay = Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY;
}
function nextTheme() {
var theme = themes.shift();
themes.push(theme);
_sendEvent('previewTheme', {
theme: JSON.stringify(theme),
state: true
});
callback(theme);
}
themeIntervalId = setInterval(nextTheme, delay);
nextTheme();
};
Mozilla.UITour.addPinnedTab = function() {
_sendEvent('addPinnedTab');
};
Mozilla.UITour.removePinnedTab = function() {
_sendEvent('removePinnedTab');
};
Mozilla.UITour.showMenu = function(name) {
_sendEvent('showMenu', {
name: name
});
};
})();

View File

@ -30,9 +30,8 @@ this.webappsUI = {
Services.obs.addObserver(this, "webapps-launch", false);
Services.obs.addObserver(this, "webapps-uninstall", false);
cpmm.addMessageListener("Webapps:Install:Return:OK", this);
cpmm.addMessageListener("Webapps:OfflineCache", this);
cpmm.addMessageListener("Webapps:Install:Return:KO", this);
cpmm.addMessageListener("Webapps:PackageEvent", this);
cpmm.addMessageListener("Webapps:UpdateState", this);
},
uninit: function webappsUI_uninit() {
@ -40,9 +39,8 @@ this.webappsUI = {
Services.obs.removeObserver(this, "webapps-launch");
Services.obs.removeObserver(this, "webapps-uninstall");
cpmm.removeMessageListener("Webapps:Install:Return:OK", this);
cpmm.removeMessageListener("Webapps:OfflineCache", this);
cpmm.removeMessageListener("Webapps:Install:Return:KO", this);
cpmm.removeMessageListener("Webapps:PackageEvent", this);
cpmm.removeMessageListener("Webapps:UpdateState", this);
},
receiveMessage: function(aMessage) {
@ -56,10 +54,10 @@ this.webappsUI = {
return;
}
if (aMessage.name == "Webapps:OfflineCache") {
if (aMessage.name == "Webapps:UpdateState") {
if (data.error) {
this.installations[manifestURL].reject(data.error);
} else if (data.installState == "installed") {
} else if (data.app.installState == "installed") {
this.installations[manifestURL].resolve();
}
} else if (aMessage.name == "Webapps:Install:Return:OK" &&
@ -70,12 +68,6 @@ this.webappsUI = {
}
} else if (aMessage.name == "Webapps:Install:Return:KO") {
this.installations[manifestURL].reject(data.error);
} else if (aMessage.name == "Webapps:PackageEvent") {
if (data.type == "installed") {
this.installations[manifestURL].resolve();
} else if (data.type == "error") {
this.installations[manifestURL].reject(data.error);
}
}
},

View File

@ -2113,6 +2113,90 @@ toolbar[mode="text"] toolbarbutton.chevron > .toolbarbutton-icon {
-moz-margin-end: 2px;
}
/* UI Tour */
@keyframes uitour-wobble {
from {
transform: rotate(0deg) translateX(2px) rotate(0deg);
}
to {
transform: rotate(360deg) translateX(2px) rotate(-360deg);
}
}
@keyframes uitour-zoom {
from {
transform: scale(0.9);
}
50% {
transform: scale(1.1);
}
to {
transform: scale(0.9);
}
}
@keyframes uitour-color {
from {
border-color: #5B9CD9;
}
50% {
border-color: #FF0000;
}
to {
border-color: #5B9CD9;
}
}
html|div#UITourHighlight {
display: none;
position: absolute;
min-height: 32px;
min-width: 32px;
display: none;
border: 2px #5B9CD9 solid;
box-shadow: 0 0 2px #5B9CD9, inset 0 0 1px #5B9CD9;
border-radius: 20px;
z-index: 10000000000;
}
html|div#UITourHighlight[active] {
display: block;
animation-delay: 2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-fill-mode: forwards;
}
html|div#UITourHighlight[active="wobble"] {
animation-name: uitour-wobble;
animation-duration: 1s;
}
html|div#UITourHighlight[active="zoom"] {
animation-name: uitour-zoom;
animation-duration: 1s;
}
html|div#UITourHighlight[active="color"] {
animation-name: uitour-color;
animation-duration: 2s;
}
#UITourTooltip {
max-width: 20em;
}
#UITourTooltipTitle {
font-weight: bold;
font-size: 130%;
margin: 0 0 5px 0;
}
#UITourTooltipDescription {
max-width: 20em;
}
/* Social toolbar item */
#social-provider-button {
-moz-image-region: rect(0, 16px, 16px, 0);
list-style-image: url(chrome://browser/skin/social/services-16.png);

View File

@ -3728,6 +3728,88 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
border-radius: 1px;
}
/* UI Tour */
@keyframes uitour-wobble {
from {
transform: rotate(0deg) translateX(2px) rotate(0deg);
}
to {
transform: rotate(360deg) translateX(2px) rotate(-360deg);
}
}
@keyframes uitour-zoom {
from {
transform: scale(0.9);
}
50% {
transform: scale(1.1);
}
to {
transform: scale(0.9);
}
}
@keyframes uitour-color {
from {
border-color: #5B9CD9;
}
50% {
border-color: #FF0000;
}
to {
border-color: #5B9CD9;
}
}
html|div#UITourHighlight {
display: none;
position: absolute;
min-height: 32px;
min-width: 32px;
display: none;
border: 2px #5B9CD9 solid;
box-shadow: 0 0 2px #5B9CD9, inset 0 0 1px #5B9CD9;
border-radius: 20px;
z-index: 10000000000;
}
html|div#UITourHighlight[active] {
display: block;
animation-delay: 2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-fill-mode: forwards;
}
html|div#UITourHighlight[active="wobble"] {
animation-name: uitour-wobble;
animation-duration: 1s;
}
html|div#UITourHighlight[active="zoom"] {
animation-name: uitour-zoom;
animation-duration: 1s;
}
html|div#UITourHighlight[active="color"] {
animation-name: uitour-color;
animation-duration: 2s;
}
#UITourTooltip {
max-width: 20em;
}
#UITourTooltipTitle {
font-weight: bold;
font-size: 130%;
margin: 0 0 5px 0;
}
#UITourTooltipDescription {
max-width: 20em;
}
/* === social toolbar button === */
#social-toolbar-item > .toolbarbutton-1 {

View File

@ -2855,6 +2855,87 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
-moz-margin-end: 5px;
}
/* UI Tour */
@keyframes uitour-wobble {
from {
transform: rotate(0deg) translateX(2px) rotate(0deg);
}
to {
transform: rotate(360deg) translateX(2px) rotate(-360deg);
}
}
@keyframes uitour-zoom {
from {
transform: scale(0.9);
}
50% {
transform: scale(1.1);
}
to {
transform: scale(0.9);
}
}
@keyframes uitour-color {
from {
border-color: #5B9CD9;
}
50% {
border-color: #FF0000;
}
to {
border-color: #5B9CD9;
}
}
html|div#UITourHighlight {
display: none;
position: absolute;
min-height: 32px;
min-width: 32px;
display: none;
border: 2px #5B9CD9 solid;
box-shadow: 0 0 2px #5B9CD9, inset 0 0 1px #5B9CD9;
border-radius: 20px;
z-index: 10000000000;
}
html|div#UITourHighlight[active] {
display: block;
animation-delay: 2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-fill-mode: forwards;
}
html|div#UITourHighlight[active="wobble"] {
animation-name: uitour-wobble;
animation-duration: 1s;
}
html|div#UITourHighlight[active="zoom"] {
animation-name: uitour-zoom;
animation-duration: 1s;
}
html|div#UITourHighlight[active="color"] {
animation-name: uitour-color;
animation-duration: 2s;
}
#UITourTooltip {
}
#UITourTooltipTitle {
font-weight: bold;
font-size: 130%;
margin: 0 0 5px 0;
}
#UITourTooltipDescription {
max-width: 20em;
}
/* Social toolbar item */
#social-provider-button {

View File

@ -29,9 +29,9 @@ toolkit/library
editor
parser
js/src
js/xpconnect
js/xpconnect/loader
mfbt
js/xpconnect
js/xpconnect/loader
view
caps
xpfe/appshell

View File

@ -86,27 +86,41 @@ dnl ========================================================
MOZ_USE_PTHREADS=
_PTHREAD_LDFLAGS=""
dnl Do not allow a separate objdir build if a srcdir build exists.
dnl Do not allow objdir == srcdir builds.
dnl ==============================================================
_topsrcdir=`cd \`dirname $0\`; pwd`
_objdir=`pwd`
if test "$_topsrcdir" != "$_objdir"
then
# Check for a couple representative files in the source tree
_conflict_files=
for file in $_topsrcdir/Makefile $_topsrcdir/config/autoconf.mk; do
if test -f $file; then
_conflict_files="$_conflict_files $file"
fi
dnl TODO Don't exempt L10N builds once bug 842760 is resolved.
if test "$_topsrcdir" = "$_objdir" -a "${with_l10n_base+set}" != set; then
echo " ***"
echo " * Building directly in the main source directory is not allowed."
echo " *"
echo " * To build, you must run configure from a separate directory"
echo " * (referred to as an object directory)."
echo " *"
echo " * If you are building with a mozconfig, you will need to change your"
echo " * mozconfig to point to a different object directory."
echo " ***"
exit 1
fi
# Check for a couple representative files in the source tree
_conflict_files=
for file in $_topsrcdir/Makefile $_topsrcdir/config/autoconf.mk; do
if test -f $file; then
_conflict_files="$_conflict_files $file"
fi
done
if test "$_conflict_files"; then
echo "***"
echo "* Your source tree contains these files:"
for file in $_conflict_files; do
echo "* $file"
done
if test "$_conflict_files"; then
echo "***"
echo "* Your source tree contains these files:"
for file in $_conflict_files; do
echo "* $file"
done
cat 1>&2 <<-EOF
cat 1>&2 <<-EOF
* This indicates that you previously built in the source tree.
* A source tree build can confuse the separate objdir build.
*
@ -115,9 +129,8 @@ then
* 2. gmake distclean
***
EOF
exit 1
break
fi
exit 1
break
fi
MOZ_BUILD_ROOT=`pwd`

View File

@ -19,6 +19,7 @@
#endif
#include "js/TypeDecls.h"
#include "js/Value.h"
#include "js/RootingAPI.h"
#include "mozilla/Assertions.h"
#include "mozilla/EventForwards.h"
@ -1637,7 +1638,7 @@ public:
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, const nsIID* aIID,
JS::Value *vp,
JS::MutableHandle<JS::Value> vp,
// If non-null aHolder will keep the Value alive
// while there's a ref to it
nsIXPConnectJSObjectHolder** aHolder = nullptr,
@ -1649,7 +1650,7 @@ public:
// Same as the WrapNative above, but use this one if aIID is nsISupports' IID.
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, JS::Value *vp,
nsISupports *native, JS::MutableHandle<JS::Value> vp,
// If non-null aHolder will keep the Value alive
// while there's a ref to it
nsIXPConnectJSObjectHolder** aHolder = nullptr,
@ -1660,7 +1661,7 @@ public:
}
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, nsWrapperCache *cache,
JS::Value *vp,
JS::MutableHandle<JS::Value> vp,
// If non-null aHolder will keep the Value alive
// while there's a ref to it
nsIXPConnectJSObjectHolder** aHolder = nullptr,
@ -2091,7 +2092,7 @@ private:
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, nsWrapperCache *cache,
const nsIID* aIID, JS::Value *vp,
const nsIID* aIID, JS::MutableHandle<JS::Value> vp,
nsIXPConnectJSObjectHolder** aHolder,
bool aAllowWrapping);

View File

@ -5659,14 +5659,14 @@ nsContentUtils::DispatchXULCommand(nsIContent* aTarget,
nsresult
nsContentUtils::WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, nsWrapperCache *cache,
const nsIID* aIID, JS::Value *vp,
const nsIID* aIID, JS::MutableHandleValue vp,
nsIXPConnectJSObjectHolder **aHolder,
bool aAllowWrapping)
{
if (!native) {
NS_ASSERTION(!aHolder || !*aHolder, "*aHolder should be null!");
*vp = JSVAL_NULL;
vp.setNull();
return NS_OK;
}
@ -5685,7 +5685,7 @@ nsContentUtils::WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsresult rv = NS_OK;
AutoPushJSContext context(cx);
rv = sXPConnect->WrapNativeToJSVal(context, scope, native, cache, aIID,
aAllowWrapping, vp, aHolder);
aAllowWrapping, vp.address(), aHolder);
return rv;
}
@ -5728,8 +5728,7 @@ nsContentUtils::CreateBlobBuffer(JSContext* aCx,
return NS_ERROR_OUT_OF_MEMORY;
}
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
return nsContentUtils::WrapNative(aCx, scope, blob, aBlob.address(), nullptr,
true);
return nsContentUtils::WrapNative(aCx, scope, blob, aBlob, nullptr, true);
}
void

View File

@ -206,9 +206,11 @@ nsDOMFileReader::GetResult(JSContext* aCx, JS::Value* aResult)
}
nsString tmpResult = mResult;
if (!xpc::StringToJsval(aCx, tmpResult, aResult)) {
JS::Rooted<JS::Value> result(aCx);
if (!xpc::StringToJsval(aCx, tmpResult, &result)) {
return NS_ERROR_FAILURE;
}
*aResult = result;
return NS_OK;
}

View File

@ -5167,7 +5167,7 @@ CustomElementConstructor(JSContext *aCx, unsigned aArgc, JS::Value* aVp)
nsresult rv = document->CreateElem(elemName, nullptr, kNameSpaceID_XHTML,
getter_AddRefs(newElement));
rv = nsContentUtils::WrapNative(aCx, global, newElement, newElement,
args.rval().address());
args.rval());
NS_ENSURE_SUCCESS(rv, false);
return true;
@ -6732,7 +6732,7 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
JS::Rooted<JSObject*> global(cx, GetScopeObject()->GetGlobalJSObject());
JS::Rooted<JS::Value> v(cx);
rv = nsContentUtils::WrapNative(cx, global, this, this, v.address(),
rv = nsContentUtils::WrapNative(cx, global, this, this, &v,
nullptr, /* aAllowWrapping = */ false);
if (rv.Failed())
return nullptr;
@ -11345,7 +11345,7 @@ nsIDocument::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aScope)
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
nsresult rv = nsContentUtils::WrapNative(aCx, obj, win,
&NS_GET_IID(nsIDOMWindow),
winVal.address(),
&winVal,
getter_AddRefs(holder),
false);
if (NS_FAILED(rv)) {

View File

@ -832,7 +832,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
JS::Rooted<JS::Value> targetv(ctx);
JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
nsContentUtils::WrapNative(ctx, global, aTarget, targetv.address(),
nsContentUtils::WrapNative(ctx, global, aTarget, &targetv,
nullptr, true);
JS::RootedObject cpows(ctx);
@ -888,7 +888,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
}
JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
nsContentUtils::WrapNative(ctx, global, defaultThisValue,
thisValue.address(), nullptr, true);
&thisValue, nullptr, true);
} else {
// If the listener is a JS object which has receiveMessage function:
if (!JS_GetProperty(ctx, object, "receiveMessage", &funval) ||

View File

@ -49,7 +49,15 @@ void
nsHostObjectProtocolHandler::RemoveDataEntry(const nsACString& aUri)
{
if (gDataTable) {
gDataTable->Remove(aUri);
nsCString uriIgnoringRef;
int32_t hashPos = aUri.FindChar('#');
if (hashPos < 0) {
uriIgnoringRef = aUri;
}
else {
uriIgnoringRef = StringHead(aUri, hashPos);
}
gDataTable->Remove(uriIgnoringRef);
if (gDataTable->Count() == 0) {
delete gDataTable;
gDataTable = nullptr;
@ -80,6 +88,27 @@ nsHostObjectProtocolHandler::GenerateURIString(const nsACString &aScheme,
return NS_OK;
}
static DataInfo*
GetDataInfo(const nsACString& aUri)
{
if (!gDataTable) {
return nullptr;
}
DataInfo* res;
nsCString uriIgnoringRef;
int32_t hashPos = aUri.FindChar('#');
if (hashPos < 0) {
uriIgnoringRef = aUri;
}
else {
uriIgnoringRef = StringHead(aUri, hashPos);
}
gDataTable->Get(uriIgnoringRef, &res);
return res;
}
nsIPrincipal*
nsHostObjectProtocolHandler::GetDataEntryPrincipal(const nsACString& aUri)
{
@ -87,8 +116,8 @@ nsHostObjectProtocolHandler::GetDataEntryPrincipal(const nsACString& aUri)
return nullptr;
}
DataInfo* res;
gDataTable->Get(aUri, &res);
DataInfo* res = GetDataInfo(aUri);
if (!res) {
return nullptr;
}
@ -114,18 +143,6 @@ nsHostObjectProtocolHandler::Traverse(const nsACString& aUri,
aCallback.NoteXPCOMChild(res->mObject);
}
static DataInfo*
GetDataInfo(const nsACString& aUri)
{
if (!gDataTable) {
return nullptr;
}
DataInfo* res;
gDataTable->Get(aUri, &res);
return res;
}
static nsISupports*
GetDataObject(nsIURI* aURI)
{

View File

@ -966,7 +966,7 @@ nsXMLHttpRequest::GetResponse(JSContext* aCx, ErrorResult& aRv)
if (aRv.Failed()) {
return JSVAL_NULL;
}
JS::Value result;
JS::Rooted<JS::Value> result(aCx);
if (!xpc::StringToJsval(aCx, str, &result)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return JSVAL_NULL;
@ -1014,7 +1014,7 @@ nsXMLHttpRequest::GetResponse(JSContext* aCx, ErrorResult& aRv)
JS::Rooted<JS::Value> result(aCx, JSVAL_NULL);
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
aRv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, result.address(),
aRv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, &result,
nullptr, true);
return result;
}
@ -1026,7 +1026,7 @@ nsXMLHttpRequest::GetResponse(JSContext* aCx, ErrorResult& aRv)
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
JS::Rooted<JS::Value> result(aCx, JSVAL_NULL);
aRv = nsContentUtils::WrapNative(aCx, scope, mResponseXML, result.address(),
aRv = nsContentUtils::WrapNative(aCx, scope, mResponseXML, &result,
nullptr, true);
return result;
}
@ -3659,7 +3659,7 @@ nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aRv)
JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
JSAutoCompartment ac(aCx, wrapper);
JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, wrapper));
aRv = nsContentUtils::WrapNative(aCx, global, result, iid, v.address());
aRv = nsContentUtils::WrapNative(aCx, global, result, iid, &v);
return aRv.Failed() ? JSVAL_NULL : v;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1 @@
== test_bug920877.html test_bug920877-ref.html

View File

@ -0,0 +1,20 @@
<html>
<body>
<script>
var img = document.createElement("img");
img.id = "img-ori";
img.src = "mixed-bmp-png.ico";
document.body.appendChild(img);
img = document.createElement("img");
img.id = "img-res32";
img.src = "mixed-bmp-png.ico#-moz-resolution=32,32";
document.body.appendChild(img);
img = document.createElement("img");
img.id = "img-res48";
img.src = "mixed-bmp-png.ico#-moz-resolution=48,48";
document.body.appendChild(img);
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -114,7 +114,7 @@ HTMLAudioElement::MozSetup(uint32_t aChannels, uint32_t aRate, ErrorResult& aRv)
#endif
mAudioStream = AudioStream::AllocateStream();
aRv = mAudioStream->Init(aChannels, aRate, mAudioChannelType);
aRv = mAudioStream->Init(aChannels, aRate, mAudioChannelType, AudioStream::HighLatency);
if (aRv.Failed()) {
mAudioStream->Shutdown();
mAudioStream = nullptr;

View File

@ -3155,7 +3155,7 @@ nsGenericHTMLElement::GetItemValue(JSContext* aCx, JSObject* aScope,
nsString string;
GetItemValueText(string);
JS::Value v;
JS::Rooted<JS::Value> v(aCx);
if (!xpc::NonVoidStringToJsval(aCx, string, &v)) {
aError.Throw(NS_ERROR_FAILURE);
return JS::UndefinedValue();

View File

@ -41,6 +41,11 @@ PRLogModuleInfo* gAudioStreamLog = nullptr;
static Mutex* gAudioPrefsLock = nullptr;
static double gVolumeScale;
static uint32_t gCubebLatency;
static bool gCubebLatencyPrefSet;
static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
StaticMutex AudioStream::mMutex;
uint32_t AudioStream::mPreferredSampleRate = 0;
/**
* When MOZ_DUMP_AUDIO is set in the environment (to anything),
@ -65,9 +70,10 @@ static int PrefChanged(const char* aPref, void* aClosure)
// Arbitrary default stream latency of 100ms. The higher this
// value, the longer stream volume changes will take to become
// audible.
uint32_t value = Preferences::GetUint(aPref, 100);
gCubebLatencyPrefSet = Preferences::HasUserValue(aPref);
uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_MS);
MutexAutoLock lock(*gAudioPrefsLock);
gCubebLatency = std::min<uint32_t>(std::max<uint32_t>(value, 20), 1000);
gCubebLatency = std::min<uint32_t>(std::max<uint32_t>(value, 1), 1000);
}
return 0;
}
@ -97,6 +103,12 @@ static uint32_t GetCubebLatency()
MutexAutoLock lock(*gAudioPrefsLock);
return gCubebLatency;
}
static bool CubebLatencyPrefSet()
{
MutexAutoLock lock(*gAudioPrefsLock);
return gCubebLatencyPrefSet;
}
#endif
#if defined(MOZ_CUBEB) && defined(__ANDROID__) && defined(MOZ_B2G)
@ -311,7 +323,8 @@ class BufferedAudioStream : public AudioStream
~BufferedAudioStream();
nsresult Init(int32_t aNumChannels, int32_t aRate,
const dom::AudioChannelType aAudioChannelType);
const dom::AudioChannelType aAudioChannelType,
AudioStream::LatencyRequest aLatencyRequest);
void Shutdown();
nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames);
uint32_t Available();
@ -431,6 +444,23 @@ int AudioStream::MaxNumberOfChannels()
return static_cast<int>(maxNumberOfChannels);
}
int AudioStream::PreferredSampleRate()
{
StaticMutexAutoLock lock(AudioStream::mMutex);
// Get the preferred samplerate for this platform, or fallback to something
// sensible if we fail. We cache the value, because this might be accessed
// often, and the complexity of the function call below depends on the
// backend used.
const int fallbackSampleRate = 44100;
if (mPreferredSampleRate == 0) {
if (cubeb_get_preferred_sample_rate(GetCubebContext(), &mPreferredSampleRate) != CUBEB_OK) {
mPreferredSampleRate = fallbackSampleRate;
}
}
return mPreferredSampleRate;
}
static void SetUint16LE(uint8_t* aDest, uint16_t aValue)
{
aDest[0] = aValue & 0xFF;
@ -526,7 +556,8 @@ BufferedAudioStream::EnsureTimeStretcherInitialized()
nsresult
BufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate,
const dom::AudioChannelType aAudioChannelType)
const dom::AudioChannelType aAudioChannelType,
AudioStream::LatencyRequest aLatencyRequest)
{
cubeb* cubebContext = GetCubebContext();
@ -562,10 +593,22 @@ BufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate,
mAudioClock.Init();
// If the latency pref is set, use it. Otherwise, if this stream is intended
// for low latency playback, try to get the lowest latency possible.
// Otherwise, for normal streams, use 100ms.
uint32_t latency;
if (aLatencyRequest == AudioStream::LowLatency && !CubebLatencyPrefSet()) {
if (cubeb_get_min_latency(cubebContext, params, &latency) != CUBEB_OK) {
latency = GetCubebLatency();
}
} else {
latency = GetCubebLatency();
}
{
cubeb_stream* stream;
if (cubeb_stream_init(cubebContext, &stream, "BufferedAudioStream", params,
GetCubebLatency(), DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
latency, DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
mCubebStream.own(stream);
}
}

View File

@ -11,6 +11,7 @@
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "Latency.h"
#include "mozilla/StaticMutex.h"
namespace soundtouch {
class SoundTouch;
@ -92,6 +93,10 @@ class AudioClock
class AudioStream
{
public:
enum LatencyRequest {
HighLatency,
LowLatency
};
AudioStream();
virtual ~AudioStream();
@ -112,11 +117,16 @@ public:
// Returns the maximum number of channels supported by the audio hardware.
static int MaxNumberOfChannels();
// Returns the samplerate the systems prefer, because it is the
// samplerate the hardware/mixer supports.
static int PreferredSampleRate();
// Initialize the audio stream. aNumChannels is the number of audio
// channels (1 for mono, 2 for stereo, etc) and aRate is the sample rate
// (22050Hz, 44100Hz, etc).
virtual nsresult Init(int32_t aNumChannels, int32_t aRate,
const dom::AudioChannelType aAudioStreamType) = 0;
const dom::AudioChannelType aAudioStreamType,
LatencyRequest aLatencyRequest) = 0;
// Closes the stream. All future use of the stream is an error.
virtual void Shutdown() = 0;
@ -178,6 +188,11 @@ public:
virtual nsresult SetPreservesPitch(bool aPreservesPitch);
protected:
// This mutex protects the mPreferedSamplerate member below.
static StaticMutex mMutex;
// Prefered samplerate, in Hz (characteristic of the
// hardware/mixer/platform/API used).
static uint32_t mPreferredSampleRate;
// Input rate in Hz (characteristic of the media being played)
int mInRate;
// Output rate in Hz (characteristic of the playback rate)

View File

@ -1059,7 +1059,7 @@ void MediaDecoderStateMachine::AudioLoop()
// circumstances, so we take care to drop the decoder monitor while
// initializing.
nsAutoPtr<AudioStream> audioStream(AudioStream::AllocateStream());
audioStream->Init(channels, rate, audioChannelType);
audioStream->Init(channels, rate, audioChannelType, AudioStream::HighLatency);
audioStream->SetVolume(volume);
if (audioStream->SetPreservesPitch(preservesPitch) != NS_OK) {
NS_WARNING("Setting the pitch preservation failed at AudioLoop start.");

View File

@ -768,7 +768,7 @@ MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTim
audioOutputStream->mStream = AudioStream::AllocateStream();
// XXX for now, allocate stereo output. But we need to fix this to
// match the system's ideal channel configuration.
audioOutputStream->mStream->Init(2, tracks->GetRate(), AUDIO_CHANNEL_NORMAL);
audioOutputStream->mStream->Init(2, tracks->GetRate(), AUDIO_CHANNEL_NORMAL, AudioStream::LowLatency);
audioOutputStream->mTrackID = tracks->GetID();
}
}

View File

@ -960,7 +960,7 @@ protected:
};
// Returns ideal audio rate for processing
inline TrackRate IdealAudioRate() { return 48000; }
inline TrackRate IdealAudioRate() { return AudioStream::PreferredSampleRate(); }
/**
* Initially, at least, we will have a singleton MediaStreamGraph per

View File

@ -119,6 +119,7 @@ OpusTrackEncoder::OpusTrackEncoder()
, mEncoder(nullptr)
, mSourceSegment(new AudioSegment())
, mLookahead(0)
, mResampler(nullptr)
{
}
@ -147,16 +148,24 @@ OpusTrackEncoder::Init(int aChannels, int aSamplingRate)
// The granule position is required to be incremented at a rate of 48KHz, and
// it is simply calculated as |granulepos = samples * (48000/source_rate)|,
// that is, the source sampling rate must divide 48000 evenly.
// If this constraint is not satisfied, we resample the input to 48kHz.
if (!((aSamplingRate >= 8000) && (kOpusSamplingRate / aSamplingRate) *
aSamplingRate == kOpusSamplingRate)) {
LOG("[Opus] Error! The source sample rate should be greater than 8000 and"
" divides 48000 evenly.");
return NS_ERROR_FAILURE;
int error;
mResampler = speex_resampler_init(mChannels,
aSamplingRate,
kOpusSamplingRate,
SPEEX_RESAMPLER_QUALITY_DEFAULT,
&error);
if (error != RESAMPLER_ERR_SUCCESS) {
return NS_ERROR_FAILURE;
}
}
mSamplingRate = aSamplingRate;
int error = 0;
mEncoder = opus_encoder_create(mSamplingRate, mChannels,
mEncoder = opus_encoder_create(GetOutputSampleRate(), mChannels,
OPUS_APPLICATION_AUDIO, &error);
mInitialized = (error == OPUS_OK);
@ -166,10 +175,16 @@ OpusTrackEncoder::Init(int aChannels, int aSamplingRate)
return error == OPUS_OK ? NS_OK : NS_ERROR_FAILURE;
}
int
OpusTrackEncoder::GetOutputSampleRate()
{
return mResampler ? kOpusSamplingRate : mSamplingRate;
}
int
OpusTrackEncoder::GetPacketDuration()
{
return mSamplingRate * kFrameDurationMs / 1000;
return GetOutputSampleRate() * kFrameDurationMs / 1000;
}
nsresult
@ -282,8 +297,35 @@ OpusTrackEncoder::GetEncodedTrack(nsTArray<uint8_t>* aOutput,
iter.Next();
}
// The ogg time stamping and pre-skip is always timed at 48000.
aOutputDuration = frameCopied * (kOpusSamplingRate / mSamplingRate);
if (mResampler) {
nsAutoTArray<AudioDataValue, 9600> resamplingDest;
// We want to consume all the input data, so we slightly oversize the
// resampled data buffer so we can fit the output data in. We cannot really
// predict the output frame count at each call.
uint32_t outframes = frameCopied * kOpusSamplingRate / mSamplingRate + 1;
uint32_t inframes = frameCopied;
resamplingDest.SetLength(outframes * mChannels);
#if MOZ_SAMPLE_TYPE_S16
short* in = reinterpret_cast<short*>(pcm.Elements());
short* out = reinterpret_cast<short*>(resamplingDest.Elements());
speex_resampler_process_interleaved_int(mResampler, in, &inframes,
out, &outframes);
#else
float* in = reinterpret_cast<float*>(pcm.Elements());
float* out = reinterpret_cast<float*>(resamplingDest.Elements());
speex_resampler_process_interleaved_float(mResampler, in, &inframes,
out, &outframes);
#endif
pcm = resamplingDest;
// This is always at 48000Hz.
aOutputDuration = outframes;
} else {
// The ogg time stamping and pre-skip is always timed at 48000.
aOutputDuration = frameCopied * (kOpusSamplingRate / mSamplingRate);
}
// Remove the raw data which has been pulled to pcm buffer.
// The value of frameCopied should equal to (or smaller than, if eos)
@ -294,6 +336,9 @@ OpusTrackEncoder::GetEncodedTrack(nsTArray<uint8_t>* aOutput,
// encoding.
if (mSourceSegment->GetDuration() == 0 && mEndOfStream) {
mDoneEncoding = true;
if (mResampler) {
speex_resampler_destroy(mResampler);
}
LOG("[Opus] Done encoding.");
}

View File

@ -6,6 +6,8 @@
#ifndef OpusTrackEncoder_h_
#define OpusTrackEncoder_h_
#include <stdint.h>
#include <speex/speex_resampler.h>
#include "TrackEncoder.h"
#include "nsCOMPtr.h"
@ -35,6 +37,12 @@ private:
DATA
} mEncoderState;
/**
* Get the samplerate of the data to be fed to the Opus encoder. This might be
* different from the intput samplerate if resampling occurs.
*/
int GetOutputSampleRate();
/**
* The Opus encoder from libopus.
*/
@ -54,6 +62,12 @@ private:
* in order to align the time of input and output.
*/
int mLookahead;
/**
* If the input sample rate does not divide 48kHz evenly, the input data are
* resampled.
*/
SpeexResamplerState* mResampler;
};
}

View File

@ -231,6 +231,7 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED_1(AudioDestinationNode, AudioNode,
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
NS_IMPL_ADDREF_INHERITED(AudioDestinationNode, AudioNode)

View File

@ -12,6 +12,7 @@
#include "nsIDOMEventListener.h"
#include "nsIAudioChannelAgent.h"
#include "AudioChannelCommon.h"
#include "nsWeakReference.h"
namespace mozilla {
namespace dom {
@ -21,6 +22,7 @@ class AudioContext;
class AudioDestinationNode : public AudioNode
, public nsIDOMEventListener
, public nsIAudioChannelAgentCallback
, public nsSupportsWeakReference
{
public:
// This node type knows what MediaStreamGraph to use based on

View File

@ -50,13 +50,13 @@ DelayProcessor::Process(const double *aPerFrameDelays,
MOZ_ASSERT(readPosition >= 0.0, "Why are we reading before the beginning of the buffer?");
// Here is a the reason why readIndex1 and readIndex will never be out
// of bounds. The maximum value for bufferLength is 180 * 48000 (see
// AudioContext::CreateDelay). The maximum value for mCurrentDelay is
// 180.0, so initially readPosition cannot be more than bufferLength +
// a fraction less than 1. Then we take care of that case by
// subtracting bufferLength from it if needed. So, if
// |bufferLength-readPosition<1.0|, readIndex1 will end up being zero.
// If |1.0<=bufferLength-readPosition<2.0|, readIndex1 will be
// of bounds. The maximum value for bufferLength is
// 180 * AudioContext.samplerate (see AudioContext::CreateDelay). The
// maximum value for mCurrentDelay is 180.0, so initially readPosition
// cannot be more than bufferLength + a fraction less than 1. Then we
// take care of that case by subtracting bufferLength from it if needed.
// So, if |bufferLength-readPosition<1.0|, readIndex1 will end up being
// zero. If |1.0<=bufferLength-readPosition<2.0|, readIndex1 will be
// bufferLength-1 and readIndex2 will be 0.
int readIndex1 = int(readPosition);
int readIndex2 = (readIndex1 + 1) % bufferLength;

View File

@ -12,15 +12,14 @@ support-files =
small-shot-expected.wav
small-shot-mono-expected.wav
small-shot.ogg
ting-dualchannel44.1-expected.wav
ting-dualchannel44.1.ogg
ting-dualchannel48-expected.wav
ting-dualchannel48.ogg
ting-expected.wav
ting-mono-dualchannel44.1-expected.wav
ting-mono-dualchannel48-expected.wav
ting-mono-expected.wav
ting.ogg
ting-44.1k-1ch.ogg
ting-44.1k-2ch.ogg
ting-48k-1ch.ogg
ting-48k-2ch.ogg
ting-44.1k-1ch.wav
ting-44.1k-2ch.wav
ting-48k-1ch.wav
ting-48k-2ch.wav
webaudio.js
[test_AudioBuffer.html]

View File

@ -13,7 +13,6 @@ SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
var ac = new AudioContext();
ok(ac, "Create a AudioContext object");
is(ac.sampleRate, 48000, "Correct sample rate");
ok(ac instanceof EventTarget, "AudioContexts must be EventTargets");
SimpleTest.finish();
});

View File

@ -105,63 +105,91 @@ function createWaveFileData(audioBuffer) {
</script>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var cx = new AudioContext();
// fuzzTolerance and fuzzToleranceMobile are used to determine fuzziness
// thresholds. They're needed to make sure that we can deal with neglibible
// differences in the binary buffer caused as a result of resampling the
// audio. fuzzToleranceMobile is typically larger on mobile platforms since
// we do fixed-point resampling as opposed to floating-point resampling on
// those platforms.
var tests = [
// A normal ogg file, 44.1khz
var files = [
// An ogg file, 44.1khz, mono
{
url: "ting.ogg",
url: "ting-44.1k-1ch.ogg",
valid: true,
expected: "ting-expected.wav",
expectedMono: "ting-mono-expected.wav",
numberOfChannels: 2,
duration: 0.6936,
length: 33294,
fuzzTolerance: 38,
fuzzToleranceMobile: 15906
expected: "ting-44.1k-1ch.wav",
numberOfChannels: 1,
frames: 30592,
sampleRate: 44100,
duration: 0.693,
fuzzTolerance: 5,
fuzzToleranceMobile: 1284
},
// An ogg file with two different channels, 44.1khz
// An ogg file, 44.1khz, stereo
{
url: "ting-dualchannel44.1.ogg",
url: "ting-44.1k-2ch.ogg",
valid: true,
expected: "ting-dualchannel44.1-expected.wav",
expectedMono: "ting-mono-dualchannel44.1-expected.wav",
expected: "ting-44.1k-2ch.wav",
numberOfChannels: 2,
duration: 0.6932,
length: 33274,
fuzzTolerance: 39,
fuzzToleranceMobile: 16798
frames: 30592,
sampleRate: 44100,
duration: 0.693,
fuzzTolerance: 6,
fuzzToleranceMobile: 2544
},
// An ogg file with two different channels, 48khz
// An ogg file, 48khz, mono
{
url: "ting-dualchannel48.ogg",
url: "ting-48k-1ch.ogg",
valid: true,
expected: "ting-dualchannel48-expected.wav",
expectedMono: "ting-mono-dualchannel48-expected.wav",
numberOfChannels: 2,
duration: 0.6373,
length: 30592,
fuzzTolerance: 9,
fuzzToleranceMobile: 8000
expected: "ting-48k-1ch.wav",
numberOfChannels: 1,
frames: 33297,
sampleRate: 48000,
duration: 0.693,
fuzzTolerance: 7,
fuzzToleranceMobile: 7070
},
// An ogg file which needs to be resampled
// An ogg file, 48khz, stereo
{
url: "small-shot.ogg",
url: "ting-48k-2ch.ogg",
valid: true,
expected: "small-shot-expected.wav",
expectedMono: "small-shot-mono-expected.wav",
expected: "ting-48k-2ch.wav",
numberOfChannels: 2,
duration: 0.2760,
length: 13248,
fuzzTolerance: 76,
fuzzToleranceMobile: 14844
frames: 33297,
sampleRate: 48000,
duration: 0.693,
fuzzTolerance: 12,
fuzzToleranceMobile: 13982
},
// A wave file
//{ url: "24bit-44khz.wav", valid: true, expected: "24bit-44khz-expected.wav" },
// Make sure decoding a wave file results in the same buffer (for both the
// resampling and non-resampling cases)
{
url: "ting-44.1k-1ch.wav",
valid: true,
expected: "ting-44.1k-1ch.wav",
numberOfChannels: 1,
frames: 30592,
sampleRate: 44100,
duration: 0.693,
fuzzTolerance: 0,
fuzzToleranceMobile: 0
},
{
url: "ting-48k-1ch.wav",
valid: true,
expected: "ting-48k-1ch.wav",
numberOfChannels: 1,
frames: 33297,
sampleRate: 48000,
duration: 0.693,
fuzzTolerance: 0,
fuzzToleranceMobile: 0
},
// // A wave file
// //{ url: "24bit-44khz.wav", valid: true, expected: "24bit-44khz-expected.wav" },
// A non-audio file
{ url: "invalid.txt", valid: false },
// A webm file with no audio
@ -171,24 +199,12 @@ var tests = [
url: "audio.ogv",
valid: true,
expected: "audio-expected.wav",
expectedMono: "audio-mono-expected.wav",
numberOfChannels: 2,
sampleRate: 44100,
frames: 47680,
duration: 1.0807,
length: 51872,
fuzzTolerance: 91427,
fuzzToleranceMobile: 119684
},
// Make sure decoding a wave file results in the same buffer
{
url: "audio-expected.wav",
valid: true,
expected: "audio-expected.wav",
expectedMono: "audio-mono-expected-2.wav",
numberOfChannels: 2,
duration: 1.0807,
length: 51872,
fuzzTolerance: 0,
fuzzToleranceMobile: 0
fuzzTolerance: 106,
fuzzToleranceMobile: 3482
}
];
@ -198,7 +214,7 @@ function fuzzyMemcmp(buf1, buf2, fuzz) {
var difference = 0;
is(buf1.length, buf2.length, "same length");
for (var i = 0; i < buf1.length; ++i) {
if (buf1[i] != buf2[i]) {
if (Math.abs(buf1[i] - buf2[i])) {
++difference;
}
}
@ -216,17 +232,20 @@ function getFuzzTolerance(test) {
}
function checkAudioBuffer(buffer, test, callback, monoTest) {
is(buffer.numberOfChannels, monoTest ? 1 : test.numberOfChannels, "Correct number of channels");
ok(Math.abs(buffer.duration - test.duration) < 1e-4, "Correct duration");
if (Math.abs(buffer.duration - test.duration) >= 1e-4) {
is(buffer.numberOfChannels, test.numberOfChannels, "Correct number of channels");
ok(Math.abs(buffer.duration - test.duration) < 1e-3, "Correct duration");
if (Math.abs(buffer.duration - test.duration) >= 1e-3) {
ok(false, "got: " + buffer.duration + ", expected: " + test.duration);
}
is(buffer.sampleRate, cx.sampleRate, "Correct sample rate");
is(buffer.length, test.length, "Correct length");
// Take into account the resampling when checking the size
var SRCRate = test.sampleRate / cx.sampleRate;
ok(Math.abs(buffer.length * SRCRate - test.frames) < test.frames * 0.01, "Correct length");
var wave = createWaveFileData(buffer);
var getExpected = new XMLHttpRequest();
getExpected.open("GET", monoTest ? test.expectedMono : test.expected, true);
getExpected.open("GET", test.expected, true);
getExpected.responseType = "arraybuffer";
getExpected.onload = function() {
ok(fuzzyMemcmp(wave, new Uint8Array(getExpected.response), getFuzzTolerance(test)), "Received expected decoded data");
@ -248,12 +267,7 @@ function runTest(test, callback) {
checkAudioBuffer(result, test, function() {
result = cx.createBuffer(xhr.response, false);
checkAudioBuffer(result, test, function() {
if ("expectedMono" in test) {
result = cx.createBuffer(xhr.response, true);
checkAudioBuffer(result, test, callback, true);
} else {
callback();
}
}, false);
}, false);
}, function onFailure() {
@ -267,17 +281,13 @@ function runTest(test, callback) {
}
function runNextTest() {
if (tests.length) {
runTest(tests.shift(), runNextTest);
if (files.length) {
runTest(files.shift(), runNextTest);
} else {
SimpleTest.finish();
}
}
SimpleTest.waitForExplicitFinish();
var cx = new AudioContext();
// Run some simple tests first
function callbackShouldNeverRun() {
ok(false, "callback should not fire");
@ -298,7 +308,7 @@ expectTypeError(function() {
cx.decodeAudioData(new Uint8Array(100), callbackShouldNeverRun, callbackShouldNeverRun);
});
if (cx.sampleRate == 48000) {
if (cx.sampleRate >= 44100) {
// Now, let's get real!
runNextTest();
} else {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,6 +3,8 @@
# To: /content/canvas/test/reftest
skip-if(xulFennec) include ../../canvas/test/reftest/reftest.list
include ../../base/test/reftest/reftest.list # bug 920877
== bug453105.html bug453105-ref.html
== optiontext.html optiontext-ref.html
== bug456008.xhtml bug456008-ref.html

View File

@ -166,7 +166,7 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding,
dom::XULElementBinding::GetConstructorObject(cx, global, defineOnGlobal);
}
rv = nsContentUtils::WrapNative(cx, global, aBoundElement, v.address(),
rv = nsContentUtils::WrapNative(cx, global, aBoundElement, &v,
getter_AddRefs(wrapper));
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -309,7 +309,7 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
JS::Rooted<JS::Value> v(cx);
nsresult rv =
nsContentUtils::WrapNative(cx, globalObject, aBoundElement, v.address(),
nsContentUtils::WrapNative(cx, globalObject, aBoundElement, &v,
getter_AddRefs(wrapper));
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -307,7 +307,7 @@ nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget,
// scope if one doesn't already exist, and potentially wraps it cross-
// compartment into our scope (via aAllowWrapping=true).
JS::Rooted<JS::Value> targetV(cx, JS::UndefinedValue());
rv = nsContentUtils::WrapNative(cx, scopeObject, scriptTarget, targetV.address(), nullptr,
rv = nsContentUtils::WrapNative(cx, scopeObject, scriptTarget, &targetV, nullptr,
/* aAllowWrapping = */ true);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -1385,7 +1385,7 @@ nsXULTemplateBuilder::InitHTMLTemplateRoot()
JS::Rooted<JSObject*> scope(jscontext, global->GetGlobalJSObject());
JS::Rooted<JS::Value> v(jscontext);
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
rv = nsContentUtils::WrapNative(jscontext, scope, mRoot, mRoot, v.address(),
rv = nsContentUtils::WrapNative(jscontext, scope, mRoot, mRoot, &v,
getter_AddRefs(wrapper));
NS_ENSURE_SUCCESS(rv, rv);
@ -1396,7 +1396,7 @@ nsXULTemplateBuilder::InitHTMLTemplateRoot()
JS::Rooted<JS::Value> jsdatabase(jscontext);
rv = nsContentUtils::WrapNative(jscontext, scope, mDB,
&NS_GET_IID(nsIRDFCompositeDataSource),
jsdatabase.address(), getter_AddRefs(wrapper));
&jsdatabase, getter_AddRefs(wrapper));
NS_ENSURE_SUCCESS(rv, rv);
bool ok;
@ -1413,7 +1413,7 @@ nsXULTemplateBuilder::InitHTMLTemplateRoot()
rv = nsContentUtils::WrapNative(jscontext, jselement,
static_cast<nsIXULTemplateBuilder*>(this),
&NS_GET_IID(nsIXULTemplateBuilder),
jsbuilder.address(), getter_AddRefs(wrapper));
&jsbuilder, getter_AddRefs(wrapper));
NS_ENSURE_SUCCESS(rv, rv);
bool ok;

View File

@ -347,27 +347,24 @@ WebappsApplication.prototype = {
this._downloadError = null;
this.initDOMRequestHelper(aWindow, [
"Webapps:OfflineCache",
"Webapps:CheckForUpdate:Return:OK",
"Webapps:CheckForUpdate:Return:KO",
"Webapps:PackageEvent",
"Webapps:Connect:Return:OK",
"Webapps:Connect:Return:KO",
"Webapps:GetConnections:Return:OK"
"Webapps:FireEvent",
"Webapps:GetConnections:Return:OK",
"Webapps:UpdateState"
]);
cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
{
messages: ["Webapps:OfflineCache",
"Webapps:PackageEvent",
"Webapps:CheckForUpdate:Return:OK"],
app: {
id: this.id,
manifestURL: this.manifestURL,
installState: this.installState,
downloading: this.downloading
}
});
cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
messages: ["Webapps:FireEvent",
"Webapps:UpdateState"],
app: {
id: this.id,
manifestURL: this.manifestURL,
installState: this.installState,
downloading: this.downloading
}
});
},
get manifest() {
@ -378,8 +375,10 @@ WebappsApplication.prototype = {
},
get updateManifest() {
return this.updateManifest = this._updateManifest ? ObjectWrapper.wrap(this._updateManifest, this._window)
: null;
return this.updateManifest =
this._updateManifest ? ObjectWrapper.wrap(this._updateManifest,
this._window)
: null;
},
set onprogress(aCallback) {
@ -512,18 +511,42 @@ WebappsApplication.prototype = {
uninit: function() {
this._onprogress = null;
cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
["Webapps:OfflineCache",
"Webapps:PackageEvent",
"Webapps:CheckForUpdate:Return:OK"]);
cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [
"Webapps:FireEvent",
"Webapps:PackageEvent"
]);
manifestCache.evict(this.manifestURL, this.innerWindowID);
},
_fireEvent: function(aName, aHandler) {
if (aHandler) {
let event = new this._window.MozApplicationEvent(aName, { application: this });
aHandler.handleEvent(event);
_fireEvent: function(aName) {
let handler = this["_on" + aName];
if (handler) {
let event = new this._window.MozApplicationEvent(aName, {
application: this
});
try {
handler.handleEvent(event);
} catch (ex) {
dump("Event handler expection " + ex + "\n");
}
}
},
_updateState: function(aMsg) {
if (aMsg.app) {
for (let prop in aMsg.app) {
this[prop] = aMsg.app[prop];
}
}
if (aMsg.error) {
this._downloadError = aMsg.error;
}
if (aMsg.manifest) {
this._manifest = aMsg.manifest;
manifestCache.evict(this.manifestURL, this.innerWindowID);
}
},
@ -540,10 +563,11 @@ WebappsApplication.prototype = {
// ondownload* callbacks should be triggered on all app instances
if ((msg.oid != this._id || !req) &&
aMessage.name !== "Webapps:OfflineCache" &&
aMessage.name !== "Webapps:PackageEvent" &&
aMessage.name !== "Webapps:CheckForUpdate:Return:OK")
aMessage.name !== "Webapps:FireEvent" &&
aMessage.name !== "Webapps:UpdateState") {
return;
}
switch (aMessage.name) {
case "Webapps:Launch:Return:KO":
this.removeMessageListeners(["Webapps:Launch:Return:OK",
@ -558,108 +582,36 @@ WebappsApplication.prototype = {
case "Webapps:CheckForUpdate:Return:KO":
Services.DOMRequest.fireError(req, msg.error);
break;
case "Webapps:CheckForUpdate:Return:OK":
if (msg.manifestURL != this.manifestURL)
return;
manifestCache.evict(this.manifestURL, this.innerWindowID);
let hiddenProps = ["manifest", "updateManifest"];
let updatableProps = ["installOrigin", "installTime", "installState",
"lastUpdateCheck", "updateTime", "progress", "downloadAvailable",
"downloading", "readyToApplyDownload", "downloadSize"];
// Props that we don't update: origin, receipts, manifestURL, removable.
updatableProps.forEach(function(prop) {
if (msg.app[prop]) {
this[prop] = msg.app[prop];
}
}, this);
hiddenProps.forEach(function(prop) {
if (msg.app[prop]) {
this["_" + prop] = msg.app[prop];
}
}, this);
if (msg.event == "downloadapplied") {
this._fireEvent("downloadapplied", this._ondownloadapplied);
} else if (msg.event == "downloadavailable") {
this._fireEvent("downloadavailable", this._ondownloadavailable);
case "Webapps:FireEvent":
if (msg.manifestURL != this.manifestURL) {
return;
}
// The parent might ask childs to trigger more than one event in one
// shot, so in order to avoid needless IPC we allow an array for the
// 'eventType' IPC message field.
if (!Array.isArray(msg.eventType)) {
msg.eventType = [msg.eventType];
}
msg.eventType.forEach((aEventType) => {
if ("_on" + aEventType in this) {
this._fireEvent(aEventType);
} else {
dump("Unsupported event type " + aEventType + "\n");
}
});
if (req) {
Services.DOMRequest.fireSuccess(req, this.manifestURL);
}
break;
case "Webapps:OfflineCache":
if (msg.manifest != this.manifestURL)
case "Webapps:UpdateState":
if (msg.manifestURL != this.manifestURL) {
return;
if ("installState" in msg) {
this.installState = msg.installState;
this.progress = msg.progress;
if (this.installState == "installed") {
this._downloadError = null;
this.downloading = false;
this.downloadAvailable = false;
this._fireEvent("downloadsuccess", this._ondownloadsuccess);
this._fireEvent("downloadapplied", this._ondownloadapplied);
} else {
this.downloading = true;
this._fireEvent("downloadprogress", this._onprogress);
}
} else if (msg.error) {
this._downloadError = msg.error;
this.downloading = false;
this._fireEvent("downloaderror", this._ondownloaderror);
}
break;
case "Webapps:PackageEvent":
if (msg.manifestURL != this.manifestURL)
return;
// Set app values according to parent process results.
let app = msg.app;
this.downloading = app.downloading;
this.downloadAvailable = app.downloadAvailable;
this.downloadSize = app.downloadSize || 0;
this.installState = app.installState;
this.progress = app.progress || msg.progress || 0;
this.readyToApplyDownload = app.readyToApplyDownload;
this.updateTime = app.updateTime;
this.origin = app.origin;
switch(msg.type) {
case "error":
case "canceled":
this._downloadError = msg.error;
this._fireEvent("downloaderror", this._ondownloaderror);
break;
case "progress":
this._fireEvent("downloadprogress", this._onprogress);
break;
case "installed":
manifestCache.evict(this.manifestURL, this.innerWindowID);
this._manifest = msg.manifest;
this._fireEvent("downloadsuccess", this._ondownloadsuccess);
this._fireEvent("downloadapplied", this._ondownloadapplied);
break;
case "downloaded":
// We don't update the packaged apps manifests until they
// are installed or until the update is unstaged.
if (msg.manifest) {
manifestCache.evict(this.manifestURL, this.innerWindowID);
this._manifest = msg.manifest;
}
this._fireEvent("downloadsuccess", this._ondownloadsuccess);
break;
case "applied":
manifestCache.evict(this.manifestURL, this.innerWindowID);
this._manifest = msg.manifest;
this._fireEvent("downloadapplied", this._ondownloadapplied);
break;
}
this._updateState(msg);
break;
case "Webapps:ClearBrowserData:Return":
this.removeMessageListeners(aMessage.name);
@ -667,10 +619,10 @@ WebappsApplication.prototype = {
break;
case "Webapps:Connect:Return:OK":
let messagePorts = [];
msg.messagePortIDs.forEach(function(aPortID) {
msg.messagePortIDs.forEach((aPortID) => {
let port = new this._window.MozInterAppMessagePort(aPortID);
messagePorts.push(port);
}, this);
});
req.resolve(messagePorts);
break;
case "Webapps:Connect:Return:KO":
@ -678,13 +630,13 @@ WebappsApplication.prototype = {
break;
case "Webapps:GetConnections:Return:OK":
let connections = [];
msg.connections.forEach(function(aConnection) {
msg.connections.forEach((aConnection) => {
let connection =
new this._window.MozInterAppConnection(aConnection.keyword,
aConnection.pubAppManifestURL,
aConnection.subAppManifestURL);
connections.push(connection);
}, this);
});
req.resolve(connections);
break;
}

View File

@ -43,7 +43,7 @@ function supportSystemMessages() {
}
// Minimum delay between two progress events while downloading, in ms.
const MIN_PROGRESS_EVENT_DELAY = 1000;
const MIN_PROGRESS_EVENT_DELAY = 1500;
const WEBAPP_RUNTIME = Services.appinfo.ID == "webapprt@mozilla.org";
@ -945,8 +945,6 @@ this.DOMApplicationRegistry = {
addMessageListener: function(aMsgNames, aApp, aMm) {
aMsgNames.forEach(function (aMsgName) {
let man = aApp && aApp.manifestURL;
debug("Adding messageListener for: " + aMsgName + "App: " +
man);
if (!(aMsgName in this.children)) {
this.children[aMsgName] = [];
}
@ -966,8 +964,8 @@ this.DOMApplicationRegistry = {
});
// If it wasn't registered before, let's update its state
if ((aMsgName === 'Webapps:PackageEvent') ||
(aMsgName === 'Webapps:OfflineCache')) {
if ((aMsgName === 'Webapps:FireEvent') ||
(aMsgName === 'Webapps:UpdateState')) {
if (man) {
let app = this.getAppByManifestURL(aApp.manifestURL);
if (app && ((aApp.installState !== app.installState) ||
@ -1124,9 +1122,9 @@ this.DOMApplicationRegistry = {
// Webapps:Install:Return:OK
// Webapps:Uninstall:Return:OK
// Webapps:Uninstall:Broadcast:Return:OK
// Webapps:OfflineCache
// Webapps:FireEvent
// Webapps:checkForUpdate:Return:OK
// Webapps:PackageEvent
// Webapps:UpdateState
broadcastMessage: function broadcastMessage(aMsgName, aContent) {
if (!(aMsgName in this.children)) {
return;
@ -1140,7 +1138,7 @@ this.DOMApplicationRegistry = {
return FileUtils.getDir(DIRECTORY_NAME, ["webapps", aId], true, true);
},
_writeFile: function ss_writeFile(aFile, aData, aCallbak) {
_writeFile: function _writeFile(aFile, aData, aCallback) {
debug("Saving " + aFile.path);
// Initialize the file output stream.
let ostream = FileUtils.openSafeFileOutputStream(aFile);
@ -1153,8 +1151,9 @@ this.DOMApplicationRegistry = {
// Asynchronously copy the data to the file.
let istream = converter.convertToInputStream(aData);
NetUtil.asyncCopy(istream, ostream, function(rc) {
if (aCallbak)
aCallbak();
if (aCallback) {
aCallback();
}
});
},
@ -1216,22 +1215,15 @@ this.DOMApplicationRegistry = {
let app = this.webapps[download.appId];
if (download.cacheUpdate) {
// Cancel hosted app download.
app.isCanceling = true;
try {
download.cacheUpdate.cancel();
} catch (e) {
delete app.isCanceling;
debug (e);
}
} else if (download.channel) {
// Cancel packaged app download.
app.isCanceling = true;
try {
download.channel.cancel(Cr.NS_BINDING_ABORTED);
} catch(e) {
delete app.isCanceling;
}
} catch(e) { }
} else {
return;
}
@ -1241,11 +1233,19 @@ this.DOMApplicationRegistry = {
app.installState = download.previousState;
app.downloading = false;
this._saveApps((function() {
this.broadcastMessage("Webapps:PackageEvent",
{ type: "canceled",
manifestURL: app.manifestURL,
app: app,
error: error });
this.broadcastMessage("Webapps:UpdateState", {
app: {
progress: 0,
installState: download.previousState,
downloading: false
},
error: error,
manifestURL: app.manifestURL,
})
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
manifestURL: app.manifestURL
});
}).bind(this));
AppDownloadManager.remove(aManifestURL);
},
@ -1267,11 +1267,14 @@ this.DOMApplicationRegistry = {
// If the caller is trying to start a download but we have nothing to
// download, send an error.
if (!app.downloadAvailable) {
this.broadcastMessage("Webapps:PackageEvent",
{ type: "canceled",
manifestURL: app.manifestURL,
app: app,
error: "NO_DOWNLOAD_AVAILABLE" });
this.broadcastMessage("Webapps:UpdateState", {
error: "NO_DOWNLOAD_AVAILABLE",
manifestURL: app.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
manifestURL: app.manifestURL
});
return;
}
@ -1303,16 +1306,21 @@ this.DOMApplicationRegistry = {
debug("appcache found");
this.startOfflineCacheDownload(manifest, app, null, isUpdate);
} else {
// hosted app with no appcache, nothing to do, but we fire a
// downloaded event
// 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,
app: app,
manifest: jsonManifest });
DOMApplicationRegistry._saveApps();
DOMApplicationRegistry._saveApps(function() {
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: jsonManifest,
manifestURL: aManifestURL
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadsuccess",
manifestURL: aManifestURL
});
});
}
}).bind(this));
@ -1321,7 +1329,8 @@ this.DOMApplicationRegistry = {
this._loadJSONAsync(file, (function(aJSON) {
if (!aJSON) {
debug("startDownload: No update manifest found at " + file.path + " " + aManifestURL);
debug("startDownload: No update manifest found at " + file.path + " " +
aManifestURL);
return;
}
@ -1350,11 +1359,14 @@ this.DOMApplicationRegistry = {
app.readyToApplyDownload = true;
app.updateTime = Date.now();
DOMApplicationRegistry._saveApps(function() {
debug("About to fire Webapps:PackageEvent");
DOMApplicationRegistry.broadcastMessage("Webapps:PackageEvent",
{ type: "downloaded",
manifestURL: aManifestURL,
app: app });
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: aManifestURL
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadsuccess",
manifestURL: aManifestURL
});
if (app.installState == "pending") {
// We restarted a failed download, apply it automatically.
DOMApplicationRegistry.applyDownload(aManifestURL);
@ -1442,11 +1454,15 @@ this.DOMApplicationRegistry = {
}
this.updateDataStore(this.webapps[id].localId, app.origin,
app.manifestURL, aData);
this.broadcastMessage("Webapps:PackageEvent",
{ type: "applied",
manifestURL: app.manifestURL,
app: app,
manifest: aData });
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: aData,
manifestURL: app.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadapplied",
manifestURL: app.manifestURL
});
}).bind(this));
}).bind(this));
}).bind(this));
@ -1475,6 +1491,14 @@ this.DOMApplicationRegistry = {
aApp.downloading = true;
aApp.progress = 0;
DOMApplicationRegistry._saveApps((function() {
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: {
downloading: true,
installState: aApp.installState,
progress: 0
},
manifestURL: aApp.manifestURL
});
let cacheUpdate = aProfileDir
? updateSvc.scheduleCustomProfileUpdate(appcacheURI, docURI, aProfileDir)
: updateSvc.scheduleAppUpdate(appcacheURI, docURI, aApp.localId, false);
@ -1598,16 +1622,17 @@ this.DOMApplicationRegistry = {
// event.
app.downloadAvailable = true;
app.downloadSize = manifest.size;
aData.event = "downloadavailable";
aData.app = {
downloadAvailable: true,
downloadSize: manifest.size,
updateManifest: aManifest
}
app.updateManifest = aManifest;
DOMApplicationRegistry._saveApps(function() {
DOMApplicationRegistry.broadcastMessage("Webapps:CheckForUpdate:Return:OK",
aData);
delete aData.app.updateManifest;
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadavailable",
manifestURL: app.manifestURL,
requestID: aData.requestID
});
});
}
@ -1662,10 +1687,17 @@ this.DOMApplicationRegistry = {
this.webapps[id] = app;
this._saveApps(function() {
let reg = DOMApplicationRegistry;
aData.app = app;
if (!manifest.appcache_path) {
aData.event = "downloadapplied";
reg.broadcastMessage("Webapps:CheckForUpdate:Return:OK", aData);
reg.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: app.manifest,
manifestURL: app.manifestURL
});
reg.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadapplied",
manifestURL: app.manifestURL,
requestID: aData.requestID
});
} else {
// Check if the appcache is updatable, and send "downloadavailable" or
// "downloadapplied".
@ -1673,14 +1705,24 @@ this.DOMApplicationRegistry = {
observe: function(aSubject, aTopic, aObsData) {
debug("updateHostedApp: updateSvc.checkForUpdate return for " +
app.manifestURL + " - event is " + aTopic);
aData.event =
let eventType =
aTopic == "offline-cache-update-available" ? "downloadavailable"
: "downloadapplied";
aData.app.downloadAvailable = (aData.event == "downloadavailable");
reg._saveApps();
reg.broadcastMessage("Webapps:CheckForUpdate:Return:OK", aData);
app.downloadAvailable = (eventType == "downloadavailable");
reg._saveApps(function() {
reg.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: app.manifest,
manifestURL: app.manifestURL
});
reg.broadcastMessage("Webapps:FireEvent", {
eventType: eventType,
manifestURL: app.manifestURL,
requestID: aData.requestID
});
});
}
}
};
debug("updateHostedApp: updateSvc.checkForUpdate for " +
manifest.fullAppcachePath());
updateSvc.checkForUpdate(Services.io.newURI(manifest.fullAppcachePath(), null, null),
@ -1715,8 +1757,8 @@ this.DOMApplicationRegistry = {
let onlyCheckAppCache = false;
#ifdef MOZ_WIDGET_GONK
let appDir = FileUtils.getDir("coreAppsDir", ["webapps"], false);
onlyCheckAppCache = (app.basePath == appDir.path);
let appDir = FileUtils.getDir("coreAppsDir", ["webapps"], false);
onlyCheckAppCache = (app.basePath == appDir.path);
#endif
if (onlyCheckAppCache) {
@ -1744,19 +1786,24 @@ this.DOMApplicationRegistry = {
debug("onlyCheckAppCache updateSvc.checkForUpdate return for " +
app.manifestURL + " - event is " + aTopic);
if (aTopic == "offline-cache-update-available") {
aData.event = "downloadavailable";
app.downloadAvailable = true;
aData.app = app;
this._saveApps(function() {
DOMApplicationRegistry.broadcastMessage(
"Webapps:CheckForUpdate:Return:OK", aData);
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadavailable",
manifestURL: app.manifestURL,
requestID: aData.requestID
});
});
} else {
aData.error = "NOT_UPDATABLE";
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
}
}
}
};
let helper = new ManifestHelper(manifest);
debug("onlyCheckAppCache - launch updateSvc.checkForUpdate for " +
helper.fullAppcachePath());
@ -1806,15 +1853,21 @@ this.DOMApplicationRegistry = {
if (oldHash != hash) {
updatePackagedApp.call(this, manifest);
} else {
// Like if we got a 304, just send a 'downloadapplied'
// or downloadavailable event.
aData.event = app.downloadAvailable ? "downloadavailable"
: "downloadapplied";
aData.app = {
lastCheckedUpdate: app.lastCheckedUpdate
}
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
this._saveApps();
this._saveApps(function() {
// Like if we got a 304, just send a 'downloadapplied'
// or downloadavailable event.
let eventType = app.downloadAvailable ? "downloadavailable"
: "downloadapplied";
aMm.sendAsyncMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
});
aMm.sendAsyncMessage("Webapps:FireEvent", {
eventType: eventType,
manifestURL: app.manifestURL,
requestID: aData.requestID
});
});
}
} else {
// Update only the appcache if the manifest has not changed
@ -1826,16 +1879,22 @@ this.DOMApplicationRegistry = {
} else if (xhr.status == 304) {
// The manifest has not changed.
if (isPackage) {
// If the app is a packaged app, we just send a 'downloadapplied'
// or downloadavailable event.
app.lastCheckedUpdate = Date.now();
aData.event = app.downloadAvailable ? "downloadavailable"
: "downloadapplied";
aData.app = {
lastCheckedUpdate: app.lastCheckedUpdate
}
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
this._saveApps();
this._saveApps(function() {
// If the app is a packaged app, we just send a 'downloadapplied'
// or downloadavailable event.
let eventType = app.downloadAvailable ? "downloadavailable"
: "downloadapplied";
aMm.sendAsyncMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
});
aMm.sendAsyncMessage("Webapps:FireEvent", {
eventType: eventType,
manifestURL: app.manifestURL,
requestID: aData.requestID
});
});
} else {
// For hosted apps, even if the manifest has not changed, we check
// for offline cache updates.
@ -2146,21 +2205,25 @@ this.DOMApplicationRegistry = {
if (supportUseCurrentProfile()) {
// Update the permissions for this app.
PermissionsInstaller.installPermissions({ manifest: aManifest,
origin: appObject.origin,
manifestURL: appObject.manifestURL },
true);
PermissionsInstaller.installPermissions({
manifest: aManifest,
origin: appObject.origin,
manifestURL: appObject.manifestURL
}, true);
}
this.updateDataStore(this.webapps[aId].localId, appObject.origin,
appObject.manifestURL, aManifest);
debug("About to fire Webapps:PackageEvent 'installed'");
this.broadcastMessage("Webapps:PackageEvent",
{ type: "installed",
manifestURL: appObject.manifestURL,
app: app,
manifest: aManifest });
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: aManifest,
manifestURL: appObject.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: ["downloadsuccess", "downloadapplied"],
manifestURL: appObject.manifestURL
});
if (installSuccessCallback) {
installSuccessCallback(aManifest, zipFile.path);
}
@ -2426,14 +2489,6 @@ this.DOMApplicationRegistry = {
dir.remove(true);
} catch (e) { }
// We avoid notifying the error to the DOM side if the app download
// was cancelled via cancelDownload, which already sends its own
// notification.
if (app.isCanceling) {
delete app.isCanceling;
return;
}
let download = AppDownloadManager.get(aApp.manifestURL);
app.downloading = false;
@ -2449,20 +2504,31 @@ this.DOMApplicationRegistry = {
delete app.staged;
}
self.broadcastMessage("Webapps:PackageEvent",
{ type: "error",
manifestURL: aApp.manifestURL,
error: aError,
app: app });
self._saveApps();
self._saveApps(function() {
self.broadcastMessage("Webapps:UpdateState", {
app: app,
error: aError,
manifestURL: aApp.manifestURL
});
self.broadcastMessage("Webapps:FireEvent", {
eventType: "downloaderror",
manifestURL: aApp.manifestURL
});
});
AppDownloadManager.remove(aApp.manifestURL);
}
function sendProgressEvent() {
self.broadcastMessage("Webapps:PackageEvent",
{ type: "progress",
manifestURL: aApp.manifestURL,
app: app });
function sendProgressEvent(aProgress) {
self.broadcastMessage("Webapps:UpdateState", {
app: {
progress: aProgress
},
manifestURL: aApp.manifestURL
});
self.broadcastMessage("Webapps:FireEvent", {
eventType: "progress",
manifestURL: aApp.manifestURL
});
}
// aStoreId must be a string of the form
@ -2548,7 +2614,7 @@ this.DOMApplicationRegistry = {
let now = Date.now();
if (now - lastProgressTime > MIN_PROGRESS_EVENT_DELAY) {
debug("onProgress: " + aProgress + "/" + aProgressMax);
sendProgressEvent();
sendProgressEvent(aProgress);
lastProgressTime = now;
self._saveApps();
}
@ -2646,16 +2712,17 @@ this.DOMApplicationRegistry = {
OS.Path.join(dirPath, "update.webapp"));
}
self.broadcastMessage("Webapps:PackageEvent", {
type: "downloaded",
manifestURL: aApp.manifestURL,
app: app });
self.broadcastMessage("Webapps:PackageEvent", {
type: "applied",
manifestURL: aApp.manifestURL,
app: app });
// Save the updated registry, and cleanup the tmp directory.
self._saveApps();
self._saveApps(function() {
self.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: aApp.manifestURL
});
self.broadcastMessage("Webapps:FireEvent", {
manifestURL: aApp.manifestURL,
eventType: ["downloadsuccess", "downloadapplied"]
});
});
let file = FileUtils.getFile("TmpD", ["webapps", id], false);
if (file && file.exists()) {
file.remove(true);
@ -2907,7 +2974,7 @@ this.DOMApplicationRegistry = {
requestChannel.asyncOpen(listener, null);
// send a first progress event to correctly set the DOM object's properties
sendProgressEvent();
sendProgressEvent(0);
};
let checkDownloadSize = function (freeBytes) {
@ -3305,7 +3372,7 @@ let AppcacheObserver = function(aApp) {
this.app = aApp;
this.startStatus = aApp.installState;
this.lastProgressTime = 0;
// send a first progress event to correctly set the DOM object's properties
// Send a first progress event to correctly set the DOM object's properties.
this._sendProgressEvent();
};
@ -3313,10 +3380,14 @@ AppcacheObserver.prototype = {
// nsIOfflineCacheUpdateObserver implementation
_sendProgressEvent: function() {
let app = this.app;
DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
{ manifest: app.manifestURL,
installState: app.installState,
progress: app.progress });
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: "progress",
manifestURL: app.manifestURL
});
},
updateStateChanged: function appObs_Update(aUpdate, aState) {
@ -3328,31 +3399,41 @@ AppcacheObserver.prototype = {
var self = this;
let setStatus = function appObs_setStatus(aStatus, aProgress) {
debug("Offlinecache setStatus to " + aStatus + " with progress " +
aProgress + " for " + app.origin);
aProgress + " for " + app.origin);
mustSave = (app.installState != aStatus);
app.installState = aStatus;
app.progress = aProgress;
if (aStatus == "installed") {
app.updateTime = Date.now();
app.downloading = false;
app.downloadAvailable = false;
if (aStatus != "installed") {
self._sendProgressEvent();
return;
}
self._sendProgressEvent();
app.updateTime = Date.now();
app.downloading = false;
app.downloadAvailable = false;
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
eventType: ["downloadsuccess", "downloadapplied"],
manifestURL: app.manifestURL
});
}
let setError = function appObs_setError(aError) {
debug("Offlinecache setError to " + aError);
// If we are canceling the download, we already send a DOWNLOAD_CANCELED
// error.
if (!app.isCanceling) {
DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
{ manifest: app.manifestURL,
error: aError });
} else {
delete app.isCanceling;
}
app.downloading = false;
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
app: app,
manifestURL: app.manifestURL
});
DOMApplicationRegistry.broadcastMessage("Webapps:FireEvent", {
error: aError,
eventType: "downloaderror",
manifestURL: app.manifestURL
});
mustSave = true;
}
@ -3369,9 +3450,9 @@ AppcacheObserver.prototype = {
setStatus("installed", aUpdate.byteProgress);
break;
case Ci.nsIOfflineCacheUpdateObserver.STATE_DOWNLOADING:
case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMSTARTED:
setStatus(this.startStatus, aUpdate.byteProgress);
break;
case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMSTARTED:
case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMPROGRESS:
let now = Date.now();
if (now - this.lastProgressTime > MIN_PROGRESS_EVENT_DELAY) {

View File

@ -159,7 +159,7 @@ var steps = [
},
function() {
ok(true, "== TEST == Update packaged app - same package");
updateApp(false, 3, 4);
updateApp(false, 3, 3);
},
function() {
ok(true, "== TEST == Check for Update after getting the same package");

View File

@ -123,7 +123,7 @@ PostMessageReadStructuredClone(JSContext* cx,
JS::Rooted<JS::Value> val(cx);
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, global, supports,
val.address(),
&val,
getter_AddRefs(wrapper)))) {
return JSVAL_TO_OBJECT(val);
}

View File

@ -1589,7 +1589,7 @@ Navigator::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
if (JSVAL_IS_PRIMITIVE(prop_val) && !JSVAL_IS_NULL(prop_val)) {
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = nsContentUtils::WrapNative(aCx, aObject, native, prop_val.address(),
rv = nsContentUtils::WrapNative(aCx, aObject, native, &prop_val,
getter_AddRefs(holder), true);
if (NS_FAILED(rv)) {

View File

@ -643,13 +643,13 @@ IdToString(JSContext *cx, jsid id)
static inline nsresult
WrapNative(JSContext *cx, JSObject *scope, nsISupports *native,
nsWrapperCache *cache, const nsIID* aIID, jsval *vp,
nsWrapperCache *cache, const nsIID* aIID, JS::MutableHandle<JS::Value> vp,
nsIXPConnectJSObjectHolder** aHolder, bool aAllowWrapping)
{
if (!native) {
NS_ASSERTION(!aHolder || !*aHolder, "*aHolder should be null!");
*vp = JSVAL_NULL;
vp.setNull();
return NS_OK;
}
@ -661,13 +661,13 @@ WrapNative(JSContext *cx, JSObject *scope, nsISupports *native,
return nsDOMClassInfo::XPConnect()->WrapNativeToJSVal(cx, scope, native,
cache, aIID,
aAllowWrapping, vp,
aAllowWrapping, vp.address(),
aHolder);
}
static inline nsresult
WrapNative(JSContext *cx, JSObject *scope, nsISupports *native,
const nsIID* aIID, bool aAllowWrapping, jsval *vp,
const nsIID* aIID, bool aAllowWrapping, JS::MutableHandle<JS::Value> vp,
// If non-null aHolder will keep the jsval alive
// while there's a ref to it
nsIXPConnectJSObjectHolder** aHolder = nullptr)
@ -679,7 +679,7 @@ WrapNative(JSContext *cx, JSObject *scope, nsISupports *native,
// Same as the WrapNative above, but use these if aIID is nsISupports' IID.
static inline nsresult
WrapNative(JSContext *cx, JSObject *scope, nsISupports *native,
bool aAllowWrapping, jsval *vp,
bool aAllowWrapping, JS::MutableHandle<JS::Value> vp,
// If non-null aHolder will keep the jsval alive
// while there's a ref to it
nsIXPConnectJSObjectHolder** aHolder = nullptr)
@ -690,7 +690,8 @@ WrapNative(JSContext *cx, JSObject *scope, nsISupports *native,
static inline nsresult
WrapNative(JSContext *cx, JSObject *scope, nsISupports *native,
nsWrapperCache *cache, bool aAllowWrapping, jsval *vp,
nsWrapperCache *cache, bool aAllowWrapping,
JS::MutableHandle<JS::Value> vp,
// If non-null aHolder will keep the jsval alive
// while there's a ref to it
nsIXPConnectJSObjectHolder** aHolder = nullptr)
@ -2221,26 +2222,25 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
// wrap parameters in the target compartment
// we also pass in the calling window as the first argument
unsigned argc = args.length() + 1;
nsAutoArrayPtr<JS::Value> argv(new JS::Value[argc]);
JS::AutoArrayRooter rooter(cx, 0, argv);
JS::AutoValueVector argv(cx);
argv.resize(argc);
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
nsCOMPtr<nsIDOMWindow> currentWin(do_GetInterface(currentInner));
rv = WrapNative(cx, obj, currentWin, &NS_GET_IID(nsIDOMWindow),
true, &argv[0], getter_AddRefs(holder));
true, argv.handleAt(0), getter_AddRefs(holder));
if (!JS_WrapValue(cx, &argv[0]))
return NS_ERROR_FAILURE;
rooter.changeLength(1);
for (size_t i = 1; i < argc; ++i) {
argv[i] = args[i - 1];
if (!JS_WrapValue(cx, &argv[i]))
return NS_ERROR_FAILURE;
rooter.changeLength(i + 1);
}
JS::Rooted<JS::Value> frval(cx);
bool ret = JS_CallFunctionValue(cx, thisObject, funval, argc, argv,
bool ret = JS_CallFunctionValue(cx, thisObject, funval,
argc, argv.begin(),
frval.address());
if (!ret) {
@ -2250,7 +2250,7 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
}
}
return WrapNative(cx, obj, native, true, args.rval().address());
return WrapNative(cx, obj, native, true, args.rval());
}
static nsresult
@ -2818,7 +2818,7 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
JS::Rooted<JS::Value> v(cx);
rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
false, v.address(), getter_AddRefs(holder));
false, &v, getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
if (install) {
@ -3100,7 +3100,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
JS::Rooted<JS::Value> v(cx);
rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
false, v.address(), getter_AddRefs(holder));
false, &v, getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
rv = constructor->Install(cx, obj, v);
@ -3202,7 +3202,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
JS::Rooted<JS::Value> val(cx);
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
false, val.address(), getter_AddRefs(holder));
false, &val, getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
rv = constructor->Install(cx, obj, val);
@ -3251,7 +3251,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
scope = aWin->GetGlobalJSObject();
}
rv = WrapNative(cx, scope, native, true, prop_val.address(),
rv = WrapNative(cx, scope, native, true, &prop_val,
getter_AddRefs(holder));
}
@ -3303,7 +3303,7 @@ ContentWindowGetter(JSContext *cx, unsigned argc, jsval *vp)
template<class Interface>
static nsresult
LocationSetterGuts(JSContext *cx, JSObject *obj, jsval *vp)
LocationSetterGuts(JSContext *cx, JSObject *obj, JS::MutableHandle<JS::Value> vp)
{
// This function duplicates some of the logic in XPC_WN_HelperSetProperty
obj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
@ -3322,7 +3322,7 @@ LocationSetterGuts(JSContext *cx, JSObject *obj, jsval *vp)
NS_ENSURE_SUCCESS(rv, rv);
// Grab the value we're being set to before we stomp on |vp|
JS::Rooted<JSString*> val(cx, ::JS_ValueToString(cx, *vp));
JS::Rooted<JSString*> val(cx, ::JS_ValueToString(cx, vp));
NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED);
// Make sure |val| stays alive below
@ -3352,7 +3352,7 @@ static bool
LocationSetter(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool strict,
JS::MutableHandle<JS::Value> vp)
{
nsresult rv = LocationSetterGuts<Interface>(cx, obj, vp.address());
nsresult rv = LocationSetterGuts<Interface>(cx, obj, vp);
if (NS_FAILED(rv)) {
xpc::Throw(cx, rv);
return false;
@ -3365,7 +3365,7 @@ static bool
LocationSetterUnwrapper(JSContext *cx, JS::Handle<JSObject*> obj_, JS::Handle<jsid> id,
bool strict, JS::MutableHandle<JS::Value> vp)
{
JS::RootedObject obj(cx, obj_);
JS::Rooted<JSObject*> obj(cx, obj_);
JSObject *wrapped = XPCWrapper::UnsafeUnwrapSecurityWrapper(obj);
if (wrapped) {
@ -3410,7 +3410,7 @@ const InterfaceShimEntry kInterfaceShimMap[] =
{ "nsIDOMXPathResult", "XPathResult" } };
static nsresult
DefineComponentsShim(JSContext *cx, JS::HandleObject global, nsPIDOMWindow *win)
DefineComponentsShim(JSContext *cx, JS::Handle<JSObject*> global, nsPIDOMWindow *win)
{
// Keep track of how often this happens.
Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
@ -3468,7 +3468,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj_, jsid id_, uint32_t flags,
JSObject **objp, bool *_retval)
{
JS::RootedObject obj(cx, obj_);
JS::Rooted<JSObject*> obj(cx, obj_);
JS::RootedId id(cx, id_);
if (!JSID_IS_STRING(id)) {
@ -3565,7 +3565,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
JS::Rooted<JS::Value> v(cx);
rv = WrapNative(cx, scope, location, &NS_GET_IID(nsIDOMLocation), true,
v.address(), getter_AddRefs(holder));
&v, getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
bool ok = JS_WrapValue(cx, v.address()) &&
@ -3590,7 +3590,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JS::Rooted<JS::Value> v(cx);
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = WrapNative(cx, obj, top, &NS_GET_IID(nsIDOMWindow), true,
v.address(), getter_AddRefs(holder));
&v, getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
// Hold on to the top window object as a global property so we
@ -3606,7 +3606,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
}
// Handle resolving if id refers to a name resolved by DOM worker code.
JS::RootedObject tmp(cx, NULL);
JS::Rooted<JSObject*> tmp(cx, NULL);
if (!ResolveWorkerClasses(cx, obj, id, flags, &tmp)) {
return NS_ERROR_FAILURE;
}
@ -3679,7 +3679,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JS::Rooted<JS::Value> v(cx);
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = WrapNative(cx, JS::CurrentGlobalOrNull(cx), document, document,
&NS_GET_IID(nsIDOMDocument), v.address(), getter_AddRefs(holder),
&NS_GET_IID(nsIDOMDocument), &v, getter_AddRefs(holder),
false);
NS_ENSURE_SUCCESS(rv, rv);
@ -3986,9 +3986,11 @@ nsArraySH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
NS_ENSURE_SUCCESS(rv, rv);
if (array_item) {
JS::Rooted<JS::Value> rval(cx);
rv = WrapNative(cx, JS::CurrentGlobalOrNull(cx), array_item, cache,
true, vp);
true, &rval);
NS_ENSURE_SUCCESS(rv, rv);
*vp = rval;
rv = NS_SUCCESS_I_DID_SOMETHING;
}
@ -4085,7 +4087,7 @@ nsHTMLDocumentSH::GetDocumentAllNodeList(JSContext *cx,
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
nsresult tmp = WrapNative(cx, JS::CurrentGlobalOrNull(cx),
static_cast<nsINodeList*>(list), list, false,
collection.address(), getter_AddRefs(holder));
&collection, getter_AddRefs(holder));
if (NS_FAILED(tmp)) {
rv = tmp;
}
@ -4189,7 +4191,7 @@ nsHTMLDocumentSH::DocumentAllGetProperty(JSContext *cx, JS::Handle<JSObject*> ob
}
if (result) {
rv = WrapNative(cx, JS::CurrentGlobalOrNull(cx), result, cache, true, vp.address());
rv = WrapNative(cx, JS::CurrentGlobalOrNull(cx), result, cache, true, vp);
if (NS_FAILED(rv)) {
xpc::Throw(cx, rv);
@ -4207,7 +4209,7 @@ nsHTMLDocumentSH::DocumentAllNewResolve(JSContext *cx, JS::Handle<JSObject*> obj
JS::Handle<jsid> id, unsigned flags,
JS::MutableHandle<JSObject*> objp)
{
JS::RootedValue v(cx);
JS::Rooted<JS::Value> v(cx);
if (nsDOMClassInfo::sItem_id == id || nsDOMClassInfo::sNamedItem_id == id) {
// Define the item() or namedItem() method.
@ -4326,8 +4328,10 @@ nsStringArraySH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
return NS_SUCCESS_I_DID_SOMETHING;
}
NS_ENSURE_TRUE(xpc::NonVoidStringToJsval(cx, val, vp),
JS::Rooted<JS::Value> rval(cx);
NS_ENSURE_TRUE(xpc::NonVoidStringToJsval(cx, val, &rval),
NS_ERROR_OUT_OF_MEMORY);
*vp = rval;
return NS_SUCCESS_I_DID_SOMETHING;
}

View File

@ -2385,8 +2385,9 @@ nsDOMWindowUtils::GetIsTestControllingRefreshes(bool *aResult)
return NS_ERROR_DOM_SECURITY_ERR;
}
nsPresContext* pc = GetPresContext();
*aResult =
GetPresContext()->RefreshDriver()->IsTestControllingRefreshesEnabled();
pc ? pc->RefreshDriver()->IsTestControllingRefreshesEnabled() : false;
return NS_OK;
}

View File

@ -3632,8 +3632,11 @@ nsGlobalWindow::GetScriptableContent(JSContext* aCx, JS::Value* aVal)
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
if (content && global) {
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
return nsContentUtils::WrapNative(aCx, global, content, aVal,
getter_AddRefs(wrapper));
JS::Rooted<JS::Value> rval(aCx);
nsresult rv = nsContentUtils::WrapNative(aCx, global, content, &rval,
getter_AddRefs(wrapper));
*aVal = rval;
return rv;
}
return NS_ERROR_FAILURE;
}
@ -6806,9 +6809,9 @@ PostMessageReadStructuredClone(JSContext* cx,
JS::Rooted<JS::Value> val(cx);
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, global, supports,
val.address(),
&val,
getter_AddRefs(wrapper)))) {
return JSVAL_TO_OBJECT(val);
return val.toObjectOrNull();
}
}
}

View File

@ -1035,23 +1035,21 @@ nsJSContext::JSObjectFromInterface(nsISupports* aTarget,
// We don't wrap here because we trust the JS engine to wrap the target
// later.
JS::Rooted<JS::Value> v(cx);
nsresult rv = nsContentUtils::WrapNative(cx, aScope, aTarget,
v.address());
nsresult rv = nsContentUtils::WrapNative(cx, aScope, aTarget, &v);
NS_ENSURE_SUCCESS(rv, rv);
#ifdef DEBUG
nsCOMPtr<nsISupports> targetSupp = do_QueryInterface(aTarget);
nsCOMPtr<nsISupports> native =
nsContentUtils::XPConnect()->GetNativeOfWrapper(cx,
JSVAL_TO_OBJECT(v));
NS_ASSERTION(native == targetSupp, "Native should be the target!");
#endif
JSObject* obj = v.toObjectOrNull();
if (obj) {
JS::ExposeObjectToActiveJS(obj);
}
#ifdef DEBUG
nsCOMPtr<nsISupports> targetSupp = do_QueryInterface(aTarget);
nsCOMPtr<nsISupports> native =
nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, obj);
NS_ASSERTION(native == targetSupp, "Native should be the target!");
#endif
*aRet = obj;
return NS_OK;
}
@ -1298,7 +1296,7 @@ nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
#endif
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
JS::Rooted<JS::Value> v(cx);
rv = nsContentUtils::WrapNative(cx, aScope, arg, v.address(),
rv = nsContentUtils::WrapNative(cx, aScope, arg, &v,
getter_AddRefs(wrapper));
if (NS_SUCCEEDED(rv)) {
*thisval = v;
@ -1500,7 +1498,7 @@ nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv)
JS::Rooted<JSObject*> global(cx, GetWindowProxy());
JS::Rooted<JS::Value> v(cx);
nsresult rv = nsContentUtils::WrapNative(cx, global,
data, iid, v.address(),
data, iid, &v,
getter_AddRefs(wrapper));
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -320,13 +320,13 @@ enum {
TOSTRING_NAME_RESERVED_SLOT = 1
};
bool
static bool
InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp)
{
JS::Rooted<JSObject*> callee(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
JS::Rooted<JSObject*> callee(cx, &args.callee());
JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
if (!obj) {
if (!args.computeThis(cx).isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
"null", "object");
return false;
@ -334,14 +334,14 @@ InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp)
JS::Value v = js::GetFunctionNativeReserved(callee,
TOSTRING_CLASS_RESERVED_SLOT);
const JSClass* clasp = static_cast<const JSClass*>(JSVAL_TO_PRIVATE(v));
const JSClass* clasp = static_cast<const JSClass*>(v.toPrivate());
v = js::GetFunctionNativeReserved(callee, TOSTRING_NAME_RESERVED_SLOT);
JSString* jsname = static_cast<JSString*>(JSVAL_TO_STRING(v));
size_t length;
const jschar* name = JS_GetInternedStringCharsAndLength(jsname, &length);
if (js::GetObjectJSClass(obj) != clasp) {
if (js::GetObjectJSClass(&args.computeThis(cx).toObject()) != clasp) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
NS_ConvertUTF16toUTF8(name).get(), "toString",
"object");
@ -357,7 +357,7 @@ InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp)
str.Append('\n');
str.AppendLiteral("}");
return xpc::NonVoidStringToJsval(cx, str, vp);
return xpc::NonVoidStringToJsval(cx, str, args.rval());
}
bool
@ -673,7 +673,7 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
bool
NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
JS::Handle<JSObject*> aScope,
JS::Value* aRetval,
JS::MutableHandle<JS::Value> aRetval,
xpcObjectHelper& aHelper,
const nsIID* aIID,
bool aAllowNativeWrapper)
@ -684,7 +684,7 @@ NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
nsWrapperCache *cache = aHelper.GetWrapperCache();
if (cache && cache->IsDOMBinding()) {
JS::RootedObject obj(aCx, cache->GetWrapper());
JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper());
if (!obj) {
obj = cache->WrapObject(aCx, aScope);
}
@ -694,7 +694,7 @@ NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
}
if (obj) {
*aRetval = JS::ObjectValue(*obj);
aRetval.setObject(*obj);
return true;
}
}
@ -751,7 +751,7 @@ InstanceClassHasProtoAtDepth(JS::Handle<JSObject*> protoObject, uint32_t protoID
bool
XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
xpcObjectHelper& helper, const nsIID* iid,
bool allowNativeWrapper, JS::Value* rval)
bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval)
{
if (!NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid,
allowNativeWrapper)) {
@ -759,7 +759,7 @@ XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
}
#ifdef DEBUG
JSObject* jsobj = JSVAL_TO_OBJECT(*rval);
JSObject* jsobj = rval.toObjectOrNull();
if (jsobj && !js::GetObjectParent(jsobj))
NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
"Why did we recreate this wrapper?");
@ -770,7 +770,7 @@ XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
bool
VariantToJsval(JSContext* aCx, JS::Handle<JSObject*> aScope,
nsIVariant* aVariant, JS::Value* aRetval)
nsIVariant* aVariant, JS::MutableHandle<JS::Value> aRetval)
{
nsresult rv;
if (!XPCVariant::VariantDataToJS(aVariant, &rv, aRetval)) {
@ -810,15 +810,14 @@ QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp)
return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
}
JS::Value* argv = JS_ARGV(cx, vp);
if (!argv[0].isObject()) {
if (!args[0].isObject()) {
return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
}
nsIJSID* iid;
SelfRef iidRef;
if (NS_FAILED(xpc_qsUnwrapArg<nsIJSID>(cx, argv[0], &iid, &iidRef.ptr,
&argv[0]))) {
if (NS_FAILED(xpc_qsUnwrapArg<nsIJSID>(cx, args[0], &iid, &iidRef.ptr,
args[0]))) {
return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
}
MOZ_ASSERT(iid);
@ -1475,7 +1474,7 @@ AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
{
for (uint32_t i = 0; i < names.Length(); ++i) {
JS::Rooted<JS::Value> v(cx);
if (!xpc::NonVoidStringToJsval(cx, names[i], v.address())) {
if (!xpc::NonVoidStringToJsval(cx, names[i], &v)) {
return false;
}
@ -1649,10 +1648,10 @@ private:
};
nsresult
ReparentWrapper(JSContext* aCx, JS::HandleObject aObjArg)
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
{
// aObj is assigned to below, so needs to be re-rooted.
JS::RootedObject aObj(aCx, aObjArg);
JS::Rooted<JSObject*> aObj(aCx, aObjArg);
const DOMClass* domClass = GetDOMClass(aObj);
JS::Rooted<JSObject*> oldParent(aCx, JS_GetParent(aObj));
@ -1756,20 +1755,9 @@ ReparentWrapper(JSContext* aCx, JS::HandleObject aObjArg)
if (ww != aObj) {
MOZ_ASSERT(cache->HasSystemOnlyWrapper());
JS::RootedObject newwrapper(aCx,
xpc::WrapperFactory::WrapSOWObject(aCx, newobj));
if (!newwrapper) {
MOZ_CRASH();
}
// Oops. We don't support transplanting objects with SOWs anymore.
MOZ_CRASH();
// Ok, now we do the special object-plus-wrapper transplant.
ww = xpc::TransplantObjectWithWrapper(aCx, aObj, ww, newobj, newwrapper);
if (!ww) {
MOZ_CRASH();
}
aObj = newobj;
SetSystemOnlyWrapperSlot(aObj, JS::ObjectValue(*ww));
} else {
aObj = xpc::TransplantObject(aCx, aObj, newobj);
if (!aObj) {
@ -1863,7 +1851,7 @@ GlobalObject::GetAsSupports() const
// using new bindings.
nsresult rv = xpc_qsUnwrapArg<nsISupports>(mCx, val, &mGlobalObject,
static_cast<nsISupports**>(getter_AddRefs(mGlobalObjectRef)),
val.address());
&val);
if (NS_FAILED(rv)) {
mGlobalObject = nullptr;
Throw(mCx, NS_ERROR_XPC_BAD_CONVERT_JS);

View File

@ -35,8 +35,8 @@
class nsPIDOMWindow;
extern nsresult
xpc_qsUnwrapArgImpl(JSContext* cx, jsval v, const nsIID& iid, void** ppArg,
nsISupports** ppArgRef, jsval* vp);
xpc_qsUnwrapArgImpl(JSContext* cx, JS::Handle<JS::Value> v, const nsIID& iid, void** ppArg,
nsISupports** ppArgRef, JS::MutableHandle<JS::Value> vp);
namespace mozilla {
namespace dom {
@ -53,8 +53,8 @@ struct SelfRef
/** Convert a jsval to an XPCOM pointer. */
template <class Interface, class StrongRefType>
inline nsresult
UnwrapArg(JSContext* cx, jsval v, Interface** ppArg,
StrongRefType** ppArgRef, jsval* vp)
UnwrapArg(JSContext* cx, JS::Handle<JS::Value> v, Interface** ppArg,
StrongRefType** ppArgRef, JS::MutableHandle<JS::Value> vp)
{
nsISupports* argRef = *ppArgRef;
nsresult rv = xpc_qsUnwrapArgImpl(cx, v, NS_GET_TEMPLATE_IID(Interface),
@ -792,7 +792,7 @@ WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
bool
NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
JS::Handle<JSObject*> aScope,
JS::Value* aRetval,
JS::MutableHandle<JS::Value> aRetval,
xpcObjectHelper& aHelper,
const nsIID* aIID,
bool aAllowNativeWrapper);
@ -811,7 +811,7 @@ HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
}
qsObjectHelper helper(value, GetWrapperCache(value));
return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval.address(),
return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval,
helper, nullptr, true);
}
@ -977,12 +977,12 @@ InstanceClassHasProtoAtDepth(JS::Handle<JSObject*> protoObject, uint32_t protoID
bool
XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
xpcObjectHelper& helper, const nsIID* iid,
bool allowNativeWrapper, JS::Value* rval);
bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval);
// Special-cased wrapping for variants
bool
VariantToJsval(JSContext* aCx, JS::Handle<JSObject*> aScope,
nsIVariant* aVariant, JS::Value* aRetval);
nsIVariant* aVariant, JS::MutableHandle<JS::Value> aRetval);
// Wrap an object "p" which is not using WebIDL bindings yet. This _will_
// actually work on WebIDL binding objects that are wrappercached, but will be
@ -994,10 +994,10 @@ WrapObject(JSContext* cx, JS::Handle<JSObject*> scope, T* p,
nsWrapperCache* cache, const nsIID* iid,
JS::MutableHandle<JS::Value> rval)
{
if (xpc_FastGetCachedWrapper(cache, scope, rval.address()))
if (xpc_FastGetCachedWrapper(cache, scope, rval))
return true;
qsObjectHelper helper(p, cache);
return XPCOMObjectToJsval(cx, scope, helper, iid, true, rval.address());
return XPCOMObjectToJsval(cx, scope, helper, iid, true, rval);
}
// A specialization of the above for nsIVariant, because that needs to
@ -1010,7 +1010,7 @@ WrapObject<nsIVariant>(JSContext* cx, JS::Handle<JSObject*> scope, nsIVariant* p
{
MOZ_ASSERT(iid);
MOZ_ASSERT(iid->Equals(NS_GET_IID(nsIVariant)));
return VariantToJsval(cx, scope, p, rval.address());
return VariantToJsval(cx, scope, p, rval);
}
// Wrap an object "p" which is not using WebIDL bindings yet. Just like the
@ -1100,8 +1100,8 @@ WrapNativeISupportsParent(JSContext* cx, JS::Handle<JSObject*> scope, T* p,
{
qsObjectHelper helper(ToSupports(p), cache);
JS::Rooted<JS::Value> v(cx);
return XPCOMObjectToJsval(cx, scope, helper, nullptr, false, v.address()) ?
JSVAL_TO_OBJECT(v) :
return XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v) ?
v.toObjectOrNull() :
nullptr;
}
@ -2005,7 +2005,7 @@ const T& NonNullHelper(const OwningNonNull<T>& aArg)
// Reparent the wrapper of aObj to whatever its native now thinks its
// parent should be.
nsresult
ReparentWrapper(JSContext* aCx, JS::HandleObject aObj);
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj);
/**
* Used to implement the hasInstance hook of an interface object.

View File

@ -1516,7 +1516,7 @@ class MethodDefiner(PropertyDefiner):
if any(m.isGetter() and m.isIndexed() for m in methods):
self.regular.append({"name": "@@iterator",
"methodInfo": False,
"selfHostedName": "ArrayIterator",
"selfHostedName": "ArrayValues",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": MemberCondition(None, None) })
@ -2409,7 +2409,7 @@ class CastableObjectUnwrapper():
"${type} *objPtr;\n"
"SelfRef objRef;\n"
"JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*${source}));\n"
"nsresult rv = UnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, val.address());\n"
"nsresult rv = UnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);\n"
"if (NS_FAILED(rv)) {\n"
"${codeOnFailure}\n"
"}\n"
@ -3230,7 +3230,7 @@ for (uint32_t i = 0; i < length; ++i) {
templateBody += (
"JS::Rooted<JS::Value> tmpVal(cx, ${val});\n" +
typePtr + " tmp;\n"
"if (NS_FAILED(UnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), tmpVal.address()))) {\n")
"if (NS_FAILED(UnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n")
templateBody += CGIndenter(onFailureBadType(failureCode,
descriptor.interface.identifier.name)).define()
templateBody += ("}\n"
@ -4121,9 +4121,9 @@ if (!returnArray) {
if type.isDOMString():
if type.nullable():
return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalRef}.address())" % result), False)
return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result), False)
else:
return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalRef}.address())" % result), False)
return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
if type.isByteString():
if type.nullable():

View File

@ -67,9 +67,9 @@ public:
MOZ_ASSERT(aReq && aAdapterPtr);
}
virtual bool ParseSuccessfulReply(JS::Value* aValue)
virtual bool ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
{
*aValue = JSVAL_VOID;
aValue.setUndefined();
const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
if (v.type() != BluetoothValue::TArrayOfBluetoothNamedValue) {
@ -113,7 +113,7 @@ public:
return false;
}
aValue->setObject(*JsDevices);
aValue.setObject(*JsDevices);
return true;
}
@ -136,9 +136,9 @@ public:
MOZ_ASSERT(aReq);
}
virtual bool ParseSuccessfulReply(JS::Value* aValue)
virtual bool ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
{
*aValue = JSVAL_VOID;
aValue.setUndefined();
const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
if (v.type() != BluetoothValue::Tbool) {
@ -147,7 +147,7 @@ public:
return false;
}
aValue->setBoolean(v.get_bool());
aValue.setBoolean(v.get_bool());
return true;
}

View File

@ -42,9 +42,9 @@ public:
}
bool
ParseSuccessfulReply(JS::Value* aValue)
ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
{
*aValue = JSVAL_VOID;
aValue.setUndefined();
const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
if (v.type() != BluetoothValue::TArrayOfBluetoothNamedValue) {

View File

@ -32,7 +32,7 @@ BluetoothReplyRunnable::~BluetoothReplyRunnable()
{}
nsresult
BluetoothReplyRunnable::FireReply(const JS::Value& aVal)
BluetoothReplyRunnable::FireReply(JS::Handle<JS::Value> aVal)
{
nsCOMPtr<nsIDOMRequestService> rs =
do_GetService(DOMREQUEST_SERVICE_CONTRACTID);
@ -62,10 +62,12 @@ BluetoothReplyRunnable::Run()
nsresult rv;
AutoSafeJSContext cx;
JS::Rooted<JS::Value> v(cx, JSVAL_VOID);
if (mReply->type() != BluetoothReply::TBluetoothReplySuccess) {
rv = FireReply(JSVAL_VOID);
rv = FireReply(v);
} else {
JS::Value v;
if (!ParseSuccessfulReply(&v)) {
rv = FireErrorString();
} else {

View File

@ -37,7 +37,7 @@ public:
protected:
virtual ~BluetoothReplyRunnable();
virtual bool ParseSuccessfulReply(JS::Value* aValue) = 0;
virtual bool ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue) = 0;
// This is an autoptr so we don't have to bring the ipdl include into the
// header. We assume we'll only be running this once and it should die on
@ -45,7 +45,7 @@ protected:
nsAutoPtr<BluetoothReply> mReply;
private:
nsresult FireReply(const JS::Value& aVal);
nsresult FireReply(JS::Handle<JS::Value> aVal);
nsresult FireErrorString();
nsCOMPtr<nsIDOMDOMRequest> mDOMRequest;
@ -59,9 +59,9 @@ public:
~BluetoothVoidReplyRunnable();
protected:
virtual bool ParseSuccessfulReply(JS::Value* aValue) MOZ_OVERRIDE
virtual bool ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue) MOZ_OVERRIDE
{
*aValue = JSVAL_VOID;
aValue.setUndefined();
return true;
}
};

View File

@ -65,7 +65,7 @@ public:
}
virtual bool
ParseSuccessfulReply(JS::Value* aValue) MOZ_OVERRIDE
ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue) MOZ_OVERRIDE
{
MOZ_CRASH("This should never be called!");
}

View File

@ -78,6 +78,13 @@ const ContentPanning = {
return;
}
let start = Date.now();
let thread = Services.tm.currentThread;
while (this._delayEvents && (Date.now() - start) < this._activeDurationMs) {
thread.processNextEvent(true);
}
this._delayEvents = false;
switch (evt.type) {
case 'mousedown':
case 'touchstart':
@ -229,6 +236,16 @@ const ContentPanning = {
// We prevent end events to avoid sending a focus event. See bug 889717.
evt.preventDefault();
}
} else if (this.target && click && !this.panning) {
this.notify(this._activationTimer);
this._delayEvents = true;
let start = Date.now();
let thread = Services.tm.currentThread;
while (this._delayEvents && (Date.now() - start) < this._activeDurationMs) {
thread.processNextEvent(true);
}
this._delayEvents = false;
}
this._finishPanning();
@ -467,6 +484,12 @@ const ContentPanning = {
return this._activationDelayMs = delay;
},
get _activeDurationMs() {
let duration = Services.prefs.getIntPref('ui.touch_activation.duration_ms');
delete this._activeDurationMs;
return this._activeDurationMs = duration;
},
_resetActive: function cp_resetActive() {
let elt = this.target || this.pointerDownTarget;
let root = elt.ownerDocument || elt.document;

View File

@ -1368,17 +1368,14 @@ InterfaceToJsval(nsPIDOMWindow* aWindow,
return JSVAL_NULL;
}
JS::RootedObject scopeObj(cx, sgo->GetGlobalJSObject());
JS::Rooted<JSObject*> scopeObj(cx, sgo->GetGlobalJSObject());
NS_ENSURE_TRUE(scopeObj, JSVAL_NULL);
JSAutoCompartment ac(cx, scopeObj);
JS::Rooted<JS::Value> someJsVal(cx);
nsresult rv = nsContentUtils::WrapNative(cx,
scopeObj,
aObject,
aIID,
someJsVal.address());
nsresult rv =
nsContentUtils::WrapNative(cx, scopeObj, aObject, aIID, &someJsVal);
if (NS_FAILED(rv)) {
return JSVAL_NULL;
}
@ -1437,7 +1434,7 @@ JS::Value StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
return JSVAL_NULL;
}
JS::Value result = JSVAL_NULL;
JS::Rooted<JS::Value> result(cx);
if (!xpc::StringToJsval(cx, aString, &result)) {
return JSVAL_NULL;
}

View File

@ -147,11 +147,11 @@ ArchiveRequest::ReaderReady(nsTArray<nsCOMPtr<nsIDOMFile> >& aFileList,
break;
case GetFile:
rv = GetFileResult(cx, result.address(), aFileList);
rv = GetFileResult(cx, &result, aFileList);
break;
case GetFiles:
rv = GetFilesResult(cx, result.address(), aFileList);
rv = GetFilesResult(cx, &result, aFileList);
break;
}
@ -208,7 +208,7 @@ ArchiveRequest::GetFilenamesResult(JSContext* aCx,
nsresult
ArchiveRequest::GetFileResult(JSContext* aCx,
JS::Value* aValue,
JS::MutableHandle<JS::Value> aValue,
nsTArray<nsCOMPtr<nsIDOMFile> >& aFileList)
{
for (uint32_t i = 0; i < aFileList.Length(); ++i) {
@ -230,7 +230,7 @@ ArchiveRequest::GetFileResult(JSContext* aCx,
nsresult
ArchiveRequest::GetFilesResult(JSContext* aCx,
JS::Value* aValue,
JS::MutableHandle<JS::Value> aValue,
nsTArray<nsCOMPtr<nsIDOMFile> >& aFileList)
{
JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, aFileList.Length(), nullptr));
@ -245,13 +245,13 @@ ArchiveRequest::GetFilesResult(JSContext* aCx,
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
nsresult rv = nsContentUtils::WrapNative(aCx, global, file,
&NS_GET_IID(nsIDOMFile),
value.address());
&value);
if (NS_FAILED(rv) || !JS_SetElement(aCx, array, i, &value)) {
return NS_ERROR_FAILURE;
}
}
aValue->setObject(*array);
aValue.setObject(*array);
return NS_OK;
}

View File

@ -66,10 +66,10 @@ private:
JS::Value* aValue,
nsTArray<nsCOMPtr<nsIDOMFile> >& aFileList);
nsresult GetFileResult(JSContext* aCx,
JS::Value* aValue,
JS::MutableHandle<JS::Value> aValue,
nsTArray<nsCOMPtr<nsIDOMFile> >& aFileList);
nsresult GetFilesResult(JSContext* aCx,
JS::Value* aValue,
JS::MutableHandle<JS::Value> aValue,
nsTArray<nsCOMPtr<nsIDOMFile> >& aFileList);
protected:

View File

@ -185,10 +185,12 @@ GetFileHelper::GetSuccessResult(JSContext* aCx, JS::Value* aVal)
mFileHandle->CreateFileObject(mLockedFile, mParams->Size());
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
JS::Rooted<JS::Value> rval(aCx);
nsresult rv =
nsContentUtils::WrapNative(aCx, global, domFile,
&NS_GET_IID(nsIDOMFile), aVal);
&NS_GET_IID(nsIDOMFile), &rval);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
*aVal = rval;
return NS_OK;
}

View File

@ -1066,11 +1066,13 @@ ReadTextHelper::GetSuccessResult(JSContext* aCx,
tmpString);
NS_ENSURE_SUCCESS(rv, rv);
if (!xpc::StringToJsval(aCx, tmpString, aVal)) {
JS::Rooted<JS::Value> rval(aCx);
if (!xpc::StringToJsval(aCx, tmpString, &rval)) {
NS_WARNING("Failed to convert string!");
return NS_ERROR_FAILURE;
}
*aVal = rval;
return NS_OK;
}

View File

@ -125,7 +125,7 @@ HelperBase::WrapNative(JSContext* aCx,
NS_ASSERTION(global, "This should never be null!");
nsresult rv =
nsContentUtils::WrapNative(aCx, global, aNative, aResult.address());
nsContentUtils::WrapNative(aCx, global, aNative, aResult);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return NS_OK;

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