diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js
index 8c528941c8ca..42e4c19149ca 100644
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -431,8 +431,6 @@ pref("services.push.pingInterval", 1800000); // 30 minutes
pref("services.push.requestTimeout", 10000);
// enable udp wakeup support
pref("services.push.udp.wakeupEnabled", true);
-// port on which UDP server socket is bound
-pref("services.push.udp.port", 2442);
// NetworkStats
#ifdef MOZ_B2G_RIL
@@ -612,6 +610,10 @@ pref("dom.ipc.processPriorityManager.temporaryPriorityLockMS", 5000);
// /still/ have the same niceness; we'd effectively have erased NSPR's thread
// priorities.
+// The kernel can only accept 6 (OomScoreAdjust, KillUnderMB) pairs. But it is
+// okay, kernel will still kill processes with larger OomScoreAdjust first even
+// its OomScoreAdjust don't have a corresponding KillUnderMB.
+
pref("hal.processPriorityManager.gonk.MASTER.OomScoreAdjust", 0);
pref("hal.processPriorityManager.gonk.MASTER.KillUnderMB", 4);
pref("hal.processPriorityManager.gonk.MASTER.Nice", 0);
@@ -624,6 +626,9 @@ pref("hal.processPriorityManager.gonk.FOREGROUND.OomScoreAdjust", 134);
pref("hal.processPriorityManager.gonk.FOREGROUND.KillUnderMB", 6);
pref("hal.processPriorityManager.gonk.FOREGROUND.Nice", 1);
+pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.OomScoreAdjust", 200);
+pref("hal.processPriorityManager.gonk.FOREGROUND_KEYBOARD.Nice", 1);
+
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.OomScoreAdjust", 400);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.KillUnderMB", 7);
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.Nice", 7);
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index 284515d07c48..ab54d6f64509 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
{
- "revision": "44efe8a2b5ce1abc18fb4da92fe774d08290bd31",
+ "revision": "df1654f408ae9ff3bdb276ad2e243bfecfd47087",
"repo_path": "/integration/gaia-central"
}
diff --git a/browser/base/content/test/general/browser_save_link-perwindowpb.js b/browser/base/content/test/general/browser_save_link-perwindowpb.js
index acce07765020..63cb4917d9da 100644
--- a/browser/base/content/test/general/browser_save_link-perwindowpb.js
+++ b/browser/base/content/test/general/browser_save_link-perwindowpb.js
@@ -111,6 +111,9 @@ function test() {
function onExamineResponse(subject) {
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (channel.URI.spec != "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.sjs") {
+ return;
+ }
try {
let cookies = channel.getResponseHeader("set-cookie");
// From browser/base/content/test/general/bug792715.sjs, we receive a Set-Cookie
@@ -122,6 +125,9 @@ function test() {
function onModifyRequest(subject) {
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (channel.URI.spec != "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.sjs") {
+ return;
+ }
try {
let cookies = channel.getRequestHeader("cookie");
// From browser/base/content/test/general/bug792715.sjs, we should never send a
diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js
index 2423b0cfc083..a4ad46dbc3bb 100644
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1204,7 +1204,7 @@ BrowserGlue.prototype = {
// potential hangs (bug 518683). The asynchronous shutdown operations
// will then be handled by a shutdown service (bug 435058).
waitingForHTMLExportToComplete = false;
- BookmarkHTMLUtils.exportToFile(FileUtils.getFile("BMarks", [])).then(
+ BookmarkHTMLUtils.exportToFile(Services.dirsvc.get("BMarks", Ci.nsIFile)).then(
function onSuccess() {
waitingForHTMLExportToComplete = true;
},
diff --git a/browser/components/sessionstore/test/unit/xpcshell.ini b/browser/components/sessionstore/test/unit/xpcshell.ini
index 65de4b2db187..10737448add5 100644
--- a/browser/components/sessionstore/test/unit/xpcshell.ini
+++ b/browser/components/sessionstore/test/unit/xpcshell.ini
@@ -7,10 +7,6 @@ support-files = data/sessionstore_valid.js
[test_backup.js]
[test_backup_once.js]
[test_startup_nosession_sync.js]
-# bug 845190 - thread pool wasn't shutdown assertions
-skip-if = (os == "win" || "linux") && debug
[test_startup_nosession_async.js]
[test_startup_session_sync.js]
-# bug 845190 - thread pool wasn't shutdown assertions
-skip-if = (os == "win" || "linux") && debug
[test_startup_session_async.js]
diff --git a/browser/devtools/app-manager/app-projects.js b/browser/devtools/app-manager/app-projects.js
index 32e482d23c09..78349f0ce768 100644
--- a/browser/devtools/app-manager/app-projects.js
+++ b/browser/devtools/app-manager/app-projects.js
@@ -88,12 +88,19 @@ const IDB = {
const store = new ObservableObject({ projects:[] });
+let loadDeferred = promise.defer();
+
IDB.open().then(function (projects) {
store.object.projects = projects;
AppProjects.emit("ready", store.object.projects);
+ loadDeferred.resolve();
});
const AppProjects = {
+ load: function() {
+ return loadDeferred.promise;
+ },
+
addPackaged: function(folder) {
let project = {
type: "packaged",
diff --git a/browser/devtools/app-manager/content/projects.js b/browser/devtools/app-manager/content/projects.js
index 02eea4068048..ed3ed1a3a40c 100644
--- a/browser/devtools/app-manager/content/projects.js
+++ b/browser/devtools/app-manager/content/projects.js
@@ -39,10 +39,8 @@ let UI = {
this.template = new Template(document.body, AppProjects.store, Utils.l10n);
this.template.start();
- AppProjects.store.on("set", (event,path,value) => {
- if (path == "projects") {
- AppProjects.store.object.projects.forEach(UI.validate);
- }
+ AppProjects.load().then(() => {
+ AppProjects.store.object.projects.forEach(UI.validate);
});
},
@@ -320,10 +318,11 @@ let UI = {
// And only when the toolbox is opened, release the button
button.disabled = false;
},
- (msg) => {
+ (err) => {
button.disabled = false;
- alert(msg);
- this.connection.log(msg);
+ let message = err.error ? err.error + ": " + err.message : String(err);
+ alert(message);
+ this.connection.log(message);
});
},
diff --git a/browser/devtools/commandline/BuiltinCommands.jsm b/browser/devtools/commandline/BuiltinCommands.jsm
index 16b6247acdfc..ff084badd54a 100644
--- a/browser/devtools/commandline/BuiltinCommands.jsm
+++ b/browser/devtools/commandline/BuiltinCommands.jsm
@@ -27,6 +27,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
"resource:///modules/devtools/gDevTools.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
"resource:///modules/devtools/AppCacheUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
+ "resource://gre/modules/Downloads.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
/* CmdAddon ---------------------------------------------------------------- */
@@ -1721,160 +1725,159 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
}
var document = args.chrome? context.environment.chromeDocument
: context.environment.document;
+ var deferred = context.defer();
if (args.delay > 0) {
- var deferred = context.defer();
document.defaultView.setTimeout(function Command_screenshotDelay() {
- let reply = this.grabScreen(document, args.filename, args.clipboard,
- args.fullpage);
- deferred.resolve(reply);
+ let promise = this.grabScreen(document, args.filename, args.clipboard,
+ args.fullpage);
+ promise.then(deferred.resolve, deferred.reject);
}.bind(this), args.delay * 1000);
- return deferred.promise;
}
else {
- return this.grabScreen(document, args.filename, args.clipboard,
- args.fullpage, args.selector);
+ let promise = this.grabScreen(document, args.filename, args.clipboard,
+ args.fullpage, args.selector);
+ promise.then(deferred.resolve, deferred.reject);
}
+ return deferred.promise;
},
grabScreen: function(document, filename, clipboard, fullpage, node) {
- let window = document.defaultView;
- let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
- let left = 0;
- let top = 0;
- let width;
- let height;
- let div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ return Task.spawn(function() {
+ let window = document.defaultView;
+ let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ let left = 0;
+ let top = 0;
+ let width;
+ let height;
+ let div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
- if (!fullpage) {
- if (!node) {
- left = window.scrollX;
- top = window.scrollY;
- width = window.innerWidth;
- height = window.innerHeight;
- } else {
- let lh = new LayoutHelpers(window);
- let rect = lh.getRect(node, window);
- top = rect.top;
- left = rect.left;
- width = rect.width;
- height = rect.height;
- }
- } else {
- width = window.innerWidth + window.scrollMaxX;
- height = window.innerHeight + window.scrollMaxY;
- }
- canvas.width = width;
- canvas.height = height;
-
- let ctx = canvas.getContext("2d");
- ctx.drawWindow(window, left, top, width, height, "#fff");
- let data = canvas.toDataURL("image/png", "");
-
- let loadContext = document.defaultView
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsILoadContext);
-
- try {
- if (clipboard) {
- let io = Cc["@mozilla.org/network/io-service;1"]
- .getService(Ci.nsIIOService);
- let channel = io.newChannel(data, null, null);
- let input = channel.open();
- let imgTools = Cc["@mozilla.org/image/tools;1"]
- .getService(Ci.imgITools);
-
- let container = {};
- imgTools.decodeImageData(input, channel.contentType, container);
-
- let wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
- .createInstance(Ci.nsISupportsInterfacePointer);
- wrapped.data = container.value;
-
- let trans = Cc["@mozilla.org/widget/transferable;1"]
- .createInstance(Ci.nsITransferable);
- trans.init(loadContext);
- trans.addDataFlavor(channel.contentType);
- trans.setTransferData(channel.contentType, wrapped, -1);
-
- let clipid = Ci.nsIClipboard;
- let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid);
- clip.setData(trans, null, clipid.kGlobalClipboard);
- div.textContent = gcli.lookup("screenshotCopied");
- return div;
- }
- }
- catch (ex) {
- div.textContent = gcli.lookup("screenshotErrorCopying");
- return div;
- }
-
- let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
-
- // Create a name for the file if not present
- if (filename == FILENAME_DEFAULT_VALUE) {
- let date = new Date();
- let dateString = date.getFullYear() + "-" + (date.getMonth() + 1) +
- "-" + date.getDate();
- dateString = dateString.split("-").map(function(part) {
- if (part.length == 1) {
- part = "0" + part;
+ if (!fullpage) {
+ if (!node) {
+ left = window.scrollX;
+ top = window.scrollY;
+ width = window.innerWidth;
+ height = window.innerHeight;
+ } else {
+ let lh = new LayoutHelpers(window);
+ let rect = lh.getRect(node, window);
+ top = rect.top;
+ left = rect.left;
+ width = rect.width;
+ height = rect.height;
}
- return part;
- }).join("-");
- let timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
- filename = gcli.lookupFormat("screenshotGeneratedFilename",
- [dateString, timeString]) + ".png";
- }
- // Check there is a .png extension to filename
- else if (!filename.match(/.png$/i)) {
- filename += ".png";
- }
+ } else {
+ width = window.innerWidth + window.scrollMaxX;
+ height = window.innerHeight + window.scrollMaxY;
+ }
+ canvas.width = width;
+ canvas.height = height;
- // If the filename is relative, tack it onto the download directory
- if (!filename.match(/[\\\/]/)) {
- let downloadMgr = Cc["@mozilla.org/download-manager;1"]
- .getService(Ci.nsIDownloadManager);
- let tempfile = downloadMgr.userDownloadsDirectory;
- tempfile.append(filename);
- filename = tempfile.path;
- }
+ let ctx = canvas.getContext("2d");
+ ctx.drawWindow(window, left, top, width, height, "#fff");
+ let data = canvas.toDataURL("image/png", "");
- try {
- file.initWithPath(filename);
- } catch (ex) {
- div.textContent = gcli.lookup("screenshotErrorSavingToFile") + " " + filename;
- return div;
- }
+ let loadContext = document.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext);
- let ioService = Cc["@mozilla.org/network/io-service;1"]
- .getService(Ci.nsIIOService);
+ if (clipboard) {
+ try {
+ let io = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService);
+ let channel = io.newChannel(data, null, null);
+ let input = channel.open();
+ let imgTools = Cc["@mozilla.org/image/tools;1"]
+ .getService(Ci.imgITools);
- let Persist = Ci.nsIWebBrowserPersist;
- let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
- .createInstance(Persist);
- persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
- Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+ let container = {};
+ imgTools.decodeImageData(input, channel.contentType, container);
- let source = ioService.newURI(data, "UTF8", null);
- persist.saveURI(source, null, null, null, null, file, loadContext);
+ let wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
+ .createInstance(Ci.nsISupportsInterfacePointer);
+ wrapped.data = container.value;
- div.textContent = gcli.lookup("screenshotSavedToFile") + " \"" + filename +
- "\"";
- div.addEventListener("click", function openFile() {
- div.removeEventListener("click", openFile);
- file.reveal();
+ let trans = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+ trans.init(loadContext);
+ trans.addDataFlavor(channel.contentType);
+ trans.setTransferData(channel.contentType, wrapped, -1);
+
+ let clipid = Ci.nsIClipboard;
+ let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid);
+ clip.setData(trans, null, clipid.kGlobalClipboard);
+ div.textContent = gcli.lookup("screenshotCopied");
+ }
+ catch (ex) {
+ div.textContent = gcli.lookup("screenshotErrorCopying");
+ }
+ throw new Task.Result(div);
+ }
+
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+
+ // Create a name for the file if not present
+ if (filename == FILENAME_DEFAULT_VALUE) {
+ let date = new Date();
+ let dateString = date.getFullYear() + "-" + (date.getMonth() + 1) +
+ "-" + date.getDate();
+ dateString = dateString.split("-").map(function(part) {
+ if (part.length == 1) {
+ part = "0" + part;
+ }
+ return part;
+ }).join("-");
+ let timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
+ filename = gcli.lookupFormat("screenshotGeneratedFilename",
+ [dateString, timeString]) + ".png";
+ }
+ // Check there is a .png extension to filename
+ else if (!filename.match(/.png$/i)) {
+ filename += ".png";
+ }
+ // If the filename is relative, tack it onto the download directory
+ if (!filename.match(/[\\\/]/)) {
+ let tempfile = yield Downloads.getPreferredDownloadsDirectory();
+ tempfile.append(filename);
+ filename = tempfile.path;
+ }
+
+ try {
+ file.initWithPath(filename);
+ } catch (ex) {
+ div.textContent = gcli.lookup("screenshotErrorSavingToFile") + " " + filename;
+ throw new Task.Result(div);
+ }
+
+ let ioService = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService);
+
+ let Persist = Ci.nsIWebBrowserPersist;
+ let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(Persist);
+ persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ let source = ioService.newURI(data, "UTF8", null);
+ persist.saveURI(source, null, null, null, null, file, loadContext);
+
+ div.textContent = gcli.lookup("screenshotSavedToFile") + " \"" + filename +
+ "\"";
+ div.addEventListener("click", function openFile() {
+ div.removeEventListener("click", openFile);
+ file.reveal();
+ });
+ div.style.cursor = "pointer";
+ let image = document.createElement("div");
+ let previewHeight = parseInt(256*height/width);
+ image.setAttribute("style",
+ "width:256px; height:" + previewHeight + "px;" +
+ "max-height: 256px;" +
+ "background-image: url('" + data + "');" +
+ "background-size: 256px " + previewHeight + "px;" +
+ "margin: 4px; display: block");
+ div.appendChild(image);
+ throw new Task.Result(div);
});
- div.style.cursor = "pointer";
- let image = document.createElement("div");
- let previewHeight = parseInt(256*height/width);
- image.setAttribute("style",
- "width:256px; height:" + previewHeight + "px;" +
- "max-height: 256px;" +
- "background-image: url('" + data + "');" +
- "background-size: 256px " + previewHeight + "px;" +
- "margin: 4px; display: block");
- div.appendChild(image);
- return div;
}
});
}(this));
diff --git a/browser/metro/base/content/ContextCommands.js b/browser/metro/base/content/ContextCommands.js
index 7c9d7716b03a..a9f2d4df6961 100644
--- a/browser/metro/base/content/ContextCommands.js
+++ b/browser/metro/base/content/ContextCommands.js
@@ -349,20 +349,22 @@ var ContextCommands = {
picker.appendFilters(Ci.nsIFilePicker.filterImages);
// prefered save location
- var dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
- picker.displayDirectory = dnldMgr.userDownloadsDirectory;
- try {
- let lastDir = Services.prefs.getComplexValue("browser.download.lastDir", Ci.nsILocalFile);
- if (this.isAccessibleDirectory(lastDir))
- picker.displayDirectory = lastDir;
- }
- catch (e) { }
+ Task.spawn(function() {
+ picker.displayDirectory = yield Downloads.getPreferredDownloadsDirectory();
- this._picker = picker;
- this._pickerUrl = mediaURL;
- this._pickerContentDisp = aPopupState.contentDisposition;
- this._contentType = aPopupState.contentType;
- picker.open(ContextCommands);
+ try {
+ let lastDir = Services.prefs.getComplexValue("browser.download.lastDir", Ci.nsILocalFile);
+ if (this.isAccessibleDirectory(lastDir))
+ picker.displayDirectory = lastDir;
+ }
+ catch (e) { }
+
+ this._picker = picker;
+ this._pickerUrl = mediaURL;
+ this._pickerContentDisp = aPopupState.contentDisposition;
+ this._contentType = aPopupState.contentType;
+ picker.open(ContextCommands);
+ }.bind(this));
},
/*
diff --git a/browser/metro/base/content/browser-scripts.js b/browser/metro/base/content/browser-scripts.js
index bf9d239fb88f..e38884cb7d53 100644
--- a/browser/metro/base/content/browser-scripts.js
+++ b/browser/metro/base/content/browser-scripts.js
@@ -10,7 +10,10 @@ Cu.import("resource://gre/modules/Services.jsm");
* JS modules
*/
-XPCOMUtils.defineLazyModuleGetter(this , "FormHistory",
+XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
+ "resource://gre/modules/Downloads.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
"resource://gre/modules/FormHistory.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
@@ -97,7 +100,7 @@ let ScriptContexts = {};
["CommandUpdater", "chrome://browser/content/commandUtil.js"],
["ContextCommands", "chrome://browser/content/ContextCommands.js"],
["Bookmarks", "chrome://browser/content/bookmarks.js"],
- ["Downloads", "chrome://browser/content/downloads.js"],
+ ["MetroDownloadsView", "chrome://browser/content/downloads.js"],
["ConsolePanelView", "chrome://browser/content/console.js"],
["Site", "chrome://browser/content/Site.js"],
["TopSites", "chrome://browser/content/TopSites.js"],
diff --git a/browser/metro/base/content/browser-ui.js b/browser/metro/base/content/browser-ui.js
index 5d186c67b030..358f9e0e094e 100644
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -142,7 +142,7 @@ var BrowserUI = {
messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps);
try {
- Downloads.init();
+ MetroDownloadsView.init();
DialogUI.init();
FormHelperUI.init();
FindHelperUI.init();
@@ -175,7 +175,7 @@ var BrowserUI = {
PanelUI.uninit();
FlyoutPanelsUI.uninit();
- Downloads.uninit();
+ MetroDownloadsView.uninit();
SettingsCharm.uninit();
messageManager.removeMessageListener("Content:StateChange", this);
PageThumbs.uninit();
diff --git a/browser/metro/base/content/browser.xul b/browser/metro/base/content/browser.xul
index 7cd7e542bd5c..888ebf3bdcf9 100644
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -255,7 +255,7 @@
+ oncommand="MetroDownloadsView.onDownloadButton()"/>
diff --git a/browser/metro/base/content/downloads.js b/browser/metro/base/content/downloads.js
index 3df815a78305..b9dcf4a4f6db 100644
--- a/browser/metro/base/content/downloads.js
+++ b/browser/metro/base/content/downloads.js
@@ -6,7 +6,7 @@
const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png";
const TOAST_URI_GENERIC_ICON_DOWNLOAD = "ms-appx:///metro/chrome/chrome/skin/images/alert-downloads-30.png"
-var Downloads = {
+var MetroDownloadsView = {
/**
* _downloadCount keeps track of the number of downloads that a single
* notification bar groups together. A download is grouped with other
@@ -217,15 +217,15 @@ var Downloads = {
label: tryAgainButtonText,
accessKey: "",
callback: function() {
- Downloads.manager.retryDownload(aDownload.id);
+ MetroDownloadsView.manager.retryDownload(aDownload.id);
}
},
{
label: cancelButtonText,
accessKey: "",
callback: function() {
- Downloads.cancelDownload(aDownload);
- Downloads._downloadProgressIndicator.reset();
+ MetroDownloadsView.cancelDownload(aDownload);
+ MetroDownloadsView._downloadProgressIndicator.reset();
}
}
];
@@ -243,7 +243,7 @@ var Downloads = {
accessKey: "",
callback: function() {
let fileURI = aDownload.target;
- let file = Downloads._getLocalFile(fileURI);
+ let file = MetroDownloadsView._getLocalFile(fileURI);
file.reveal();
}
}
@@ -264,7 +264,7 @@ var Downloads = {
label: runButtonText,
accessKey: "",
callback: function() {
- Downloads.openDownload(aDownload);
+ MetroDownloadsView.openDownload(aDownload);
}
});
}
@@ -288,12 +288,12 @@ var Downloads = {
switch (aTopic) {
case "alertclickcallback":
let fileURI = aDownload.target;
- let file = Downloads._getLocalFile(fileURI);
+ let file = MetroDownloadsView._getLocalFile(fileURI);
file.reveal();
let downloadCompleteNotification =
- Downloads._notificationBox.getNotificationWithValue("download-complete");
- Downloads._notificationBox.removeNotification(downloadCompleteNotification);
+ MetroDownloadsView._notificationBox.getNotificationWithValue("download-complete");
+ MetroDownloadsView._notificationBox.removeNotification(downloadCompleteNotification);
break;
}
}
@@ -306,11 +306,11 @@ var Downloads = {
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "alertclickcallback":
- Downloads.openDownload(aDownload);
+ MetroDownloadsView.openDownload(aDownload);
let downloadCompleteNotification =
- Downloads._notificationBox.getNotificationWithValue("download-complete");
- Downloads._notificationBox.removeNotification(downloadCompleteNotification);
+ MetroDownloadsView._notificationBox.getNotificationWithValue("download-complete");
+ MetroDownloadsView._notificationBox.removeNotification(downloadCompleteNotification);
break;
}
}
@@ -406,8 +406,8 @@ var Downloads = {
label: cancelButtonText,
accessKey: "",
callback: function() {
- Downloads.cancelDownloads();
- Downloads._downloadProgressIndicator.reset();
+ MetroDownloadsView.cancelDownloads();
+ MetroDownloadsView._downloadProgressIndicator.reset();
}
}
];
@@ -446,8 +446,8 @@ var Downloads = {
switch (aEvent.type) {
case "AlertClose":
if (aEvent.notification.value == "download-complete" &&
- !Downloads._notificationBox.getNotificationWithValue("download-complete")) {
- Downloads._downloadProgressIndicator.reset();
+ !MetroDownloadsView._notificationBox.getNotificationWithValue("download-complete")) {
+ MetroDownloadsView._downloadProgressIndicator.reset();
}
break;
}
@@ -562,10 +562,10 @@ AlertDownloadProgressListener.prototype = {
let contentLength = aDownload.size;
if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) {
- Downloads.showAlert(aDownload.target.spec.replace("file:", "download:"),
- strings.GetStringFromName("alertDownloadsNoSpace"),
- strings.GetStringFromName("alertDownloadsSize"));
- Downloads.cancelDownload(aDownload);
+ MetroDownloadsView.showAlert(aDownload.target.spec.replace("file:", "download:"),
+ strings.GetStringFromName("alertDownloadsNoSpace"),
+ strings.GetStringFromName("alertDownloadsSize"));
+ MetroDownloadsView.cancelDownload(aDownload);
}
},
diff --git a/browser/metro/base/tests/mochitest/browser_downloads.js b/browser/metro/base/tests/mochitest/browser_downloads.js
index 5c323b352d44..68cab8e2a7f0 100644
--- a/browser/metro/base/tests/mochitest/browser_downloads.js
+++ b/browser/metro/base/tests/mochitest/browser_downloads.js
@@ -47,7 +47,7 @@ function equalNumbers(){
}
function getPromisedDbResult(aStatement) {
- let dbConnection = Downloads.manager.DBConnection;
+ let dbConnection = MetroDownloadsView.manager.DBConnection;
let statement = ("string" == typeof aStatement) ?
dbConnection.createAsyncStatement(
aStatement
@@ -129,7 +129,7 @@ function resetDownloads(){
// Services.prefs.clearUserPref("browser.download.panel.shown");
// Ensure that data is unloaded.
- let dlMgr = Downloads.manager;
+ let dlMgr = MetroDownloadsView.manager;
let dlsToRemove = [];
// Clear all completed/cancelled downloads
dlMgr.cleanUp();
@@ -247,11 +247,11 @@ gTests.push({
// we're going to add stuff to the downloads db.
yield spawn( gen_addDownloadRows( DownloadData ) );
- todo( false, "Check that Downloads._progressNotificationInfo and Downloads._downloadCount \
+ todo( false, "Check that MetroDownloadsView._progressNotificationInfo and MetroDownloadsView._downloadCount \
have the correct length (DownloadData.length) \
May also test that the correct notifications show up for various states.");
- todo(false, "Iterate through download objects in Downloads._progressNotificationInfo \
+ todo(false, "Iterate through download objects in MetroDownloadsView._progressNotificationInfo \
and confirm that the downloads they refer to are the same as those in \
DownloadData.");
} catch(e) {
@@ -299,7 +299,7 @@ gTests.push({
is(downloadRows.length, 3, "Correct number of downloads in the db before removal");
- todo(false, "Get some download from Downloads._progressNotificationInfo, \
+ todo(false, "Get some download from MetroDownloadsView._progressNotificationInfo, \
confirm that its file exists, then remove it.");
// remove is async(?), wait a bit
diff --git a/browser/metro/components/HelperAppDialog.js b/browser/metro/components/HelperAppDialog.js
index f44f980a9209..371f68162f86 100644
--- a/browser/metro/components/HelperAppDialog.js
+++ b/browser/metro/components/HelperAppDialog.js
@@ -18,6 +18,10 @@ XPCOMUtils.defineLazyGetter(this, "ContentUtil", function() {
Cu.import("resource:///modules/ContentUtil.jsm");
return ContentUtil;
});
+XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
+ "resource://gre/modules/Downloads.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
// -----------------------------------------------------------------------
// HelperApp Launcher Dialog
@@ -134,98 +138,98 @@ HelperAppLauncherDialog.prototype = {
},
promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
- let file = null;
- let prefs = Services.prefs;
-
- if (!aForcePrompt) {
- // Check to see if the user wishes to auto save to the default download
- // folder without prompting. Note that preference might not be set.
- let autodownload = true;
- try {
- autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR);
- } catch (e) { }
-
- if (autodownload) {
- // Retrieve the user's default download directory
- let dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
- let defaultFolder = dnldMgr.userDownloadsDirectory;
+ return Task.spawn(function() {
+ let file = null;
+ let prefs = Services.prefs;
+ if (!aForcePrompt) {
+ // Check to see if the user wishes to auto save to the default download
+ // folder without prompting. Note that preference might not be set.
+ let autodownload = true;
try {
- file = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExt);
- }
- catch (e) {
- }
+ autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR);
+ } catch (e) { }
- // Check to make sure we have a valid directory, otherwise, prompt
- if (file)
- return file;
+ if (autodownload) {
+ // Retrieve the user's default download directory
+ let defaultFolder = yield Downloads.getPreferredDownloadsDirectory();
+
+ try {
+ file = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExt);
+ }
+ catch (e) {
+ }
+
+ // Check to make sure we have a valid directory, otherwise, prompt
+ if (file)
+ throw new Task.Result(file);
+ }
}
- }
- // Use file picker to show dialog.
- let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
- let windowTitle = "";
- let parent = aContext.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
- picker.init(parent, windowTitle, Ci.nsIFilePicker.modeSave);
- picker.defaultString = aDefaultFile;
+ // Use file picker to show dialog.
+ let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ let windowTitle = "";
+ let parent = aContext.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+ picker.init(parent, windowTitle, Ci.nsIFilePicker.modeSave);
+ picker.defaultString = aDefaultFile;
- if (aSuggestedFileExt) {
- // aSuggestedFileExtension includes the period, so strip it
- picker.defaultExtension = aSuggestedFileExt.substring(1);
- }
- else {
+ if (aSuggestedFileExt) {
+ // aSuggestedFileExtension includes the period, so strip it
+ picker.defaultExtension = aSuggestedFileExt.substring(1);
+ }
+ else {
+ try {
+ picker.defaultExtension = aLauncher.MIMEInfo.primaryExtension;
+ }
+ catch (e) { }
+ }
+
+ var wildCardExtension = "*";
+ if (aSuggestedFileExt) {
+ wildCardExtension += aSuggestedFileExt;
+ picker.appendFilter(aLauncher.MIMEInfo.description, wildCardExtension);
+ }
+
+ picker.appendFilters(Ci.nsIFilePicker.filterAll);
+
+ // Default to lastDir if it is valid, otherwise use the user's default
+ // downloads directory. userDownloadsDirectory should always return a
+ // valid directory, so we can safely default to it.
+ picker.displayDirectory = yield Downloads.getPreferredDownloadsDirectory();
+
+ // The last directory preference may not exist, which will throw.
try {
- picker.defaultExtension = aLauncher.MIMEInfo.primaryExtension;
+ let lastDir = prefs.getComplexValue("browser.download.lastDir", Ci.nsILocalFile);
+ if (isUsableDirectory(lastDir))
+ picker.displayDirectory = lastDir;
}
catch (e) { }
- }
- var wildCardExtension = "*";
- if (aSuggestedFileExt) {
- wildCardExtension += aSuggestedFileExt;
- picker.appendFilter(aLauncher.MIMEInfo.description, wildCardExtension);
- }
-
- picker.appendFilters(Ci.nsIFilePicker.filterAll);
-
- // Default to lastDir if it is valid, otherwise use the user's default
- // downloads directory. userDownloadsDirectory should always return a
- // valid directory, so we can safely default to it.
- var dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
- picker.displayDirectory = dnldMgr.userDownloadsDirectory;
-
- // The last directory preference may not exist, which will throw.
- try {
- let lastDir = prefs.getComplexValue("browser.download.lastDir", Ci.nsILocalFile);
- if (isUsableDirectory(lastDir))
- picker.displayDirectory = lastDir;
- }
- catch (e) { }
-
- if (picker.show() == Ci.nsIFilePicker.returnCancel) {
- // null result means user cancelled.
- return null;
- }
-
- // Be sure to save the directory the user chose through the Save As...
- // dialog as the new browser.download.dir since the old one
- // didn't exist.
- file = picker.file;
-
- if (file) {
- try {
- // Remove the file so that it's not there when we ensure non-existence later;
- // this is safe because for the file to exist, the user would have had to
- // confirm that he wanted the file overwritten.
- if (file.exists())
- file.remove(false);
+ if (picker.show() == Ci.nsIFilePicker.returnCancel) {
+ // null result means user cancelled.
+ throw new Task.Result(null);
}
- catch (e) { }
- var newDir = file.parent.QueryInterface(Ci.nsILocalFile);
- prefs.setComplexValue("browser.download.lastDir", Ci.nsILocalFile, newDir);
- file = this.validateLeafName(newDir, file.leafName, null);
- }
- return file;
+
+ // Be sure to save the directory the user chose through the Save As...
+ // dialog as the new browser.download.dir since the old one
+ // didn't exist.
+ file = picker.file;
+
+ if (file) {
+ try {
+ // Remove the file so that it's not there when we ensure non-existence later;
+ // this is safe because for the file to exist, the user would have had to
+ // confirm that he wanted the file overwritten.
+ if (file.exists())
+ file.remove(false);
+ }
+ catch (e) { }
+ var newDir = file.parent.QueryInterface(Ci.nsILocalFile);
+ prefs.setComplexValue("browser.download.lastDir", Ci.nsILocalFile, newDir);
+ file = this.validateLeafName(newDir, file.leafName, null);
+ }
+ throw new Task.Result(file);
+ }.bind(this));
},
validateLeafName: function hald_validateLeafName(aLocalFile, aLeafName, aFileExt) {
diff --git a/browser/modules/WindowsPreviewPerTab.jsm b/browser/modules/WindowsPreviewPerTab.jsm
index dc0463a1cdd6..d9d989ad1221 100644
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -397,7 +397,7 @@ function TabWindow(win) {
this.win = win;
this.tabbrowser = win.gBrowser;
- this.previews = [];
+ this.previews = new Map();
for (let i = 0; i < this.tabEvents.length; i++)
this.tabbrowser.tabContainer.addEventListener(this.tabEvents[i], this, false);
@@ -451,7 +451,7 @@ TabWindow.prototype = {
newTab: function (tab) {
let controller = new PreviewController(this, tab);
// It's OK to add the preview now while the favicon still loads.
- this.previews.splice(tab._tPos, 0, controller.preview);
+ this.previews.set(tab, controller.preview);
AeroPeek.addPreview(controller.preview);
// updateTitleAndTooltip relies on having controller.preview which is lazily resolved.
// Now that we've updated this.previews, it will resolve successfully.
@@ -485,10 +485,7 @@ TabWindow.prototype = {
preview.move(null);
preview.controller.wrappedJSObject.destroy();
- // We don't want to splice from the array if the tabs aren't being removed
- // from the tab bar as well (as is the case when the window closes).
- if (!this._destroying)
- this.previews.splice(tab._tPos, 1);
+ this.previews.delete(tab);
AeroPeek.removePreview(preview);
},
@@ -501,26 +498,31 @@ TabWindow.prototype = {
// Because making a tab visible requires that the tab it is next to be
// visible, it is far simpler to unset the 'next' tab and recreate them all
// at once.
- this.previews.forEach(function (preview) {
+ for (let [tab, preview] of this.previews) {
preview.move(null);
preview.visible = enable;
- });
+ }
this.updateTabOrdering();
},
previewFromTab: function (tab) {
- return this.previews[tab._tPos];
+ return this.previews.get(tab);
},
updateTabOrdering: function () {
+ let previews = this.previews;
+ let tabs = this.tabbrowser.tabs;
+
+ // Previews are internally stored using a map, so we need to iterate the
+ // tabbrowser's array of tabs to retrieve previews in the same order.
+ let inorder = [previews.get(t) for (t of tabs) if (previews.has(t))];
+
// Since the internal taskbar array has not yet been updated we must force
// on it the sorting order of our local array. To do so we must walk
// the local array backwards, otherwise we would send move requests in the
// wrong order. See bug 522610 for details.
- for (let i = this.previews.length - 1; i >= 0; i--) {
- let p = this.previews[i];
- let next = i == this.previews.length - 1 ? null : this.previews[i+1];
- p.move(next);
+ for (let i = inorder.length - 1; i >= 0; i--) {
+ inorder[i].move(inorder[i + 1] || null);
}
},
@@ -540,11 +542,6 @@ TabWindow.prototype = {
this.previewFromTab(tab).active = true;
break;
case "TabMove":
- let oldPos = evt.detail;
- let newPos = tab._tPos;
- let preview = this.previews[oldPos];
- this.previews.splice(oldPos, 1);
- this.previews.splice(newPos, 0, preview);
this.updateTabOrdering();
break;
case "tabviewshown":
@@ -564,8 +561,10 @@ TabWindow.prototype = {
getFaviconAsImage(aIconURL, PrivateBrowsingUtils.isWindowPrivate(this.win), function (img) {
let index = self.tabbrowser.browsers.indexOf(aBrowser);
// Only add it if we've found the index. The tab could have closed!
- if (index != -1)
- self.previews[index].icon = img;
+ if (index != -1) {
+ let tab = self.tabbrowser.tabs[index];
+ self.previews.get(tab).icon = img;
+ }
});
}
}
diff --git a/content/html/content/src/HTMLMediaElement.cpp b/content/html/content/src/HTMLMediaElement.cpp
index f73c68e9f330..24ad24eb7e88 100644
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -2938,6 +2938,7 @@ void HTMLMediaElement::SeekCompleted()
DispatchAsyncEvent(NS_LITERAL_STRING("seeked"));
// We changed whether we're seeking so we need to AddRemoveSelfReference
AddRemoveSelfReference();
+ mTextTracks->DidSeek();
}
void HTMLMediaElement::NotifySuspendedByCache(bool aIsSuspended)
@@ -3866,7 +3867,7 @@ HTMLMediaElement::AddTextTrack(TextTrackKind aKind,
const nsAString& aLabel,
const nsAString& aLanguage)
{
- return mTextTracks->AddTextTrack(aKind, aLabel, aLanguage);
+ return mTextTracks->AddTextTrack(this, aKind, aLabel, aLanguage);
}
} // namespace dom
diff --git a/content/html/content/src/HTMLTrackElement.cpp b/content/html/content/src/HTMLTrackElement.cpp
index 7f2b3d5ea95d..7a9f5bd40eb1 100644
--- a/content/html/content/src/HTMLTrackElement.cpp
+++ b/content/html/content/src/HTMLTrackElement.cpp
@@ -126,7 +126,7 @@ HTMLTrackElement::Track()
if (!mTrack) {
// We're expected to always have an internal TextTrack so create
// an empty object to return if we don't already have one.
- mTrack = new TextTrack(OwnerDoc()->GetParentObject());
+ mTrack = new TextTrack(OwnerDoc()->GetParentObject(), mMediaParent);
}
return mTrack;
@@ -146,7 +146,8 @@ HTMLTrackElement::CreateTextTrack()
kind = TextTrackKind::Subtitles;
}
- mTrack = new TextTrack(OwnerDoc()->GetParentObject(), kind, label, srcLang);
+ mTrack = new TextTrack(OwnerDoc()->GetParentObject(), mMediaParent, kind,
+ label, srcLang);
if (mMediaParent) {
mMediaParent->AddTextTrack(mTrack);
diff --git a/content/media/TextTrack.cpp b/content/media/TextTrack.cpp
index 4e9ef27f3f2d..2bfd4745b092 100644
--- a/content/media/TextTrack.cpp
+++ b/content/media/TextTrack.cpp
@@ -6,16 +6,19 @@
#include "mozilla/dom/TextTrack.h"
#include "mozilla/dom/TextTrackBinding.h"
+#include "mozilla/dom/TextTrackCue.h"
#include "mozilla/dom/TextTrackCueList.h"
#include "mozilla/dom/TextTrackRegion.h"
#include "mozilla/dom/TextTrackRegionList.h"
+#include "mozilla/dom/HTMLMediaElement.h"
namespace mozilla {
namespace dom {
-NS_IMPL_CYCLE_COLLECTION_INHERITED_4(TextTrack,
+NS_IMPL_CYCLE_COLLECTION_INHERITED_5(TextTrack,
nsDOMEventTargetHelper,
mParent,
+ mMediaElement,
mCueList,
mActiveCueList,
mRegionList)
@@ -25,31 +28,46 @@ NS_IMPL_RELEASE_INHERITED(TextTrack, nsDOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TextTrack)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+TextTrack::TextTrack(nsISupports* aParent)
+ : mParent(aParent)
+{
+ SetDefaultSettings();
+ SetIsDOMBinding();
+}
+
+TextTrack::TextTrack(nsISupports* aParent, HTMLMediaElement* aMediaElement)
+ : mParent(aParent)
+ , mMediaElement(aMediaElement)
+{
+ SetDefaultSettings();
+ SetIsDOMBinding();
+}
+
TextTrack::TextTrack(nsISupports* aParent,
+ HTMLMediaElement* aMediaElement,
TextTrackKind aKind,
const nsAString& aLabel,
const nsAString& aLanguage)
: mParent(aParent)
- , mKind(aKind)
- , mLabel(aLabel)
- , mLanguage(aLanguage)
- , mMode(TextTrackMode::Hidden)
- , mCueList(new TextTrackCueList(aParent))
- , mActiveCueList(new TextTrackCueList(aParent))
- , mRegionList(new TextTrackRegionList(aParent))
+ , mMediaElement(aMediaElement)
{
+ SetDefaultSettings();
+ mKind = aKind;
+ mLabel = aLabel;
+ mLanguage = aLanguage;
SetIsDOMBinding();
}
-TextTrack::TextTrack(nsISupports* aParent)
- : mParent(aParent)
- , mKind(TextTrackKind::Subtitles)
- , mMode(TextTrackMode::Disabled)
- , mCueList(new TextTrackCueList(aParent))
- , mActiveCueList(new TextTrackCueList(aParent))
- , mRegionList(new TextTrackRegionList(aParent))
+void
+TextTrack::SetDefaultSettings()
{
- SetIsDOMBinding();
+ mKind = TextTrackKind::Subtitles;
+ mMode = TextTrackMode::Hidden;
+ mCueList = new TextTrackCueList(mParent);
+ mActiveCueList = new TextTrackCueList(mParent);
+ mRegionList = new TextTrackRegionList(mParent);
+ mCuePos = 0;
+ mDirty = false;
}
void
@@ -74,12 +92,14 @@ void
TextTrack::AddCue(TextTrackCue& aCue)
{
mCueList->AddCue(aCue);
+ SetDirty();
}
void
TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv)
{
mCueList->RemoveCue(aCue, aRv);
+ SetDirty();
}
void
@@ -111,5 +131,43 @@ TextTrack::RemoveRegion(const TextTrackRegion& aRegion, ErrorResult& aRv)
mRegionList->RemoveTextTrackRegion(aRegion);
}
+TextTrackCueList*
+TextTrack::GetActiveCues()
+{
+ if (mMode == TextTrackMode::Disabled || !mMediaElement) {
+ return nullptr;
+ }
+
+ // If we are dirty, i.e. an event happened that may cause the sorted mCueList
+ // to have changed like a seek or an insert for a cue, than we need to rebuild
+ // the active cue list from scratch.
+ if (mDirty) {
+ mCuePos = 0;
+ mDirty = true;
+ mActiveCueList->RemoveAll();
+ }
+
+ double playbackTime = mMediaElement->CurrentTime();
+ // Remove all the cues from the active cue list whose end times now occur
+ // earlier then the current playback time. When we reach a cue whose end time
+ // is valid we can safely stop iterating as the list is sorted.
+ for (uint32_t i = 0; i < mActiveCueList->Length() &&
+ (*mActiveCueList)[i]->EndTime() < playbackTime; i++) {
+ mActiveCueList->RemoveCueAt(i);
+ }
+ // Add all the cues, starting from the position of the last cue that was
+ // added, that have valid start and end times for the current playback time.
+ // We can stop iterating safely once we encounter a cue that does not have
+ // valid times for the current playback time as the cue list is sorted.
+ for (; mCuePos < mCueList->Length(); mCuePos++) {
+ TextTrackCue* cue = (*mCueList)[mCuePos];
+ if (cue->StartTime() > playbackTime || cue->EndTime() < playbackTime) {
+ break;
+ }
+ mActiveCueList->AddCue(*cue);
+ }
+ return mActiveCueList;
+}
+
} // namespace dom
} // namespace mozilla
diff --git a/content/media/TextTrack.h b/content/media/TextTrack.h
index 3f75571aa009..266041ef744f 100644
--- a/content/media/TextTrack.h
+++ b/content/media/TextTrack.h
@@ -20,6 +20,7 @@ class TextTrackCue;
class TextTrackCueList;
class TextTrackRegion;
class TextTrackRegionList;
+class HTMLMediaElement;
class TextTrack MOZ_FINAL : public nsDOMEventTargetHelper
{
@@ -29,10 +30,15 @@ public:
TextTrack(nsISupports* aParent);
TextTrack(nsISupports* aParent,
+ HTMLMediaElement* aMediaElement);
+ TextTrack(nsISupports* aParent,
+ HTMLMediaElement* aMediaElement,
TextTrackKind aKind,
const nsAString& aLabel,
const nsAString& aLanguage);
+ void SetDefaultSettings();
+
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle aScope) MOZ_OVERRIDE;
@@ -76,13 +82,7 @@ public:
return mCueList;
}
- TextTrackCueList* GetActiveCues() const
- {
- if (mMode == TextTrackMode::Disabled) {
- return nullptr;
- }
- return mActiveCueList;
- }
+ TextTrackCueList* GetActiveCues();
TextTrackRegionList* GetRegions() const
{
@@ -101,11 +101,13 @@ public:
void AddCue(TextTrackCue& aCue);
void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv);
void CueChanged(TextTrackCue& aCue);
+ void SetDirty() { mDirty = true; }
IMPL_EVENT_HANDLER(cuechange)
private:
nsCOMPtr mParent;
+ nsRefPtr mMediaElement;
TextTrackKind mKind;
nsString mLabel;
@@ -117,6 +119,9 @@ private:
nsRefPtr mCueList;
nsRefPtr mActiveCueList;
nsRefPtr mRegionList;
+
+ uint32_t mCuePos;
+ bool mDirty;
};
} // namespace dom
diff --git a/content/media/TextTrackCueList.cpp b/content/media/TextTrackCueList.cpp
index 66aa32072f2c..aa968b5c6c3a 100644
--- a/content/media/TextTrackCueList.cpp
+++ b/content/media/TextTrackCueList.cpp
@@ -10,6 +10,20 @@
namespace mozilla {
namespace dom {
+class CompareCuesByTime
+{
+public:
+ bool Equals(TextTrackCue* aOne, TextTrackCue* aTwo) const {
+ return aOne->StartTime() == aTwo->StartTime() &&
+ aOne->EndTime() == aTwo->EndTime();
+ }
+ bool LessThan(TextTrackCue* aOne, TextTrackCue* aTwo) const {
+ return aOne->StartTime() < aTwo->StartTime() ||
+ (aOne->StartTime() == aTwo->StartTime() &&
+ aOne->EndTime() < aTwo->EndTime());
+ }
+};
+
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(TextTrackCueList, mParent, mList)
NS_IMPL_CYCLE_COLLECTING_ADDREF(TextTrackCueList)
@@ -48,6 +62,12 @@ TextTrackCueList::IndexedGetter(uint32_t aIndex, bool& aFound)
return aFound ? mList[aIndex] : nullptr;
}
+TextTrackCue*
+TextTrackCueList::operator[](uint32_t aIndex)
+{
+ return mList.SafeElementAt(aIndex, nullptr);
+}
+
TextTrackCue*
TextTrackCueList::GetCueById(const nsAString& aId)
{
@@ -64,22 +84,36 @@ TextTrackCueList::GetCueById(const nsAString& aId)
}
void
-TextTrackCueList::AddCue(TextTrackCue& cue)
+TextTrackCueList::AddCue(TextTrackCue& aCue)
{
- if (mList.Contains(&cue)) {
+ if (mList.Contains(&aCue)) {
return;
}
- mList.AppendElement(&cue);
+ mList.InsertElementSorted(&aCue, CompareCuesByTime());
}
void
-TextTrackCueList::RemoveCue(TextTrackCue& cue, ErrorResult& aRv)
+TextTrackCueList::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv)
{
- if (!mList.Contains(&cue)) {
+ if (!mList.Contains(&aCue)) {
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
return;
}
- mList.RemoveElement(&cue);
+ mList.RemoveElement(&aCue);
+}
+
+void
+TextTrackCueList::RemoveCueAt(uint32_t aIndex)
+{
+ if (aIndex < mList.Length()) {
+ mList.RemoveElementAt(aIndex);
+ }
+}
+
+void
+TextTrackCueList::RemoveAll()
+{
+ mList.Clear();
}
} // namespace dom
diff --git a/content/media/TextTrackCueList.h b/content/media/TextTrackCueList.h
index 10123eeacbba..645c59606489 100644
--- a/content/media/TextTrackCueList.h
+++ b/content/media/TextTrackCueList.h
@@ -45,14 +45,23 @@ public:
void Update(double aTime);
TextTrackCue* IndexedGetter(uint32_t aIndex, bool& aFound);
+ TextTrackCue* operator[](uint32_t aIndex);
TextTrackCue* GetCueById(const nsAString& aId);
- void AddCue(TextTrackCue& cue);
- void RemoveCue(TextTrackCue& cue, ErrorResult& aRv);
+ // Adds a cue to mList by performing an insertion sort on mList.
+ // We expect most files to already be sorted, so an insertion sort starting
+ // from the end of the current array should be more efficient than a general
+ // sort step after all cues are loaded.
+ void AddCue(TextTrackCue& aCue);
+ void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv);
+ void RemoveCueAt(uint32_t aIndex);
+ void RemoveAll();
private:
nsCOMPtr mParent;
+ // A sorted list of TextTrackCues sorted by earliest start time. If the start
+ // times are equal then it will be sorted by end time, earliest first.
nsTArray< nsRefPtr > mList;
};
diff --git a/content/media/TextTrackList.cpp b/content/media/TextTrackList.cpp
index 380c8d0b16ce..f8dbc98cce64 100644
--- a/content/media/TextTrackList.cpp
+++ b/content/media/TextTrackList.cpp
@@ -47,11 +47,13 @@ TextTrackList::IndexedGetter(uint32_t aIndex, bool& aFound)
}
already_AddRefed
-TextTrackList::AddTextTrack(TextTrackKind aKind,
+TextTrackList::AddTextTrack(HTMLMediaElement* aMediaElement,
+ TextTrackKind aKind,
const nsAString& aLabel,
const nsAString& aLanguage)
{
- nsRefPtr track = new TextTrack(mGlobal, aKind, aLabel, aLanguage);
+ nsRefPtr track = new TextTrack(mGlobal, aMediaElement, aKind,
+ aLabel, aLanguage);
mTextTracks.AppendElement(track);
// TODO: dispatch addtrack event
return track.forget();
@@ -76,5 +78,13 @@ TextTrackList::RemoveTextTrack(const TextTrack& aTrack)
mTextTracks.RemoveElement(&aTrack);
}
+void
+TextTrackList::DidSeek()
+{
+ for (uint32_t i = 0; i < mTextTracks.Length(); i++) {
+ mTextTracks[i]->SetDirty();
+ }
+}
+
} // namespace dom
} // namespace mozilla
diff --git a/content/media/TextTrackList.h b/content/media/TextTrackList.h
index c6fbd7a9932c..be307554e4ab 100644
--- a/content/media/TextTrackList.h
+++ b/content/media/TextTrackList.h
@@ -40,7 +40,8 @@ public:
TextTrack* IndexedGetter(uint32_t aIndex, bool& aFound);
- already_AddRefed AddTextTrack(TextTrackKind aKind,
+ already_AddRefed AddTextTrack(HTMLMediaElement* aMediaElement,
+ TextTrackKind aKind,
const nsAString& aLabel,
const nsAString& aLanguage);
TextTrack* GetTrackById(const nsAString& aId);
@@ -50,6 +51,7 @@ public:
}
void RemoveTextTrack(const TextTrack& aTrack);
+ void DidSeek();
IMPL_EVENT_HANDLER(addtrack)
IMPL_EVENT_HANDLER(removetrack)
diff --git a/content/media/test/Makefile.in b/content/media/test/Makefile.in
index 8a5e64b31527..d507d2df3b6c 100644
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -148,6 +148,7 @@ MOCHITEST_FILES = \
test_webvtt_disabled.html \
test_bug895305.html \
test_bug895091.html \
+ test_bug883173.html \
$(NULL)
# Don't run in suite
@@ -275,6 +276,7 @@ MOCHITEST_FILES += \
basic.vtt \
region.vtt \
long.vtt \
+ bug883173.vtt \
$(NULL)
# Wave sample files
diff --git a/content/media/test/bug883173.vtt b/content/media/test/bug883173.vtt
new file mode 100644
index 000000000000..61f086bccea2
--- /dev/null
+++ b/content/media/test/bug883173.vtt
@@ -0,0 +1,16 @@
+WEBVTT
+
+00:03.000 --> 00:04.000
+Should display fifth.
+
+00:01.000 --> 00:02.000
+Should display first.
+
+00:01.000 --> 00:03.000
+Should display second.
+
+00:02.000 --> 00:04.000
+Should display forth.
+
+00:02.000 --> 00:03.000
+Should display third.
diff --git a/content/media/test/test_bug883173.html b/content/media/test/test_bug883173.html
new file mode 100644
index 000000000000..1376534ba30e
--- /dev/null
+++ b/content/media/test/test_bug883173.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+ Test for Bug 883173 - TextTrackCue(List) Sorting
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dom/apps/tests/Makefile.in b/dom/apps/tests/Makefile.in
index 6ef33afabf53..3bfeef807d1d 100644
--- a/dom/apps/tests/Makefile.in
+++ b/dom/apps/tests/Makefile.in
@@ -16,6 +16,7 @@ MOCHITEST_FILES = \
test_packaged_app_update.html \
test_packaged_app_common.js \
test_uninstall_errors.html \
+ test_bug_795164.html \
$(NULL)
MOCHITEST_CHROME_FILES = \
diff --git a/dom/apps/tests/test_bug_795164.html b/dom/apps/tests/test_bug_795164.html
new file mode 100644
index 000000000000..42de6d0b4f40
--- /dev/null
+++ b/dom/apps/tests/test_bug_795164.html
@@ -0,0 +1,112 @@
+
+
+
+
+
+ Test for Bug 795164
+
+
+
+
+
+Mozilla Bug 795164
+
+
+
+
+
+
+
+
diff --git a/dom/apps/tests/test_uninstall_errors.html b/dom/apps/tests/test_uninstall_errors.html
index 8f1bada9af43..f499829accd9 100644
--- a/dom/apps/tests/test_uninstall_errors.html
+++ b/dom/apps/tests/test_uninstall_errors.html
@@ -78,7 +78,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=830258
};
yield undefined;
- var request = navigator.mozApps.mgmt.uninstall(app2);
+ request = navigator.mozApps.mgmt.uninstall(app2);
request.onsuccess = function() {
ok(true, "Succeed to uninstall the app2 as expected");
continueTest();
diff --git a/dom/browser-element/BrowserElementChildPreload.js b/dom/browser-element/BrowserElementChildPreload.js
index 05493556af04..7682b74e841e 100644
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -448,8 +448,12 @@ BrowserElementChild.prototype = {
_iconChangedHandler: function(e) {
debug('Got iconchanged: (' + e.target.href + ')');
+ let icon = { href: e.target.href };
+ if (e.target.getAttribute('sizes')) {
+ icon.sizes = e.target.getAttribute('sizes');
+ }
- sendAsyncMsg('iconchange', { _payload_: e.target.href });
+ sendAsyncMsg('iconchange', icon);
},
_openSearchHandler: function(e) {
diff --git a/dom/browser-element/mochitest/browserElement_Iconchange.js b/dom/browser-element/mochitest/browserElement_Iconchange.js
index 390b2734451a..df9e76e997bd 100644
--- a/dom/browser-element/mochitest/browserElement_Iconchange.js
+++ b/dom/browser-element/mochitest/browserElement_Iconchange.js
@@ -12,8 +12,9 @@ function createHtml(link) {
return 'data:text/html,' + link + '';
}
-function createLink(name) {
- return '';
+function createLink(name, sizes) {
+ var s = sizes ? 'sizes="' + sizes + '"' : '';
+ return '';
}
function runTest() {
@@ -40,7 +41,7 @@ function runTest() {
numIconChanges++;
if (numIconChanges == 1) {
- is(e.detail, 'http://example.com/myicon.png');
+ is(e.detail.href, 'http://example.com/myicon.png');
// We should recieve iconchange events when the user creates new links
// to a favicon, but only when we listen for them
@@ -57,13 +58,13 @@ function runTest() {
/* allowDelayedLoad = */ false);
}
else if (numIconChanges == 2) {
- is(e.detail, 'http://example.com/newicon.png');
+ is(e.detail.href, 'http://example.com/newicon.png');
// Full new pages should trigger iconchange events
iframe1.src = createHtml(createLink('3rdicon'));
}
else if (numIconChanges == 3) {
- is(e.detail, 'http://example.com/3rdicon.png');
+ is(e.detail.href, 'http://example.com/3rdicon.png');
// the rel attribute can have various space seperated values, make
// sure we only pick up correct values for 'icon'
@@ -74,11 +75,11 @@ function runTest() {
iframe1.src = createHtml(createLink('another') + createLink('icon'));
}
else if (numIconChanges == 4) {
- is(e.detail, 'http://example.com/another.png');
+ is(e.detail.href, 'http://example.com/another.png');
// 2 events will be triggered by previous test, wait for next
}
else if (numIconChanges == 5) {
- is(e.detail, 'http://example.com/icon.png');
+ is(e.detail.href, 'http://example.com/icon.png');
// Make sure icon check is case insensitive
SpecialPowers.getBrowserFrameMessageManager(iframe1)
@@ -86,7 +87,12 @@ function runTest() {
/* allowDelayedLoad = */ false);
}
else if (numIconChanges == 6) {
- is(e.detail, 'http://example.com/ucaseicon.png');
+ is(e.detail.href, 'http://example.com/ucaseicon.png');
+ iframe1.src = createHtml(createLink('testsize', '50x50'));
+ }
+ else if (numIconChanges == 7) {
+ is(e.detail.href, 'http://example.com/testsize.png');
+ is(e.detail.sizes, '50x50');
SimpleTest.finish();
} else {
ok(false, 'Too many iconchange events.');
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index f4cce346f363..d7520e37f8ce 100644
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -495,14 +495,17 @@ PrivilegesForApp(mozIApplication* aApp)
ContentParent::GetInitialProcessPriority(Element* aFrameElement)
{
// Frames with mozapptype == critical which are expecting a system message
- // get FOREGROUND_HIGH priority. All other frames get FOREGROUND priority.
+ // get FOREGROUND_HIGH priority.
if (!aFrameElement) {
return PROCESS_PRIORITY_FOREGROUND;
}
- if (!aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
- NS_LITERAL_STRING("critical"), eCaseMatters)) {
+ if (aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
+ NS_LITERAL_STRING("keyboard"), eCaseMatters)) {
+ return PROCESS_PRIORITY_FOREGROUND_KEYBOARD;
+ } else if (!aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
+ NS_LITERAL_STRING("critical"), eCaseMatters)) {
return PROCESS_PRIORITY_FOREGROUND;
}
diff --git a/dom/ipc/ProcessPriorityManager.cpp b/dom/ipc/ProcessPriorityManager.cpp
index 081e45d669b8..0bd4a24cd530 100644
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -855,7 +855,9 @@ ParticularProcessPriorityManager::ComputePriority()
}
if (isVisible) {
- return PROCESS_PRIORITY_FOREGROUND;
+ return HasAppType("keyboard") ?
+ PROCESS_PRIORITY_FOREGROUND_KEYBOARD :
+ PROCESS_PRIORITY_FOREGROUND;
}
if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
diff --git a/dom/mobilemessage/src/gonk/MmsService.js b/dom/mobilemessage/src/gonk/MmsService.js
index 24fad7c7dde7..3bd6c66e6942 100644
--- a/dom/mobilemessage/src/gonk/MmsService.js
+++ b/dom/mobilemessage/src/gonk/MmsService.js
@@ -248,7 +248,9 @@ XPCOMUtils.defineLazyGetter(this, "gMmsConnection", function () {
* otherwise.
*/
acquire: function acquire(callback) {
+ this.refCount++;
this.connectTimer.cancel();
+ this.disconnectTimer.cancel();
// If the MMS network is not yet connected, buffer the
// MMS request and try to setup the MMS network first.
@@ -280,8 +282,6 @@ XPCOMUtils.defineLazyGetter(this, "gMmsConnection", function () {
return false;
}
- this.refCount++;
-
callback(true, _HTTP_STATUS_ACQUIRE_CONNECTION_SUCCESS);
return true;
},
@@ -554,12 +554,6 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
}
// Setup event listeners
- xhr.onerror = function () {
- if (DEBUG) debug("xhr error, response headers: " +
- xhr.getAllResponseHeaders());
- releaseMmsConnectionAndCallback(xhr.status, null);
- };
-
xhr.onreadystatechange = function () {
if (xhr.readyState != Ci.nsIXMLHttpRequest.DONE) {
return;
diff --git a/dom/push/src/PushService.jsm b/dom/push/src/PushService.jsm
index 7b705df23137..de80d8adf3d3 100644
--- a/dom/push/src/PushService.jsm
+++ b/dom/push/src/PushService.jsm
@@ -436,6 +436,20 @@ this.PushService = {
*/
_willBeWokenUpByUDP: false,
+ /**
+ * Sends a message to the Push Server through an open websocket.
+ * typeof(msg) shall be an object
+ */
+ _wsSendMessage: function(msg) {
+ if (!this._ws) {
+ debug("No WebSocket initialized. Cannot send a message.");
+ return;
+ }
+ msg = JSON.stringify(msg);
+ debug("Sending message: " + msg);
+ this._ws.sendMsg(msg);
+ },
+
init: function() {
debug("init()");
if (!prefs.get("enabled"))
@@ -454,8 +468,6 @@ this.PushService = {
this._requestTimeout = prefs.get("requestTimeout");
- this._udpPort = prefs.get("udp.port");
-
this._startListeningIfChannelsPresent();
Services.obs.addObserver(this, "xpcom-shutdown", false);
@@ -730,7 +742,7 @@ this.PushService = {
// handle the exception, as the lack of a pong will lead to the socket
// being reset.
try {
- this._ws.sendMsg('{}');
+ this._wsSendMessage({});
} catch (e) {
}
@@ -949,7 +961,7 @@ this.PushService = {
this._shutdownWS();
}
- this._ws.sendMsg(JSON.stringify(data));
+ this._wsSendMessage(data);
// Process the next one as soon as possible.
setTimeout(this._processNextRequestInQueue.bind(this), 0);
},
@@ -1280,6 +1292,10 @@ this.PushService = {
// Since we've had a successful connection reset the retry fail count.
this._retryFailCount = 0;
+ // Openning an available UDP port.
+ // _listenForUDPWakeup will return the opened port number
+ this._udpPort = this._listenForUDPWakeup();
+
let data = {
messageType: "hello",
}
@@ -1305,7 +1321,7 @@ this.PushService = {
// On success, ids is an array, on error its not.
data["channelIDs"] = ids.map ?
ids.map(function(el) { return el.channelID; }) : [];
- this._ws.sendMsg(JSON.stringify(data));
+ this._wsSendMessage(data);
this._currentState = STATE_WAITING_FOR_HELLO;
}
@@ -1400,7 +1416,6 @@ this.PushService = {
debug("Server closed with promise to wake up");
this._willBeWokenUpByUDP = true;
// TODO: there should be no pending requests
- this._listenForUDPWakeup();
}
},
@@ -1424,9 +1439,11 @@ this.PushService = {
this._udpServer = Cc["@mozilla.org/network/server-socket-udp;1"]
.createInstance(Ci.nsIUDPServerSocket);
- this._udpServer.init(this._udpPort, false);
+ this._udpServer.init(-1, false);
this._udpServer.asyncListen(this);
debug("listenForUDPWakeup listening on " + this._udpPort);
+
+ return this._udpServer.port;
},
/**
@@ -1446,6 +1463,7 @@ this.PushService = {
*/
onStopListening: function(aServ, aStatus) {
debug("UDP Server socket was shutdown. Status: " + aStatus);
+ this._udpPort = undefined;
this._beginWSSetup();
},
diff --git a/dom/tests/mochitest/webapps/Makefile.in b/dom/tests/mochitest/webapps/Makefile.in
index 948359966c5a..1c5bcd382652 100644
--- a/dom/tests/mochitest/webapps/Makefile.in
+++ b/dom/tests/mochitest/webapps/Makefile.in
@@ -7,7 +7,6 @@ MOCHITEST_CHROME_FILES = \
cross_origin.html \
head.js \
test_bug_765063.xul \
- test_bug_795164.xul \
test_bug_771294.xul \
test_cross_origin.xul \
test_install_app.xul \
diff --git a/dom/tests/mochitest/webapps/test_bug_795164.xul b/dom/tests/mochitest/webapps/test_bug_795164.xul
deleted file mode 100644
index c43ecf32fa99..000000000000
--- a/dom/tests/mochitest/webapps/test_bug_795164.xul
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- Mozilla Bug 795164
-
-
-
diff --git a/hal/Hal.cpp b/hal/Hal.cpp
index a05699eea12c..b1596bb3bed6 100644
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -868,6 +868,8 @@ ProcessPriorityToString(ProcessPriority aPriority)
return "FOREGROUND_HIGH";
case PROCESS_PRIORITY_FOREGROUND:
return "FOREGROUND";
+ case PROCESS_PRIORITY_FOREGROUND_KEYBOARD:
+ return "FOREGROUND_KEYBOARD";
case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
return "BACKGROUND_PERCEIVABLE";
case PROCESS_PRIORITY_BACKGROUND_HOMESCREEN:
@@ -914,6 +916,13 @@ ProcessPriorityToString(ProcessPriority aPriority,
if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
return "FOREGROUND:CPU_LOW";
}
+ case PROCESS_PRIORITY_FOREGROUND_KEYBOARD:
+ if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
+ return "FOREGROUND_KEYBOARD:CPU_NORMAL";
+ }
+ if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
+ return "FOREGROUND_KEYBOARD:CPU_LOW";
+ }
case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
return "BACKGROUND_PERCEIVABLE:CPU_NORMAL";
diff --git a/hal/HalTypes.h b/hal/HalTypes.h
index 9b9649349f8b..925bdcb97df3 100644
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -83,6 +83,7 @@ enum ProcessPriority {
PROCESS_PRIORITY_BACKGROUND,
PROCESS_PRIORITY_BACKGROUND_HOMESCREEN,
PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE,
+ PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
// Any priority greater than or equal to FOREGROUND is considered
// "foreground" for the purposes of priority testing, for example
// CurrentProcessIsForeground().
diff --git a/mobile/android/base/GeckoPreferences.java b/mobile/android/base/GeckoPreferences.java
index ce5761efd5ca..17dd4c65e8c8 100644
--- a/mobile/android/base/GeckoPreferences.java
+++ b/mobile/android/base/GeckoPreferences.java
@@ -8,6 +8,7 @@ package org.mozilla.gecko;
import org.mozilla.gecko.background.announcements.AnnouncementsConstants;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.healthreport.HealthReportConstants;
+import org.mozilla.gecko.preferences.SearchEnginePreference;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.GeckoPreferenceFragment;
import org.mozilla.gecko.util.ThreadUtils;
@@ -44,8 +45,11 @@ import android.text.TextWatcher;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
+import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
@@ -87,9 +91,15 @@ public class GeckoPreferences
@Override
protected void onCreate(Bundle savedInstanceState) {
- // For fragment-capable devices, display the default fragment if no explicit fragment to show.
+
+ // For Android v11+ where we use Fragments (v11+ only due to bug 866352),
+ // check that PreferenceActivity.EXTRA_SHOW_FRAGMENT has been set
+ // (or set it) before super.onCreate() is called so Android can display
+ // the correct Fragment resource.
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB &&
!getIntent().hasExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT)) {
+ // Set up the default fragment if there is no explicit fragment to show.
setupTopLevelFragmentIntent();
}
@@ -97,6 +107,10 @@ public class GeckoPreferences
// Use setResourceToOpen to specify these extras.
Bundle intentExtras = getIntent().getExtras();
+
+ // For versions of Android lower than Honeycomb, use xml resources instead of
+ // Fragments because of an Android bug in ActionBar (described in bug 866352 and
+ // fixed in bug 833625).
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
int res = 0;
if (intentExtras != null && intentExtras.containsKey(INTENT_EXTRA_RESOURCES)) {
@@ -119,6 +133,25 @@ public class GeckoPreferences
registerEventListener("Sanitize:Finished");
+ // Add handling for long-press click.
+ // This is only for Android 3.0 and below (which use the long-press-context-menu paradigm).
+ final ListView mListView = getListView();
+ mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
+ // Call long-click handler if it the item implements it.
+ final ListAdapter listAdapter = ((ListView) parent).getAdapter();
+ final Object listItem = listAdapter.getItem(position);
+
+ // Only SearchEnginePreference handles long clicks.
+ if (listItem instanceof SearchEnginePreference && listItem instanceof View.OnLongClickListener) {
+ final View.OnLongClickListener longClickListener = (View.OnLongClickListener) listItem;
+ return longClickListener.onLongClick(view);
+ }
+ return false;
+ }
+ });
+
if (Build.VERSION.SDK_INT >= 14)
getActionBar().setHomeButtonEnabled(true);
diff --git a/mobile/android/base/db/LocalBrowserDB.java b/mobile/android/base/db/LocalBrowserDB.java
index 21df106f43c5..af3586b094b5 100644
--- a/mobile/android/base/db/LocalBrowserDB.java
+++ b/mobile/android/base/db/LocalBrowserDB.java
@@ -232,12 +232,13 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
@Override
public Cursor getTopSites(ContentResolver cr, int limit) {
- // Filter out sites that are pinned
+ // Filter out bookmarks that don't have real parents (e.g. pinned sites or reading list items)
String selection = DBUtils.concatenateWhere("", Combined.URL + " NOT IN (SELECT " +
Bookmarks.URL + " FROM bookmarks WHERE " +
- DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " == ? AND " +
+ DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " < ? AND " +
DBUtils.qualifyColumn("bookmarks", Bookmarks.IS_DELETED) + " == 0)");
- String[] selectionArgs = DBUtils.appendSelectionArgs(new String[0], new String[] { String.valueOf(Bookmarks.FIXED_PINNED_LIST_ID) });
+ String[] selectionArgs = new String[] { String.valueOf(Bookmarks.FIXED_ROOT_ID) };
+
return filterAllSites(cr,
new String[] { Combined._ID,
Combined.URL,
diff --git a/mobile/android/base/preferences/SearchEnginePreference.java b/mobile/android/base/preferences/SearchEnginePreference.java
index 181a196b3732..9be33c71632c 100644
--- a/mobile/android/base/preferences/SearchEnginePreference.java
+++ b/mobile/android/base/preferences/SearchEnginePreference.java
@@ -16,8 +16,10 @@ import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
+
import org.json.JSONException;
import org.json.JSONObject;
+
import org.mozilla.gecko.R;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.util.ThreadUtils;
@@ -26,7 +28,7 @@ import org.mozilla.gecko.widget.FaviconView;
/**
* Represents an element in the list of search engines on the preferences menu.
*/
-public class SearchEnginePreference extends Preference {
+public class SearchEnginePreference extends Preference implements View.OnLongClickListener {
private static final String LOGTAG = "SearchEnginePreference";
// Indices in button array of the AlertDialog of the three buttons.
@@ -104,6 +106,13 @@ public class SearchEnginePreference extends Preference {
mFaviconView.updateAndScaleImage(mIconBitmap, getTitle().toString());
}
+ @Override
+ public boolean onLongClick(View view) {
+ // Show the preference dialog on long-press.
+ showDialog();
+ return true;
+ }
+
/**
* Configure this Preference object from the Gecko search engine JSON object.
* @param geckoEngineJSON The Gecko-formatted JSON object representing the search engine.
diff --git a/mobile/android/base/tests/PixelTest.java.in b/mobile/android/base/tests/PixelTest.java.in
index adfb0ad79047..6cf2c79b816c 100644
--- a/mobile/android/base/tests/PixelTest.java.in
+++ b/mobile/android/base/tests/PixelTest.java.in
@@ -2,7 +2,6 @@
package @ANDROID_PACKAGE_NAME@.tests;
import @ANDROID_PACKAGE_NAME@.*;
-import android.os.Build;
abstract class PixelTest extends BaseTest {
private static final long PAINT_CLEAR_DELAY = 10000; // milliseconds
@@ -10,11 +9,7 @@ abstract class PixelTest extends BaseTest {
protected final PaintedSurface loadAndGetPainted(String url) {
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();
loadUrl(url);
- // Skip this on the Tegras (Android 2.2) since they are too slow,
- // which results in too many intermittent failures.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
- verifyHomePagerHidden();
- }
+ verifyHomePagerHidden();
paintExpecter.blockUntilClear(PAINT_CLEAR_DELAY);
paintExpecter.unregisterListener();
PaintedSurface p = mDriver.getPaintedSurface();
diff --git a/toolkit/components/osfile/modules/osfile_async_worker.js b/toolkit/components/osfile/modules/osfile_async_worker.js
index 990ac1fdb646..fdff79b029dc 100644
--- a/toolkit/components/osfile/modules/osfile_async_worker.js
+++ b/toolkit/components/osfile/modules/osfile_async_worker.js
@@ -12,384 +12,379 @@ if (this.Components) {
(function(exports) {
"use strict";
+ importScripts("resource://gre/modules/osfile.jsm");
+
+ let LOG = exports.OS.Shared.LOG.bind(exports.OS.Shared.LOG, "Agent");
+
+ /**
+ * Communications with the controller.
+ *
+ * Accepts messages:
+ * {fun:function_name, args:array_of_arguments_or_null, id:id}
+ *
+ * Sends messages:
+ * {ok: result, id:id} / {fail: serialized_form_of_OS.File.Error, id:id}
+ */
+ self.onmessage = function onmessage(msg) {
+ let data = msg.data;
+ LOG("Received message", data);
+ let id = data.id;
+
+ let start;
+ let options;
+ if (data.args) {
+ options = data.args[data.args.length - 1];
+ }
+ // If |outExecutionDuration| option was supplied, start measuring the
+ // duration of the operation.
+ if (options && typeof options === "object" && "outExecutionDuration" in options) {
+ start = Date.now();
+ }
+
+ let result;
+ let exn;
+ let durationMs;
try {
- importScripts("resource://gre/modules/osfile.jsm");
+ let method = data.fun;
+ LOG("Calling method", method);
+ result = Agent[method].apply(Agent, data.args);
+ LOG("Method", method, "succeeded");
+ } catch (ex) {
+ exn = ex;
+ LOG("Error while calling agent method", exn, exn.stack);
+ }
- let LOG = exports.OS.Shared.LOG.bind(exports.OS.Shared.LOG, "Agent");
+ if (start) {
+ // Record duration
+ durationMs = Date.now() - start;
+ LOG("Method took", durationMs, "ms");
+ }
- /**
- * Communications with the controller.
- *
- * Accepts messages:
- * {fun:function_name, args:array_of_arguments_or_null, id:id}
- *
- * Sends messages:
- * {ok: result, id:id} / {fail: serialized_form_of_OS.File.Error, id:id}
- */
- self.onmessage = function onmessage(msg) {
- let data = msg.data;
- LOG("Received message", data);
- let id = data.id;
+ // Now, post a reply, possibly as an uncaught error.
+ // We post this message from outside the |try ... catch| block
+ // to avoid capturing errors that take place during |postMessage| and
+ // built-in serialization.
+ if (!exn) {
+ LOG("Sending positive reply", result, "id is", id);
+ if (result instanceof Transfer) {
+ // Take advantage of zero-copy transfers
+ self.postMessage({ok: result.data, id: id, durationMs: durationMs},
+ result.transfers);
+ } else {
+ self.postMessage({ok: result, id:id, durationMs: durationMs});
+ }
+ } else if (exn == StopIteration) {
+ // StopIteration cannot be serialized automatically
+ LOG("Sending back StopIteration");
+ self.postMessage({StopIteration: true, id: id, durationMs: durationMs});
+ } else if (exn instanceof exports.OS.File.Error) {
+ LOG("Sending back OS.File error", exn, "id is", id);
+ // Instances of OS.File.Error know how to serialize themselves
+ // (deserialization ensures that we end up with OS-specific
+ // instances of |OS.File.Error|)
+ self.postMessage({fail: exports.OS.File.Error.toMsg(exn), id:id, durationMs: durationMs});
+ } else {
+ LOG("Sending back regular error", exn, exn.stack, "id is", id);
+ // Other exceptions do not, and should be propagated through DOM's
+ // built-in mechanism for uncaught errors, although this mechanism
+ // may lose interesting information.
+ throw exn;
+ }
+ };
- let start;
- let options;
- if (data.args) {
- options = data.args[data.args.length - 1];
- }
- // If |outExecutionDuration| option was supplied, start measuring the
- // duration of the operation.
- if (options && typeof options === "object" && "outExecutionDuration" in options) {
- start = Date.now();
- }
+ /**
+ * A data structure used to track opened resources
+ */
+ let ResourceTracker = function ResourceTracker() {
+ // A number used to generate ids
+ this._idgen = 0;
+ // A map from id to resource
+ this._map = new Map();
+ };
+ ResourceTracker.prototype = {
+ /**
+ * Get a resource from its unique identifier.
+ */
+ get: function(id) {
+ let result = this._map.get(id);
+ if (result == null) {
+ return result;
+ }
+ return result.resource;
+ },
+ /**
+ * Remove a resource from its unique identifier.
+ */
+ remove: function(id) {
+ if (!this._map.has(id)) {
+ throw new Error("Cannot find resource id " + id);
+ }
+ this._map.delete(id);
+ },
+ /**
+ * Add a resource, return a new unique identifier
+ *
+ * @param {*} resource A resource.
+ * @param {*=} info Optional information. For debugging purposes.
+ *
+ * @return {*} A unique identifier. For the moment, this is a number,
+ * but this might not remain the case forever.
+ */
+ add: function(resource, info) {
+ let id = this._idgen++;
+ this._map.set(id, {resource:resource, info:info});
+ return id;
+ },
+ /**
+ * Return a list of all open resources i.e. the ones still present in
+ * ResourceTracker's _map.
+ */
+ listOpenedResources: function listOpenedResources() {
+ return [resource.info.path for ([id, resource] of this._map)];
+ }
+ };
- let result;
- let exn;
- let durationMs;
- try {
- let method = data.fun;
- LOG("Calling method", method);
- result = Agent[method].apply(Agent, data.args);
- LOG("Method", method, "succeeded");
- } catch (ex) {
- exn = ex;
- LOG("Error while calling agent method", exn, exn.stack);
- }
+ /**
+ * A map of unique identifiers to opened files.
+ */
+ let OpenedFiles = new ResourceTracker();
- if (start) {
- // Record duration
- durationMs = Date.now() - start;
- LOG("Method took", durationMs, "ms");
- }
+ /**
+ * Execute a function in the context of a given file.
+ *
+ * @param {*} id A unique identifier, as used by |OpenFiles|.
+ * @param {Function} f A function to call.
+ * @param {boolean} ignoreAbsent If |true|, the error is ignored. Otherwise, the error causes an exception.
+ * @return The return value of |f()|
+ *
+ * This function attempts to get the file matching |id|. If
+ * the file exists, it executes |f| within the |this| set
+ * to the corresponding file. Otherwise, it throws an error.
+ */
+ let withFile = function withFile(id, f, ignoreAbsent) {
+ let file = OpenedFiles.get(id);
+ if (file == null) {
+ if (!ignoreAbsent) {
+ throw OS.File.Error.closed("accessing file");
+ }
+ return undefined;
+ }
+ return f.call(file);
+ };
- // Now, post a reply, possibly as an uncaught error.
- // We post this message from outside the |try ... catch| block
- // to avoid capturing errors that take place during |postMessage| and
- // built-in serialization.
- if (!exn) {
- LOG("Sending positive reply", result, "id is", id);
- if (result instanceof Transfer) {
- // Take advantage of zero-copy transfers
- self.postMessage({ok: result.data, id: id, durationMs: durationMs},
- result.transfers);
- } else {
- self.postMessage({ok: result, id:id, durationMs: durationMs});
+ let OpenedDirectoryIterators = new ResourceTracker();
+ let withDir = function withDir(fd, f, ignoreAbsent) {
+ let file = OpenedDirectoryIterators.get(fd);
+ if (file == null) {
+ if (!ignoreAbsent) {
+ throw OS.File.Error.closed("accessing directory");
+ }
+ return undefined;
+ }
+ if (!(file instanceof File.DirectoryIterator)) {
+ throw new Error("file is not a directory iterator " + file.__proto__.toSource());
+ }
+ return f.call(file);
+ };
+
+ let Type = exports.OS.Shared.Type;
+
+ let File = exports.OS.File;
+
+ /**
+ * A constructor used to transfer data to the caller
+ * without copy.
+ *
+ * @param {*} data The data to return to the caller.
+ * @param {Array} transfers An array of Transferable
+ * values that should be moved instead of being copied.
+ *
+ * @constructor
+ */
+ let Transfer = function Transfer(data, transfers) {
+ this.data = data;
+ this.transfers = transfers;
+ };
+
+ /**
+ * The agent.
+ *
+ * It is in charge of performing method-specific deserialization
+ * of messages, calling the function/method of OS.File and serializing
+ * back the results.
+ */
+ let Agent = {
+ // Update worker's OS.Shared.DEBUG flag message from controller.
+ SET_DEBUG: function SET_DEBUG (aDEBUG) {
+ exports.OS.Shared.DEBUG = aDEBUG;
+ },
+ // Return worker's current OS.Shared.DEBUG value to controller.
+ // Note: This is used for testing purposes.
+ GET_DEBUG: function GET_DEBUG () {
+ return exports.OS.Shared.DEBUG;
+ },
+ // Report file descriptors leaks.
+ System_shutdown: function System_shutdown () {
+ // Return information about both opened files and opened
+ // directory iterators.
+ return {
+ openedFiles: OpenedFiles.listOpenedResources(),
+ openedDirectoryIterators:
+ OpenedDirectoryIterators.listOpenedResources()
+ };
+ },
+ // Functions of OS.File
+ stat: function stat(path) {
+ return exports.OS.File.Info.toMsg(
+ exports.OS.File.stat(Type.path.fromMsg(path)));
+ },
+ getCurrentDirectory: function getCurrentDirectory() {
+ return exports.OS.Shared.Type.path.toMsg(File.getCurrentDirectory());
+ },
+ setCurrentDirectory: function setCurrentDirectory(path) {
+ File.setCurrentDirectory(exports.OS.Shared.Type.path.fromMsg(path));
+ },
+ copy: function copy(sourcePath, destPath, options) {
+ return File.copy(Type.path.fromMsg(sourcePath),
+ Type.path.fromMsg(destPath), options);
+ },
+ move: function move(sourcePath, destPath, options) {
+ return File.move(Type.path.fromMsg(sourcePath),
+ Type.path.fromMsg(destPath), options);
+ },
+ makeDir: function makeDir(path, options) {
+ return File.makeDir(Type.path.fromMsg(path), options);
+ },
+ removeEmptyDir: function removeEmptyDir(path, options) {
+ return File.removeEmptyDir(Type.path.fromMsg(path), options);
+ },
+ remove: function remove(path) {
+ return File.remove(Type.path.fromMsg(path));
+ },
+ open: function open(path, mode, options) {
+ let filePath = Type.path.fromMsg(path);
+ let file = File.open(filePath, mode, options);
+ return OpenedFiles.add(file, {
+ // Adding path information to keep track of opened files
+ // to report leaks when debugging.
+ path: filePath
+ });
+ },
+ read: function read(path, bytes, options) {
+ let data = File.read(Type.path.fromMsg(path), bytes);
+ return new Transfer({buffer: data.buffer, byteOffset: data.byteOffset, byteLength: data.byteLength}, [data.buffer]);
+ },
+ exists: function exists(path) {
+ return File.exists(Type.path.fromMsg(path));
+ },
+ writeAtomic: function writeAtomic(path, buffer, options) {
+ if (options.tmpPath) {
+ options.tmpPath = Type.path.fromMsg(options.tmpPath);
+ }
+ return File.writeAtomic(Type.path.fromMsg(path),
+ Type.voidptr_t.fromMsg(buffer),
+ options
+ );
+ },
+ new_DirectoryIterator: function new_DirectoryIterator(path, options) {
+ let directoryPath = Type.path.fromMsg(path);
+ let iterator = new File.DirectoryIterator(directoryPath, options);
+ return OpenedDirectoryIterators.add(iterator, {
+ // Adding path information to keep track of opened directory
+ // iterators to report leaks when debugging.
+ path: directoryPath
+ });
+ },
+ // Methods of OS.File
+ File_prototype_close: function close(fd) {
+ return withFile(fd,
+ function do_close() {
+ try {
+ return this.close();
+ } finally {
+ OpenedFiles.remove(fd);
}
- } else if (exn == StopIteration) {
- // StopIteration cannot be serialized automatically
- LOG("Sending back StopIteration");
- self.postMessage({StopIteration: true, id: id, durationMs: durationMs});
- } else if (exn instanceof exports.OS.File.Error) {
- LOG("Sending back OS.File error", exn, "id is", id);
- // Instances of OS.File.Error know how to serialize themselves
- // (deserialization ensures that we end up with OS-specific
- // instances of |OS.File.Error|)
- self.postMessage({fail: exports.OS.File.Error.toMsg(exn), id:id, durationMs: durationMs});
- } else {
- LOG("Sending back regular error", exn, exn.stack, "id is", id);
- // Other exceptions do not, and should be propagated through DOM's
- // built-in mechanism for uncaught errors, although this mechanism
- // may lose interesting information.
- throw exn;
- }
- };
-
- /**
- * A data structure used to track opened resources
- */
- let ResourceTracker = function ResourceTracker() {
- // A number used to generate ids
- this._idgen = 0;
- // A map from id to resource
- this._map = new Map();
- };
- ResourceTracker.prototype = {
- /**
- * Get a resource from its unique identifier.
- */
- get: function(id) {
- let result = this._map.get(id);
- if (result == null) {
- return result;
- }
- return result.resource;
- },
- /**
- * Remove a resource from its unique identifier.
- */
- remove: function(id) {
- if (!this._map.has(id)) {
- throw new Error("Cannot find resource id " + id);
- }
- this._map.delete(id);
- },
- /**
- * Add a resource, return a new unique identifier
- *
- * @param {*} resource A resource.
- * @param {*=} info Optional information. For debugging purposes.
- *
- * @return {*} A unique identifier. For the moment, this is a number,
- * but this might not remain the case forever.
- */
- add: function(resource, info) {
- let id = this._idgen++;
- this._map.set(id, {resource:resource, info:info});
- return id;
- },
- /**
- * Return a list of all open resources i.e. the ones still present in
- * ResourceTracker's _map.
- */
- listOpenedResources: function listOpenedResources() {
- return [resource.info.path for ([id, resource] of this._map)];
- }
- };
-
- /**
- * A map of unique identifiers to opened files.
- */
- let OpenedFiles = new ResourceTracker();
-
- /**
- * Execute a function in the context of a given file.
- *
- * @param {*} id A unique identifier, as used by |OpenFiles|.
- * @param {Function} f A function to call.
- * @param {boolean} ignoreAbsent If |true|, the error is ignored. Otherwise, the error causes an exception.
- * @return The return value of |f()|
- *
- * This function attempts to get the file matching |id|. If
- * the file exists, it executes |f| within the |this| set
- * to the corresponding file. Otherwise, it throws an error.
- */
- let withFile = function withFile(id, f, ignoreAbsent) {
- let file = OpenedFiles.get(id);
- if (file == null) {
- if (!ignoreAbsent) {
- throw OS.File.Error.closed("accessing file");
- }
- return undefined;
- }
- return f.call(file);
- };
-
- let OpenedDirectoryIterators = new ResourceTracker();
- let withDir = function withDir(fd, f, ignoreAbsent) {
- let file = OpenedDirectoryIterators.get(fd);
- if (file == null) {
- if (!ignoreAbsent) {
- throw OS.File.Error.closed("accessing directory");
- }
- return undefined;
- }
- if (!(file instanceof File.DirectoryIterator)) {
- throw new Error("file is not a directory iterator " + file.__proto__.toSource());
- }
- return f.call(file);
- };
-
- let Type = exports.OS.Shared.Type;
-
- let File = exports.OS.File;
-
- /**
- * A constructor used to transfer data to the caller
- * without copy.
- *
- * @param {*} data The data to return to the caller.
- * @param {Array} transfers An array of Transferable
- * values that should be moved instead of being copied.
- *
- * @constructor
- */
- let Transfer = function Transfer(data, transfers) {
- this.data = data;
- this.transfers = transfers;
- };
-
- /**
- * The agent.
- *
- * It is in charge of performing method-specific deserialization
- * of messages, calling the function/method of OS.File and serializing
- * back the results.
- */
- let Agent = {
- // Update worker's OS.Shared.DEBUG flag message from controller.
- SET_DEBUG: function SET_DEBUG (aDEBUG) {
- exports.OS.Shared.DEBUG = aDEBUG;
- },
- // Return worker's current OS.Shared.DEBUG value to controller.
- // Note: This is used for testing purposes.
- GET_DEBUG: function GET_DEBUG () {
- return exports.OS.Shared.DEBUG;
- },
- // Report file descriptors leaks.
- System_shutdown: function System_shutdown () {
- // Return information about both opened files and opened
- // directory iterators.
- return {
- openedFiles: OpenedFiles.listOpenedResources(),
- openedDirectoryIterators:
- OpenedDirectoryIterators.listOpenedResources()
- };
- },
- // Functions of OS.File
- stat: function stat(path) {
- return exports.OS.File.Info.toMsg(
- exports.OS.File.stat(Type.path.fromMsg(path)));
- },
- getCurrentDirectory: function getCurrentDirectory() {
- return exports.OS.Shared.Type.path.toMsg(File.getCurrentDirectory());
- },
- setCurrentDirectory: function setCurrentDirectory(path) {
- File.setCurrentDirectory(exports.OS.Shared.Type.path.fromMsg(path));
- },
- copy: function copy(sourcePath, destPath, options) {
- return File.copy(Type.path.fromMsg(sourcePath),
- Type.path.fromMsg(destPath), options);
- },
- move: function move(sourcePath, destPath, options) {
- return File.move(Type.path.fromMsg(sourcePath),
- Type.path.fromMsg(destPath), options);
- },
- makeDir: function makeDir(path, options) {
- return File.makeDir(Type.path.fromMsg(path), options);
- },
- removeEmptyDir: function removeEmptyDir(path, options) {
- return File.removeEmptyDir(Type.path.fromMsg(path), options);
- },
- remove: function remove(path) {
- return File.remove(Type.path.fromMsg(path));
- },
- open: function open(path, mode, options) {
- let filePath = Type.path.fromMsg(path);
- let file = File.open(filePath, mode, options);
- return OpenedFiles.add(file, {
- // Adding path information to keep track of opened files
- // to report leaks when debugging.
- path: filePath
- });
- },
- read: function read(path, bytes, options) {
- let data = File.read(Type.path.fromMsg(path), bytes);
+ });
+ },
+ File_prototype_stat: function stat(fd) {
+ return withFile(fd,
+ function do_stat() {
+ return exports.OS.File.Info.toMsg(this.stat());
+ });
+ },
+ File_prototype_read: function read(fd, nbytes, options) {
+ return withFile(fd,
+ function do_read() {
+ let data = this.read(nbytes, options);
return new Transfer({buffer: data.buffer, byteOffset: data.byteOffset, byteLength: data.byteLength}, [data.buffer]);
- },
- exists: function exists(path) {
- return File.exists(Type.path.fromMsg(path));
- },
- writeAtomic: function writeAtomic(path, buffer, options) {
- if (options.tmpPath) {
- options.tmpPath = Type.path.fromMsg(options.tmpPath);
- }
- return File.writeAtomic(Type.path.fromMsg(path),
- Type.voidptr_t.fromMsg(buffer),
- options
- );
- },
- new_DirectoryIterator: function new_DirectoryIterator(path, options) {
- let directoryPath = Type.path.fromMsg(path);
- let iterator = new File.DirectoryIterator(directoryPath, options);
- return OpenedDirectoryIterators.add(iterator, {
- // Adding path information to keep track of opened directory
- // iterators to report leaks when debugging.
- path: directoryPath
- });
- },
- // Methods of OS.File
- File_prototype_close: function close(fd) {
- return withFile(fd,
- function do_close() {
- try {
- return this.close();
- } finally {
- OpenedFiles.remove(fd);
- }
- });
- },
- File_prototype_stat: function stat(fd) {
- return withFile(fd,
- function do_stat() {
- return exports.OS.File.Info.toMsg(this.stat());
- });
- },
- File_prototype_read: function read(fd, nbytes, options) {
- return withFile(fd,
- function do_read() {
- let data = this.read(nbytes, options);
- return new Transfer({buffer: data.buffer, byteOffset: data.byteOffset, byteLength: data.byteLength}, [data.buffer]);
- }
- );
- },
- File_prototype_readTo: function readTo(fd, buffer, options) {
- return withFile(fd,
- function do_readTo() {
- return this.readTo(exports.OS.Shared.Type.voidptr_t.fromMsg(buffer),
- options);
- });
- },
- File_prototype_write: function write(fd, buffer, options) {
- return withFile(fd,
- function do_write() {
- return this.write(exports.OS.Shared.Type.voidptr_t.fromMsg(buffer),
- options);
- });
- },
- File_prototype_setPosition: function setPosition(fd, pos, whence) {
- return withFile(fd,
- function do_setPosition() {
- return this.setPosition(pos, whence);
- });
- },
- File_prototype_getPosition: function getPosition(fd) {
- return withFile(fd,
- function do_getPosition() {
- return this.getPosition();
- });
- },
- // Methods of OS.File.DirectoryIterator
- DirectoryIterator_prototype_next: function next(dir) {
- return withDir(dir,
- function do_next() {
- try {
- return File.DirectoryIterator.Entry.toMsg(this.next());
- } catch (x) {
- if (x == StopIteration) {
- OpenedDirectoryIterators.remove(dir);
- }
- throw x;
- }
- }, false);
- },
- DirectoryIterator_prototype_nextBatch: function nextBatch(dir, size) {
- return withDir(dir,
- function do_nextBatch() {
- let result;
- try {
- result = this.nextBatch(size);
- } catch (x) {
- OpenedDirectoryIterators.remove(dir);
- throw x;
- }
- return result.map(File.DirectoryIterator.Entry.toMsg);
- }, false);
- },
- DirectoryIterator_prototype_close: function close(dir) {
- return withDir(dir,
- function do_close() {
- this.close();
- OpenedDirectoryIterators.remove(dir);
- }, true);// ignore error to support double-closing |DirectoryIterator|
- },
- DirectoryIterator_prototype_exists: function exists(dir) {
- return withDir(dir,
- function do_exists() {
- return this.exists();
- });
}
- };
- } catch(ex) {
- dump("WORKER ERROR DURING SETUP " + ex + "\n");
- dump("WORKER ERROR DETAIL " + ("moduleStack" in ex?ex.moduleStack:ex.stack) + "\n");
- }
+ );
+ },
+ File_prototype_readTo: function readTo(fd, buffer, options) {
+ return withFile(fd,
+ function do_readTo() {
+ return this.readTo(exports.OS.Shared.Type.voidptr_t.fromMsg(buffer),
+ options);
+ });
+ },
+ File_prototype_write: function write(fd, buffer, options) {
+ return withFile(fd,
+ function do_write() {
+ return this.write(exports.OS.Shared.Type.voidptr_t.fromMsg(buffer),
+ options);
+ });
+ },
+ File_prototype_setPosition: function setPosition(fd, pos, whence) {
+ return withFile(fd,
+ function do_setPosition() {
+ return this.setPosition(pos, whence);
+ });
+ },
+ File_prototype_getPosition: function getPosition(fd) {
+ return withFile(fd,
+ function do_getPosition() {
+ return this.getPosition();
+ });
+ },
+ // Methods of OS.File.DirectoryIterator
+ DirectoryIterator_prototype_next: function next(dir) {
+ return withDir(dir,
+ function do_next() {
+ try {
+ return File.DirectoryIterator.Entry.toMsg(this.next());
+ } catch (x) {
+ if (x == StopIteration) {
+ OpenedDirectoryIterators.remove(dir);
+ }
+ throw x;
+ }
+ }, false);
+ },
+ DirectoryIterator_prototype_nextBatch: function nextBatch(dir, size) {
+ return withDir(dir,
+ function do_nextBatch() {
+ let result;
+ try {
+ result = this.nextBatch(size);
+ } catch (x) {
+ OpenedDirectoryIterators.remove(dir);
+ throw x;
+ }
+ return result.map(File.DirectoryIterator.Entry.toMsg);
+ }, false);
+ },
+ DirectoryIterator_prototype_close: function close(dir) {
+ return withDir(dir,
+ function do_close() {
+ this.close();
+ OpenedDirectoryIterators.remove(dir);
+ }, true);// ignore error to support double-closing |DirectoryIterator|
+ },
+ DirectoryIterator_prototype_exists: function exists(dir) {
+ return withDir(dir,
+ function do_exists() {
+ return this.exists();
+ });
+ }
+ };
})(this);
diff --git a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
index 92add75c8550..eb2a9a4c4ec8 100644
--- a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
@@ -7,6 +7,4 @@ tail =
[test_osfile_async.js]
[test_profiledir.js]
[test_logging.js]
-# bug 845190 - thread pool wasn't shutdown assertions
-skip-if = (os == "win" || "linux") && debug
[test_creationDate.js]
diff --git a/toolkit/components/search/tests/xpcshell/xpcshell.ini b/toolkit/components/search/tests/xpcshell/xpcshell.ini
index f7df9fe22d04..324081ad3924 100644
--- a/toolkit/components/search/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/search/tests/xpcshell/xpcshell.ini
@@ -15,8 +15,6 @@ support-files =
[test_nocache.js]
[test_645970.js]
-# Bug 845190: Too many intermittent assertions on Linux (ASSERTION: thread pool wasn't shutdown)
-skip-if = debug && os == "linux"
[test_identifiers.js]
[test_init_async_multiple.js]
[test_init_async_multiple_then_sync.js]
diff --git a/toolkit/mozapps/downloads/nsHelperAppDlg.js b/toolkit/mozapps/downloads/nsHelperAppDlg.js
index 534de784e947..2ba3e329fcc7 100644
--- a/toolkit/mozapps/downloads/nsHelperAppDlg.js
+++ b/toolkit/mozapps/downloads/nsHelperAppDlg.js
@@ -102,6 +102,8 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/DownloadLastDir.jsm", downloadModule);
Components.utils.import("resource://gre/modules/DownloadPaths.jsm");
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
+Components.utils.import("resource://gre/modules/Downloads.jsm");
+Components.utils.import("resource://gre/modules/Task.jsm");
/* ctor
*/
@@ -203,115 +205,113 @@ nsUnknownContentTypeDialog.prototype = {
getService(Components.interfaces.nsIStringBundleService).
createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
- if (!aForcePrompt) {
- // Check to see if the user wishes to auto save to the default download
- // folder without prompting. Note that preference might not be set.
- let autodownload = false;
- try {
- autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR);
- } catch (e) { }
-
- if (autodownload) {
- // Retrieve the user's default download directory
- let dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
- .getService(Components.interfaces.nsIDownloadManager);
- let defaultFolder = dnldMgr.userDownloadsDirectory;
-
+ Task.spawn(function() {
+ if (!aForcePrompt) {
+ // Check to see if the user wishes to auto save to the default download
+ // folder without prompting. Note that preference might not be set.
+ let autodownload = false;
try {
- result = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExtension);
- }
- catch (ex) {
- if (ex.result == Components.results.NS_ERROR_FILE_ACCESS_DENIED) {
- let prompter = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
- getService(Components.interfaces.nsIPromptService);
+ autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR);
+ } catch (e) { }
- // Display error alert (using text supplied by back-end)
- prompter.alert(this.dialog,
- bundle.GetStringFromName("badPermissions.title"),
- bundle.GetStringFromName("badPermissions"));
+ if (autodownload) {
+ // Retrieve the user's default download directory
+ let defaultFolder = yield Downloads.getPreferredDownloadsDirectory();
- aLauncher.saveDestinationAvailable(null);
+ try {
+ result = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExtension);
+ }
+ catch (ex) {
+ if (ex.result == Components.results.NS_ERROR_FILE_ACCESS_DENIED) {
+ let prompter = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Components.interfaces.nsIPromptService);
+
+ // Display error alert (using text supplied by back-end)
+ prompter.alert(this.dialog,
+ bundle.GetStringFromName("badPermissions.title"),
+ bundle.GetStringFromName("badPermissions"));
+
+ aLauncher.saveDestinationAvailable(null);
+ return;
+ }
+ }
+
+ // Check to make sure we have a valid directory, otherwise, prompt
+ if (result) {
+ aLauncher.saveDestinationAvailable(result);
return;
}
}
+ }
- // Check to make sure we have a valid directory, otherwise, prompt
- if (result) {
- aLauncher.saveDestinationAvailable(result);
+ // Use file picker to show dialog.
+ var nsIFilePicker = Components.interfaces.nsIFilePicker;
+ var picker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+ var windowTitle = bundle.GetStringFromName("saveDialogTitle");
+ var parent = aContext.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindow);
+ picker.init(parent, windowTitle, nsIFilePicker.modeSave);
+ picker.defaultString = aDefaultFile;
+
+ let gDownloadLastDir = new downloadModule.DownloadLastDir(parent);
+
+ if (aSuggestedFileExtension) {
+ // aSuggestedFileExtension includes the period, so strip it
+ picker.defaultExtension = aSuggestedFileExtension.substring(1);
+ }
+ else {
+ try {
+ picker.defaultExtension = this.mLauncher.MIMEInfo.primaryExtension;
+ }
+ catch (ex) { }
+ }
+
+ var wildCardExtension = "*";
+ if (aSuggestedFileExtension) {
+ wildCardExtension += aSuggestedFileExtension;
+ picker.appendFilter(this.mLauncher.MIMEInfo.description, wildCardExtension);
+ }
+
+ picker.appendFilters( nsIFilePicker.filterAll );
+
+ // Default to lastDir if it is valid, otherwise use the user's default
+ // downloads directory. userDownloadsDirectory should always return a
+ // valid directory, so we can safely default to it.
+ picker.displayDirectory = yield Downloads.getPreferredDownloadsDirectory();
+
+ gDownloadLastDir.getFileAsync(aLauncher.source, function LastDirCallback(lastDir) {
+ if (lastDir && isUsableDirectory(lastDir))
+ picker.displayDirectory = lastDir;
+
+ if (picker.show() == nsIFilePicker.returnCancel) {
+ // null result means user cancelled.
+ aLauncher.saveDestinationAvailable(null);
return;
}
- }
- }
- // Use file picker to show dialog.
- var nsIFilePicker = Components.interfaces.nsIFilePicker;
- var picker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
- var windowTitle = bundle.GetStringFromName("saveDialogTitle");
- var parent = aContext.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindow);
- picker.init(parent, windowTitle, nsIFilePicker.modeSave);
- picker.defaultString = aDefaultFile;
+ // Be sure to save the directory the user chose through the Save As...
+ // dialog as the new browser.download.dir since the old one
+ // didn't exist.
+ result = picker.file;
- let gDownloadLastDir = new downloadModule.DownloadLastDir(parent);
+ if (result) {
+ try {
+ // Remove the file so that it's not there when we ensure non-existence later;
+ // this is safe because for the file to exist, the user would have had to
+ // confirm that he wanted the file overwritten.
+ if (result.exists())
+ result.remove(false);
+ }
+ catch (e) { }
+ var newDir = result.parent.QueryInterface(Components.interfaces.nsILocalFile);
- if (aSuggestedFileExtension) {
- // aSuggestedFileExtension includes the period, so strip it
- picker.defaultExtension = aSuggestedFileExtension.substring(1);
- }
- else {
- try {
- picker.defaultExtension = this.mLauncher.MIMEInfo.primaryExtension;
- }
- catch (ex) { }
- }
+ // Do not store the last save directory as a pref inside the private browsing mode
+ gDownloadLastDir.setFile(aLauncher.source, newDir);
- var wildCardExtension = "*";
- if (aSuggestedFileExtension) {
- wildCardExtension += aSuggestedFileExtension;
- picker.appendFilter(this.mLauncher.MIMEInfo.description, wildCardExtension);
- }
-
- picker.appendFilters( nsIFilePicker.filterAll );
-
- // Default to lastDir if it is valid, otherwise use the user's default
- // downloads directory. userDownloadsDirectory should always return a
- // valid directory, so we can safely default to it.
- var dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
- .getService(Components.interfaces.nsIDownloadManager);
- picker.displayDirectory = dnldMgr.userDownloadsDirectory;
-
- gDownloadLastDir.getFileAsync(aLauncher.source, function LastDirCallback(lastDir) {
- if (lastDir && isUsableDirectory(lastDir))
- picker.displayDirectory = lastDir;
-
- if (picker.show() == nsIFilePicker.returnCancel) {
- // null result means user cancelled.
- aLauncher.saveDestinationAvailable(null);
- return;
- }
-
- // Be sure to save the directory the user chose through the Save As...
- // dialog as the new browser.download.dir since the old one
- // didn't exist.
- result = picker.file;
-
- if (result) {
- try {
- // Remove the file so that it's not there when we ensure non-existence later;
- // this is safe because for the file to exist, the user would have had to
- // confirm that he wanted the file overwritten.
- if (result.exists())
- result.remove(false);
+ result = this.validateLeafName(newDir, result.leafName, null);
}
- catch (e) { }
- var newDir = result.parent.QueryInterface(Components.interfaces.nsILocalFile);
-
- // Do not store the last save directory as a pref inside the private browsing mode
- gDownloadLastDir.setFile(aLauncher.source, newDir);
-
- result = this.validateLeafName(newDir, result.leafName, null);
- }
- aLauncher.saveDestinationAvailable(result);
- }.bind(this));
+ aLauncher.saveDestinationAvailable(result);
+ }.bind(this));
+ }.bind(this)).then(null, Components.utils.reportError);
},
/**