зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound.
This commit is contained in:
Коммит
11cd5e5051
|
@ -431,8 +431,6 @@ pref("services.push.pingInterval", 1800000); // 30 minutes
|
||||||
pref("services.push.requestTimeout", 10000);
|
pref("services.push.requestTimeout", 10000);
|
||||||
// enable udp wakeup support
|
// enable udp wakeup support
|
||||||
pref("services.push.udp.wakeupEnabled", true);
|
pref("services.push.udp.wakeupEnabled", true);
|
||||||
// port on which UDP server socket is bound
|
|
||||||
pref("services.push.udp.port", 2442);
|
|
||||||
|
|
||||||
// NetworkStats
|
// NetworkStats
|
||||||
#ifdef MOZ_B2G_RIL
|
#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
|
// /still/ have the same niceness; we'd effectively have erased NSPR's thread
|
||||||
// priorities.
|
// 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.OomScoreAdjust", 0);
|
||||||
pref("hal.processPriorityManager.gonk.MASTER.KillUnderMB", 4);
|
pref("hal.processPriorityManager.gonk.MASTER.KillUnderMB", 4);
|
||||||
pref("hal.processPriorityManager.gonk.MASTER.Nice", 0);
|
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.KillUnderMB", 6);
|
||||||
pref("hal.processPriorityManager.gonk.FOREGROUND.Nice", 1);
|
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.OomScoreAdjust", 400);
|
||||||
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.KillUnderMB", 7);
|
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.KillUnderMB", 7);
|
||||||
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.Nice", 7);
|
pref("hal.processPriorityManager.gonk.BACKGROUND_PERCEIVABLE.Nice", 7);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"revision": "44efe8a2b5ce1abc18fb4da92fe774d08290bd31",
|
"revision": "df1654f408ae9ff3bdb276ad2e243bfecfd47087",
|
||||||
"repo_path": "/integration/gaia-central"
|
"repo_path": "/integration/gaia-central"
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,9 @@ function test() {
|
||||||
|
|
||||||
function onExamineResponse(subject) {
|
function onExamineResponse(subject) {
|
||||||
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
|
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
if (channel.URI.spec != "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.sjs") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
let cookies = channel.getResponseHeader("set-cookie");
|
let cookies = channel.getResponseHeader("set-cookie");
|
||||||
// From browser/base/content/test/general/bug792715.sjs, we receive a Set-Cookie
|
// From browser/base/content/test/general/bug792715.sjs, we receive a Set-Cookie
|
||||||
|
@ -122,6 +125,9 @@ function test() {
|
||||||
|
|
||||||
function onModifyRequest(subject) {
|
function onModifyRequest(subject) {
|
||||||
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
|
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
if (channel.URI.spec != "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.sjs") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
let cookies = channel.getRequestHeader("cookie");
|
let cookies = channel.getRequestHeader("cookie");
|
||||||
// From browser/base/content/test/general/bug792715.sjs, we should never send a
|
// From browser/base/content/test/general/bug792715.sjs, we should never send a
|
||||||
|
|
|
@ -1204,7 +1204,7 @@ BrowserGlue.prototype = {
|
||||||
// potential hangs (bug 518683). The asynchronous shutdown operations
|
// potential hangs (bug 518683). The asynchronous shutdown operations
|
||||||
// will then be handled by a shutdown service (bug 435058).
|
// will then be handled by a shutdown service (bug 435058).
|
||||||
waitingForHTMLExportToComplete = false;
|
waitingForHTMLExportToComplete = false;
|
||||||
BookmarkHTMLUtils.exportToFile(FileUtils.getFile("BMarks", [])).then(
|
BookmarkHTMLUtils.exportToFile(Services.dirsvc.get("BMarks", Ci.nsIFile)).then(
|
||||||
function onSuccess() {
|
function onSuccess() {
|
||||||
waitingForHTMLExportToComplete = true;
|
waitingForHTMLExportToComplete = true;
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,10 +7,6 @@ support-files = data/sessionstore_valid.js
|
||||||
[test_backup.js]
|
[test_backup.js]
|
||||||
[test_backup_once.js]
|
[test_backup_once.js]
|
||||||
[test_startup_nosession_sync.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_nosession_async.js]
|
||||||
[test_startup_session_sync.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]
|
[test_startup_session_async.js]
|
||||||
|
|
|
@ -88,12 +88,19 @@ const IDB = {
|
||||||
|
|
||||||
const store = new ObservableObject({ projects:[] });
|
const store = new ObservableObject({ projects:[] });
|
||||||
|
|
||||||
|
let loadDeferred = promise.defer();
|
||||||
|
|
||||||
IDB.open().then(function (projects) {
|
IDB.open().then(function (projects) {
|
||||||
store.object.projects = projects;
|
store.object.projects = projects;
|
||||||
AppProjects.emit("ready", store.object.projects);
|
AppProjects.emit("ready", store.object.projects);
|
||||||
|
loadDeferred.resolve();
|
||||||
});
|
});
|
||||||
|
|
||||||
const AppProjects = {
|
const AppProjects = {
|
||||||
|
load: function() {
|
||||||
|
return loadDeferred.promise;
|
||||||
|
},
|
||||||
|
|
||||||
addPackaged: function(folder) {
|
addPackaged: function(folder) {
|
||||||
let project = {
|
let project = {
|
||||||
type: "packaged",
|
type: "packaged",
|
||||||
|
|
|
@ -39,10 +39,8 @@ let UI = {
|
||||||
this.template = new Template(document.body, AppProjects.store, Utils.l10n);
|
this.template = new Template(document.body, AppProjects.store, Utils.l10n);
|
||||||
this.template.start();
|
this.template.start();
|
||||||
|
|
||||||
AppProjects.store.on("set", (event,path,value) => {
|
AppProjects.load().then(() => {
|
||||||
if (path == "projects") {
|
AppProjects.store.object.projects.forEach(UI.validate);
|
||||||
AppProjects.store.object.projects.forEach(UI.validate);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -320,10 +318,11 @@ let UI = {
|
||||||
// And only when the toolbox is opened, release the button
|
// And only when the toolbox is opened, release the button
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
},
|
},
|
||||||
(msg) => {
|
(err) => {
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
alert(msg);
|
let message = err.error ? err.error + ": " + err.message : String(err);
|
||||||
this.connection.log(msg);
|
alert(message);
|
||||||
|
this.connection.log(message);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
|
||||||
"resource:///modules/devtools/gDevTools.jsm");
|
"resource:///modules/devtools/gDevTools.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
|
XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
|
||||||
"resource:///modules/devtools/AppCacheUtils.jsm");
|
"resource:///modules/devtools/AppCacheUtils.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||||
|
"resource://gre/modules/Downloads.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||||
|
"resource://gre/modules/Task.jsm");
|
||||||
|
|
||||||
/* CmdAddon ---------------------------------------------------------------- */
|
/* CmdAddon ---------------------------------------------------------------- */
|
||||||
|
|
||||||
|
@ -1721,160 +1725,159 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
|
||||||
}
|
}
|
||||||
var document = args.chrome? context.environment.chromeDocument
|
var document = args.chrome? context.environment.chromeDocument
|
||||||
: context.environment.document;
|
: context.environment.document;
|
||||||
|
var deferred = context.defer();
|
||||||
if (args.delay > 0) {
|
if (args.delay > 0) {
|
||||||
var deferred = context.defer();
|
|
||||||
document.defaultView.setTimeout(function Command_screenshotDelay() {
|
document.defaultView.setTimeout(function Command_screenshotDelay() {
|
||||||
let reply = this.grabScreen(document, args.filename, args.clipboard,
|
let promise = this.grabScreen(document, args.filename, args.clipboard,
|
||||||
args.fullpage);
|
args.fullpage);
|
||||||
deferred.resolve(reply);
|
promise.then(deferred.resolve, deferred.reject);
|
||||||
}.bind(this), args.delay * 1000);
|
}.bind(this), args.delay * 1000);
|
||||||
return deferred.promise;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return this.grabScreen(document, args.filename, args.clipboard,
|
let promise = this.grabScreen(document, args.filename, args.clipboard,
|
||||||
args.fullpage, args.selector);
|
args.fullpage, args.selector);
|
||||||
|
promise.then(deferred.resolve, deferred.reject);
|
||||||
}
|
}
|
||||||
|
return deferred.promise;
|
||||||
},
|
},
|
||||||
grabScreen: function(document, filename, clipboard, fullpage, node) {
|
grabScreen: function(document, filename, clipboard, fullpage, node) {
|
||||||
let window = document.defaultView;
|
return Task.spawn(function() {
|
||||||
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
let window = document.defaultView;
|
||||||
let left = 0;
|
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||||
let top = 0;
|
let left = 0;
|
||||||
let width;
|
let top = 0;
|
||||||
let height;
|
let width;
|
||||||
let div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
|
let height;
|
||||||
|
let div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
|
||||||
|
|
||||||
if (!fullpage) {
|
if (!fullpage) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
left = window.scrollX;
|
left = window.scrollX;
|
||||||
top = window.scrollY;
|
top = window.scrollY;
|
||||||
width = window.innerWidth;
|
width = window.innerWidth;
|
||||||
height = window.innerHeight;
|
height = window.innerHeight;
|
||||||
} else {
|
} else {
|
||||||
let lh = new LayoutHelpers(window);
|
let lh = new LayoutHelpers(window);
|
||||||
let rect = lh.getRect(node, window);
|
let rect = lh.getRect(node, window);
|
||||||
top = rect.top;
|
top = rect.top;
|
||||||
left = rect.left;
|
left = rect.left;
|
||||||
width = rect.width;
|
width = rect.width;
|
||||||
height = rect.height;
|
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;
|
|
||||||
}
|
}
|
||||||
return part;
|
} else {
|
||||||
}).join("-");
|
width = window.innerWidth + window.scrollMaxX;
|
||||||
let timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
|
height = window.innerHeight + window.scrollMaxY;
|
||||||
filename = gcli.lookupFormat("screenshotGeneratedFilename",
|
}
|
||||||
[dateString, timeString]) + ".png";
|
canvas.width = width;
|
||||||
}
|
canvas.height = height;
|
||||||
// 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
|
let ctx = canvas.getContext("2d");
|
||||||
if (!filename.match(/[\\\/]/)) {
|
ctx.drawWindow(window, left, top, width, height, "#fff");
|
||||||
let downloadMgr = Cc["@mozilla.org/download-manager;1"]
|
let data = canvas.toDataURL("image/png", "");
|
||||||
.getService(Ci.nsIDownloadManager);
|
|
||||||
let tempfile = downloadMgr.userDownloadsDirectory;
|
|
||||||
tempfile.append(filename);
|
|
||||||
filename = tempfile.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
let loadContext = document.defaultView
|
||||||
file.initWithPath(filename);
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
} catch (ex) {
|
.getInterface(Ci.nsIWebNavigation)
|
||||||
div.textContent = gcli.lookup("screenshotErrorSavingToFile") + " " + filename;
|
.QueryInterface(Ci.nsILoadContext);
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ioService = Cc["@mozilla.org/network/io-service;1"]
|
if (clipboard) {
|
||||||
.getService(Ci.nsIIOService);
|
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 container = {};
|
||||||
let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
|
imgTools.decodeImageData(input, channel.contentType, container);
|
||||||
.createInstance(Persist);
|
|
||||||
persist.persistFlags = Persist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
|
|
||||||
Persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
|
|
||||||
|
|
||||||
let source = ioService.newURI(data, "UTF8", null);
|
let wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
|
||||||
persist.saveURI(source, null, null, null, null, file, loadContext);
|
.createInstance(Ci.nsISupportsInterfacePointer);
|
||||||
|
wrapped.data = container.value;
|
||||||
|
|
||||||
div.textContent = gcli.lookup("screenshotSavedToFile") + " \"" + filename +
|
let trans = Cc["@mozilla.org/widget/transferable;1"]
|
||||||
"\"";
|
.createInstance(Ci.nsITransferable);
|
||||||
div.addEventListener("click", function openFile() {
|
trans.init(loadContext);
|
||||||
div.removeEventListener("click", openFile);
|
trans.addDataFlavor(channel.contentType);
|
||||||
file.reveal();
|
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));
|
}(this));
|
||||||
|
|
|
@ -349,20 +349,22 @@ var ContextCommands = {
|
||||||
picker.appendFilters(Ci.nsIFilePicker.filterImages);
|
picker.appendFilters(Ci.nsIFilePicker.filterImages);
|
||||||
|
|
||||||
// prefered save location
|
// prefered save location
|
||||||
var dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
|
Task.spawn(function() {
|
||||||
picker.displayDirectory = dnldMgr.userDownloadsDirectory;
|
picker.displayDirectory = yield Downloads.getPreferredDownloadsDirectory();
|
||||||
try {
|
|
||||||
let lastDir = Services.prefs.getComplexValue("browser.download.lastDir", Ci.nsILocalFile);
|
|
||||||
if (this.isAccessibleDirectory(lastDir))
|
|
||||||
picker.displayDirectory = lastDir;
|
|
||||||
}
|
|
||||||
catch (e) { }
|
|
||||||
|
|
||||||
this._picker = picker;
|
try {
|
||||||
this._pickerUrl = mediaURL;
|
let lastDir = Services.prefs.getComplexValue("browser.download.lastDir", Ci.nsILocalFile);
|
||||||
this._pickerContentDisp = aPopupState.contentDisposition;
|
if (this.isAccessibleDirectory(lastDir))
|
||||||
this._contentType = aPopupState.contentType;
|
picker.displayDirectory = lastDir;
|
||||||
picker.open(ContextCommands);
|
}
|
||||||
|
catch (e) { }
|
||||||
|
|
||||||
|
this._picker = picker;
|
||||||
|
this._pickerUrl = mediaURL;
|
||||||
|
this._pickerContentDisp = aPopupState.contentDisposition;
|
||||||
|
this._contentType = aPopupState.contentType;
|
||||||
|
picker.open(ContextCommands);
|
||||||
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -10,7 +10,10 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||||
* JS modules
|
* JS modules
|
||||||
*/
|
*/
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this , "FormHistory",
|
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||||
|
"resource://gre/modules/Downloads.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||||
"resource://gre/modules/FormHistory.jsm");
|
"resource://gre/modules/FormHistory.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||||
|
@ -97,7 +100,7 @@ let ScriptContexts = {};
|
||||||
["CommandUpdater", "chrome://browser/content/commandUtil.js"],
|
["CommandUpdater", "chrome://browser/content/commandUtil.js"],
|
||||||
["ContextCommands", "chrome://browser/content/ContextCommands.js"],
|
["ContextCommands", "chrome://browser/content/ContextCommands.js"],
|
||||||
["Bookmarks", "chrome://browser/content/bookmarks.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"],
|
["ConsolePanelView", "chrome://browser/content/console.js"],
|
||||||
["Site", "chrome://browser/content/Site.js"],
|
["Site", "chrome://browser/content/Site.js"],
|
||||||
["TopSites", "chrome://browser/content/TopSites.js"],
|
["TopSites", "chrome://browser/content/TopSites.js"],
|
||||||
|
|
|
@ -142,7 +142,7 @@ var BrowserUI = {
|
||||||
messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps);
|
messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Downloads.init();
|
MetroDownloadsView.init();
|
||||||
DialogUI.init();
|
DialogUI.init();
|
||||||
FormHelperUI.init();
|
FormHelperUI.init();
|
||||||
FindHelperUI.init();
|
FindHelperUI.init();
|
||||||
|
@ -175,7 +175,7 @@ var BrowserUI = {
|
||||||
|
|
||||||
PanelUI.uninit();
|
PanelUI.uninit();
|
||||||
FlyoutPanelsUI.uninit();
|
FlyoutPanelsUI.uninit();
|
||||||
Downloads.uninit();
|
MetroDownloadsView.uninit();
|
||||||
SettingsCharm.uninit();
|
SettingsCharm.uninit();
|
||||||
messageManager.removeMessageListener("Content:StateChange", this);
|
messageManager.removeMessageListener("Content:StateChange", this);
|
||||||
PageThumbs.uninit();
|
PageThumbs.uninit();
|
||||||
|
|
|
@ -255,7 +255,7 @@
|
||||||
<observes element="bcast_urlbarState" attribute="*"/>
|
<observes element="bcast_urlbarState" attribute="*"/>
|
||||||
<hbox id="toolbar-context-page" pack="end">
|
<hbox id="toolbar-context-page" pack="end">
|
||||||
<circularprogressindicator id="download-progress"
|
<circularprogressindicator id="download-progress"
|
||||||
oncommand="Downloads.onDownloadButton()"/>
|
oncommand="MetroDownloadsView.onDownloadButton()"/>
|
||||||
<toolbarbutton id="star-button" class="appbar-primary"
|
<toolbarbutton id="star-button" class="appbar-primary"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
oncommand="Appbar.onStarButton()"/>
|
oncommand="Appbar.onStarButton()"/>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png";
|
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"
|
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
|
* _downloadCount keeps track of the number of downloads that a single
|
||||||
* notification bar groups together. A download is grouped with other
|
* notification bar groups together. A download is grouped with other
|
||||||
|
@ -217,15 +217,15 @@ var Downloads = {
|
||||||
label: tryAgainButtonText,
|
label: tryAgainButtonText,
|
||||||
accessKey: "",
|
accessKey: "",
|
||||||
callback: function() {
|
callback: function() {
|
||||||
Downloads.manager.retryDownload(aDownload.id);
|
MetroDownloadsView.manager.retryDownload(aDownload.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: cancelButtonText,
|
label: cancelButtonText,
|
||||||
accessKey: "",
|
accessKey: "",
|
||||||
callback: function() {
|
callback: function() {
|
||||||
Downloads.cancelDownload(aDownload);
|
MetroDownloadsView.cancelDownload(aDownload);
|
||||||
Downloads._downloadProgressIndicator.reset();
|
MetroDownloadsView._downloadProgressIndicator.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -243,7 +243,7 @@ var Downloads = {
|
||||||
accessKey: "",
|
accessKey: "",
|
||||||
callback: function() {
|
callback: function() {
|
||||||
let fileURI = aDownload.target;
|
let fileURI = aDownload.target;
|
||||||
let file = Downloads._getLocalFile(fileURI);
|
let file = MetroDownloadsView._getLocalFile(fileURI);
|
||||||
file.reveal();
|
file.reveal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ var Downloads = {
|
||||||
label: runButtonText,
|
label: runButtonText,
|
||||||
accessKey: "",
|
accessKey: "",
|
||||||
callback: function() {
|
callback: function() {
|
||||||
Downloads.openDownload(aDownload);
|
MetroDownloadsView.openDownload(aDownload);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -288,12 +288,12 @@ var Downloads = {
|
||||||
switch (aTopic) {
|
switch (aTopic) {
|
||||||
case "alertclickcallback":
|
case "alertclickcallback":
|
||||||
let fileURI = aDownload.target;
|
let fileURI = aDownload.target;
|
||||||
let file = Downloads._getLocalFile(fileURI);
|
let file = MetroDownloadsView._getLocalFile(fileURI);
|
||||||
file.reveal();
|
file.reveal();
|
||||||
|
|
||||||
let downloadCompleteNotification =
|
let downloadCompleteNotification =
|
||||||
Downloads._notificationBox.getNotificationWithValue("download-complete");
|
MetroDownloadsView._notificationBox.getNotificationWithValue("download-complete");
|
||||||
Downloads._notificationBox.removeNotification(downloadCompleteNotification);
|
MetroDownloadsView._notificationBox.removeNotification(downloadCompleteNotification);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,11 +306,11 @@ var Downloads = {
|
||||||
observe: function (aSubject, aTopic, aData) {
|
observe: function (aSubject, aTopic, aData) {
|
||||||
switch (aTopic) {
|
switch (aTopic) {
|
||||||
case "alertclickcallback":
|
case "alertclickcallback":
|
||||||
Downloads.openDownload(aDownload);
|
MetroDownloadsView.openDownload(aDownload);
|
||||||
|
|
||||||
let downloadCompleteNotification =
|
let downloadCompleteNotification =
|
||||||
Downloads._notificationBox.getNotificationWithValue("download-complete");
|
MetroDownloadsView._notificationBox.getNotificationWithValue("download-complete");
|
||||||
Downloads._notificationBox.removeNotification(downloadCompleteNotification);
|
MetroDownloadsView._notificationBox.removeNotification(downloadCompleteNotification);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -406,8 +406,8 @@ var Downloads = {
|
||||||
label: cancelButtonText,
|
label: cancelButtonText,
|
||||||
accessKey: "",
|
accessKey: "",
|
||||||
callback: function() {
|
callback: function() {
|
||||||
Downloads.cancelDownloads();
|
MetroDownloadsView.cancelDownloads();
|
||||||
Downloads._downloadProgressIndicator.reset();
|
MetroDownloadsView._downloadProgressIndicator.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -446,8 +446,8 @@ var Downloads = {
|
||||||
switch (aEvent.type) {
|
switch (aEvent.type) {
|
||||||
case "AlertClose":
|
case "AlertClose":
|
||||||
if (aEvent.notification.value == "download-complete" &&
|
if (aEvent.notification.value == "download-complete" &&
|
||||||
!Downloads._notificationBox.getNotificationWithValue("download-complete")) {
|
!MetroDownloadsView._notificationBox.getNotificationWithValue("download-complete")) {
|
||||||
Downloads._downloadProgressIndicator.reset();
|
MetroDownloadsView._downloadProgressIndicator.reset();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -562,10 +562,10 @@ AlertDownloadProgressListener.prototype = {
|
||||||
|
|
||||||
let contentLength = aDownload.size;
|
let contentLength = aDownload.size;
|
||||||
if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) {
|
if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) {
|
||||||
Downloads.showAlert(aDownload.target.spec.replace("file:", "download:"),
|
MetroDownloadsView.showAlert(aDownload.target.spec.replace("file:", "download:"),
|
||||||
strings.GetStringFromName("alertDownloadsNoSpace"),
|
strings.GetStringFromName("alertDownloadsNoSpace"),
|
||||||
strings.GetStringFromName("alertDownloadsSize"));
|
strings.GetStringFromName("alertDownloadsSize"));
|
||||||
Downloads.cancelDownload(aDownload);
|
MetroDownloadsView.cancelDownload(aDownload);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ function equalNumbers(){
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPromisedDbResult(aStatement) {
|
function getPromisedDbResult(aStatement) {
|
||||||
let dbConnection = Downloads.manager.DBConnection;
|
let dbConnection = MetroDownloadsView.manager.DBConnection;
|
||||||
let statement = ("string" == typeof aStatement) ?
|
let statement = ("string" == typeof aStatement) ?
|
||||||
dbConnection.createAsyncStatement(
|
dbConnection.createAsyncStatement(
|
||||||
aStatement
|
aStatement
|
||||||
|
@ -129,7 +129,7 @@ function resetDownloads(){
|
||||||
// Services.prefs.clearUserPref("browser.download.panel.shown");
|
// Services.prefs.clearUserPref("browser.download.panel.shown");
|
||||||
|
|
||||||
// Ensure that data is unloaded.
|
// Ensure that data is unloaded.
|
||||||
let dlMgr = Downloads.manager;
|
let dlMgr = MetroDownloadsView.manager;
|
||||||
let dlsToRemove = [];
|
let dlsToRemove = [];
|
||||||
// Clear all completed/cancelled downloads
|
// Clear all completed/cancelled downloads
|
||||||
dlMgr.cleanUp();
|
dlMgr.cleanUp();
|
||||||
|
@ -247,11 +247,11 @@ gTests.push({
|
||||||
// we're going to add stuff to the downloads db.
|
// we're going to add stuff to the downloads db.
|
||||||
yield spawn( gen_addDownloadRows( DownloadData ) );
|
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) \
|
have the correct length (DownloadData.length) \
|
||||||
May also test that the correct notifications show up for various states.");
|
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 \
|
and confirm that the downloads they refer to are the same as those in \
|
||||||
DownloadData.");
|
DownloadData.");
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
@ -299,7 +299,7 @@ gTests.push({
|
||||||
|
|
||||||
is(downloadRows.length, 3, "Correct number of downloads in the db before removal");
|
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.");
|
confirm that its file exists, then remove it.");
|
||||||
|
|
||||||
// remove is async(?), wait a bit
|
// remove is async(?), wait a bit
|
||||||
|
|
|
@ -18,6 +18,10 @@ XPCOMUtils.defineLazyGetter(this, "ContentUtil", function() {
|
||||||
Cu.import("resource:///modules/ContentUtil.jsm");
|
Cu.import("resource:///modules/ContentUtil.jsm");
|
||||||
return ContentUtil;
|
return ContentUtil;
|
||||||
});
|
});
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||||
|
"resource://gre/modules/Downloads.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||||
|
"resource://gre/modules/Task.jsm");
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// HelperApp Launcher Dialog
|
// HelperApp Launcher Dialog
|
||||||
|
@ -134,98 +138,98 @@ HelperAppLauncherDialog.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
|
promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
|
||||||
let file = null;
|
return Task.spawn(function() {
|
||||||
let prefs = Services.prefs;
|
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;
|
|
||||||
|
|
||||||
|
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 {
|
try {
|
||||||
file = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExt);
|
autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR);
|
||||||
}
|
} catch (e) { }
|
||||||
catch (e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to make sure we have a valid directory, otherwise, prompt
|
if (autodownload) {
|
||||||
if (file)
|
// Retrieve the user's default download directory
|
||||||
return file;
|
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.
|
// Use file picker to show dialog.
|
||||||
let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||||
let windowTitle = "";
|
let windowTitle = "";
|
||||||
let parent = aContext.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
|
let parent = aContext.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
|
||||||
picker.init(parent, windowTitle, Ci.nsIFilePicker.modeSave);
|
picker.init(parent, windowTitle, Ci.nsIFilePicker.modeSave);
|
||||||
picker.defaultString = aDefaultFile;
|
picker.defaultString = aDefaultFile;
|
||||||
|
|
||||||
if (aSuggestedFileExt) {
|
if (aSuggestedFileExt) {
|
||||||
// aSuggestedFileExtension includes the period, so strip it
|
// aSuggestedFileExtension includes the period, so strip it
|
||||||
picker.defaultExtension = aSuggestedFileExt.substring(1);
|
picker.defaultExtension = aSuggestedFileExt.substring(1);
|
||||||
}
|
}
|
||||||
else {
|
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 {
|
try {
|
||||||
picker.defaultExtension = aLauncher.MIMEInfo.primaryExtension;
|
let lastDir = prefs.getComplexValue("browser.download.lastDir", Ci.nsILocalFile);
|
||||||
|
if (isUsableDirectory(lastDir))
|
||||||
|
picker.displayDirectory = lastDir;
|
||||||
}
|
}
|
||||||
catch (e) { }
|
catch (e) { }
|
||||||
}
|
|
||||||
|
|
||||||
var wildCardExtension = "*";
|
if (picker.show() == Ci.nsIFilePicker.returnCancel) {
|
||||||
if (aSuggestedFileExt) {
|
// null result means user cancelled.
|
||||||
wildCardExtension += aSuggestedFileExt;
|
throw new Task.Result(null);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
catch (e) { }
|
|
||||||
var newDir = file.parent.QueryInterface(Ci.nsILocalFile);
|
// Be sure to save the directory the user chose through the Save As...
|
||||||
prefs.setComplexValue("browser.download.lastDir", Ci.nsILocalFile, newDir);
|
// dialog as the new browser.download.dir since the old one
|
||||||
file = this.validateLeafName(newDir, file.leafName, null);
|
// didn't exist.
|
||||||
}
|
file = picker.file;
|
||||||
return 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) {
|
validateLeafName: function hald_validateLeafName(aLocalFile, aLeafName, aFileExt) {
|
||||||
|
|
|
@ -397,7 +397,7 @@ function TabWindow(win) {
|
||||||
this.win = win;
|
this.win = win;
|
||||||
this.tabbrowser = win.gBrowser;
|
this.tabbrowser = win.gBrowser;
|
||||||
|
|
||||||
this.previews = [];
|
this.previews = new Map();
|
||||||
|
|
||||||
for (let i = 0; i < this.tabEvents.length; i++)
|
for (let i = 0; i < this.tabEvents.length; i++)
|
||||||
this.tabbrowser.tabContainer.addEventListener(this.tabEvents[i], this, false);
|
this.tabbrowser.tabContainer.addEventListener(this.tabEvents[i], this, false);
|
||||||
|
@ -451,7 +451,7 @@ TabWindow.prototype = {
|
||||||
newTab: function (tab) {
|
newTab: function (tab) {
|
||||||
let controller = new PreviewController(this, tab);
|
let controller = new PreviewController(this, tab);
|
||||||
// It's OK to add the preview now while the favicon still loads.
|
// 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);
|
AeroPeek.addPreview(controller.preview);
|
||||||
// updateTitleAndTooltip relies on having controller.preview which is lazily resolved.
|
// updateTitleAndTooltip relies on having controller.preview which is lazily resolved.
|
||||||
// Now that we've updated this.previews, it will resolve successfully.
|
// Now that we've updated this.previews, it will resolve successfully.
|
||||||
|
@ -485,10 +485,7 @@ TabWindow.prototype = {
|
||||||
preview.move(null);
|
preview.move(null);
|
||||||
preview.controller.wrappedJSObject.destroy();
|
preview.controller.wrappedJSObject.destroy();
|
||||||
|
|
||||||
// We don't want to splice from the array if the tabs aren't being removed
|
this.previews.delete(tab);
|
||||||
// from the tab bar as well (as is the case when the window closes).
|
|
||||||
if (!this._destroying)
|
|
||||||
this.previews.splice(tab._tPos, 1);
|
|
||||||
AeroPeek.removePreview(preview);
|
AeroPeek.removePreview(preview);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -501,26 +498,31 @@ TabWindow.prototype = {
|
||||||
// Because making a tab visible requires that the tab it is next to be
|
// 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
|
// visible, it is far simpler to unset the 'next' tab and recreate them all
|
||||||
// at once.
|
// at once.
|
||||||
this.previews.forEach(function (preview) {
|
for (let [tab, preview] of this.previews) {
|
||||||
preview.move(null);
|
preview.move(null);
|
||||||
preview.visible = enable;
|
preview.visible = enable;
|
||||||
});
|
}
|
||||||
this.updateTabOrdering();
|
this.updateTabOrdering();
|
||||||
},
|
},
|
||||||
|
|
||||||
previewFromTab: function (tab) {
|
previewFromTab: function (tab) {
|
||||||
return this.previews[tab._tPos];
|
return this.previews.get(tab);
|
||||||
},
|
},
|
||||||
|
|
||||||
updateTabOrdering: function () {
|
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
|
// 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
|
// 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
|
// the local array backwards, otherwise we would send move requests in the
|
||||||
// wrong order. See bug 522610 for details.
|
// wrong order. See bug 522610 for details.
|
||||||
for (let i = this.previews.length - 1; i >= 0; i--) {
|
for (let i = inorder.length - 1; i >= 0; i--) {
|
||||||
let p = this.previews[i];
|
inorder[i].move(inorder[i + 1] || null);
|
||||||
let next = i == this.previews.length - 1 ? null : this.previews[i+1];
|
|
||||||
p.move(next);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -540,11 +542,6 @@ TabWindow.prototype = {
|
||||||
this.previewFromTab(tab).active = true;
|
this.previewFromTab(tab).active = true;
|
||||||
break;
|
break;
|
||||||
case "TabMove":
|
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();
|
this.updateTabOrdering();
|
||||||
break;
|
break;
|
||||||
case "tabviewshown":
|
case "tabviewshown":
|
||||||
|
@ -564,8 +561,10 @@ TabWindow.prototype = {
|
||||||
getFaviconAsImage(aIconURL, PrivateBrowsingUtils.isWindowPrivate(this.win), function (img) {
|
getFaviconAsImage(aIconURL, PrivateBrowsingUtils.isWindowPrivate(this.win), function (img) {
|
||||||
let index = self.tabbrowser.browsers.indexOf(aBrowser);
|
let index = self.tabbrowser.browsers.indexOf(aBrowser);
|
||||||
// Only add it if we've found the index. The tab could have closed!
|
// Only add it if we've found the index. The tab could have closed!
|
||||||
if (index != -1)
|
if (index != -1) {
|
||||||
self.previews[index].icon = img;
|
let tab = self.tabbrowser.tabs[index];
|
||||||
|
self.previews.get(tab).icon = img;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2938,6 +2938,7 @@ void HTMLMediaElement::SeekCompleted()
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("seeked"));
|
DispatchAsyncEvent(NS_LITERAL_STRING("seeked"));
|
||||||
// We changed whether we're seeking so we need to AddRemoveSelfReference
|
// We changed whether we're seeking so we need to AddRemoveSelfReference
|
||||||
AddRemoveSelfReference();
|
AddRemoveSelfReference();
|
||||||
|
mTextTracks->DidSeek();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLMediaElement::NotifySuspendedByCache(bool aIsSuspended)
|
void HTMLMediaElement::NotifySuspendedByCache(bool aIsSuspended)
|
||||||
|
@ -3866,7 +3867,7 @@ HTMLMediaElement::AddTextTrack(TextTrackKind aKind,
|
||||||
const nsAString& aLabel,
|
const nsAString& aLabel,
|
||||||
const nsAString& aLanguage)
|
const nsAString& aLanguage)
|
||||||
{
|
{
|
||||||
return mTextTracks->AddTextTrack(aKind, aLabel, aLanguage);
|
return mTextTracks->AddTextTrack(this, aKind, aLabel, aLanguage);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
|
@ -126,7 +126,7 @@ HTMLTrackElement::Track()
|
||||||
if (!mTrack) {
|
if (!mTrack) {
|
||||||
// We're expected to always have an internal TextTrack so create
|
// We're expected to always have an internal TextTrack so create
|
||||||
// an empty object to return if we don't already have one.
|
// 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;
|
return mTrack;
|
||||||
|
@ -146,7 +146,8 @@ HTMLTrackElement::CreateTextTrack()
|
||||||
kind = TextTrackKind::Subtitles;
|
kind = TextTrackKind::Subtitles;
|
||||||
}
|
}
|
||||||
|
|
||||||
mTrack = new TextTrack(OwnerDoc()->GetParentObject(), kind, label, srcLang);
|
mTrack = new TextTrack(OwnerDoc()->GetParentObject(), mMediaParent, kind,
|
||||||
|
label, srcLang);
|
||||||
|
|
||||||
if (mMediaParent) {
|
if (mMediaParent) {
|
||||||
mMediaParent->AddTextTrack(mTrack);
|
mMediaParent->AddTextTrack(mTrack);
|
||||||
|
|
|
@ -6,16 +6,19 @@
|
||||||
|
|
||||||
#include "mozilla/dom/TextTrack.h"
|
#include "mozilla/dom/TextTrack.h"
|
||||||
#include "mozilla/dom/TextTrackBinding.h"
|
#include "mozilla/dom/TextTrackBinding.h"
|
||||||
|
#include "mozilla/dom/TextTrackCue.h"
|
||||||
#include "mozilla/dom/TextTrackCueList.h"
|
#include "mozilla/dom/TextTrackCueList.h"
|
||||||
#include "mozilla/dom/TextTrackRegion.h"
|
#include "mozilla/dom/TextTrackRegion.h"
|
||||||
#include "mozilla/dom/TextTrackRegionList.h"
|
#include "mozilla/dom/TextTrackRegionList.h"
|
||||||
|
#include "mozilla/dom/HTMLMediaElement.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_INHERITED_4(TextTrack,
|
NS_IMPL_CYCLE_COLLECTION_INHERITED_5(TextTrack,
|
||||||
nsDOMEventTargetHelper,
|
nsDOMEventTargetHelper,
|
||||||
mParent,
|
mParent,
|
||||||
|
mMediaElement,
|
||||||
mCueList,
|
mCueList,
|
||||||
mActiveCueList,
|
mActiveCueList,
|
||||||
mRegionList)
|
mRegionList)
|
||||||
|
@ -25,31 +28,46 @@ NS_IMPL_RELEASE_INHERITED(TextTrack, nsDOMEventTargetHelper)
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TextTrack)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TextTrack)
|
||||||
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
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,
|
TextTrack::TextTrack(nsISupports* aParent,
|
||||||
|
HTMLMediaElement* aMediaElement,
|
||||||
TextTrackKind aKind,
|
TextTrackKind aKind,
|
||||||
const nsAString& aLabel,
|
const nsAString& aLabel,
|
||||||
const nsAString& aLanguage)
|
const nsAString& aLanguage)
|
||||||
: mParent(aParent)
|
: mParent(aParent)
|
||||||
, mKind(aKind)
|
, mMediaElement(aMediaElement)
|
||||||
, mLabel(aLabel)
|
|
||||||
, mLanguage(aLanguage)
|
|
||||||
, mMode(TextTrackMode::Hidden)
|
|
||||||
, mCueList(new TextTrackCueList(aParent))
|
|
||||||
, mActiveCueList(new TextTrackCueList(aParent))
|
|
||||||
, mRegionList(new TextTrackRegionList(aParent))
|
|
||||||
{
|
{
|
||||||
|
SetDefaultSettings();
|
||||||
|
mKind = aKind;
|
||||||
|
mLabel = aLabel;
|
||||||
|
mLanguage = aLanguage;
|
||||||
SetIsDOMBinding();
|
SetIsDOMBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
TextTrack::TextTrack(nsISupports* aParent)
|
void
|
||||||
: mParent(aParent)
|
TextTrack::SetDefaultSettings()
|
||||||
, mKind(TextTrackKind::Subtitles)
|
|
||||||
, mMode(TextTrackMode::Disabled)
|
|
||||||
, mCueList(new TextTrackCueList(aParent))
|
|
||||||
, mActiveCueList(new TextTrackCueList(aParent))
|
|
||||||
, mRegionList(new TextTrackRegionList(aParent))
|
|
||||||
{
|
{
|
||||||
SetIsDOMBinding();
|
mKind = TextTrackKind::Subtitles;
|
||||||
|
mMode = TextTrackMode::Hidden;
|
||||||
|
mCueList = new TextTrackCueList(mParent);
|
||||||
|
mActiveCueList = new TextTrackCueList(mParent);
|
||||||
|
mRegionList = new TextTrackRegionList(mParent);
|
||||||
|
mCuePos = 0;
|
||||||
|
mDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -74,12 +92,14 @@ void
|
||||||
TextTrack::AddCue(TextTrackCue& aCue)
|
TextTrack::AddCue(TextTrackCue& aCue)
|
||||||
{
|
{
|
||||||
mCueList->AddCue(aCue);
|
mCueList->AddCue(aCue);
|
||||||
|
SetDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv)
|
TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv)
|
||||||
{
|
{
|
||||||
mCueList->RemoveCue(aCue, aRv);
|
mCueList->RemoveCue(aCue, aRv);
|
||||||
|
SetDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -111,5 +131,43 @@ TextTrack::RemoveRegion(const TextTrackRegion& aRegion, ErrorResult& aRv)
|
||||||
mRegionList->RemoveTextTrackRegion(aRegion);
|
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 dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
@ -20,6 +20,7 @@ class TextTrackCue;
|
||||||
class TextTrackCueList;
|
class TextTrackCueList;
|
||||||
class TextTrackRegion;
|
class TextTrackRegion;
|
||||||
class TextTrackRegionList;
|
class TextTrackRegionList;
|
||||||
|
class HTMLMediaElement;
|
||||||
|
|
||||||
class TextTrack MOZ_FINAL : public nsDOMEventTargetHelper
|
class TextTrack MOZ_FINAL : public nsDOMEventTargetHelper
|
||||||
{
|
{
|
||||||
|
@ -29,10 +30,15 @@ public:
|
||||||
|
|
||||||
TextTrack(nsISupports* aParent);
|
TextTrack(nsISupports* aParent);
|
||||||
TextTrack(nsISupports* aParent,
|
TextTrack(nsISupports* aParent,
|
||||||
|
HTMLMediaElement* aMediaElement);
|
||||||
|
TextTrack(nsISupports* aParent,
|
||||||
|
HTMLMediaElement* aMediaElement,
|
||||||
TextTrackKind aKind,
|
TextTrackKind aKind,
|
||||||
const nsAString& aLabel,
|
const nsAString& aLabel,
|
||||||
const nsAString& aLanguage);
|
const nsAString& aLanguage);
|
||||||
|
|
||||||
|
void SetDefaultSettings();
|
||||||
|
|
||||||
virtual JSObject* WrapObject(JSContext* aCx,
|
virtual JSObject* WrapObject(JSContext* aCx,
|
||||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
@ -76,13 +82,7 @@ public:
|
||||||
return mCueList;
|
return mCueList;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextTrackCueList* GetActiveCues() const
|
TextTrackCueList* GetActiveCues();
|
||||||
{
|
|
||||||
if (mMode == TextTrackMode::Disabled) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return mActiveCueList;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextTrackRegionList* GetRegions() const
|
TextTrackRegionList* GetRegions() const
|
||||||
{
|
{
|
||||||
|
@ -101,11 +101,13 @@ public:
|
||||||
void AddCue(TextTrackCue& aCue);
|
void AddCue(TextTrackCue& aCue);
|
||||||
void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv);
|
void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv);
|
||||||
void CueChanged(TextTrackCue& aCue);
|
void CueChanged(TextTrackCue& aCue);
|
||||||
|
void SetDirty() { mDirty = true; }
|
||||||
|
|
||||||
IMPL_EVENT_HANDLER(cuechange)
|
IMPL_EVENT_HANDLER(cuechange)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nsCOMPtr<nsISupports> mParent;
|
nsCOMPtr<nsISupports> mParent;
|
||||||
|
nsRefPtr<HTMLMediaElement> mMediaElement;
|
||||||
|
|
||||||
TextTrackKind mKind;
|
TextTrackKind mKind;
|
||||||
nsString mLabel;
|
nsString mLabel;
|
||||||
|
@ -117,6 +119,9 @@ private:
|
||||||
nsRefPtr<TextTrackCueList> mCueList;
|
nsRefPtr<TextTrackCueList> mCueList;
|
||||||
nsRefPtr<TextTrackCueList> mActiveCueList;
|
nsRefPtr<TextTrackCueList> mActiveCueList;
|
||||||
nsRefPtr<TextTrackRegionList> mRegionList;
|
nsRefPtr<TextTrackRegionList> mRegionList;
|
||||||
|
|
||||||
|
uint32_t mCuePos;
|
||||||
|
bool mDirty;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
|
@ -10,6 +10,20 @@
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
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_COLLECTION_WRAPPERCACHE_2(TextTrackCueList, mParent, mList)
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TextTrackCueList)
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(TextTrackCueList)
|
||||||
|
@ -48,6 +62,12 @@ TextTrackCueList::IndexedGetter(uint32_t aIndex, bool& aFound)
|
||||||
return aFound ? mList[aIndex] : nullptr;
|
return aFound ? mList[aIndex] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextTrackCue*
|
||||||
|
TextTrackCueList::operator[](uint32_t aIndex)
|
||||||
|
{
|
||||||
|
return mList.SafeElementAt(aIndex, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
TextTrackCue*
|
TextTrackCue*
|
||||||
TextTrackCueList::GetCueById(const nsAString& aId)
|
TextTrackCueList::GetCueById(const nsAString& aId)
|
||||||
{
|
{
|
||||||
|
@ -64,22 +84,36 @@ TextTrackCueList::GetCueById(const nsAString& aId)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TextTrackCueList::AddCue(TextTrackCue& cue)
|
TextTrackCueList::AddCue(TextTrackCue& aCue)
|
||||||
{
|
{
|
||||||
if (mList.Contains(&cue)) {
|
if (mList.Contains(&aCue)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mList.AppendElement(&cue);
|
mList.InsertElementSorted(&aCue, CompareCuesByTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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);
|
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||||
return;
|
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
|
} // namespace dom
|
||||||
|
|
|
@ -45,14 +45,23 @@ public:
|
||||||
void Update(double aTime);
|
void Update(double aTime);
|
||||||
|
|
||||||
TextTrackCue* IndexedGetter(uint32_t aIndex, bool& aFound);
|
TextTrackCue* IndexedGetter(uint32_t aIndex, bool& aFound);
|
||||||
|
TextTrackCue* operator[](uint32_t aIndex);
|
||||||
TextTrackCue* GetCueById(const nsAString& aId);
|
TextTrackCue* GetCueById(const nsAString& aId);
|
||||||
|
|
||||||
void AddCue(TextTrackCue& cue);
|
// Adds a cue to mList by performing an insertion sort on mList.
|
||||||
void RemoveCue(TextTrackCue& cue, ErrorResult& aRv);
|
// 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:
|
private:
|
||||||
nsCOMPtr<nsISupports> mParent;
|
nsCOMPtr<nsISupports> 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<TextTrackCue> > mList;
|
nsTArray< nsRefPtr<TextTrackCue> > mList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -47,11 +47,13 @@ TextTrackList::IndexedGetter(uint32_t aIndex, bool& aFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<TextTrack>
|
already_AddRefed<TextTrack>
|
||||||
TextTrackList::AddTextTrack(TextTrackKind aKind,
|
TextTrackList::AddTextTrack(HTMLMediaElement* aMediaElement,
|
||||||
|
TextTrackKind aKind,
|
||||||
const nsAString& aLabel,
|
const nsAString& aLabel,
|
||||||
const nsAString& aLanguage)
|
const nsAString& aLanguage)
|
||||||
{
|
{
|
||||||
nsRefPtr<TextTrack> track = new TextTrack(mGlobal, aKind, aLabel, aLanguage);
|
nsRefPtr<TextTrack> track = new TextTrack(mGlobal, aMediaElement, aKind,
|
||||||
|
aLabel, aLanguage);
|
||||||
mTextTracks.AppendElement(track);
|
mTextTracks.AppendElement(track);
|
||||||
// TODO: dispatch addtrack event
|
// TODO: dispatch addtrack event
|
||||||
return track.forget();
|
return track.forget();
|
||||||
|
@ -76,5 +78,13 @@ TextTrackList::RemoveTextTrack(const TextTrack& aTrack)
|
||||||
mTextTracks.RemoveElement(&aTrack);
|
mTextTracks.RemoveElement(&aTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TextTrackList::DidSeek()
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < mTextTracks.Length(); i++) {
|
||||||
|
mTextTracks[i]->SetDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
@ -40,7 +40,8 @@ public:
|
||||||
|
|
||||||
TextTrack* IndexedGetter(uint32_t aIndex, bool& aFound);
|
TextTrack* IndexedGetter(uint32_t aIndex, bool& aFound);
|
||||||
|
|
||||||
already_AddRefed<TextTrack> AddTextTrack(TextTrackKind aKind,
|
already_AddRefed<TextTrack> AddTextTrack(HTMLMediaElement* aMediaElement,
|
||||||
|
TextTrackKind aKind,
|
||||||
const nsAString& aLabel,
|
const nsAString& aLabel,
|
||||||
const nsAString& aLanguage);
|
const nsAString& aLanguage);
|
||||||
TextTrack* GetTrackById(const nsAString& aId);
|
TextTrack* GetTrackById(const nsAString& aId);
|
||||||
|
@ -50,6 +51,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveTextTrack(const TextTrack& aTrack);
|
void RemoveTextTrack(const TextTrack& aTrack);
|
||||||
|
void DidSeek();
|
||||||
|
|
||||||
IMPL_EVENT_HANDLER(addtrack)
|
IMPL_EVENT_HANDLER(addtrack)
|
||||||
IMPL_EVENT_HANDLER(removetrack)
|
IMPL_EVENT_HANDLER(removetrack)
|
||||||
|
|
|
@ -148,6 +148,7 @@ MOCHITEST_FILES = \
|
||||||
test_webvtt_disabled.html \
|
test_webvtt_disabled.html \
|
||||||
test_bug895305.html \
|
test_bug895305.html \
|
||||||
test_bug895091.html \
|
test_bug895091.html \
|
||||||
|
test_bug883173.html \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# Don't run in suite
|
# Don't run in suite
|
||||||
|
@ -275,6 +276,7 @@ MOCHITEST_FILES += \
|
||||||
basic.vtt \
|
basic.vtt \
|
||||||
region.vtt \
|
region.vtt \
|
||||||
long.vtt \
|
long.vtt \
|
||||||
|
bug883173.vtt \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# Wave sample files
|
# Wave sample files
|
||||||
|
|
|
@ -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.
|
|
@ -0,0 +1,58 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=883173
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
<title>Test for Bug 883173 - TextTrackCue(List) Sorting</title>
|
||||||
|
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content">
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
<script class="testbody" type="text/javascript">
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
|
||||||
|
function() {
|
||||||
|
var video = document.createElement("video");
|
||||||
|
video.src = "seek.webm";
|
||||||
|
video.preload = "auto";
|
||||||
|
var trackElement = document.createElement("track");
|
||||||
|
trackElement.src = "bug883173.vtt";
|
||||||
|
trackElement.kind = "subtitles";
|
||||||
|
document.getElementById("content").appendChild(video);
|
||||||
|
video.appendChild(trackElement);
|
||||||
|
video.addEventListener("loadedmetadata",
|
||||||
|
function run_tests() {
|
||||||
|
// Re-queue run_tests() at the end of the event loop until the track
|
||||||
|
// element has loaded its data.
|
||||||
|
if (trackElement.readyState == 1) {
|
||||||
|
setTimeout(run_tests, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
|
||||||
|
|
||||||
|
var expected = [[1, 2], [1, 3], [2, 3], [2, 4], [3, 4]];
|
||||||
|
var cueList = trackElement.track.cues;
|
||||||
|
|
||||||
|
is(cueList.length, expected.length, "Cue list length should be 5.");
|
||||||
|
for (var i = 0; i < expected.length; i++) {
|
||||||
|
is(cueList[i].startTime, expected[i][0], "Cue's start time should be " + expected[i][0]);
|
||||||
|
is(cueList[i].endTime, expected[i][1], "Cue's end time should be " + expected[i][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -16,6 +16,7 @@ MOCHITEST_FILES = \
|
||||||
test_packaged_app_update.html \
|
test_packaged_app_update.html \
|
||||||
test_packaged_app_common.js \
|
test_packaged_app_common.js \
|
||||||
test_uninstall_errors.html \
|
test_uninstall_errors.html \
|
||||||
|
test_bug_795164.html \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
MOCHITEST_CHROME_FILES = \
|
MOCHITEST_CHROME_FILES = \
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=795164
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for Bug 795164</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
<script type="application/javascript;version=1.7">
|
||||||
|
|
||||||
|
/** Test for Bug 795164 **/
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
|
var url1 = 'http://test1.example.com/tests/dom/apps/tests/file_app.sjs?apptype=hosted&getmanifest=true';
|
||||||
|
var url2 = 'http://test2.example.com/tests/dom/apps/tests/file_app.sjs?apptype=hosted&getmanifest=true';
|
||||||
|
|
||||||
|
var gGenerator = runTest();
|
||||||
|
var launchableValue = undefined;
|
||||||
|
|
||||||
|
function go() {
|
||||||
|
SpecialPowers.pushPermissions(
|
||||||
|
[{ "type": "webapps-manage", "allow": 1, "context": document }],
|
||||||
|
function() { gGenerator.next() });
|
||||||
|
}
|
||||||
|
|
||||||
|
function continueTest() {
|
||||||
|
try { gGenerator.next(); }
|
||||||
|
catch (e) { dump("Got exception: " + e + "\n"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function mozAppsError() {
|
||||||
|
ok(false, "mozApps error: " + this.error.name);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTest() {
|
||||||
|
// Set up.
|
||||||
|
launchableValue = SpecialPowers.setAllAppsLaunchable(true);
|
||||||
|
SpecialPowers.autoConfirmAppInstall(continueTest);
|
||||||
|
yield undefined;
|
||||||
|
|
||||||
|
// Keeping track of the number of times `mozApps.mgmt.onuninstall` gets triggered
|
||||||
|
let uninstallCount = 0;
|
||||||
|
|
||||||
|
navigator.mozApps.mgmt.onuninstall = function() {
|
||||||
|
uninstallCount++;
|
||||||
|
};
|
||||||
|
|
||||||
|
let request = navigator.mozApps.install(url1);
|
||||||
|
request.onerror = mozAppsError;
|
||||||
|
request.onsuccess = continueTest;
|
||||||
|
yield undefined;
|
||||||
|
let app1 = request.result;
|
||||||
|
ok(app1, "App1 is non-null");
|
||||||
|
|
||||||
|
request = navigator.mozApps.install(url2);
|
||||||
|
request.onerror = mozAppsError;
|
||||||
|
request.onsuccess = continueTest;
|
||||||
|
yield undefined;
|
||||||
|
let app2 = request.result;
|
||||||
|
ok(app2, "App2 is non-null");
|
||||||
|
|
||||||
|
request = navigator.mozApps.mgmt.uninstall(app1);
|
||||||
|
request.onsuccess = function() {
|
||||||
|
ok(true, "Succeed to uninstall the app1 as expected");
|
||||||
|
continueTest();
|
||||||
|
};
|
||||||
|
request.onerror = function() {
|
||||||
|
ok(false, "Fail to uninstall the app1");
|
||||||
|
continueTest();
|
||||||
|
};
|
||||||
|
yield undefined;
|
||||||
|
|
||||||
|
request = navigator.mozApps.mgmt.uninstall(app2);
|
||||||
|
request.onsuccess = function() {
|
||||||
|
ok(true, "Succeed to uninstall the app2 as expected");
|
||||||
|
continueTest();
|
||||||
|
};
|
||||||
|
request.onerror = function() {
|
||||||
|
ok(false, "Fail to uninstall the app2");
|
||||||
|
continueTest();
|
||||||
|
};
|
||||||
|
yield undefined;
|
||||||
|
|
||||||
|
is(uninstallCount, 2, "mgmt.onuninstall got triggered only twice");
|
||||||
|
|
||||||
|
navigator.mozApps.mgmt.onuninstall = null;
|
||||||
|
|
||||||
|
// All done.
|
||||||
|
ok(true, "All done");
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finish() {
|
||||||
|
SpecialPowers.setAllAppsLaunchable(launchableValue);
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="go()">
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=795164">Mozilla Bug 795164</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content" style="display: none">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -78,7 +78,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=830258
|
||||||
};
|
};
|
||||||
yield undefined;
|
yield undefined;
|
||||||
|
|
||||||
var request = navigator.mozApps.mgmt.uninstall(app2);
|
request = navigator.mozApps.mgmt.uninstall(app2);
|
||||||
request.onsuccess = function() {
|
request.onsuccess = function() {
|
||||||
ok(true, "Succeed to uninstall the app2 as expected");
|
ok(true, "Succeed to uninstall the app2 as expected");
|
||||||
continueTest();
|
continueTest();
|
||||||
|
|
|
@ -448,8 +448,12 @@ BrowserElementChild.prototype = {
|
||||||
|
|
||||||
_iconChangedHandler: function(e) {
|
_iconChangedHandler: function(e) {
|
||||||
debug('Got iconchanged: (' + e.target.href + ')');
|
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) {
|
_openSearchHandler: function(e) {
|
||||||
|
|
|
@ -12,8 +12,9 @@ function createHtml(link) {
|
||||||
return 'data:text/html,<html><head>' + link + '<body></body></html>';
|
return 'data:text/html,<html><head>' + link + '<body></body></html>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLink(name) {
|
function createLink(name, sizes) {
|
||||||
return '<link rel="icon" type="image/png" href="http://example.com/' + name + '.png">';
|
var s = sizes ? 'sizes="' + sizes + '"' : '';
|
||||||
|
return '<link rel="icon" type="image/png" ' + s + ' href="http://example.com/' + name + '.png">';
|
||||||
}
|
}
|
||||||
|
|
||||||
function runTest() {
|
function runTest() {
|
||||||
|
@ -40,7 +41,7 @@ function runTest() {
|
||||||
numIconChanges++;
|
numIconChanges++;
|
||||||
|
|
||||||
if (numIconChanges == 1) {
|
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
|
// We should recieve iconchange events when the user creates new links
|
||||||
// to a favicon, but only when we listen for them
|
// to a favicon, but only when we listen for them
|
||||||
|
@ -57,13 +58,13 @@ function runTest() {
|
||||||
/* allowDelayedLoad = */ false);
|
/* allowDelayedLoad = */ false);
|
||||||
}
|
}
|
||||||
else if (numIconChanges == 2) {
|
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
|
// Full new pages should trigger iconchange events
|
||||||
iframe1.src = createHtml(createLink('3rdicon'));
|
iframe1.src = createHtml(createLink('3rdicon'));
|
||||||
}
|
}
|
||||||
else if (numIconChanges == 3) {
|
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
|
// the rel attribute can have various space seperated values, make
|
||||||
// sure we only pick up correct values for 'icon'
|
// sure we only pick up correct values for 'icon'
|
||||||
|
@ -74,11 +75,11 @@ function runTest() {
|
||||||
iframe1.src = createHtml(createLink('another') + createLink('icon'));
|
iframe1.src = createHtml(createLink('another') + createLink('icon'));
|
||||||
}
|
}
|
||||||
else if (numIconChanges == 4) {
|
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
|
// 2 events will be triggered by previous test, wait for next
|
||||||
}
|
}
|
||||||
else if (numIconChanges == 5) {
|
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
|
// Make sure icon check is case insensitive
|
||||||
SpecialPowers.getBrowserFrameMessageManager(iframe1)
|
SpecialPowers.getBrowserFrameMessageManager(iframe1)
|
||||||
|
@ -86,7 +87,12 @@ function runTest() {
|
||||||
/* allowDelayedLoad = */ false);
|
/* allowDelayedLoad = */ false);
|
||||||
}
|
}
|
||||||
else if (numIconChanges == 6) {
|
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();
|
SimpleTest.finish();
|
||||||
} else {
|
} else {
|
||||||
ok(false, 'Too many iconchange events.');
|
ok(false, 'Too many iconchange events.');
|
||||||
|
|
|
@ -495,14 +495,17 @@ PrivilegesForApp(mozIApplication* aApp)
|
||||||
ContentParent::GetInitialProcessPriority(Element* aFrameElement)
|
ContentParent::GetInitialProcessPriority(Element* aFrameElement)
|
||||||
{
|
{
|
||||||
// Frames with mozapptype == critical which are expecting a system message
|
// 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) {
|
if (!aFrameElement) {
|
||||||
return PROCESS_PRIORITY_FOREGROUND;
|
return PROCESS_PRIORITY_FOREGROUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
|
if (aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
|
||||||
NS_LITERAL_STRING("critical"), eCaseMatters)) {
|
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;
|
return PROCESS_PRIORITY_FOREGROUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -855,7 +855,9 @@ ParticularProcessPriorityManager::ComputePriority()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
return PROCESS_PRIORITY_FOREGROUND;
|
return HasAppType("keyboard") ?
|
||||||
|
PROCESS_PRIORITY_FOREGROUND_KEYBOARD :
|
||||||
|
PROCESS_PRIORITY_FOREGROUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
|
if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
|
||||||
|
|
|
@ -248,7 +248,9 @@ XPCOMUtils.defineLazyGetter(this, "gMmsConnection", function () {
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
acquire: function acquire(callback) {
|
acquire: function acquire(callback) {
|
||||||
|
this.refCount++;
|
||||||
this.connectTimer.cancel();
|
this.connectTimer.cancel();
|
||||||
|
this.disconnectTimer.cancel();
|
||||||
|
|
||||||
// If the MMS network is not yet connected, buffer the
|
// If the MMS network is not yet connected, buffer the
|
||||||
// MMS request and try to setup the MMS network first.
|
// MMS request and try to setup the MMS network first.
|
||||||
|
@ -280,8 +282,6 @@ XPCOMUtils.defineLazyGetter(this, "gMmsConnection", function () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refCount++;
|
|
||||||
|
|
||||||
callback(true, _HTTP_STATUS_ACQUIRE_CONNECTION_SUCCESS);
|
callback(true, _HTTP_STATUS_ACQUIRE_CONNECTION_SUCCESS);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -554,12 +554,6 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup event listeners
|
// Setup event listeners
|
||||||
xhr.onerror = function () {
|
|
||||||
if (DEBUG) debug("xhr error, response headers: " +
|
|
||||||
xhr.getAllResponseHeaders());
|
|
||||||
releaseMmsConnectionAndCallback(xhr.status, null);
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.onreadystatechange = function () {
|
xhr.onreadystatechange = function () {
|
||||||
if (xhr.readyState != Ci.nsIXMLHttpRequest.DONE) {
|
if (xhr.readyState != Ci.nsIXMLHttpRequest.DONE) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -436,6 +436,20 @@ this.PushService = {
|
||||||
*/
|
*/
|
||||||
_willBeWokenUpByUDP: false,
|
_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() {
|
init: function() {
|
||||||
debug("init()");
|
debug("init()");
|
||||||
if (!prefs.get("enabled"))
|
if (!prefs.get("enabled"))
|
||||||
|
@ -454,8 +468,6 @@ this.PushService = {
|
||||||
|
|
||||||
this._requestTimeout = prefs.get("requestTimeout");
|
this._requestTimeout = prefs.get("requestTimeout");
|
||||||
|
|
||||||
this._udpPort = prefs.get("udp.port");
|
|
||||||
|
|
||||||
this._startListeningIfChannelsPresent();
|
this._startListeningIfChannelsPresent();
|
||||||
|
|
||||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
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
|
// handle the exception, as the lack of a pong will lead to the socket
|
||||||
// being reset.
|
// being reset.
|
||||||
try {
|
try {
|
||||||
this._ws.sendMsg('{}');
|
this._wsSendMessage({});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -949,7 +961,7 @@ this.PushService = {
|
||||||
this._shutdownWS();
|
this._shutdownWS();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._ws.sendMsg(JSON.stringify(data));
|
this._wsSendMessage(data);
|
||||||
// Process the next one as soon as possible.
|
// Process the next one as soon as possible.
|
||||||
setTimeout(this._processNextRequestInQueue.bind(this), 0);
|
setTimeout(this._processNextRequestInQueue.bind(this), 0);
|
||||||
},
|
},
|
||||||
|
@ -1280,6 +1292,10 @@ this.PushService = {
|
||||||
// Since we've had a successful connection reset the retry fail count.
|
// Since we've had a successful connection reset the retry fail count.
|
||||||
this._retryFailCount = 0;
|
this._retryFailCount = 0;
|
||||||
|
|
||||||
|
// Openning an available UDP port.
|
||||||
|
// _listenForUDPWakeup will return the opened port number
|
||||||
|
this._udpPort = this._listenForUDPWakeup();
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
messageType: "hello",
|
messageType: "hello",
|
||||||
}
|
}
|
||||||
|
@ -1305,7 +1321,7 @@ this.PushService = {
|
||||||
// On success, ids is an array, on error its not.
|
// On success, ids is an array, on error its not.
|
||||||
data["channelIDs"] = ids.map ?
|
data["channelIDs"] = ids.map ?
|
||||||
ids.map(function(el) { return el.channelID; }) : [];
|
ids.map(function(el) { return el.channelID; }) : [];
|
||||||
this._ws.sendMsg(JSON.stringify(data));
|
this._wsSendMessage(data);
|
||||||
this._currentState = STATE_WAITING_FOR_HELLO;
|
this._currentState = STATE_WAITING_FOR_HELLO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1400,7 +1416,6 @@ this.PushService = {
|
||||||
debug("Server closed with promise to wake up");
|
debug("Server closed with promise to wake up");
|
||||||
this._willBeWokenUpByUDP = true;
|
this._willBeWokenUpByUDP = true;
|
||||||
// TODO: there should be no pending requests
|
// 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"]
|
this._udpServer = Cc["@mozilla.org/network/server-socket-udp;1"]
|
||||||
.createInstance(Ci.nsIUDPServerSocket);
|
.createInstance(Ci.nsIUDPServerSocket);
|
||||||
this._udpServer.init(this._udpPort, false);
|
this._udpServer.init(-1, false);
|
||||||
this._udpServer.asyncListen(this);
|
this._udpServer.asyncListen(this);
|
||||||
debug("listenForUDPWakeup listening on " + this._udpPort);
|
debug("listenForUDPWakeup listening on " + this._udpPort);
|
||||||
|
|
||||||
|
return this._udpServer.port;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1446,6 +1463,7 @@ this.PushService = {
|
||||||
*/
|
*/
|
||||||
onStopListening: function(aServ, aStatus) {
|
onStopListening: function(aServ, aStatus) {
|
||||||
debug("UDP Server socket was shutdown. Status: " + aStatus);
|
debug("UDP Server socket was shutdown. Status: " + aStatus);
|
||||||
|
this._udpPort = undefined;
|
||||||
this._beginWSSetup();
|
this._beginWSSetup();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ MOCHITEST_CHROME_FILES = \
|
||||||
cross_origin.html \
|
cross_origin.html \
|
||||||
head.js \
|
head.js \
|
||||||
test_bug_765063.xul \
|
test_bug_765063.xul \
|
||||||
test_bug_795164.xul \
|
|
||||||
test_bug_771294.xul \
|
test_bug_771294.xul \
|
||||||
test_cross_origin.xul \
|
test_cross_origin.xul \
|
||||||
test_install_app.xul \
|
test_install_app.xul \
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
|
|
||||||
<!-- Any copyright is dedicated to the Public Domain.
|
|
||||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
|
||||||
|
|
||||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
|
||||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
|
||||||
|
|
||||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
||||||
title="Mozilla Bug 795164">
|
|
||||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
|
||||||
<script type="application/javascript" src="head.js"/>
|
|
||||||
<!-- test results are displayed in the html:body -->
|
|
||||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=741549"
|
|
||||||
target="_blank">Mozilla Bug 795164</a>
|
|
||||||
</body>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
var url1 = "http://test1.example.com/chrome/dom/tests/mochitest/webapps/apps/basic.webapp";
|
|
||||||
var url2 = "http://test2.example.com/chrome/dom/tests/mochitest/webapps/apps/basic.webapp";
|
|
||||||
|
|
||||||
var app1, app2;
|
|
||||||
|
|
||||||
// Keeping track of the number of times `mozApps.mgmt.onuninstall` gets triggered
|
|
||||||
var uninstallCount = 0;
|
|
||||||
|
|
||||||
var steps = [
|
|
||||||
listenToUninstall,
|
|
||||||
installTwoApps,
|
|
||||||
uninstallTwoApps,
|
|
||||||
checkUninstallCount,
|
|
||||||
cleanUp
|
|
||||||
];
|
|
||||||
|
|
||||||
runAll(steps);
|
|
||||||
|
|
||||||
function listenToUninstall(next) {
|
|
||||||
navigator.mozApps.mgmt.onuninstall = function onUnInstall() {
|
|
||||||
uninstallCount++;
|
|
||||||
};
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
function installTwoApps(next) {
|
|
||||||
confirmNextInstall();
|
|
||||||
navigator.mozApps.install(url1, null).onsuccess = function onInstall(evt) {
|
|
||||||
app1 = evt.target.result;
|
|
||||||
confirmNextInstall();
|
|
||||||
navigator.mozApps.install(url2, null).onsuccess = function onInstall(evt) {
|
|
||||||
app2 = evt.target.result;
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function uninstallTwoApps(next) {
|
|
||||||
navigator.mozApps.mgmt.uninstall(app1).onsuccess = function onUninstallApp1() {
|
|
||||||
navigator.mozApps.mgmt.uninstall(app2).onsuccess = function onUninstallApp2() {
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkUninstallCount(next) {
|
|
||||||
is(uninstallCount, 2, "mgmt.onuninstall got triggered only twice");
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanUp(next) {
|
|
||||||
navigator.mozApps.mgmt.onuninstall = null;
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</window>
|
|
|
@ -868,6 +868,8 @@ ProcessPriorityToString(ProcessPriority aPriority)
|
||||||
return "FOREGROUND_HIGH";
|
return "FOREGROUND_HIGH";
|
||||||
case PROCESS_PRIORITY_FOREGROUND:
|
case PROCESS_PRIORITY_FOREGROUND:
|
||||||
return "FOREGROUND";
|
return "FOREGROUND";
|
||||||
|
case PROCESS_PRIORITY_FOREGROUND_KEYBOARD:
|
||||||
|
return "FOREGROUND_KEYBOARD";
|
||||||
case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
|
case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
|
||||||
return "BACKGROUND_PERCEIVABLE";
|
return "BACKGROUND_PERCEIVABLE";
|
||||||
case PROCESS_PRIORITY_BACKGROUND_HOMESCREEN:
|
case PROCESS_PRIORITY_BACKGROUND_HOMESCREEN:
|
||||||
|
@ -914,6 +916,13 @@ ProcessPriorityToString(ProcessPriority aPriority,
|
||||||
if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
|
if (aCPUPriority == PROCESS_CPU_PRIORITY_LOW) {
|
||||||
return "FOREGROUND:CPU_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:
|
case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
|
||||||
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
|
if (aCPUPriority == PROCESS_CPU_PRIORITY_NORMAL) {
|
||||||
return "BACKGROUND_PERCEIVABLE:CPU_NORMAL";
|
return "BACKGROUND_PERCEIVABLE:CPU_NORMAL";
|
||||||
|
|
|
@ -83,6 +83,7 @@ enum ProcessPriority {
|
||||||
PROCESS_PRIORITY_BACKGROUND,
|
PROCESS_PRIORITY_BACKGROUND,
|
||||||
PROCESS_PRIORITY_BACKGROUND_HOMESCREEN,
|
PROCESS_PRIORITY_BACKGROUND_HOMESCREEN,
|
||||||
PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE,
|
PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE,
|
||||||
|
PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
|
||||||
// Any priority greater than or equal to FOREGROUND is considered
|
// Any priority greater than or equal to FOREGROUND is considered
|
||||||
// "foreground" for the purposes of priority testing, for example
|
// "foreground" for the purposes of priority testing, for example
|
||||||
// CurrentProcessIsForeground().
|
// CurrentProcessIsForeground().
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.mozilla.gecko;
|
||||||
import org.mozilla.gecko.background.announcements.AnnouncementsConstants;
|
import org.mozilla.gecko.background.announcements.AnnouncementsConstants;
|
||||||
import org.mozilla.gecko.background.common.GlobalConstants;
|
import org.mozilla.gecko.background.common.GlobalConstants;
|
||||||
import org.mozilla.gecko.background.healthreport.HealthReportConstants;
|
import org.mozilla.gecko.background.healthreport.HealthReportConstants;
|
||||||
|
import org.mozilla.gecko.preferences.SearchEnginePreference;
|
||||||
import org.mozilla.gecko.util.GeckoEventListener;
|
import org.mozilla.gecko.util.GeckoEventListener;
|
||||||
import org.mozilla.gecko.GeckoPreferenceFragment;
|
import org.mozilla.gecko.GeckoPreferenceFragment;
|
||||||
import org.mozilla.gecko.util.ThreadUtils;
|
import org.mozilla.gecko.util.ThreadUtils;
|
||||||
|
@ -44,8 +45,11 @@ import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ListAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -87,9 +91,15 @@ public class GeckoPreferences
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
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 &&
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB &&
|
||||||
!getIntent().hasExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT)) {
|
!getIntent().hasExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT)) {
|
||||||
|
// Set up the default fragment if there is no explicit fragment to show.
|
||||||
setupTopLevelFragmentIntent();
|
setupTopLevelFragmentIntent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +107,10 @@ public class GeckoPreferences
|
||||||
|
|
||||||
// Use setResourceToOpen to specify these extras.
|
// Use setResourceToOpen to specify these extras.
|
||||||
Bundle intentExtras = getIntent().getExtras();
|
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) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||||
int res = 0;
|
int res = 0;
|
||||||
if (intentExtras != null && intentExtras.containsKey(INTENT_EXTRA_RESOURCES)) {
|
if (intentExtras != null && intentExtras.containsKey(INTENT_EXTRA_RESOURCES)) {
|
||||||
|
@ -119,6 +133,25 @@ public class GeckoPreferences
|
||||||
|
|
||||||
registerEventListener("Sanitize:Finished");
|
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)
|
if (Build.VERSION.SDK_INT >= 14)
|
||||||
getActionBar().setHomeButtonEnabled(true);
|
getActionBar().setHomeButtonEnabled(true);
|
||||||
|
|
||||||
|
|
|
@ -232,12 +232,13 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Cursor getTopSites(ContentResolver cr, int limit) {
|
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 " +
|
String selection = DBUtils.concatenateWhere("", Combined.URL + " NOT IN (SELECT " +
|
||||||
Bookmarks.URL + " FROM bookmarks WHERE " +
|
Bookmarks.URL + " FROM bookmarks WHERE " +
|
||||||
DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " == ? AND " +
|
DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " < ? AND " +
|
||||||
DBUtils.qualifyColumn("bookmarks", Bookmarks.IS_DELETED) + " == 0)");
|
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,
|
return filterAllSites(cr,
|
||||||
new String[] { Combined._ID,
|
new String[] { Combined._ID,
|
||||||
Combined.URL,
|
Combined.URL,
|
||||||
|
|
|
@ -16,8 +16,10 @@ import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import org.mozilla.gecko.R;
|
import org.mozilla.gecko.R;
|
||||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||||
import org.mozilla.gecko.util.ThreadUtils;
|
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.
|
* 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";
|
private static final String LOGTAG = "SearchEnginePreference";
|
||||||
|
|
||||||
// Indices in button array of the AlertDialog of the three buttons.
|
// 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());
|
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.
|
* Configure this Preference object from the Gecko search engine JSON object.
|
||||||
* @param geckoEngineJSON The Gecko-formatted JSON object representing the search engine.
|
* @param geckoEngineJSON The Gecko-formatted JSON object representing the search engine.
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
package @ANDROID_PACKAGE_NAME@.tests;
|
package @ANDROID_PACKAGE_NAME@.tests;
|
||||||
|
|
||||||
import @ANDROID_PACKAGE_NAME@.*;
|
import @ANDROID_PACKAGE_NAME@.*;
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
abstract class PixelTest extends BaseTest {
|
abstract class PixelTest extends BaseTest {
|
||||||
private static final long PAINT_CLEAR_DELAY = 10000; // milliseconds
|
private static final long PAINT_CLEAR_DELAY = 10000; // milliseconds
|
||||||
|
@ -10,11 +9,7 @@ abstract class PixelTest extends BaseTest {
|
||||||
protected final PaintedSurface loadAndGetPainted(String url) {
|
protected final PaintedSurface loadAndGetPainted(String url) {
|
||||||
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();
|
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();
|
||||||
loadUrl(url);
|
loadUrl(url);
|
||||||
// Skip this on the Tegras (Android 2.2) since they are too slow,
|
verifyHomePagerHidden();
|
||||||
// which results in too many intermittent failures.
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
|
|
||||||
verifyHomePagerHidden();
|
|
||||||
}
|
|
||||||
paintExpecter.blockUntilClear(PAINT_CLEAR_DELAY);
|
paintExpecter.blockUntilClear(PAINT_CLEAR_DELAY);
|
||||||
paintExpecter.unregisterListener();
|
paintExpecter.unregisterListener();
|
||||||
PaintedSurface p = mDriver.getPaintedSurface();
|
PaintedSurface p = mDriver.getPaintedSurface();
|
||||||
|
|
|
@ -12,384 +12,379 @@ if (this.Components) {
|
||||||
(function(exports) {
|
(function(exports) {
|
||||||
"use strict";
|
"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 {
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// Now, post a reply, possibly as an uncaught error.
|
||||||
* Communications with the controller.
|
// We post this message from outside the |try ... catch| block
|
||||||
*
|
// to avoid capturing errors that take place during |postMessage| and
|
||||||
* Accepts messages:
|
// built-in serialization.
|
||||||
* {fun:function_name, args:array_of_arguments_or_null, id:id}
|
if (!exn) {
|
||||||
*
|
LOG("Sending positive reply", result, "id is", id);
|
||||||
* Sends messages:
|
if (result instanceof Transfer) {
|
||||||
* {ok: result, id:id} / {fail: serialized_form_of_OS.File.Error, id:id}
|
// Take advantage of zero-copy transfers
|
||||||
*/
|
self.postMessage({ok: result.data, id: id, durationMs: durationMs},
|
||||||
self.onmessage = function onmessage(msg) {
|
result.transfers);
|
||||||
let data = msg.data;
|
} else {
|
||||||
LOG("Received message", data);
|
self.postMessage({ok: result, id:id, durationMs: durationMs});
|
||||||
let id = data.id;
|
}
|
||||||
|
} 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;
|
* A data structure used to track opened resources
|
||||||
if (data.args) {
|
*/
|
||||||
options = data.args[data.args.length - 1];
|
let ResourceTracker = function ResourceTracker() {
|
||||||
}
|
// A number used to generate ids
|
||||||
// If |outExecutionDuration| option was supplied, start measuring the
|
this._idgen = 0;
|
||||||
// duration of the operation.
|
// A map from id to resource
|
||||||
if (options && typeof options === "object" && "outExecutionDuration" in options) {
|
this._map = new Map();
|
||||||
start = Date.now();
|
};
|
||||||
}
|
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;
|
* A map of unique identifiers to opened files.
|
||||||
let durationMs;
|
*/
|
||||||
try {
|
let OpenedFiles = new ResourceTracker();
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start) {
|
/**
|
||||||
// Record duration
|
* Execute a function in the context of a given file.
|
||||||
durationMs = Date.now() - start;
|
*
|
||||||
LOG("Method took", durationMs, "ms");
|
* @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.
|
let OpenedDirectoryIterators = new ResourceTracker();
|
||||||
// We post this message from outside the |try ... catch| block
|
let withDir = function withDir(fd, f, ignoreAbsent) {
|
||||||
// to avoid capturing errors that take place during |postMessage| and
|
let file = OpenedDirectoryIterators.get(fd);
|
||||||
// built-in serialization.
|
if (file == null) {
|
||||||
if (!exn) {
|
if (!ignoreAbsent) {
|
||||||
LOG("Sending positive reply", result, "id is", id);
|
throw OS.File.Error.closed("accessing directory");
|
||||||
if (result instanceof Transfer) {
|
}
|
||||||
// Take advantage of zero-copy transfers
|
return undefined;
|
||||||
self.postMessage({ok: result.data, id: id, durationMs: durationMs},
|
}
|
||||||
result.transfers);
|
if (!(file instanceof File.DirectoryIterator)) {
|
||||||
} else {
|
throw new Error("file is not a directory iterator " + file.__proto__.toSource());
|
||||||
self.postMessage({ok: result, id:id, durationMs: durationMs});
|
}
|
||||||
|
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");
|
File_prototype_stat: function stat(fd) {
|
||||||
self.postMessage({StopIteration: true, id: id, durationMs: durationMs});
|
return withFile(fd,
|
||||||
} else if (exn instanceof exports.OS.File.Error) {
|
function do_stat() {
|
||||||
LOG("Sending back OS.File error", exn, "id is", id);
|
return exports.OS.File.Info.toMsg(this.stat());
|
||||||
// Instances of OS.File.Error know how to serialize themselves
|
});
|
||||||
// (deserialization ensures that we end up with OS-specific
|
},
|
||||||
// instances of |OS.File.Error|)
|
File_prototype_read: function read(fd, nbytes, options) {
|
||||||
self.postMessage({fail: exports.OS.File.Error.toMsg(exn), id:id, durationMs: durationMs});
|
return withFile(fd,
|
||||||
} else {
|
function do_read() {
|
||||||
LOG("Sending back regular error", exn, exn.stack, "id is", id);
|
let data = this.read(nbytes, options);
|
||||||
// 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);
|
|
||||||
return new Transfer({buffer: data.buffer, byteOffset: data.byteOffset, byteLength: data.byteLength}, [data.buffer]);
|
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");
|
File_prototype_readTo: function readTo(fd, buffer, options) {
|
||||||
dump("WORKER ERROR DETAIL " + ("moduleStack" in ex?ex.moduleStack:ex.stack) + "\n");
|
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);
|
})(this);
|
||||||
|
|
|
@ -7,6 +7,4 @@ tail =
|
||||||
[test_osfile_async.js]
|
[test_osfile_async.js]
|
||||||
[test_profiledir.js]
|
[test_profiledir.js]
|
||||||
[test_logging.js]
|
[test_logging.js]
|
||||||
# bug 845190 - thread pool wasn't shutdown assertions
|
|
||||||
skip-if = (os == "win" || "linux") && debug
|
|
||||||
[test_creationDate.js]
|
[test_creationDate.js]
|
||||||
|
|
|
@ -15,8 +15,6 @@ support-files =
|
||||||
|
|
||||||
[test_nocache.js]
|
[test_nocache.js]
|
||||||
[test_645970.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_identifiers.js]
|
||||||
[test_init_async_multiple.js]
|
[test_init_async_multiple.js]
|
||||||
[test_init_async_multiple_then_sync.js]
|
[test_init_async_multiple_then_sync.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/DownloadLastDir.jsm", downloadModule);
|
||||||
Components.utils.import("resource://gre/modules/DownloadPaths.jsm");
|
Components.utils.import("resource://gre/modules/DownloadPaths.jsm");
|
||||||
Components.utils.import("resource://gre/modules/DownloadUtils.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
|
/* ctor
|
||||||
*/
|
*/
|
||||||
|
@ -203,115 +205,113 @@ nsUnknownContentTypeDialog.prototype = {
|
||||||
getService(Components.interfaces.nsIStringBundleService).
|
getService(Components.interfaces.nsIStringBundleService).
|
||||||
createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
|
createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
|
||||||
|
|
||||||
if (!aForcePrompt) {
|
Task.spawn(function() {
|
||||||
// Check to see if the user wishes to auto save to the default download
|
if (!aForcePrompt) {
|
||||||
// folder without prompting. Note that preference might not be set.
|
// Check to see if the user wishes to auto save to the default download
|
||||||
let autodownload = false;
|
// folder without prompting. Note that preference might not be set.
|
||||||
try {
|
let autodownload = false;
|
||||||
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;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExtension);
|
autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR);
|
||||||
}
|
} catch (e) { }
|
||||||
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)
|
if (autodownload) {
|
||||||
prompter.alert(this.dialog,
|
// Retrieve the user's default download directory
|
||||||
bundle.GetStringFromName("badPermissions.title"),
|
let defaultFolder = yield Downloads.getPreferredDownloadsDirectory();
|
||||||
bundle.GetStringFromName("badPermissions"));
|
|
||||||
|
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check to make sure we have a valid directory, otherwise, prompt
|
// Use file picker to show dialog.
|
||||||
if (result) {
|
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||||
aLauncher.saveDestinationAvailable(result);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use file picker to show dialog.
|
// Be sure to save the directory the user chose through the Save As...
|
||||||
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
// dialog as the new browser.download.dir since the old one
|
||||||
var picker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
|
// didn't exist.
|
||||||
var windowTitle = bundle.GetStringFromName("saveDialogTitle");
|
result = picker.file;
|
||||||
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 (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) {
|
// Do not store the last save directory as a pref inside the private browsing mode
|
||||||
// aSuggestedFileExtension includes the period, so strip it
|
gDownloadLastDir.setFile(aLauncher.source, newDir);
|
||||||
picker.defaultExtension = aSuggestedFileExtension.substring(1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
picker.defaultExtension = this.mLauncher.MIMEInfo.primaryExtension;
|
|
||||||
}
|
|
||||||
catch (ex) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
var wildCardExtension = "*";
|
result = this.validateLeafName(newDir, result.leafName, null);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
catch (e) { }
|
aLauncher.saveDestinationAvailable(result);
|
||||||
var newDir = result.parent.QueryInterface(Components.interfaces.nsILocalFile);
|
}.bind(this));
|
||||||
|
}.bind(this)).then(null, Components.utils.reportError);
|
||||||
// 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));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Загрузка…
Ссылка в новой задаче