Merge m-i to a CLOSED TREE m-c

This commit is contained in:
Phil Ringnalda 2013-09-16 23:12:03 -07:00
commit 61ea412a96
77 changed files with 443 additions and 1200 deletions

View File

@ -159,11 +159,10 @@ SocialUI = {
}
break;
case "social:profile-changed":
// make sure anything that happens here only affects the provider for
// which the profile is changing, and that anything we call actually
// needs to change based on profile data.
if (this._matchesCurrentProvider(data)) {
SocialToolbar.updateProvider();
SocialMarks.update();
SocialChatBar.update();
}
break;
case "social:frameworker-error":
@ -216,20 +215,13 @@ SocialUI = {
// enabled == true means we at least have a defaultProvider
let provider = Social.provider || Social.defaultProvider;
// We only need to update the command itself - all our menu items use it.
let label;
if (Social.providers.length == 1) {
label = gNavigatorBundle.getFormattedString(Social.provider
? "social.turnOff.label"
: "social.turnOn.label",
[provider.name]);
} else {
label = gNavigatorBundle.getString(Social.provider
? "social.turnOffAll.label"
: "social.turnOnAll.label");
}
let accesskey = gNavigatorBundle.getString(Social.provider
? "social.turnOff.accesskey"
: "social.turnOn.accesskey");
let label = gNavigatorBundle.getFormattedString(Social.provider ?
"social.turnOff.label" :
"social.turnOn.label",
[provider.name]);
let accesskey = gNavigatorBundle.getString(Social.provider ?
"social.turnOff.accesskey" :
"social.turnOn.accesskey");
toggleCommand.setAttribute("label", label);
toggleCommand.setAttribute("accesskey", accesskey);
}
@ -876,19 +868,17 @@ SocialToolbar = {
toggleNotificationsCommand.setAttribute("hidden", !socialEnabled);
let parent = document.getElementById("social-notification-panel");
while (parent.hasChildNodes()) {
let frame = parent.firstChild;
SharedFrame.forgetGroup(frame.id);
parent.removeChild(frame);
}
let tbi = document.getElementById("social-provider-button");
if (tbi) {
// buttons after social-provider-button are ambient icons, remove the
// button and the attached shared frame.
// buttons after social-provider-button are ambient icons
while (tbi.nextSibling) {
let tb = tbi.nextSibling;
let nid = tb.getAttribute("notificationFrameId");
let frame = document.getElementById(nid);
if (frame) {
SharedFrame.forgetGroup(frame.id);
parent.removeChild(frame);
}
tbi.parentNode.removeChild(tb);
tbi.parentNode.removeChild(tbi.nextSibling);
}
}
},

View File

@ -1119,25 +1119,18 @@ var gBrowserInit = {
// If the user manually opens the download manager before the timeout, the
// downloads will start right away, and getting the service again won't hurt.
setTimeout(function() {
try {
let DownloadsCommon =
Cu.import("resource:///modules/DownloadsCommon.jsm", {}).DownloadsCommon;
if (DownloadsCommon.useJSTransfer) {
// Open the data link without initalizing nsIDownloadManager.
DownloadsCommon.initializeAllDataLinks();
let DownloadsTaskbar =
Cu.import("resource:///modules/DownloadsTaskbar.jsm", {}).DownloadsTaskbar;
DownloadsTaskbar.registerIndicator(window);
} else {
// Initalizing nsIDownloadManager will trigger the data link.
Services.downloads;
let DownloadTaskbarProgress =
Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", {}).DownloadTaskbarProgress;
DownloadTaskbarProgress.onBrowserWindowLoad(window);
}
} catch (ex) {
Cu.reportError(ex);
let DownloadsCommon =
Cu.import("resource:///modules/DownloadsCommon.jsm", {}).DownloadsCommon;
if (DownloadsCommon.useJSTransfer) {
// Open the data link without initalizing nsIDownloadManager.
DownloadsCommon.initializeAllDataLinks();
} else {
// Initalizing nsIDownloadManager will trigger the data link.
Services.downloads;
}
let DownloadTaskbarProgress =
Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", {}).DownloadTaskbarProgress;
DownloadTaskbarProgress.onBrowserWindowLoad(window);
}, 10000);
// The object handling the downloads indicator is also initialized here in the

View File

@ -7,7 +7,6 @@ function test() {
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
runSocialTestWithProvider(gProviders, function (finishcb) {
Social.enabled = true;
runSocialTests(tests, undefined, undefined, function() {
Services.prefs.clearUserPref("social.allowMultipleWorkers");
finishcb();

View File

@ -2,36 +2,18 @@
* 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/. */
let manifests = [{ // normal provider
name: "provider example.com",
let manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
}, { // used for testing install
name: "provider test1",
origin: "https://test1.example.com",
statusURL: "https://test1.example.com/browser/browser/base/content/test/social/social_panel.html",
iconURL: "https://test1.example.com/browser/browser/base/content/test/moz.png",
}];
};
function test() {
waitForExplicitFinish();
// required to test status button in combination with the toolbaritem
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
// Preset the currentSet so the statusbutton is in the toolbar on addition. We
// bypass the SocialStatus class here since it requires the manifest already
// be installed.
let tbh = SocialStatus._toolbarHelper;
tbh.setPersistentPosition(tbh.idFromOrgin(manifests[1].origin));
runSocialTestWithProvider(manifests, function (finishcb) {
runSocialTests(tests, undefined, undefined, function() {
Services.prefs.clearUserPref("social.allowMultipleWorkers");
SocialStatus.removePosition(manifests[1].origin);
finishcb();
});
runSocialTestWithProvider(manifest, function (finishcb) {
runSocialTests(tests, undefined, undefined, finishcb);
});
}
@ -55,7 +37,7 @@ var tests = {
},
testProfileSet: function(next) {
let statusIcon = document.getElementById("social-provider-button").style.listStyleImage;
is(statusIcon, "url(\"" + manifests[0].iconURL + "\")", "manifest iconURL is showing");
is(statusIcon, "url(\"" + manifest.iconURL + "\")", "manifest iconURL is showing");
let profile = {
portrait: "https://example.com/portrait.jpg",
userName: "trickster",
@ -185,32 +167,14 @@ var tests = {
}, "statusIcon was never found");
},
testProfileUnset: function(next) {
let panel = document.getElementById("social-notification-panel");
// load the status button for provider 2
let provider = Social._getProviderFromOrigin(manifests[1].origin);
let id = SocialStatus._toolbarHelper.idFromOrgin(provider.origin);
let btn = document.getElementById(id)
ok(btn, "got a status button");
// cheat a little, we want the iframe for the status button to be created,
// not testing the statusbutton itself here.
SocialStatus._attachNotificatonPanel(btn, provider);
let numIcons = Object.keys(Social.provider.ambientNotificationIcons).length;
let ambientIcons = document.querySelectorAll("#social-toolbar-item > toolbarbutton[type='badged']");
is(numIcons, ambientIcons.length, "all ambient icons exist");
is(panel.childNodes.length, ambientIcons.length + 1, "frames all exist");
// we need to wait until after social:profile-changed has completed
waitForNotification("social:profile-changed", function() {
// let the notifications finish first
executeSoon(function() {
let icons = document.querySelectorAll("#social-toolbar-item > toolbarbutton[type='badged']");
is(icons.length, 0, "ambient icons have been removed");
is(panel.childNodes.length, 1, "frame still exists");
next();
});
});
Social.provider.updateUserProfile({});
// check dom values
let ambientIcons = document.querySelectorAll("#social-toolbar-item > box");
for (let ambientIcon of ambientIcons) {
ok(ambientIcon.collapsed, "ambient icon (" + ambientIcon.id + ") is collapsed");
}
next();
},
testMenuitemsExist: function(next) {
let toggleSidebarMenuitems = document.getElementsByClassName("social-toggle-sidebar-menuitem");
@ -230,5 +194,5 @@ var tests = {
is(cmd.getAttribute("checked"), enabled ? "true" : "false");
Services.prefs.clearUserPref("social.toast-notifications.enabled");
next();
}
},
}

View File

@ -298,14 +298,6 @@ function checkSocialUI(win) {
is(numGoodTests, numTests, "The Social UI tests succeeded.")
}
function waitForNotification(topic, cb) {
function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
cb();
}
Services.obs.addObserver(observer, topic, false);
}
// blocklist testing
function updateBlocklist(aCallback) {
var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]

View File

@ -1,180 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
/* 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/. */
/**
* Handles the download progress indicator in the taskbar.
*/
"use strict";
this.EXPORTED_SYMBOLS = [
"DownloadsTaskbar",
];
////////////////////////////////////////////////////////////////////////////////
//// Globals
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/Downloads.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
"resource:///modules/RecentWindow.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function () {
if (!("@mozilla.org/windows-taskbar;1" in Cc)) {
return null;
}
let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"]
.getService(Ci.nsIWinTaskbar);
return winTaskbar.available && winTaskbar;
});
XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function () {
return ("@mozilla.org/widget/macdocksupport;1" in Cc) &&
Cc["@mozilla.org/widget/macdocksupport;1"]
.getService(Ci.nsITaskbarProgress);
});
////////////////////////////////////////////////////////////////////////////////
//// DownloadsTaskbar
/**
* Handles the download progress indicator in the taskbar.
*/
this.DownloadsTaskbar = {
/**
* Underlying DownloadSummary providing the aggregate download information, or
* null if the indicator has never been initialized.
*/
_summary: null,
/**
* nsITaskbarProgress object to which download information is dispatched.
* This can be null if the indicator has never been initialized or if the
* indicator is currently hidden on Windows.
*/
_taskbarProgress: null,
/**
* This method is called after a new browser window is opened, and ensures
* that the download progress indicator is displayed in the taskbar.
*
* On Windows, the indicator is attached to the first browser window that
* calls this method. When the window is closed, the indicator is moved to
* another browser window, if available, in no particular order. When there
* are no browser windows visible, the indicator is hidden.
*
* On Mac OS X, the indicator is initialized globally when this method is
* called for the first time. Subsequent calls have no effect.
*
* @param aBrowserWindow
* nsIDOMWindow object of the newly opened browser window to which the
* indicator may be attached.
*/
registerIndicator: function (aBrowserWindow)
{
if (!this._taskbarProgress) {
if (gMacTaskbarProgress) {
// On Mac OS X, we have to register the global indicator only once.
this._taskbarProgress = gMacTaskbarProgress;
// Free the XPCOM reference on shutdown, to prevent detecting a leak.
Services.obs.addObserver(() => {
this._taskbarProgress = null;
gMacTaskbarProgress = null;
}, "quit-application-granted", false);
} else if (gWinTaskbar) {
// On Windows, the indicator is currently hidden because we have no
// previous browser window, thus we should attach the indicator now.
this._attachIndicator(aBrowserWindow);
} else {
// The taskbar indicator is not available on this platform.
return;
}
}
// Ensure that the DownloadSummary object will be created asynchronously.
if (!this._summary) {
Downloads.getSummary(Downloads.ALL).then(summary => {
// In case the method is re-entered, we simply ignore redundant
// invocations of the callback, instead of keeping separate state.
if (this._summary) {
return;
}
this._summary = summary;
this._summary.addView(this);
});
}
},
/**
* On Windows, attaches the taskbar indicator to the specified browser window.
*/
_attachIndicator: function (aWindow)
{
// Activate the indicator on the specified window.
let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIXULWindow).docShell;
this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell);
// If the DownloadSummary object has already been created, we should update
// the state of the new indicator, otherwise it will be updated as soon as
// the DownloadSummary view is registered.
if (this._summary) {
this.onSummaryChanged();
}
aWindow.addEventListener("unload", () => {
// Locate another browser window, excluding the one being closed.
let browserWindow = RecentWindow.getMostRecentBrowserWindow();
if (browserWindow) {
// Move the progress indicator to the other browser window.
this._attachIndicator(browserWindow);
} else {
// The last browser window has been closed. We remove the reference to
// the taskbar progress object so that the indicator will be registered
// again on the next browser window that is opened.
this._taskbarProgress = null;
}
}, false);
},
//////////////////////////////////////////////////////////////////////////////
//// DownloadSummary view
onSummaryChanged: function ()
{
// If the last browser window has been closed, we have no indicator anymore.
if (!this._taskbarProgress) {
return;
}
if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
this._taskbarProgress.setProgressState(
Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0);
} else {
// For a brief moment before completion, some download components may
// report more transferred bytes than the total number of bytes. Thus,
// ensure that we never break the expectations of the progress indicator.
let progressCurrentBytes = Math.min(this._summary.progressTotalBytes,
this._summary.progressCurrentBytes);
this._taskbarProgress.setProgressState(
Ci.nsITaskbarProgress.STATE_NORMAL,
progressCurrentBytes,
this._summary.progressTotalBytes);
}
},
};

View File

@ -13,6 +13,5 @@ EXTRA_COMPONENTS += [
EXTRA_JS_MODULES += [
'DownloadsCommon.jsm',
'DownloadsLogger.jsm',
'DownloadsTaskbar.jsm',
]

View File

@ -428,8 +428,6 @@ social.turnOff.accesskey=T
# LOCALIZATION NOTE (social.turnOn.label): %S is the name of the social provider
social.turnOn.label=Turn on %S
social.turnOn.accesskey=T
social.turnOffAll.label=Turn off all Services
social.turnOnAll.label=Turn on all Services
# LOCALIZATION NOTE (social.markpageMenu.label): %S is the name of the social provider
social.markpageMenu.label=Save Page to %S

View File

@ -34,8 +34,9 @@ function prefObserver(subject, topic, data) {
if (enable && !Social.provider) {
// this will result in setting Social.provider
SocialService.getOrderedProviderList(function(providers) {
Social.enabled = true;
Social._updateProviderCache(providers);
Social.enabled = true;
Services.obs.notifyObservers(null, "social:providers-changed", null);
});
} else if (!enable && Social.provider) {
Social.provider = null;
@ -167,7 +168,7 @@ this.Social = {
// Retrieve the current set of providers, and set the current provider.
SocialService.getOrderedProviderList(function (providers) {
Social._updateProviderCache(providers);
Social._updateWorkerState(SocialService.enabled);
Social._updateWorkerState(true);
});
}
@ -180,16 +181,10 @@ this.Social = {
Services.obs.notifyObservers(null, "social:" + topic, origin);
return;
}
if (topic == "provider-enabled") {
Social._updateProviderCache(providers);
Social._updateWorkerState(Social.enabled);
Services.obs.notifyObservers(null, "social:" + topic, origin);
return;
}
if (topic == "provider-disabled") {
// a provider was removed from the list of providers, that does not
// affect worker state for other providers
if (topic == "provider-enabled" || topic == "provider-disabled") {
Social._updateProviderCache(providers);
Social._updateWorkerState(true);
Services.obs.notifyObservers(null, "social:providers-changed", null);
Services.obs.notifyObservers(null, "social:" + topic, origin);
return;
}
@ -199,6 +194,7 @@ this.Social = {
Social._updateProviderCache(providers);
let provider = Social._getProviderFromOrigin(origin);
provider.reload();
Services.obs.notifyObservers(null, "social:providers-changed", null);
}
});
},
@ -214,7 +210,6 @@ this.Social = {
// Called to update our cache of providers and set the current provider
_updateProviderCache: function (providers) {
this.providers = providers;
Services.obs.notifyObservers(null, "social:providers-changed", null);
// If social is currently disabled there's nothing else to do other than
// to notify about the lack of a provider.

View File

@ -4,4 +4,4 @@
# 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/.
DIRS += ['chrome', 'unit']
DIRS += ['chrome']

View File

@ -1,9 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DIRS += ['social']
XPCSHELL_TESTS_MANIFESTS += ['social/xpcshell.ini']

View File

@ -1,11 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPCSHELL_RESOURCES = \
xpcshell.ini \
head.js \
blocklist.xml \
test_social.js \
test_socialDisabledStartup.js \
$(NULL)

View File

@ -1,6 +0,0 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
<emItems>
<emItem blockID="s1" id="bad.com@services.mozilla.org"></emItem>
</emItems>
</blocklist>

View File

@ -1,146 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var Social, SocialService;
let manifests = [
{
name: "provider 1",
origin: "https://example1.com",
sidebarURL: "https://example1.com/sidebar/",
},
{
name: "provider 2",
origin: "https://example2.com",
sidebarURL: "https://example1.com/sidebar/",
}
];
const MANIFEST_PREFS = Services.prefs.getBranch("social.manifest.");
// SocialProvider class relies on blocklisting being enabled. To enable
// blocklisting, we have to setup an app and initialize the blocklist (see
// initApp below).
const gProfD = do_get_profile();
const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
function createAppInfo(id, name, version, platformVersion) {
gAppInfo = {
// nsIXULAppInfo
vendor: "Mozilla",
name: name,
ID: id,
version: version,
appBuildID: "2007010101",
platformVersion: platformVersion ? platformVersion : "1.0",
platformBuildID: "2007010101",
// nsIXULRuntime
inSafeMode: false,
logConsoleErrors: true,
OS: "XPCShell",
XPCOMABI: "noarch-spidermonkey",
invalidateCachesOnRestart: function invalidateCachesOnRestart() {
// Do nothing
},
// nsICrashReporter
annotations: {},
annotateCrashReport: function(key, data) {
this.annotations[key] = data;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo,
Ci.nsIXULRuntime,
Ci.nsICrashReporter,
Ci.nsISupports])
};
var XULAppInfoFactory = {
createInstance: function (outer, iid) {
if (outer != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return gAppInfo.QueryInterface(iid);
}
};
var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
XULAPPINFO_CONTRACTID, XULAppInfoFactory);
}
function initApp() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
// prepare a blocklist file for the blocklist service
var blocklistFile = gProfD.clone();
blocklistFile.append("blocklist.xml");
if (blocklistFile.exists())
blocklistFile.remove(false);
var source = do_get_file("blocklist.xml");
source.copyTo(gProfD, "blocklist.xml");
}
function setManifestPref(manifest) {
let string = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
string.data = JSON.stringify(manifest);
Services.prefs.setComplexValue("social.manifest." + manifest.origin, Ci.nsISupportsString, string);
}
function do_wait_observer(topic, cb) {
function observer(subject, topic, data) {
Services.obs.removeObserver(observer, topic);
cb();
}
Services.obs.addObserver(observer, topic, false);
}
function do_initialize_social(enabledOnStartup, cb) {
initApp();
manifests.forEach(function (manifest) {
setManifestPref(manifest);
});
// Set both providers active and flag the first one as "current"
let activeVal = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
let active = {};
for (let m of manifests)
active[m.origin] = 1;
activeVal.data = JSON.stringify(active);
Services.prefs.setComplexValue("social.activeProviders",
Ci.nsISupportsString, activeVal);
Services.prefs.setCharPref("social.provider.current", manifests[0].origin);
Services.prefs.setBoolPref("social.enabled", enabledOnStartup);
do_register_cleanup(function() {
manifests.forEach(function (manifest) {
Services.prefs.clearUserPref("social.manifest." + manifest.origin);
});
Services.prefs.clearUserPref("social.enabled");
Services.prefs.clearUserPref("social.provider.current");
Services.prefs.clearUserPref("social.activeProviders");
});
// expecting 2 providers installed
do_wait_observer("social:providers-changed", function() {
do_check_eq(Social.providers.length, 2, "2 providers installed");
cb();
});
// import and initialize everything
SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
do_check_eq(SocialService.enabled, enabledOnStartup, "service is doing its thing");
do_check_true(SocialService.hasEnabledProviders, "Service has enabled providers");
Social = Cu.import("resource:///modules/Social.jsm", {}).Social;
do_check_false(Social.initialized, "Social is not initialized");
Social.init();
do_check_true(Social.initialized, "Social is initialized");
}

View File

@ -1,6 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.

View File

@ -1,34 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
function run_test() {
// we are testing worker startup specifically
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
do_register_cleanup(function() {
Services.prefs.clearUserPref("social.allowMultipleWorkers");
});
do_test_pending();
add_test(testStartupEnabled);
add_test(testDisableAfterStartup);
do_initialize_social(true, run_next_test);
}
function testStartupEnabled() {
// wait on startup before continuing
do_check_eq(Social.providers.length, 2, "two social providers enabled");
do_check_true(Social.providers[0].enabled, "provider is enabled");
do_check_true(Social.providers[1].enabled, "provider is enabled");
run_next_test();
}
function testDisableAfterStartup() {
do_wait_observer("social:provider-set", function() {
do_check_eq(Social.enabled, false, "Social is disabled");
do_check_false(Social.providers[0].enabled, "provider is enabled");
do_check_false(Social.providers[1].enabled, "provider is enabled");
do_test_finished();
run_next_test();
});
Social.enabled = false;
}

View File

@ -1,35 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
function run_test() {
// we are testing worker startup specifically
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
do_register_cleanup(function() {
Services.prefs.clearUserPref("social.allowMultipleWorkers");
});
do_test_pending();
add_test(testStartupDisabled);
add_test(testEnableAfterStartup);
do_initialize_social(false, run_next_test);
}
function testStartupDisabled() {
// wait on startup before continuing
do_check_eq(Social.providers.length, 2, "two social providers available");
do_check_false(Social.providers[0].enabled, "provider is enabled");
do_check_false(Social.providers[1].enabled, "provider is enabled");
run_next_test();
}
function testEnableAfterStartup() {
do_wait_observer("social:provider-set", function() {
do_check_true(Social.enabled, "Social is enabled");
do_check_eq(Social.providers.length, 2, "two social providers available");
do_check_true(Social.providers[0].enabled, "provider is enabled");
do_check_true(Social.providers[1].enabled, "provider is enabled");
do_test_finished();
run_next_test();
});
Social.enabled = true;
}

View File

@ -1,8 +0,0 @@
[DEFAULT]
head = head.js
tail =
firefox-appdir = browser
[test_social.js]
[test_socialDisabledStartup.js]

View File

@ -716,9 +716,9 @@ nsScriptLoader::ProcessOffThreadRequest(void **aOffThreadToken)
{
nsCOMPtr<nsScriptLoadRequest> request = mOffThreadScriptRequest;
mOffThreadScriptRequest = nullptr;
nsresult rv = ProcessRequest(request, aOffThreadToken);
mDocument->UnblockOnload(false);
return ProcessRequest(request, aOffThreadToken);
return rv;
}
NS_IMETHODIMP

View File

@ -47,6 +47,38 @@ HasOpaqueAncestorLayer(Layer* aLayer)
return false;
}
/**
* Returns a rectangle of content painted opaquely by aLayer. Very consertative;
* bails by returning an empty rect in any tricky situations.
*/
static nsIntRect
GetOpaqueRect(Layer* aLayer)
{
nsIntRect result;
// Just bail if there's anything difficult to handle.
if (!aLayer->GetEffectiveTransform().IsIdentity() ||
aLayer->GetEffectiveOpacity() != 1.0f ||
aLayer->GetMaskLayer()) {
return result;
}
if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
result = aLayer->GetEffectiveVisibleRegion().GetLargestRectangle();
} else {
// Drill down into RefLayers because that's what we particularly care about;
// layer construction for aLayer will not have known about the opaqueness
// of any RefLayer subtrees.
RefLayer* refLayer = aLayer->AsRefLayer();
if (refLayer && refLayer->GetFirstChild()) {
result = GetOpaqueRect(refLayer->GetFirstChild());
}
}
const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
if (clipRect) {
result.IntersectRect(result, *clipRect);
}
return result;
}
template<class ContainerT> void
ContainerRender(ContainerT* aContainer,
const nsIntPoint& aOffset,
@ -143,6 +175,23 @@ ContainerRender(ContainerT* aContainer,
continue;
}
if (i + 1 < children.Length() &&
layerToRender->GetLayer()->GetEffectiveTransform().IsIdentity()) {
LayerComposite* nextLayer = static_cast<LayerComposite*>(children.ElementAt(i + 1)->ImplData());
nsIntRect nextLayerOpaqueRect;
if (nextLayer && nextLayer->GetLayer()) {
nextLayerOpaqueRect = GetOpaqueRect(nextLayer->GetLayer());
}
if (!nextLayerOpaqueRect.IsEmpty()) {
nsIntRegion visibleRegion;
visibleRegion.Sub(layerToRender->GetShadowVisibleRegion(), nextLayerOpaqueRect);
layerToRender->SetShadowVisibleRegion(visibleRegion);
if (visibleRegion.IsEmpty()) {
continue;
}
}
}
nsIntRect clipRect = layerToRender->GetLayer()->
CalculateScissorRect(aClipRect, &aManager->GetWorldTransform());
if (clipRect.IsEmpty()) {

View File

@ -46,6 +46,7 @@
#include "nsCanvasFrame.h"
#include "StickyScrollContainer.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/Preferences.h"
#include <stdint.h>
#include <algorithm>

View File

@ -6,7 +6,6 @@
#include "gfxPlatform.h"
#include "AnimationCommon.h"
#include "nsRuleData.h"
#include "nsCSSFrameConstructor.h"
#include "nsCSSValue.h"
#include "nsStyleContext.h"
#include "nsIFrame.h"
@ -16,7 +15,6 @@
#include "FrameLayerBuilder.h"
#include "nsDisplayList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Preferences.h"
#include "RestyleManager.h"
using namespace mozilla::layers;

View File

@ -13,7 +13,7 @@
#include "mozilla/css/Declaration.h"
#include "nsPrintfCString.h"
#include "mozilla/Preferences.h"
#include "gfxFontConstants.h"
namespace mozilla {
namespace css {

View File

@ -11,8 +11,6 @@
#ifndef mozilla_css_StyleRule_h__
#define mozilla_css_StyleRule_h__
#include "mozilla/Attributes.h"
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/Rule.h"
@ -21,7 +19,6 @@
#include "nsCOMPtr.h"
#include "nsCSSPseudoElements.h"
#include "nsCSSPseudoClasses.h"
#include "nsAutoPtr.h"
class nsIAtom;
class nsCSSStyleSheet;

View File

@ -12,9 +12,7 @@
#include "nsStyleSet.h"
#include "nsCSSRules.h"
#include "nsStyleAnimation.h"
#include "nsSMILKeySpline.h"
#include "nsEventDispatcher.h"
#include "nsCSSFrameConstructor.h"
#include "nsLayoutUtils.h"
#include <math.h>

View File

@ -8,15 +8,12 @@
#include "mozilla/Attributes.h"
#include "AnimationCommon.h"
#include "nsCSSPseudoElements.h"
#include "nsStyleContext.h"
#include "nsDataHashtable.h"
#include "nsGUIEvent.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Preferences.h"
#include "nsThreadUtils.h"
class nsCSSKeyframesRule;
class nsStyleContext;
namespace mozilla {
namespace css {

View File

@ -14,6 +14,8 @@
#include "mozilla/MemoryReporting.h"
#include "nsCSSProps.h"
#include "nsCSSPropertySet.h"
#include "nsCSSValue.h"
#include "imgRequestProxy.h"
struct nsRuleData;
class nsCSSExpandedDataBlock;

View File

@ -8,7 +8,6 @@
#include "nsCSSKeywords.h"
#include "nsString.h"
#include "nsStaticNameTable.h"
#include "nsReadableUtils.h"
// required to make the symbol external, so that TestCSSPropertyLookup.cpp can link with it
extern const char* const kCSSRawKeywords[];

View File

@ -25,7 +25,6 @@
#include "nsNetUtil.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsIAtom.h"
#include "nsColor.h"
#include "nsCSSPseudoClasses.h"

View File

@ -19,8 +19,10 @@
#include "mozilla/LookAndFeel.h" // for system colors
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsStaticNameTable.h"
#include "nsStyleConsts.h"
#include "gfxFontConstants.h"
#include "nsStyleStruct.h"
#include "mozilla/Preferences.h"

View File

@ -12,9 +12,8 @@
#define nsCSSProps_h___
#include "nsString.h"
#include "nsChangeHint.h"
#include "nsCSSProperty.h"
#include "nsStyleStruct.h"
#include "nsStyleStructFwd.h"
#include "nsCSSKeywords.h"
// Flags for ParseVariant method

View File

@ -10,6 +10,7 @@
#include "nsCSSPseudoClasses.h"
#include "nsStaticAtom.h"
#include "mozilla/Preferences.h"
#include "nsString.h"
using namespace mozilla;

View File

@ -8,7 +8,7 @@
#ifndef nsCSSPseudoClasses_h___
#define nsCSSPseudoClasses_h___
#include "nsString.h"
#include "nsStringFwd.h"
class nsIAtom;

View File

@ -19,7 +19,6 @@
#include "nsCSSRuleProcessor.h"
#include "nsRuleProcessorData.h"
#include <algorithm>
#include "nsCRT.h"
#include "nsIAtom.h"
#include "pldhash.h"
#include "nsICSSPseudoComparator.h"
@ -28,9 +27,7 @@
#include "mozilla/css/GroupRule.h"
#include "nsIDocument.h"
#include "nsPresContext.h"
#include "nsEventStateManager.h"
#include "nsGkAtoms.h"
#include "nsString.h"
#include "nsUnicharUtils.h"
#include "nsError.h"
#include "nsRuleWalker.h"
@ -44,17 +41,11 @@
#include "nsAttrValue.h"
#include "nsAttrValueInlines.h"
#include "nsAttrName.h"
#include "nsServiceManagerUtils.h"
#include "nsTArray.h"
#include "nsContentUtils.h"
#include "nsIMediaList.h"
#include "nsCSSRules.h"
#include "nsIPrincipal.h"
#include "nsStyleSet.h"
#include "prlog.h"
#include "nsIObserverService.h"
#include "nsNetCID.h"
#include "mozilla/Services.h"
#include "mozilla/dom/Element.h"
#include "nsNthIndexCache.h"
#include "mozilla/Preferences.h"

View File

@ -18,7 +18,6 @@
#include "nsCSSStyleSheet.h"
#include "nsTArray.h"
#include "nsAutoPtr.h"
#include "nsCSSRules.h"
#include "nsRuleWalker.h"
#include "nsEventStates.h"
@ -28,6 +27,8 @@ struct nsCSSSelectorList;
struct RuleCascadeData;
struct TreeMatchContext;
class nsCSSKeyframesRule;
class nsCSSPageRule;
class nsCSSFontFeatureValuesRule;
/**
* The CSS style rule processor provides a mechanism for sibling style

View File

@ -23,7 +23,6 @@
#include "nsIDOMCSSSupportsRule.h"
#include "nsIDOMMozCSSKeyframeRule.h"
#include "nsIDOMMozCSSKeyframesRule.h"
#include "nsIDOMCSSStyleDeclaration.h"
#include "nsAutoPtr.h"
#include "nsCSSProperty.h"
#include "nsCSSValue.h"

View File

@ -8,6 +8,7 @@
#include "nsCSSScanner.h"
#include "nsStyleUtil.h"
#include "nsTraceRefcnt.h"
#include "mozilla/css/ErrorReporter.h"
#include "mozilla/Likely.h"
#include "mozilla/Util.h"

View File

@ -18,6 +18,9 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/ImageLoader.h"
#include "mozilla/Likely.h"
#include "gfxFontConstants.h"
#include "nsPresContext.h"
#include "imgRequestProxy.h"
namespace css = mozilla::css;

View File

@ -29,8 +29,6 @@ class nsIDocument;
class nsIPrincipal;
class nsPresContext;
class nsIURI;
template <class T>
class nsPtrHashKey;
// Deletes a linked list iteratively to avoid blowing up the stack (bug 456196).
#define NS_CSS_DELETE_LIST_MEMBER(type_, ptr_, member_) \

View File

@ -43,7 +43,6 @@
#include "mozilla/dom/Element.h"
#include "prtime.h"
#include "nsWrapperCacheInlines.h"
#include "nsUTF8Utils.h"
#include <algorithm>
using namespace mozilla;

View File

@ -15,8 +15,10 @@
#include "nsCSSProperty.h"
#include "nsDOMCSSDeclaration.h"
#include "nsStyleContext.h"
#include "nsStyleStruct.h"
#include "nsIWeakReferenceUtils.h"
#include "mozilla/gfx/Types.h"
#include "nsCoord.h"
#include "nsColor.h"
#include "nsIContent.h"
namespace mozilla {
@ -28,8 +30,37 @@ class Element;
class nsIFrame;
class nsIPresShell;
class nsDOMCSSValueList;
class nsMargin;
class nsROCSSPrimitiveValue;
class nsStyleContext;
class nsStyleBackground;
class nsStyleBorder;
class nsStyleContent;
class nsStyleColumn;
class nsStyleColor;
class nsStyleCoord;
class nsStyleCorners;
class nsStyleDisplay;
class nsStyleFilter;
class nsStyleFont;
class nsStyleGradient;
class nsStyleImage;
class nsStyleList;
class nsStyleMargin;
class nsStyleOutline;
class nsStylePadding;
class nsStylePosition;
class nsStyleQuotes;
class nsStyleSides;
class nsStyleSVG;
class nsStyleSVGReset;
class nsStyleTable;
class nsStyleText;
class nsStyleTextReset;
class nsStyleTimingFunction;
class nsStyleUIReset;
class nsStyleVisibility;
class nsStyleXUL;
class nsTimingFunction;
class gfx3DMatrix;
class nsComputedDOMStyle MOZ_FINAL : public nsDOMCSSDeclaration

View File

@ -14,7 +14,6 @@
#include "nsIDOMMutationEvent.h"
#include "nsIURI.h"
#include "nsNodeUtils.h"
#include "xpcpublic.h"
#include "nsWrapperCacheInlines.h"
namespace css = mozilla::css;

View File

@ -15,7 +15,6 @@
class nsIPrincipal;
class nsIDocument;
class nsIURI;
namespace mozilla {
namespace css {

View File

@ -9,7 +9,6 @@
#define nsDOMCSSRect_h_
#include "mozilla/Attributes.h"
#include "nsISupportsImpl.h"
#include "nsIDOMRect.h"
#include "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.h"

View File

@ -27,7 +27,6 @@
#include "nsContentPolicyUtils.h"
#include "nsCrossSiteListenerProxy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsChannelPolicy.h"
#include "nsIDocShell.h"
#include "nsIWebNavigation.h"

View File

@ -17,12 +17,10 @@
#include "nsTHashtable.h"
#include "nsCSSRules.h"
class nsISupports;
class nsPresContext;
class nsIPrincipal;
class nsFontFaceLoader;
class nsCSSFontFaceRule;
// nsUserFontSet - defines the loading mechanism for downloadable fonts
class nsUserFontSet : public gfxUserFontSet

View File

@ -12,8 +12,6 @@
#include "mozilla/css/StyleRule.h"
#include "nsIStyleRuleProcessor.h"
#include "nsPresContext.h"
#include "nsIDocument.h"
#include "nsCOMPtr.h"
#include "nsRuleWalker.h"
#include "nsRuleProcessorData.h"
#include "mozilla/dom/Element.h"

View File

@ -13,9 +13,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
#include "nsCOMPtr.h"
#include "nsDataHashtable.h"
#include "nsIStyleSheet.h"
#include "nsIStyleRuleProcessor.h"
struct MiscContainer;

View File

@ -32,7 +32,6 @@
#include "nsCSSRuleProcessor.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/Element.h"
#include "nsCSSFrameConstructor.h"
#include "nsHashKeys.h"
#include "RestyleManager.h"

View File

@ -17,12 +17,12 @@
#include "nsCOMPtr.h"
#include "nsIStyleRule.h"
#include "nsIStyleRuleProcessor.h"
#include "nsIStyleSheet.h"
#include "pldhash.h"
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
#include "nsString.h"
class nsIDocument;
class nsMappedAttributes;
class nsHTMLStyleSheet MOZ_FINAL : public nsIStyleRuleProcessor

View File

@ -11,7 +11,9 @@
#include "nsStyleConsts.h"
#include "nsPresContext.h"
#include "nsCSSValue.h"
#ifdef XP_WIN
#include "mozilla/LookAndFeel.h"
#endif
#include "nsCSSRuleProcessor.h"
using namespace mozilla;

View File

@ -12,6 +12,7 @@
#define nsRuleData_h_
#include "nsCSSProps.h"
#include "nsCSSValue.h"
#include "nsStyleStructFwd.h"
class nsPresContext;

View File

@ -8,7 +8,7 @@
#ifndef nsStyleAnimation_h_
#define nsStyleAnimation_h_
#include "nsAString.h"
#include "nsStringFwd.h"
#include "nsCRTGlue.h"
#include "nsStringBuffer.h"
#include "nsCSSProperty.h"

View File

@ -33,6 +33,7 @@
#include "GeckoProfiler.h"
#include "nsHTMLCSSStyleSheet.h"
#include "nsHTMLStyleSheet.h"
#include "nsCSSRules.h"
using namespace mozilla;
using namespace mozilla::dom;

View File

@ -26,7 +26,6 @@
#include "nsCSSPseudoElements.h"
#include "gfxFontFeatures.h"
class nsIURI;
class nsCSSFontFaceRule;
class nsCSSKeyframesRule;
class nsCSSFontFeatureValuesRule;

View File

@ -13,6 +13,7 @@
#include "nsRuleNode.h"
#include "nsCSSKeywords.h"
#include "nsStyleAnimation.h"
#include "gfxMatrix.h"
namespace css = mozilla::css;

View File

@ -11,10 +11,8 @@
#define nsStyleTransformMatrix_h_
#include "nsCSSValue.h"
#include "gfxMatrix.h"
#include "gfx3DMatrix.h"
struct nsCSSValueList;
class nsStyleContext;
class nsPresContext;
struct nsRect;

View File

@ -7,7 +7,6 @@
#include "nsStyleConsts.h"
#include "nsIContent.h"
#include "nsReadableUtils.h"
#include "nsCSSProps.h"
#include "nsRuleNode.h"
#include "nsROCSSPrimitiveValue.h"

View File

@ -7,15 +7,16 @@
#include "nsCoord.h"
#include "nsCSSProperty.h"
#include "gfxFontFeatures.h"
#include "nsIPrincipal.h"
#include "nsSubstring.h"
#include "nsStringFwd.h"
class nsCSSValue;
class nsStringComparator;
class nsStyleCoord;
class nsIContent;
class nsIPrincipal;
class nsIURI;
struct gfxFontFeature;
struct gfxAlternateValue;
class nsCSSValueList;
template <class E> class nsTArray;

View File

@ -15,17 +15,13 @@
#include "mozilla/TimeStamp.h"
#include "nsRefreshDriver.h"
#include "nsRuleProcessorData.h"
#include "nsIStyleRule.h"
#include "nsRuleWalker.h"
#include "nsRuleData.h"
#include "gfxColor.h"
#include "nsCSSPropertySet.h"
#include "nsStyleAnimation.h"
#include "nsEventDispatcher.h"
#include "nsGUIEvent.h"
#include "mozilla/dom/Element.h"
#include "nsIFrame.h"
#include "nsCSSFrameConstructor.h"
#include "Layers.h"
#include "FrameLayerBuilder.h"
#include "nsDisplayList.h"

View File

@ -18,6 +18,8 @@
*/
#include "primpl.h"
#include <stdio.h>
#include <windows.h>
/*
* AddThreadToCVWaitQueueInternal --
@ -150,6 +152,10 @@ md_UnlockAndPostNotifies(
thred->md.prev = thred->md.next = NULL;
rv = ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
if (!rv) {
fprintf(stderr, "ReleaseSemaphore failed, handle: %x, last error: %x\n",
(void*)thred->md.blocked_sema, GetLastError());
}
PR_ASSERT(rv != 0);
thred = next;
}
@ -246,6 +252,10 @@ void _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
/* Wait for notification or timeout; don't really care which */
rv = WaitForSingleObject(thred->md.blocked_sema, msecs);
if (rv == WAIT_FAILED) {
fprintf(stderr, "WaitForSingleObject failed, handle: %x, last error: %x\n",
(void*)thred->md.blocked_sema, GetLastError());
}
EnterCriticalSection(&(lock->mutex));

View File

@ -5,6 +5,8 @@
#include "primpl.h"
#include <process.h> /* for _beginthreadex() */
#include <stdio.h>
#include <windows.h>
#if defined(_MSC_VER) && _MSC_VER <= 1200
/*
@ -217,6 +219,10 @@ _PR_MD_CLEAN_THREAD(PRThread *thread)
if (thread->md.handle) {
rv = CloseHandle(thread->md.handle);
if (!rv) {
fprintf(stderr, "CloseHandle failed, handle: %x, last error: %x\n",
(void*)thread->md.handle, GetLastError());
}
PR_ASSERT(rv);
thread->md.handle = 0;
}

View File

@ -131,7 +131,11 @@
"content/html/document/test/test_bug446483.html": "",
"content/html/document/test/test_bug741266.html": "",
"content/media/test/test_autoplay_contentEditable.html": "x86 only",
"content/media/test/test_playback_rate.html": "bug 845162",
"content/media/test/test_seek.html": "bug 845162",
"content/media/test/test_played.html": "bug 751539",
"content/media/test/test_buffered.html": "",
"content/media/test/test_bug465498.html": "",
"content/media/test/test_bug448534.html": "x86 only bug 914439",
"content/media/test/test_bug495145.html": "x86 only bug 914439",
"content/media/test/test_bug495300.html": "x86 only bug 914439",

View File

@ -13,9 +13,6 @@
*
* DownloadCombinedList
* Provides a unified, unordered list combining public and private downloads.
*
* DownloadSummary
* Provides an aggregated view on the contents of a DownloadList.
*/
"use strict";
@ -23,7 +20,6 @@
this.EXPORTED_SYMBOLS = [
"DownloadList",
"DownloadCombinedList",
"DownloadSummary",
];
////////////////////////////////////////////////////////////////////////////////
@ -336,185 +332,3 @@ DownloadCombinedList.prototype = {
this._notifyAllViews("onDownloadRemoved", aDownload);
},
};
////////////////////////////////////////////////////////////////////////////////
//// DownloadSummary
/**
* Provides an aggregated view on the contents of a DownloadList.
*/
function DownloadSummary() {
this._downloads = [];
this._views = new Set();
}
DownloadSummary.prototype = {
/**
* Array of Download objects that are currently part of the summary.
*/
_downloads: null,
/**
* Underlying DownloadList whose contents should be summarized.
*/
_list: null,
/**
* This method may be called once to bind this object to a DownloadList.
*
* Views on the summarized data can be registered before this object is bound
* to an actual list. This allows the summary to be used without requiring
* the initialization of the DownloadList first.
*
* @param aList
* Underlying DownloadList whose contents should be summarized.
*/
bindToList: function (aList)
{
if (this._list) {
throw new Error("bindToList may be called only once.");
}
aList.addView(this);
// Set the list reference only after addView has returned, so that we don't
// send a notification to our views for each download that is added.
this._list = aList;
this._onListChanged();
},
/**
* Set of currently registered views.
*/
_views: null,
/**
* Adds a view that will be notified of changes to the summary. The newly
* added view will receive an initial onSummaryChanged notification.
*
* @param aView
* The view object to add. The following methods may be defined:
* {
* onSummaryChanged: function () {
* // Called after any property of the summary has changed.
* },
* }
*/
addView: function (aView)
{
this._views.add(aView);
if ("onSummaryChanged" in aView) {
try {
aView.onSummaryChanged();
} catch (ex) {
Cu.reportError(ex);
}
}
},
/**
* Removes a view that was previously added using addView. The removed view
* will not receive any more notifications after this method returns.
*
* @param aView
* The view object to remove.
*/
removeView: function (aView)
{
this._views.delete(aView);
},
/**
* Indicates whether all the downloads are currently stopped.
*/
allHaveStopped: true,
/**
* Indicates the total number of bytes to be transferred before completing all
* the downloads that are currently in progress.
*
* For downloads that do not have a known final size, the number of bytes
* currently transferred is reported as part of this property.
*
* This is zero if no downloads are currently in progress.
*/
progressTotalBytes: 0,
/**
* Number of bytes currently transferred as part of all the downloads that are
* currently in progress.
*
* This is zero if no downloads are currently in progress.
*/
progressCurrentBytes: 0,
/**
* This function is called when any change in the list of downloads occurs,
* and will recalculate the summary and notify the views in case the
* aggregated properties are different.
*/
_onListChanged: function () {
let allHaveStopped = true;
let progressTotalBytes = 0;
let progressCurrentBytes = 0;
// Recalculate the aggregated state. See the description of the individual
// properties for an explanation of the summarization logic.
for (let download of this._downloads) {
if (!download.stopped) {
allHaveStopped = false;
progressTotalBytes += download.hasProgress ? download.totalBytes
: download.currentBytes;
progressCurrentBytes += download.currentBytes;
}
}
// Exit now if the properties did not change.
if (this.allHaveStopped == allHaveStopped &&
this.progressTotalBytes == progressTotalBytes &&
this.progressCurrentBytes == progressCurrentBytes) {
return;
}
this.allHaveStopped = allHaveStopped;
this.progressTotalBytes = progressTotalBytes;
this.progressCurrentBytes = progressCurrentBytes;
// Notify all the views that our properties changed.
for (let view of this._views) {
try {
if ("onSummaryChanged" in view) {
view.onSummaryChanged();
}
} catch (ex) {
Cu.reportError(ex);
}
}
},
//////////////////////////////////////////////////////////////////////////////
//// DownloadList view
onDownloadAdded: function (aDownload)
{
this._downloads.push(aDownload);
if (this._list) {
this._onListChanged();
}
},
onDownloadChanged: function (aDownload)
{
this._onListChanged();
},
onDownloadRemoved: function (aDownload)
{
let index = this._downloads.indexOf(aDownload);
if (index != -1) {
this._downloads.splice(index, 1);
}
this._onListChanged();
},
};

View File

@ -31,8 +31,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration",
"resource://gre/modules/DownloadIntegration.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadList",
"resource://gre/modules/DownloadList.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadSummary",
"resource://gre/modules/DownloadList.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
"resource://gre/modules/DownloadUIHelper.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
@ -164,87 +162,44 @@ this.Downloads = {
* @rejects JavaScript exception.
*/
getList: function (aType)
{
if (!this._promiseListsInitialized) {
this._promiseListsInitialized = Task.spawn(function () {
let publicList = new DownloadList();
let privateList = new DownloadList();
let combinedList = new DownloadCombinedList(publicList, privateList);
try {
yield DownloadIntegration.addListObservers(publicList, false);
yield DownloadIntegration.addListObservers(privateList, true);
yield DownloadIntegration.initializePublicDownloadList(publicList);
} catch (ex) {
Cu.reportError(ex);
}
let publicSummary = yield this.getSummary(Downloads.PUBLIC);
let privateSummary = yield this.getSummary(Downloads.PRIVATE);
let combinedSummary = yield this.getSummary(Downloads.ALL);
publicSummary.bindToList(publicList);
privateSummary.bindToList(privateList);
combinedSummary.bindToList(combinedList);
this._lists[Downloads.PUBLIC] = publicList;
this._lists[Downloads.PRIVATE] = privateList;
this._lists[Downloads.ALL] = combinedList;
}.bind(this));
}
return this._promiseListsInitialized.then(() => this._lists[aType]);
},
/**
* Promise resolved when the initialization of the download lists has
* completed, or null if initialization has never been requested.
*/
_promiseListsInitialized: null,
/**
* After initialization, this object is populated with one key for each type
* of download list that can be returned (Downloads.PUBLIC, Downloads.PRIVATE,
* or Downloads.ALL). The values are the DownloadList objects.
*/
_lists: {},
/**
* Retrieves the specified type of DownloadSummary object. There is one
* download summary for each type, and this method always retrieves a
* reference to the same download summary when called with the same argument.
*
* Calling this function does not cause the list of public downloads to be
* reloaded from the previous session. The summary will behave as if no
* downloads are present until the getList method is called.
*
* @param aType
* This can be Downloads.PUBLIC, Downloads.PRIVATE, or Downloads.ALL.
*
* @return {Promise}
* @resolves The requested DownloadList or DownloadCombinedList object.
* @rejects JavaScript exception.
*/
getSummary: function (aType)
{
if (aType != Downloads.PUBLIC && aType != Downloads.PRIVATE &&
aType != Downloads.ALL) {
throw new Error("Invalid aType argument.");
}
if (!(aType in this._summaries)) {
this._summaries[aType] = new DownloadSummary();
if (!(aType in this._listPromises)) {
this._listPromises[aType] = Task.spawn(function () {
let list;
if (aType == Downloads.ALL) {
list = new DownloadCombinedList(
(yield this.getList(Downloads.PUBLIC)),
(yield this.getList(Downloads.PRIVATE)));
} else {
list = new DownloadList();
try {
yield DownloadIntegration.addListObservers(
list, aType == Downloads.PRIVATE);
if (aType == Downloads.PUBLIC) {
yield DownloadIntegration.initializePublicDownloadList(list);
}
} catch (ex) {
Cu.reportError(ex);
}
}
throw new Task.Result(list);
}.bind(this));
}
return Promise.resolve(this._summaries[aType]);
return this._listPromises[aType];
},
/**
* This object is populated by the getSummary method with one key for each
* type of object that can be returned (Downloads.PUBLIC, Downloads.PRIVATE,
* or Downloads.ALL). The values are the DownloadSummary objects.
* This object is populated by the getList method with one key for each type
* of object that can be returned (Downloads.PUBLIC, Downloads.PRIVATE, or
* Downloads.ALL). The values are the promises returned by the method.
*/
_summaries: {},
_listPromises: {},
/**
* Returns the system downloads directory asynchronously.

View File

@ -35,6 +35,36 @@ function promiseStartDownload(aSourceUrl) {
});
}
/**
* Waits for a download to reach half of its progress, in case it has not
* reached the expected progress already.
*
* @param aDownload
* The Download object to wait upon.
*
* @return {Promise}
* @resolves When the download has reached half of its progress.
* @rejects Never.
*/
function promiseDownloadMidway(aDownload) {
let deferred = Promise.defer();
// Wait for the download to reach half of its progress.
let onchange = function () {
if (!aDownload.stopped && !aDownload.canceled && aDownload.progress == 50) {
aDownload.onchange = null;
deferred.resolve();
}
};
// Register for the notification, but also call the function directly in
// case the download already reached the expected progress.
aDownload.onchange = onchange;
onchange();
return deferred.promise;
}
/**
* Waits for a download to finish, in case it has not finished already.
*

View File

@ -463,36 +463,6 @@ function promiseStartExternalHelperAppServiceDownload(aSourceUrl) {
return deferred.promise;
}
/**
* Waits for a download to reach half of its progress, in case it has not
* reached the expected progress already.
*
* @param aDownload
* The Download object to wait upon.
*
* @return {Promise}
* @resolves When the download has reached half of its progress.
* @rejects Never.
*/
function promiseDownloadMidway(aDownload) {
let deferred = Promise.defer();
// Wait for the download to reach half of its progress.
let onchange = function () {
if (!aDownload.stopped && !aDownload.canceled && aDownload.progress == 50) {
aDownload.onchange = null;
deferred.resolve();
}
};
// Register for the notification, but also call the function directly in
// case the download already reached the expected progress.
aDownload.onchange = onchange;
onchange();
return deferred.promise;
}
/**
* Returns a new public or private DownloadList object.
*
@ -505,13 +475,19 @@ function promiseDownloadMidway(aDownload) {
*/
function promiseNewList(aIsPrivate)
{
// We need to clear all the internal state for the list and summary objects,
// since all the objects are interdependent internally.
Downloads._promiseListsInitialized = null;
Downloads._lists = {};
Downloads._summaries = {};
let type = aIsPrivate ? Downloads.PRIVATE : Downloads.PUBLIC;
return Downloads.getList(aIsPrivate ? Downloads.PRIVATE : Downloads.PUBLIC);
// Force the creation of a new list.
if (type in Downloads._listPromises) {
delete Downloads._listPromises[type];
}
// Invalidate the combined list, if any.
if (Downloads.ALL in Downloads._listPromises) {
delete Downloads._listPromises[Downloads.ALL];
}
return Downloads.getList(type);
}
/**

View File

@ -278,7 +278,7 @@ add_task(function test_mix_notifications()
mustInterruptResponses();
let publicList = yield promiseNewList();
let privateList = yield Downloads.getList(Downloads.PRIVATE);
let privateList = yield promiseNewList(true);
let download1 = yield promiseNewDownload(httpUrl("interruptible.txt"));
let download2 = yield promiseNewDownload(httpUrl("interruptible.txt"));
let promiseAttempt1 = download1.start();

View File

@ -137,7 +137,7 @@ add_task(function test_remove()
add_task(function test_DownloadCombinedList_add_remove_getAll()
{
let publicList = yield promiseNewList();
let privateList = yield Downloads.getList(Downloads.PRIVATE);
let privateList = yield promiseNewList(true);
let combinedList = yield Downloads.getList(Downloads.ALL);
let publicDownload = yield promiseNewDownload();
@ -448,120 +448,3 @@ add_task(function test_removeFinished()
let downloads = yield list.getAll()
do_check_eq(downloads.length, 1);
});
/**
* Tests the global DownloadSummary objects for the public, private, and
* combined download lists.
*/
add_task(function test_DownloadSummary()
{
mustInterruptResponses();
let publicList = yield promiseNewList();
let privateList = yield Downloads.getList(Downloads.PRIVATE);
let publicSummary = yield Downloads.getSummary(Downloads.PUBLIC);
let privateSummary = yield Downloads.getSummary(Downloads.PRIVATE);
let combinedSummary = yield Downloads.getSummary(Downloads.ALL);
// Add a public download that has succeeded.
let succeededPublicDownload = yield promiseNewDownload();
yield succeededPublicDownload.start();
publicList.add(succeededPublicDownload);
// Add a public download that has been canceled midway.
let canceledPublicDownload =
yield promiseNewDownload(httpUrl("interruptible.txt"));
canceledPublicDownload.start();
yield promiseDownloadMidway(canceledPublicDownload);
yield canceledPublicDownload.cancel();
publicList.add(canceledPublicDownload);
// Add a public download that is in progress.
let inProgressPublicDownload =
yield promiseNewDownload(httpUrl("interruptible.txt"));
inProgressPublicDownload.start();
yield promiseDownloadMidway(inProgressPublicDownload);
publicList.add(inProgressPublicDownload);
// Add a private download that is in progress.
let inProgressPrivateDownload = yield Downloads.createDownload({
source: { url: httpUrl("interruptible.txt"), isPrivate: true },
target: getTempFile(TEST_TARGET_FILE_NAME).path,
});
inProgressPrivateDownload.start();
yield promiseDownloadMidway(inProgressPrivateDownload);
privateList.add(inProgressPrivateDownload);
// Verify that the summary includes the total number of bytes and the
// currently transferred bytes only for the downloads that are not stopped.
// For simplicity, we assume that after a download is added to the list, its
// current state is immediately propagated to the summary object, which is
// true in the current implementation, though it is not guaranteed as all the
// download operations may happen asynchronously.
do_check_false(publicSummary.allHaveStopped);
do_check_eq(publicSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
do_check_eq(publicSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
do_check_false(privateSummary.allHaveStopped);
do_check_eq(privateSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
do_check_eq(privateSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
do_check_false(combinedSummary.allHaveStopped);
do_check_eq(combinedSummary.progressTotalBytes, TEST_DATA_SHORT.length * 4);
do_check_eq(combinedSummary.progressCurrentBytes, TEST_DATA_SHORT.length * 2);
yield inProgressPublicDownload.cancel();
// Stopping the download should have excluded it from the summary.
do_check_true(publicSummary.allHaveStopped);
do_check_eq(publicSummary.progressTotalBytes, 0);
do_check_eq(publicSummary.progressCurrentBytes, 0);
do_check_false(privateSummary.allHaveStopped);
do_check_eq(privateSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
do_check_eq(privateSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
do_check_false(combinedSummary.allHaveStopped);
do_check_eq(combinedSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
do_check_eq(combinedSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
yield inProgressPrivateDownload.cancel();
// All the downloads should be stopped now.
do_check_true(publicSummary.allHaveStopped);
do_check_eq(publicSummary.progressTotalBytes, 0);
do_check_eq(publicSummary.progressCurrentBytes, 0);
do_check_true(privateSummary.allHaveStopped);
do_check_eq(privateSummary.progressTotalBytes, 0);
do_check_eq(privateSummary.progressCurrentBytes, 0);
do_check_true(combinedSummary.allHaveStopped);
do_check_eq(combinedSummary.progressTotalBytes, 0);
do_check_eq(combinedSummary.progressCurrentBytes, 0);
});
/**
* Checks that views receive the summary change notification. This is tested on
* the combined summary when adding a public download, as we assume that if we
* pass the test in this case we will also pass it in the others.
*/
add_task(function test_DownloadSummary_notifications()
{
let list = yield promiseNewList();
let summary = yield Downloads.getSummary(Downloads.ALL);
let download = yield promiseNewDownload();
list.add(download);
// Check that we receive change notifications.
let receivedOnSummaryChanged = false;
summary.addView({
onSummaryChanged: function () {
receivedOnSummaryChanged = true;
},
});
yield download.start();
do_check_true(receivedOnSummaryChanged);
});

View File

@ -118,26 +118,6 @@ add_task(function test_getList()
do_check_neq(publicListOne, privateListOne);
});
/**
* Tests that the getSummary function returns the same summary when called
* multiple times with the same argument, but returns different summaries when
* called with different arguments. More detailed tests are implemented
* separately for the DownloadSummary object in the DownloadList module.
*/
add_task(function test_getSummary()
{
let publicSummaryOne = yield Downloads.getSummary(Downloads.PUBLIC);
let privateSummaryOne = yield Downloads.getSummary(Downloads.PRIVATE);
let publicSummaryTwo = yield Downloads.getSummary(Downloads.PUBLIC);
let privateSummaryTwo = yield Downloads.getSummary(Downloads.PRIVATE);
do_check_eq(publicSummaryOne, publicSummaryTwo);
do_check_eq(privateSummaryOne, privateSummaryTwo);
do_check_neq(publicSummaryOne, privateSummaryOne);
});
/**
* Tests that the getSystemDownloadsDirectory returns a valid nsFile
* download directory object.

View File

@ -66,11 +66,6 @@ function injectController(doc, topic, data) {
return;
}
// we always handle window.close on social content, even if they are not
// "enabled". "enabled" is about the worker state and a provider may
// still be in e.g. the share panel without having their worker enabled.
handleWindowClose(window);
SocialService.getProvider(doc.nodePrincipal.origin, function(provider) {
if (provider && provider.enabled) {
attachToWindow(provider, window);
@ -219,9 +214,7 @@ function attachToWindow(provider, targetWindow) {
schedule(function () { port.close(); });
});
}
}
function handleWindowClose(targetWindow) {
// We allow window.close() to close the panel, so add an event handler for
// this, then cancel the event (so the window itself doesn't die) and
// close the panel instead.

View File

@ -40,12 +40,16 @@ var PREF_EM_CHECK_COMPATIBILITY;
const TOOLKIT_ID = "toolkit@mozilla.org";
const SHUTDOWN_EVENT = "profile-before-change";
const VALID_TYPES_REGEXP = /^[\w\-]+$/;
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
"resource://gre/modules/AddonRepository.jsm");
XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
let certUtils = {};
@ -453,8 +457,6 @@ var AddonManagerInternal = {
this.recordTimestamp("AMI_startup_begin");
Services.obs.addObserver(this, SHUTDOWN_EVENT, false);
let appChanged = undefined;
let oldAppVersion = null;
@ -547,6 +549,10 @@ var AddonManagerInternal = {
}
}
// Register our shutdown handler with the AsyncShutdown manager
AsyncShutdown.profileBeforeChange.addBlocker("AddonManager: shutting down providers",
this.shutdown.bind(this));
// Once we start calling providers we must allow all normal methods to work.
gStarted = true;
@ -678,13 +684,58 @@ var AddonManagerInternal = {
}
},
/**
* Calls a method on all registered providers, if the provider implements
* the method. The called method is expected to return a promise, and
* callProvidersAsync returns a promise that resolves when every provider
* method has either resolved or rejected. Rejection reasons are logged
* but otherwise ignored. Return values are ignored. Any parameters after the
* method parameter are passed to the provider's method.
*
* @param aMethod
* The method name to call
* @see callProvider
*/
callProvidersAsync: function AMI_callProviders(aMethod, ...aArgs) {
if (!aMethod || typeof aMethod != "string")
throw Components.Exception("aMethod must be a non-empty string",
Cr.NS_ERROR_INVALID_ARG);
let allProviders = [];
let providers = this.providers.slice(0);
for (let provider of providers) {
try {
if (aMethod in provider) {
// Resolve a new promise with the result of the method, to handle both
// methods that return values (or nothing) and methods that return promises.
let providerResult = provider[aMethod].apply(provider, aArgs);
let nextPromise = Promise.resolve(providerResult);
// Log and swallow the errors from methods that do return promises.
nextPromise = nextPromise.then(
null,
e => ERROR("Exception calling provider " + aMethod, e));
allProviders.push(nextPromise);
}
}
catch (e) {
ERROR("Exception calling provider " + aMethod, e);
}
}
// Because we use promise.then to catch and log all errors above, Promise.all()
// will never exit early because of a rejection.
return Promise.all(allProviders);
},
/**
* Shuts down the addon manager and all registered providers, this must clean
* up everything in order for automated tests to fake restarts.
* @return Promise{null} that resolves when all providers and dependent modules
* have finished shutting down
*/
shutdown: function AMI_shutdown() {
LOG("shutdown");
Services.obs.removeObserver(this, SHUTDOWN_EVENT);
// Clean up listeners
Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
@ -692,19 +743,32 @@ var AddonManagerInternal = {
Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
Services.prefs.removeObserver(PREF_EM_HOTFIX_ID, this);
// Always clean up listeners, but only shutdown providers if they've been
// started.
if (gStarted)
this.callProviders("shutdown");
// Only shut down providers if they've been started. Shut down
// AddonRepository after providers (if any).
let shuttingDown = null;
if (gStarted) {
shuttingDown = this.callProvidersAsync("shutdown")
.then(null,
err => ERROR("Failure during async provider shutdown", err))
.then(() => AddonRepository.shutdown());
}
else {
shuttingDown = AddonRepository.shutdown();
}
this.managerListeners.splice(0, this.managerListeners.length);
this.installListeners.splice(0, this.installListeners.length);
this.addonListeners.splice(0, this.addonListeners.length);
this.typeListeners.splice(0, this.typeListeners.length);
for (let type in this.startupChanges)
delete this.startupChanges[type];
gStarted = false;
gStartupComplete = false;
shuttingDown.then(val => LOG("Async provider shutdown done"),
err => ERROR("Failure during AddonRepository shutdown", err))
.then(() => {
this.managerListeners.splice(0, this.managerListeners.length);
this.installListeners.splice(0, this.installListeners.length);
this.addonListeners.splice(0, this.addonListeners.length);
this.typeListeners.splice(0, this.typeListeners.length);
for (let type in this.startupChanges)
delete this.startupChanges[type];
gStarted = false;
gStartupComplete = false;
});
return shuttingDown;
},
/**
@ -713,11 +777,6 @@ var AddonManagerInternal = {
* @see nsIObserver
*/
observe: function AMI_observe(aSubject, aTopic, aData) {
if (aTopic == SHUTDOWN_EVENT) {
this.shutdown();
return;
}
switch (aData) {
case PREF_EM_CHECK_COMPATIBILITY: {
let oldValue = gCheckCompatibility;
@ -910,7 +969,6 @@ var AddonManagerInternal = {
if (this.updateEnabled) {
let scope = {};
Components.utils.import("resource://gre/modules/AddonRepository.jsm", scope);
Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
scope.LightweightThemeManager.updateCurrentTheme();
@ -921,7 +979,7 @@ var AddonManagerInternal = {
// Repopulate repository cache first, to ensure compatibility overrides
// are up to date before checking for addon updates.
scope.AddonRepository.backgroundUpdateCheck(
AddonRepository.backgroundUpdateCheck(
ids, function BUC_backgroundUpdateCheckCallback() {
AddonManagerInternal.updateAddonRepositoryData(
function BUC_updateAddonCallback() {
@ -1144,7 +1202,7 @@ var AddonManagerInternal = {
* An optional array of extra InstallListeners to also call
* @return false if any of the listeners returned false, true otherwise
*/
callInstallListeners: function AMI_callInstallListeners(aMethod,
callInstallListeners: function AMI_callInstallListeners(aMethod,
aExtraListeners, ...aArgs) {
if (!gStarted)
throw Components.Exception("AddonManager is not initialized",
@ -1248,7 +1306,7 @@ var AddonManagerInternal = {
this.callProviders("updateAddonAppDisabledStates");
},
/**
* Notifies all providers that the repository has updated its data for
* installed add-ons.
@ -1632,7 +1690,7 @@ var AddonManagerInternal = {
if (!aListener || typeof aListener != "object")
throw Components.Exception("aListener must be a InstallListener object",
Cr.NS_ERROR_INVALID_ARG);
if (!this.installListeners.some(function addInstallListener_matchListener(i) {
return i == aListener; }))
this.installListeners.push(aListener);
@ -1763,7 +1821,7 @@ var AddonManagerInternal = {
new AsyncObjectCaller(aIDs, null, {
nextObject: function getAddonsByIDs_nextObject(aCaller, aID) {
AddonManagerInternal.getAddonByID(aID,
AddonManagerInternal.getAddonByID(aID,
function getAddonsByIDs_getAddonByID(aAddon) {
addons.push(aAddon);
aCaller.callNext();
@ -2444,7 +2502,7 @@ this.AddonManager = {
if (!aAddon || typeof aAddon != "object")
throw Components.Exception("aAddon must be specified",
Cr.NS_ERROR_INVALID_ARG);
if (!("applyBackgroundUpdates" in aAddon))
return false;
if (aAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_ENABLE)

View File

@ -23,6 +23,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DeferredSave",
"resource://gre/modules/DeferredSave.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository_SQLiteMigrator",
"resource://gre/modules/AddonRepository_SQLiteMigrator.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
this.EXPORTED_SYMBOLS = [ "AddonRepository" ];
@ -525,34 +527,17 @@ this.AddonRepository = {
// Maximum number of results to return
_maxResults: null,
/**
* Initialize AddonRepository.
*/
initialize: function AddonRepo_initialize() {
Services.obs.addObserver(this, "xpcom-shutdown", false);
},
/**
* Observe xpcom-shutdown notification, so we can shutdown cleanly.
*/
observe: function AddonRepo_observe(aSubject, aTopic, aData) {
if (aTopic == "xpcom-shutdown") {
Services.obs.removeObserver(this, "xpcom-shutdown");
this.shutdown();
}
},
/**
* Shut down AddonRepository
* return: promise{integer} resolves with the result of flushing
* the AddonRepository database
*/
shutdown: function AddonRepo_shutdown() {
this.cancelSearch();
this._addons = null;
this._pendingCallbacks = null;
AddonDatabase.shutdown(function shutdown_databaseShutdown() {
Services.obs.notifyObservers(null, "addon-repository-shutdown", null);
});
return AddonDatabase.shutdown(false);
},
/**
@ -1514,7 +1499,6 @@ this.AddonRepository = {
}
};
AddonRepository.initialize();
var AddonDatabase = {
// true if the database connection has been opened
@ -1644,13 +1628,11 @@ var AddonDatabase = {
* An optional boolean to skip flushing data to disk. Useful
* when the database is going to be deleted afterwards.
*/
shutdown: function AD_shutdown(aCallback, aSkipFlush) {
shutdown: function AD_shutdown(aSkipFlush) {
this.databaseOk = true;
aCallback = aCallback || function() {};
if (!this.initialized) {
aCallback();
return;
return Promise.resolve(0);
}
this.initialized = false;
@ -1660,9 +1642,9 @@ var AddonDatabase = {
});
if (aSkipFlush) {
aCallback();
return Promise.resolve(0);
} else {
this.Writer.flush().then(aCallback, aCallback);
return this.Writer.flush();
}
},
@ -1676,13 +1658,14 @@ var AddonDatabase = {
delete: function AD_delete(aCallback) {
this.DB = BLANK_DB();
this.Writer.flush().then(null, () => {}).then(() => {
this.shutdown(() => {
let promise = OS.File.remove(this.jsonFile.path, {});
if (aCallback)
promise.then(aCallback, aCallback);
}, true);
});
this.Writer.flush()
.then(null, () => {})
// shutdown(true) never rejects
.then(() => this.shutdown(true))
.then(() => OS.File.remove(this.jsonFile.path, {}))
.then(null, error => ERROR("Unable to delete Addon Repository file " +
this.jsonFile.path, error))
.then(aCallback);
},
toJSON: function AD_toJSON() {

View File

@ -1685,6 +1685,8 @@ var XPIProvider = {
this.installs = [];
this.installLocations = [];
this.installLocationsByName = {};
// Hook for tests to detect when saving database at shutdown time fails
this._shutdownError = null;
AddonManagerPrivate.recordTimestamp("XPI_startup_begin");
@ -1873,6 +1875,9 @@ var XPIProvider = {
/**
* Shuts down the database and releases all references.
* Return: Promise{integer} resolves / rejects with the result of
* flushing the XPI Database if it was loaded,
* 0 otherwise.
*/
shutdown: function XPI_shutdown() {
LOG("shutdown");
@ -1903,10 +1908,19 @@ var XPIProvider = {
delete this._uriMappings;
if (gLazyObjectsLoaded) {
XPIDatabase.shutdown(function shutdownCallback(saveError) {
LOG("Notifying XPI shutdown observers");
Services.obs.notifyObservers(null, "xpi-provider-shutdown", saveError);
});
let done = XPIDatabase.shutdown();
done.then(
ret => {
LOG("Notifying XPI shutdown observers");
Services.obs.notifyObservers(null, "xpi-provider-shutdown", null);
},
err => {
LOG("Notifying XPI shutdown observers");
this._shutdownError = err;
Services.obs.notifyObservers(null, "xpi-provider-shutdown", err);
}
);
return done;
}
else {
LOG("Notifying XPI shutdown observers");
@ -3190,6 +3204,9 @@ var XPIProvider = {
// active state of add-ons but didn't commit them properly (normally due
// to the application crashing)
let hasPendingChanges = Prefs.getBoolPref(PREF_PENDING_OPERATIONS);
if (hasPendingChanges) {
updateReasons.push("hasPendingChanges");
}
// If the schema appears to have changed then we should update the database
if (DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0)) {
@ -3249,9 +3266,6 @@ var XPIProvider = {
let extensionListChanged = false;
// If the database needs to be updated then open it and then update it
// from the filesystem
if (hasPendingChanges) {
updateReasons.push("hasPendingChanges");
}
if (updateReasons.length > 0) {
AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_load_reasons", updateReasons);
XPIDatabase.syncLoadDB(false);

View File

@ -978,8 +978,11 @@ this.XPIDatabase = {
/**
* Shuts down the database connection and releases all cached objects.
* Return: Promise{integer} resolves / rejects with the result of the DB
* flush after the database is flushed and
* all cleanup is done
*/
shutdown: function XPIDB_shutdown(aCallback) {
shutdown: function XPIDB_shutdown() {
LOG("shutdown");
if (this.initialized) {
// If our last database I/O had an error, try one last time to save.
@ -997,21 +1000,17 @@ this.XPIDatabase = {
"XPIDB_saves_late", this._deferredSave.dirty ? 1 : 0);
}
// Make sure any pending writes of the DB are complete, and we
// finish cleaning up, and then call back
this.flush()
.then(null, error => {
// Return a promise that any pending writes of the DB are complete and we
// are finished cleaning up
let flushPromise = this.flush();
flushPromise.then(null, error => {
ERROR("Flush of XPI database failed", error);
AddonManagerPrivate.recordSimpleMeasure("XPIDB_shutdownFlush_failed", 1);
return 0;
})
.then(count => {
// If our last attempt to read or write the DB failed, force a new
// extensions.ini to be written to disk on the next startup
let lastSaveFailed = this.lastError;
if (lastSaveFailed)
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
})
.then(count => {
// Clear out the cached addons data loaded from JSON
delete this.addonDB;
delete this._dbPromise;
@ -1019,15 +1018,10 @@ this.XPIDatabase = {
delete this._deferredSave;
// re-enable the schema version setter
delete this._schemaVersionSet;
if (aCallback)
aCallback(lastSaveFailed);
});
return flushPromise;
}
else {
if (aCallback)
aCallback(null);
}
return Promise.resolve(0);
},
/**
@ -1382,6 +1376,13 @@ this.XPIDatabase = {
* Synchronously calculates and updates all the active flags in the database.
*/
updateActiveAddons: function XPIDB_updateActiveAddons() {
if (!this.addonDB) {
WARN("updateActiveAddons called when DB isn't loaded");
// force the DB to load
AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_updateActive",
XPIProvider.runPhase);
this.syncLoadDB(true);
}
LOG("Updating add-on states");
for (let [, addon] of this.addonDB) {
let newActive = (addon.visible && !addon.userDisabled &&

View File

@ -400,29 +400,16 @@ function shutdownManager() {
if (!gInternalManager)
return;
let xpiShutdown = false;
Services.obs.addObserver({
observe: function(aSubject, aTopic, aData) {
xpiShutdown = true;
gXPISaveError = aData;
Services.obs.removeObserver(this, "xpi-provider-shutdown");
}
}, "xpi-provider-shutdown", false);
let repositoryShutdown = false;
Services.obs.addObserver({
observe: function(aSubject, aTopic, aData) {
repositoryShutdown = true;
Services.obs.removeObserver(this, "addon-repository-shutdown");
}
}, "addon-repository-shutdown", false);
let shutdownDone = false;
Services.obs.notifyObservers(null, "quit-application-granted", null);
let scope = Components.utils.import("resource://gre/modules/AddonManager.jsm");
scope.AddonManagerInternal.shutdown();
gInternalManager = null;
scope.AddonManagerInternal.shutdown()
.then(
() => shutdownDone = true,
err => shutdownDone = true);
AddonRepository.shutdown();
gInternalManager = null;
// Load the add-ons list as it was after application shutdown
loadAddonsList();
@ -433,13 +420,16 @@ function shutdownManager() {
let thr = Services.tm.mainThread;
// Wait until we observe the shutdown notifications
while (!repositoryShutdown || !xpiShutdown) {
while (!shutdownDone) {
thr.processNextEvent(true);
}
// Force the XPIProvider provider to reload to better
// simulate real-world usage.
scope = Components.utils.import("resource://gre/modules/XPIProvider.jsm");
// This would be cleaner if I could get it as the rejection reason from
// the AddonManagerInternal.shutdown() promise
gXPISaveError = scope.XPIProvider._shutdownError;
AddonManagerPrivate.unregisterProvider(scope.XPIProvider);
Components.utils.unload("resource://gre/modules/XPIProvider.jsm");
}

View File

@ -78,40 +78,6 @@ function run_test() {
db.schemaVersion = 1;
db.close();
Services.obs.addObserver({
observe: function () {
Services.obs.removeObserver(this, "addon-repository-shutdown");
// Check the DB schema has changed once AddonRepository has freed it.
db = AM_Cc["@mozilla.org/storage/service;1"].
getService(AM_Ci.mozIStorageService).
openDatabase(dbfile);
do_check_eq(db.schemaVersion, EXPECTED_SCHEMA_VERSION);
do_check_true(db.indexExists("developer_idx"));
do_check_true(db.indexExists("screenshot_idx"));
do_check_true(db.indexExists("compatibility_override_idx"));
do_check_true(db.tableExists("compatibility_override"));
do_check_true(db.indexExists("icon_idx"));
do_check_true(db.tableExists("icon"));
// Check the trigger is working
db.executeSimpleSQL("INSERT INTO addon (id, type, name) VALUES('test_addon', 'extension', 'Test Addon')");
let internalID = db.lastInsertRowID;
db.executeSimpleSQL("INSERT INTO compatibility_override (addon_internal_id, num, type) VALUES('" + internalID + "', '1', 'incompatible')");
let stmt = db.createStatement("SELECT COUNT(*) AS count FROM compatibility_override");
stmt.executeStep();
do_check_eq(stmt.row.count, 1);
stmt.reset();
db.executeSimpleSQL("DELETE FROM addon");
stmt.executeStep();
do_check_eq(stmt.row.count, 0);
stmt.finalize();
db.close();
do_test_finished();
}
}, "addon-repository-shutdown", null);
Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
AddonRepository.getCachedAddonByID("test1@tests.mozilla.org", function (aAddon) {
@ -123,6 +89,39 @@ function run_test() {
do_check_true(aAddon.screenshots[0].thumbnailHeight === null);
do_check_eq(aAddon.iconURL, undefined);
do_check_eq(JSON.stringify(aAddon.icons), "{}");
AddonRepository.shutdown();
AddonRepository.shutdown().then(
function checkAfterRepoShutdown() {
// Check the DB schema has changed once AddonRepository has freed it.
db = AM_Cc["@mozilla.org/storage/service;1"].
getService(AM_Ci.mozIStorageService).
openDatabase(dbfile);
do_check_eq(db.schemaVersion, EXPECTED_SCHEMA_VERSION);
do_check_true(db.indexExists("developer_idx"));
do_check_true(db.indexExists("screenshot_idx"));
do_check_true(db.indexExists("compatibility_override_idx"));
do_check_true(db.tableExists("compatibility_override"));
do_check_true(db.indexExists("icon_idx"));
do_check_true(db.tableExists("icon"));
// Check the trigger is working
db.executeSimpleSQL("INSERT INTO addon (id, type, name) VALUES('test_addon', 'extension', 'Test Addon')");
let internalID = db.lastInsertRowID;
db.executeSimpleSQL("INSERT INTO compatibility_override (addon_internal_id, num, type) VALUES('" + internalID + "', '1', 'incompatible')");
let stmt = db.createStatement("SELECT COUNT(*) AS count FROM compatibility_override");
stmt.executeStep();
do_check_eq(stmt.row.count, 1);
stmt.reset();
db.executeSimpleSQL("DELETE FROM addon");
stmt.executeStep();
do_check_eq(stmt.row.count, 0);
stmt.finalize();
db.close();
do_test_finished();
},
do_report_unexpected_exception
);
});
}