mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Merge fx-team to m-c.
This commit is contained in:
commit
ed0259103b
@ -1067,7 +1067,8 @@ pref("devtools.gcli.allowSet", false);
|
||||
pref("devtools.commands.dir", "");
|
||||
|
||||
// Disable the app manager
|
||||
pref("devtools.appmanager.enabled", false);
|
||||
pref("devtools.appmanager.enabled", true);
|
||||
pref("devtools.appmanager.simulatorInstallPage", "https://addons.mozilla.org/firefox/addon/firefox-os-simulator/");
|
||||
|
||||
// Toolbox preferences
|
||||
pref("devtools.toolbox.footer.height", 250);
|
||||
|
@ -46,7 +46,7 @@
|
||||
accesskey="&saveLinkCmd.accesskey;"
|
||||
oncommand="gContextMenu.saveLink();"/>
|
||||
<menu id="context-marklinkMenu" label="&social.marklinkMenu.label;"
|
||||
accesskey="&social.marklink.accesskey;">
|
||||
accesskey="&social.marklinkMenu.accesskey;">
|
||||
<menupopup/>
|
||||
</menu>
|
||||
<menuitem id="context-copyemail"
|
||||
@ -252,7 +252,7 @@
|
||||
accesskey="&savePageCmd.accesskey2;"
|
||||
oncommand="gContextMenu.savePageAs();"/>
|
||||
<menu id="context-markpageMenu" label="&social.markpageMenu.label;"
|
||||
accesskey="&social.markpage.accesskey;">
|
||||
accesskey="&social.markpageMenu.accesskey;">
|
||||
<menupopup/>
|
||||
</menu>
|
||||
<menuseparator id="context-sep-viewbgimage"/>
|
||||
|
@ -1658,12 +1658,12 @@ SocialMarks = {
|
||||
{
|
||||
type: "link",
|
||||
id: "context-marklinkMenu",
|
||||
label: "social.marklink.label"
|
||||
label: "social.marklinkMenu.label"
|
||||
},
|
||||
{
|
||||
type: "page",
|
||||
id: "context-markpageMenu",
|
||||
label: "social.markpage.label"
|
||||
label: "social.markpageMenu.label"
|
||||
}
|
||||
];
|
||||
for (let cfg of contextMenus) {
|
||||
|
@ -252,12 +252,12 @@ function checkSocialUI(win) {
|
||||
{
|
||||
type: "link",
|
||||
id: "context-marklinkMenu",
|
||||
label: "social.marklink.label"
|
||||
label: "social.marklinkMenu.label"
|
||||
},
|
||||
{
|
||||
type: "page",
|
||||
id: "context-markpageMenu",
|
||||
label: "social.markpage.label"
|
||||
label: "social.markpageMenu.label"
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -90,6 +90,8 @@ static RedirEntry kRedirMap[] = {
|
||||
{ "healthreport", "chrome://browser/content/abouthealthreport/abouthealth.xhtml",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
#endif
|
||||
{ "app-manager", "chrome://browser/content/devtools/app-manager/index.xul",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
};
|
||||
static const int kRedirTotal = NS_ARRAY_LENGTH(kRedirMap);
|
||||
|
||||
|
@ -109,6 +109,7 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
#endif
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "app-manager", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
#if defined(XP_WIN)
|
||||
{ NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
|
||||
#elif defined(XP_MACOSX)
|
||||
|
@ -678,12 +678,12 @@ let SessionStoreInternal = {
|
||||
break;
|
||||
case "TabPinned":
|
||||
// If possible, update cached data without having to invalidate it
|
||||
TabStateCache.update(aEvent.originalTarget, "pinned", true);
|
||||
TabStateCache.updateField(aEvent.originalTarget, "pinned", true);
|
||||
this.saveStateDelayed(win);
|
||||
break;
|
||||
case "TabUnpinned":
|
||||
// If possible, update cached data without having to invalidate it
|
||||
TabStateCache.update(aEvent.originalTarget, "pinned", false);
|
||||
TabStateCache.updateField(aEvent.originalTarget, "pinned", false);
|
||||
this.saveStateDelayed(win);
|
||||
break;
|
||||
}
|
||||
@ -1330,7 +1330,7 @@ let SessionStoreInternal = {
|
||||
}
|
||||
|
||||
// If possible, update cached data without having to invalidate it
|
||||
TabStateCache.update(aTab, "hidden", false);
|
||||
TabStateCache.updateField(aTab, "hidden", false);
|
||||
|
||||
// Default delay of 2 seconds gives enough time to catch multiple TabShow
|
||||
// events due to changing groups in Panorama.
|
||||
@ -1345,7 +1345,7 @@ let SessionStoreInternal = {
|
||||
}
|
||||
|
||||
// If possible, update cached data without having to invalidate it
|
||||
TabStateCache.update(aTab, "hidden", true);
|
||||
TabStateCache.updateField(aTab, "hidden", true);
|
||||
|
||||
// Default delay of 2 seconds gives enough time to catch multiple TabHide
|
||||
// events due to changing groups in Panorama.
|
||||
@ -1665,7 +1665,6 @@ let SessionStoreInternal = {
|
||||
},
|
||||
|
||||
setTabValue: function ssi_setTabValue(aTab, aKey, aStringValue) {
|
||||
TabStateCache.delete(aTab);
|
||||
// If the tab hasn't been restored, then set the data there, otherwise we
|
||||
// could lose newly added data.
|
||||
let saveTo;
|
||||
@ -1679,12 +1678,13 @@ let SessionStoreInternal = {
|
||||
aTab.__SS_extdata = {};
|
||||
saveTo = aTab.__SS_extdata;
|
||||
}
|
||||
|
||||
saveTo[aKey] = aStringValue;
|
||||
TabStateCache.updateField(aTab, "extData", saveTo);
|
||||
this.saveStateDelayed(aTab.ownerDocument.defaultView);
|
||||
},
|
||||
|
||||
deleteTabValue: function ssi_deleteTabValue(aTab, aKey) {
|
||||
TabStateCache.delete(aTab);
|
||||
// We want to make sure that if data is accessed early, we attempt to delete
|
||||
// that data from __SS_data as well. Otherwise we'll throw in cases where
|
||||
// data can be set or read.
|
||||
@ -1696,9 +1696,19 @@ let SessionStoreInternal = {
|
||||
deleteFrom = aTab.linkedBrowser.__SS_data.extData;
|
||||
}
|
||||
|
||||
if (deleteFrom && deleteFrom[aKey])
|
||||
if (deleteFrom && aKey in deleteFrom) {
|
||||
delete deleteFrom[aKey];
|
||||
this.saveStateDelayed(aTab.ownerDocument.defaultView);
|
||||
|
||||
// Keep the extData object only if it is not empty, to save
|
||||
// a little disk space when serializing the tab state later.
|
||||
if (Object.keys(deleteFrom).length) {
|
||||
TabStateCache.updateField(aTab, "extData", deleteFrom);
|
||||
} else {
|
||||
TabStateCache.removeField(aTab, "extData");
|
||||
}
|
||||
|
||||
this.saveStateDelayed(aTab.ownerDocument.defaultView);
|
||||
}
|
||||
},
|
||||
|
||||
persistTabAttribute: function ssi_persistTabAttribute(aName) {
|
||||
@ -4639,7 +4649,7 @@ let TabStateCache = {
|
||||
* @param {string} aField The field to update.
|
||||
* @param {*} aValue The new value to place in the field.
|
||||
*/
|
||||
update: function(aKey, aField, aValue) {
|
||||
updateField: function(aKey, aField, aValue) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
let data = this._data.get(key);
|
||||
if (data) {
|
||||
@ -4648,6 +4658,22 @@ let TabStateCache = {
|
||||
TabStateCacheTelemetry.recordAccess(!!data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a given field from a cached tab state.
|
||||
*
|
||||
* @param {XULElement} aKey The tab or the associated browser.
|
||||
* If the tab/browser is not present, do nothing.
|
||||
* @param {string} aField The field to remove.
|
||||
*/
|
||||
removeField: function(aKey, aField) {
|
||||
let key = this._normalizeToBrowser(aKey);
|
||||
let data = this._data.get(key);
|
||||
if (data && aField in data) {
|
||||
delete data[aField];
|
||||
}
|
||||
TabStateCacheTelemetry.recordAccess(!!data);
|
||||
},
|
||||
|
||||
_normalizeToBrowser: function(aKey) {
|
||||
let nodeName = aKey.localName;
|
||||
if (nodeName == "tab") {
|
||||
|
@ -3,6 +3,7 @@ const ObservableObject = require("devtools/shared/observable-object");
|
||||
const promise = require("sdk/core/promise");
|
||||
|
||||
const {EventEmitter} = Cu.import("resource:///modules/devtools/shared/event-emitter.js");
|
||||
const {generateUUID} = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator);
|
||||
|
||||
/**
|
||||
* IndexedDB wrapper that just save project objects
|
||||
@ -96,7 +97,13 @@ const AppProjects = {
|
||||
addPackaged: function(folder) {
|
||||
let project = {
|
||||
type: "packaged",
|
||||
location: folder.path
|
||||
location: folder.path,
|
||||
// We need a unique id, that is the app origin,
|
||||
// in order to identify the app when being installed on the device.
|
||||
// The packaged app local path is a valid id, but only on the client.
|
||||
// This origin will be used to generate the true id of an app:
|
||||
// its manifest URL.
|
||||
packagedAppOrigin: generateUUID().toString().slice(1, -1)
|
||||
};
|
||||
return IDB.add(project).then(function () {
|
||||
store.object.projects.push(project);
|
||||
|
@ -7,12 +7,14 @@ const Ci = Components.interfaces;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/devtools/gDevTools.jsm");
|
||||
|
||||
const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm")
|
||||
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const {require} = devtools;
|
||||
|
||||
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
|
||||
const ConnectionStore = require("devtools/app-manager/connection-store");
|
||||
const DeviceStore = require("devtools/app-manager/device-store");
|
||||
const simulatorsStore = require("devtools/app-manager/simulators-store");
|
||||
|
||||
let UI = {
|
||||
init: function() {
|
||||
@ -43,6 +45,7 @@ let UI = {
|
||||
this.store = Utils.mergeStores({
|
||||
"device": new DeviceStore(this.connection),
|
||||
"connection": new ConnectionStore(this.connection),
|
||||
"simulators": simulatorsStore,
|
||||
});
|
||||
|
||||
let pre = document.querySelector("#logs > pre");
|
||||
@ -96,4 +99,47 @@ let UI = {
|
||||
Services.prefs.setCharPref("devtools.debugger.remote-host", host);
|
||||
Services.prefs.setIntPref("devtools.debugger.remote-port", port);
|
||||
},
|
||||
|
||||
showSimulatorList: function() {
|
||||
document.body.classList.add("show-simulators");
|
||||
},
|
||||
|
||||
cancelShowSimulatorList: function() {
|
||||
document.body.classList.remove("show-simulators");
|
||||
},
|
||||
|
||||
installSimulator: function() {
|
||||
let url = Services.prefs.getCharPref("devtools.appmanager.simulatorInstallPage");
|
||||
window.open(url);
|
||||
},
|
||||
|
||||
startSimulator: function(version) {
|
||||
let port = ConnectionManager.getFreeTCPPort();
|
||||
let simulator = Simulator.getByVersion(version);
|
||||
if (!simulator) {
|
||||
this.connection.log("Error: can't find simulator: " + version);
|
||||
return;
|
||||
}
|
||||
if (!simulator.launch) {
|
||||
this.connection.log("Error: invalid simulator: " + version);
|
||||
return;
|
||||
}
|
||||
this.connection.log("Found simulator: " + version);
|
||||
this.connection.log("Starting simulator...");
|
||||
|
||||
this.simulator = simulator;
|
||||
this.simulator.launch({ port: port })
|
||||
.then(() => {
|
||||
this.connection.log("Simulator ready. Connecting.");
|
||||
this.connection.port = port;
|
||||
this.connection.host = "localhost";
|
||||
this.connection.once("connected", function() {
|
||||
this.connection.log("Connected to simulator.");
|
||||
this.connection.keepConnecting = false;
|
||||
});
|
||||
this.connection.keepConnecting = true;
|
||||
this.connection.connect();
|
||||
});
|
||||
document.body.classList.remove("show-simulators");
|
||||
},
|
||||
}
|
||||
|
@ -38,9 +38,9 @@
|
||||
<span>&connection.notConnected;</span>
|
||||
<button class="action-primary left" onclick="UI.connect()" id="connect-button" template='{"type":"localizedContent","property":"connection.connectTo","paths":["connection.host","connection.port"]}'></button>
|
||||
<button class="right" onclick="UI.editConnectionParameters()">&connection.changeHostAndPort;</button>
|
||||
<div id="start-simulator-box" template='{"type":"attribute","path":"simulators.versions.length","name":"simulators-count"}'>
|
||||
<div id="start-simulator-box">
|
||||
<span>&connection.or;</span>
|
||||
<button id="start-simulator-button" class="action-primary" onclick="UI.startSimulator()">&connection.startSimulator;</button>
|
||||
<button id="start-simulator-button" class="action-primary" onclick="UI.showSimulatorList()">&connection.startSimulator;</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -81,6 +81,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Simulator -->
|
||||
<div id="banner-simulators" class="banner" template='{"type":"attribute","path":"simulators.versions.length","name":"simulator-count"}'>
|
||||
<div class="connected-indicator"></div>
|
||||
<div class="banner-box">
|
||||
<div class="banner-content">
|
||||
<div class="no-simulator">
|
||||
<span>&connection.noSimulatorInstalled;</span>
|
||||
<button class="action-primary" onclick="UI.installSimulator()">&connection.installFirstSimulator;</button>
|
||||
</div>
|
||||
<div class="found-simulator">
|
||||
<span template-loop='{"arrayPath":"simulators.versions","childSelector":"#simulator-item-template"}'></span>
|
||||
<button class="action-primary" onclick="UI.installSimulator()">&connection.installAnotherSimulator;</button>
|
||||
</div>
|
||||
<button class="action-cancel" onclick="UI.cancelShowSimulatorList()">&connection.cancel;</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Logs -->
|
||||
<div id="banner-logs">
|
||||
<div id="logs" class="banner-box">
|
||||
@ -92,6 +111,14 @@
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<template id="simulator-item-template">
|
||||
<span>
|
||||
<button class="simulator-item" onclick="UI.startSimulator(this.dataset.version)" template='{"type":"attribute","path":"version","name":"data-version"}'>
|
||||
<span template='{"type":"textContent", "path":"version"}'></span>
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script type="application/javascript;version=1.8" src="utils.js"></script>
|
||||
<script type="application/javascript;version=1.8" src="template.js"></script>
|
||||
<script type="application/javascript;version=1.8" src="connection-footer.js"></script>
|
||||
|
@ -167,7 +167,13 @@ let UI = {
|
||||
|
||||
openToolbox: function(manifest) {
|
||||
this._getTargetForApp(manifest).then((target) => {
|
||||
gDevTools.showToolbox(target, "webconsole", devtools.Toolbox.HostType.WINDOW);
|
||||
gDevTools.showToolbox(target,
|
||||
null,
|
||||
devtools.Toolbox.HostType.WINDOW).then(toolbox => {
|
||||
this.connection.once(Connection.Events.DISCONNECTED, () => {
|
||||
toolbox.destroy();
|
||||
});
|
||||
});
|
||||
}, console.error);
|
||||
},
|
||||
|
||||
|
@ -14,6 +14,8 @@ const {AppProjects} = require("devtools/app-manager/app-projects");
|
||||
const {AppValidator} = require("devtools/app-manager/app-validator");
|
||||
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
|
||||
const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
const {installHosted, installPackaged} = require("devtools/app-actor-front");
|
||||
|
||||
const promise = require("sdk/core/promise");
|
||||
|
||||
window.addEventListener("message", function(event) {
|
||||
@ -163,6 +165,39 @@ let UI = {
|
||||
}
|
||||
},
|
||||
|
||||
install: function(button, location) {
|
||||
button.dataset.originalTextContent = button.textContent;
|
||||
button.textContent = Utils.l10n("project.installing");
|
||||
button.disabled = true;
|
||||
let project = AppProjects.get(location);
|
||||
let install;
|
||||
if (project.type == "packaged") {
|
||||
install = installPackaged(this.connection.client, this.listTabsResponse.webappsActor, project.location, project.packagedAppOrigin);
|
||||
} else {
|
||||
let manifestURLObject = Services.io.newURI(project.location, null, null);
|
||||
let origin = Services.io.newURI(manifestURLObject.prePath, null, null);
|
||||
let appId = origin.host;
|
||||
let metadata = {
|
||||
origin: origin.spec,
|
||||
manifestURL: project.location
|
||||
};
|
||||
install = installHosted(this.connection.client, this.listTabsResponse.webappsActor, appId, metadata, project.manifest);
|
||||
}
|
||||
install.then(function () {
|
||||
button.disabled = false;
|
||||
button.textContent = Utils.l10n("project.installed");
|
||||
setTimeout(function() {
|
||||
button.textContent = button.dataset.originalTextContent;
|
||||
}, 1500);
|
||||
},
|
||||
function (res) {
|
||||
button.disabled = false;
|
||||
let message = res.error + ": " + res.message;
|
||||
alert(message);
|
||||
this.connection.log(message);
|
||||
});
|
||||
},
|
||||
|
||||
start: function(location) {
|
||||
let project = AppProjects.get(location);
|
||||
let request = {
|
||||
@ -223,8 +258,11 @@ let UI = {
|
||||
this._getTargetForApp(manifest).then((target) => {
|
||||
gDevTools.showToolbox(target,
|
||||
null,
|
||||
devtools.Toolbox.HostType.WINDOW,
|
||||
this.connection.uid);
|
||||
devtools.Toolbox.HostType.WINDOW).then(toolbox => {
|
||||
this.connection.once(Connection.Events.DISCONNECTED, () => {
|
||||
toolbox.destroy();
|
||||
});
|
||||
});
|
||||
}, console.error);
|
||||
},
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
<div id="new-packaged-project" onclick="UI.addPackaged()">&projects.addPackaged;</div>
|
||||
<div id="new-hosted-project">&projects.addHosted;
|
||||
<form onsubmit="UI.addHosted(); return false;" id="new-hosted-project-wrapper">
|
||||
<input value="" id="url-input" type="url" pattern="https?://.+" placeholder="&projects.hostedManifestPlaceHolder;" size="50" />
|
||||
<input value="" id="url-input" type="url" pattern="https?://.+" placeholder="&projects.hostedManifestPlaceHolder2;" size="50" />
|
||||
<div onclick="UI.addHosted()" id="new-hosted-project-click"></div>
|
||||
<input type="submit" hidden="true"></input>
|
||||
</form>
|
||||
@ -71,14 +71,10 @@
|
||||
</div>
|
||||
<div class="project-buttons">
|
||||
<button class="project-button-refresh" onclick="UI.update(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.reloadFiles;</button>
|
||||
<!-- Not available until bug 911785 is fixed
|
||||
<button class="device-action project-button-install" onclick="UI.install(this, this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.installApp;</button>
|
||||
-->
|
||||
<button class="device-action project-button-start" onclick="UI.start(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.startApp;</button>
|
||||
<button class="device-action project-button-stop" onclick="UI.stop(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.stopApp;</button>
|
||||
<!-- Not available until bug 911785 is fixed
|
||||
<button class="device-action project-button-debug" onclick="UI.openToolbox(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.debugApp;</button>
|
||||
-->
|
||||
</div>
|
||||
<div class="project-errors" template='{"type":"textContent","path":"errors"}'></div>
|
||||
<div class="project-warnings" template='{"type":"textContent","path":"warnings"}'></div>
|
||||
|
21
browser/devtools/app-manager/simulators-store.js
Normal file
21
browser/devtools/app-manager/simulators-store.js
Normal file
@ -0,0 +1,21 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const {Cu} = require("chrome");
|
||||
const ObservableObject = require("devtools/shared/observable-object");
|
||||
const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
|
||||
|
||||
let store = new ObservableObject({versions:[]});
|
||||
|
||||
function feedStore() {
|
||||
store.object.versions = Simulator.availableVersions().map(v => {
|
||||
return {version:v}
|
||||
});
|
||||
}
|
||||
|
||||
Simulator.on("register", feedStore);
|
||||
Simulator.on("unregister", feedStore);
|
||||
feedStore();
|
||||
|
||||
module.exports = store;
|
@ -378,7 +378,7 @@ let gDevToolsBrowser = {
|
||||
* Open the App Manager
|
||||
*/
|
||||
openAppManager: function(gBrowser) {
|
||||
gBrowser.selectedTab = gBrowser.addTab("chrome://browser/content/devtools/app-manager/index.xul");
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:app-manager");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1,14 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<div id="id1"></div>
|
||||
<div id="id2"></div>
|
||||
<div id="id3">
|
||||
<ul class="aList">
|
||||
<li class="item"></li>
|
||||
<li class="item"></li>
|
||||
<li class="item"></li>
|
||||
<li class="item">
|
||||
<span id="id4"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>select last selected test</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="id1"></div>
|
||||
<div id="id2"></div>
|
||||
<div id="id3">
|
||||
<ul class="aList">
|
||||
<li class="item"></li>
|
||||
<li class="item"></li>
|
||||
<li class="item"></li>
|
||||
<li class="item">
|
||||
<span id="id4"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -30,23 +30,39 @@ function test() {
|
||||
}
|
||||
|
||||
function endTests() {
|
||||
toolbox.destroy();
|
||||
toolbox = inspector = page1 = page2 = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
executeSoon(() => {
|
||||
toolbox.destroy();
|
||||
toolbox = inspector = page1 = page2 = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function testReSelectingAnElement(id, callback) {
|
||||
function loadPageAnd(page, callback) {
|
||||
inspector.once("markuploaded", () => {
|
||||
executeSoon(callback);
|
||||
});
|
||||
|
||||
if (page) {
|
||||
content.location = page;
|
||||
} else {
|
||||
content.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
function reloadAndReselect(id, callback) {
|
||||
let div = content.document.getElementById(id);
|
||||
inspector.selection.setNode(div);
|
||||
|
||||
inspector.once("inspector-updated", () => {
|
||||
is(inspector.selection.node, div);
|
||||
inspector.once("markuploaded", () => {
|
||||
|
||||
loadPageAnd(false, () => {
|
||||
is(inspector.selection.node.id, id, "Node re-selected after reload");
|
||||
callback();
|
||||
executeSoon(callback);
|
||||
});
|
||||
content.location.reload();
|
||||
});
|
||||
|
||||
inspector.selection.setNode(div);
|
||||
}
|
||||
|
||||
// Test that nodes selected on the test page remain selected after reload
|
||||
@ -54,10 +70,10 @@ function test() {
|
||||
{
|
||||
// Select a few nodes and check they are re-selected after reload of the same
|
||||
// page
|
||||
testReSelectingAnElement("id1", () => {
|
||||
testReSelectingAnElement("id2", () => {
|
||||
testReSelectingAnElement("id3", () => {
|
||||
testReSelectingAnElement("id4", testBodySelectedOnNavigate);
|
||||
reloadAndReselect("id1", () => {
|
||||
reloadAndReselect("id2", () => {
|
||||
reloadAndReselect("id3", () => {
|
||||
reloadAndReselect("id4", testBodySelectedOnNavigate);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -68,15 +84,16 @@ function test() {
|
||||
function testBodySelectedOnNavigate() {
|
||||
// Last node selected was id4, go to a different page and check body is
|
||||
// selected
|
||||
inspector.once("markuploaded", () => {
|
||||
is(
|
||||
inspector.selection.node.tagName.toLowerCase(),
|
||||
"body",
|
||||
"Node not found, selecting body"
|
||||
);
|
||||
testSameNodeSelectedOnNavigateAwayAndBack();
|
||||
loadPageAnd(page2, () => {
|
||||
executeSoon(() => {
|
||||
is(
|
||||
inspector.selection.node.tagName.toLowerCase(),
|
||||
"body",
|
||||
"Node not found, body selected"
|
||||
);
|
||||
executeSoon(testSameNodeSelectedOnNavigateAwayAndBack);
|
||||
});
|
||||
});
|
||||
content.location = page2;
|
||||
}
|
||||
|
||||
// Test that the node selected on page 1 gets selected again after a navigation
|
||||
@ -85,19 +102,25 @@ function test() {
|
||||
// On page2, select id5
|
||||
let id = "id5";
|
||||
let div = content.document.getElementById(id);
|
||||
inspector.selection.setNode(div);
|
||||
|
||||
inspector.once("inspector-updated", () => {
|
||||
is(inspector.selection.node.id, id);
|
||||
// go to page1 but do not select anything
|
||||
inspector.once("markuploaded", () => {
|
||||
// go back to page2 and check id5 is still the current selection
|
||||
inspector.once("markuploaded", () => {
|
||||
is(inspector.selection.node.id, id, "Node re-selected after navigation");
|
||||
endTests();
|
||||
|
||||
executeSoon(() => {
|
||||
// go to page1 but do not select anything
|
||||
loadPageAnd(page1, () => {
|
||||
|
||||
executeSoon(() => {
|
||||
// go back to page2 and check id5 is still the current selection
|
||||
loadPageAnd(page2, () => {
|
||||
is(inspector.selection.node.id, id, "Node re-selected after navigation");
|
||||
executeSoon(endTests);
|
||||
});
|
||||
});
|
||||
});
|
||||
content.location = page2;
|
||||
});
|
||||
content.location = page1;
|
||||
});
|
||||
|
||||
inspector.selection.setNode(div);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<div id="id5"></div>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>select last selected test</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="id5"></div>
|
||||
</body>
|
||||
</html>
|
@ -234,9 +234,11 @@ ResponsiveUI.prototype = {
|
||||
onPageUnload: function() {
|
||||
if (this.closing)
|
||||
return;
|
||||
this.touchEnableBefore = this.touchEventHandler.enabled;
|
||||
this.disableTouch();
|
||||
delete this.touchEventHandler;
|
||||
if (this.touchEventHandler) {
|
||||
this.touchEnableBefore = this.touchEventHandler.enabled;
|
||||
this.disableTouch();
|
||||
delete this.touchEventHandler;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1041,7 +1041,9 @@ CssRuleView.prototype = {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
|
||||
this.elementStyle.destroy();
|
||||
if (this.elementStyle) {
|
||||
this.elementStyle.destroy();
|
||||
}
|
||||
|
||||
this.popup.destroy();
|
||||
},
|
||||
@ -1210,6 +1212,11 @@ CssRuleView.prototype = {
|
||||
return this._showPseudoElements;
|
||||
},
|
||||
|
||||
_getRuleViewHeaderClassName: function(isPseudo) {
|
||||
let baseClassName = "theme-gutter ruleview-header";
|
||||
return isPseudo ? baseClassName + " ruleview-expandable-header" : baseClassName;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates editor UI for each of the rules in _elementStyle.
|
||||
*/
|
||||
@ -1230,7 +1237,7 @@ CssRuleView.prototype = {
|
||||
if (seenPseudoElement && !seenNormalElement && !rule.pseudoElement) {
|
||||
seenNormalElement = true;
|
||||
let div = this.doc.createElementNS(HTML_NS, "div");
|
||||
div.className = "theme-gutter ruleview-header";
|
||||
div.className = this._getRuleViewHeaderClassName();
|
||||
div.textContent = this.selectedElementLabel;
|
||||
this.element.appendChild(div);
|
||||
}
|
||||
@ -1238,7 +1245,7 @@ CssRuleView.prototype = {
|
||||
let inheritedSource = rule.inheritedSource;
|
||||
if (inheritedSource != lastInheritedSource) {
|
||||
let div = this.doc.createElementNS(HTML_NS, "div");
|
||||
div.className = "theme-gutter ruleview-header";
|
||||
div.className = this._getRuleViewHeaderClassName();
|
||||
div.textContent = inheritedSource;
|
||||
lastInheritedSource = inheritedSource;
|
||||
this.element.appendChild(div);
|
||||
@ -1248,8 +1255,11 @@ CssRuleView.prototype = {
|
||||
seenPseudoElement = true;
|
||||
|
||||
let div = this.doc.createElementNS(HTML_NS, "div");
|
||||
div.className = "theme-gutter ruleview-header";
|
||||
div.className = this._getRuleViewHeaderClassName(true);
|
||||
div.textContent = this.pseudoElementLabel;
|
||||
div.addEventListener("dblclick", () => {
|
||||
this.togglePseudoElementVisibility(!this.showPseudoElements);
|
||||
}, false);
|
||||
|
||||
let twisty = this.pseudoElementTwisty =
|
||||
this.doc.createElementNS(HTML_NS, "span");
|
||||
|
@ -50,7 +50,11 @@
|
||||
}
|
||||
|
||||
.ruleview-header {
|
||||
vertical-align:middle;
|
||||
vertical-align: middle;
|
||||
height: 1.5em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.ruleview-header.ruleview-expandable-header {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -48,7 +48,10 @@ function testTopLeft()
|
||||
ok (!view.element.classList.contains("show-pseudo-elements"), "Pseudo Elements are collapsed by twisty");
|
||||
expander.click();
|
||||
ok (view.element.classList.contains("show-pseudo-elements"), "Pseudo Elements are expanded again");
|
||||
expander.click();
|
||||
|
||||
// Make sure that dblclicking on the header container also toggles the pseudo elements
|
||||
EventUtils.synthesizeMouseAtCenter(gutters[0], {clickCount: 2}, inspector.sidebar.getWindowForTab("ruleview"));
|
||||
ok (!view.element.classList.contains("show-pseudo-elements"), "Pseudo Elements are collapsed by dblclicking");
|
||||
|
||||
let defaultView = element.ownerDocument.defaultView;
|
||||
let elementRule = elementRules[0];
|
||||
|
@ -645,9 +645,9 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
<!ENTITY social.chatBar.label "Focus chats">
|
||||
<!ENTITY social.chatBar.accesskey "c">
|
||||
|
||||
<!ENTITY social.markpage.accesskey "P">
|
||||
<!ENTITY social.markpageMenu.accesskey "P">
|
||||
<!ENTITY social.markpageMenu.label "Save Page To…">
|
||||
<!ENTITY social.marklink.accesskey "L">
|
||||
<!ENTITY social.marklinkMenu.accesskey "L">
|
||||
<!ENTITY social.marklinkMenu.label "Save Link To…">
|
||||
|
||||
<!ENTITY getUserMedia.selectCamera.label "Camera to share:">
|
||||
|
@ -430,10 +430,10 @@ social.turnOff.accesskey=T
|
||||
social.turnOn.label=Turn on %S
|
||||
social.turnOn.accesskey=T
|
||||
|
||||
# LOCALIZATION NOTE (social.markpage.label): %S is the name of the social provider
|
||||
social.markpage.label=Save Page to %S
|
||||
# LOCALIZATION NOTE (social.marklink.label): %S is the name of the social provider
|
||||
social.marklink.label=Save Link to %S
|
||||
# LOCALIZATION NOTE (social.markpageMenu.label): %S is the name of the social provider
|
||||
social.markpageMenu.label=Save Page to %S
|
||||
# LOCALIZATION NOTE (social.marklinkMenu.label): %S is the name of the social provider
|
||||
social.marklinkMenu.label=Save Link to %S
|
||||
|
||||
# LOCALIZATION NOTE (social.error.message): %1$S is brandShortName (e.g. Firefox), %2$S is the name of the social provider
|
||||
social.error.message=%1$S is unable to connect with %2$S right now.
|
||||
|
@ -32,6 +32,9 @@
|
||||
<!ENTITY connection.disconnecting "Disconnecting…">
|
||||
<!ENTITY connection.cancel "Cancel">
|
||||
<!ENTITY connection.or "or">
|
||||
<!ENTITY connection.noSimulatorInstalled "No simulator installed.">
|
||||
<!ENTITY connection.installFirstSimulator "Install simulator.">
|
||||
<!ENTITY connection.installAnotherSimulator "Add">
|
||||
|
||||
<!ENTITY projects.localApps "Local Apps">
|
||||
<!ENTITY projects.addApp "Add">
|
||||
@ -45,5 +48,5 @@
|
||||
<!ENTITY projects.startApp "Start">
|
||||
<!ENTITY projects.stopApp "Stop">
|
||||
<!ENTITY projects.debugApp "Debug">
|
||||
<!ENTITY projects.hostedManifestPlaceHolder "http://example.com/app/webapp.manifest">
|
||||
<!ENTITY projects.hostedManifestPlaceHolder2 "http://example.com/app/manifest.webapp">
|
||||
<!ENTITY projects.noProject "No project linked. Add a new packaged app below (a directory) or a hosted app (link to a manifest file).">
|
||||
|
@ -11,6 +11,8 @@ device.deviceSize=Device size: %1$Sx%2$S (%3$S DPI)
|
||||
connection.connectedToDevice=Connected to %1$S
|
||||
connection.connectTo=Connect to %1$S:%2$S
|
||||
project.filePickerTitle=Select a webapp folder
|
||||
project.installing=Installing...
|
||||
project.installed=Installed!
|
||||
validator.nonExistingFolder=The project folder doesn't exists
|
||||
validator.expectProjectFolder=The project folder ends up being a file
|
||||
validator.wrongManifestFileName=Packaged apps require a manifest file that can only be named 'manifest.webapp' at project root folder
|
||||
|
@ -20,6 +20,8 @@ var ContextUI = {
|
||||
|
||||
init: function init() {
|
||||
Elements.browsers.addEventListener('URLChanged', this, true);
|
||||
Elements.browsers.addEventListener("AlertActive", this, true);
|
||||
Elements.browsers.addEventListener("AlertClose", this, true);
|
||||
Elements.tabList.addEventListener('TabSelect', this, true);
|
||||
Elements.panelUI.addEventListener('ToolPanelShown', this, false);
|
||||
Elements.panelUI.addEventListener('ToolPanelHidden', this, false);
|
||||
@ -325,6 +327,10 @@ var ContextUI = {
|
||||
case "ToolPanelHidden":
|
||||
this.dismiss();
|
||||
break;
|
||||
case "AlertActive":
|
||||
case "AlertClose":
|
||||
ContentAreaObserver.updateContentArea();
|
||||
break;
|
||||
case "touchstart":
|
||||
if (!BrowserUI.isStartTabVisible) {
|
||||
this.dismiss();
|
||||
|
@ -53,6 +53,7 @@
|
||||
|
||||
// Fire notification closed event.
|
||||
let event = new Event('AlertClose');
|
||||
event.notification = aItem;
|
||||
this.dispatchEvent(event);
|
||||
|
||||
return aItem;
|
||||
|
@ -1143,9 +1143,12 @@ Browser.MainDragger.prototype = {
|
||||
},
|
||||
|
||||
_hideScrollbars: function _hideScrollbars() {
|
||||
this._scrollScales.x = 0, this._scrollScales.y = 0;
|
||||
this._scrollScales.x = 0;
|
||||
this._scrollScales.y = 0;
|
||||
this._horizontalScrollbar.removeAttribute("panning");
|
||||
this._verticalScrollbar.removeAttribute("panning");
|
||||
this._horizontalScrollbar.removeAttribute("width");
|
||||
this._verticalScrollbar.removeAttribute("height");
|
||||
this._horizontalScrollbar.style.MozTransform = "";
|
||||
this._verticalScrollbar.style.MozTransform = "";
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ var Downloads = {
|
||||
Services.obs.addObserver(this, "dl-request", true);
|
||||
|
||||
this._notificationBox = Browser.getNotificationBox();
|
||||
this._notificationBox.addEventListener('AlertClose', this.handleEvent, true);
|
||||
|
||||
this._progress = new DownloadProgressListener(this);
|
||||
this.manager.addListener(this._progress);
|
||||
@ -225,6 +226,7 @@ var Downloads = {
|
||||
accessKey: "",
|
||||
callback: function() {
|
||||
Downloads.cancelDownload(aDownload);
|
||||
Downloads._downloadProgressIndicator.reset();
|
||||
}
|
||||
}
|
||||
];
|
||||
@ -397,6 +399,7 @@ var Downloads = {
|
||||
accessKey: "",
|
||||
callback: function() {
|
||||
Downloads.cancelDownloads();
|
||||
Downloads._downloadProgressIndicator.reset();
|
||||
}
|
||||
}
|
||||
];
|
||||
@ -431,6 +434,17 @@ var Downloads = {
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "AlertClose":
|
||||
if (aEvent.notification.value == "download-complete" &&
|
||||
!Downloads._notificationBox.getNotificationWithValue("download-complete")) {
|
||||
Downloads._downloadProgressIndicator.reset();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
let message = "";
|
||||
let msgTitle = "";
|
||||
@ -459,7 +473,6 @@ var Downloads = {
|
||||
this._showDownloadCompleteToast(download);
|
||||
this._showDownloadCompleteNotification(download);
|
||||
}
|
||||
this._downloadProgressIndicator.reset();
|
||||
this._progressNotificationInfo.clear();
|
||||
this._downloadCount = 0;
|
||||
this._notificationBox.removeNotification(this._progressNotification);
|
||||
@ -469,7 +482,6 @@ var Downloads = {
|
||||
case "dl-failed":
|
||||
download = aSubject.QueryInterface(Ci.nsIDownload);
|
||||
this._showDownloadFailedNotification(download);
|
||||
this._downloadProgressIndicator.reset();
|
||||
break;
|
||||
case "dl-request":
|
||||
setTimeout(function() {
|
||||
|
@ -46,7 +46,8 @@
|
||||
.ruleview-warning {
|
||||
background: url("chrome://browser/skin/devtools/alerticon-warning.png");
|
||||
-moz-margin-start: 5px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 13px;
|
||||
height: 12px;
|
||||
}
|
||||
|
@ -50,7 +50,8 @@
|
||||
.ruleview-warning {
|
||||
background: url("chrome://browser/skin/devtools/alerticon-warning.png");
|
||||
-moz-margin-start: 5px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 13px;
|
||||
height: 12px;
|
||||
}
|
||||
|
@ -33,10 +33,12 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
body.show-simulators .banner,
|
||||
body.edit-connection .banner {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body.show-simulators #banner-simulators,
|
||||
body.edit-connection #banner-editing {
|
||||
display: flex !important;
|
||||
}
|
||||
@ -70,10 +72,6 @@ body.edit-connection #banner-editing {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#start-simulator-box[simulators-count="0"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/************** PIXELS **************/
|
||||
|
||||
* {
|
||||
@ -193,8 +191,18 @@ button.action-cancel {
|
||||
background: linear-gradient(to bottom, #69B8FF, #339FFF );
|
||||
}
|
||||
|
||||
#banner-simulators .connected-indicator,
|
||||
#banner-disconnected .connected-indicator,
|
||||
#banner-editing .connected-indicator,
|
||||
#banner-disconnecting .connected-indicator {
|
||||
background: linear-gradient(to bottom, #375A87, #1C4375 );
|
||||
}
|
||||
|
||||
#banner-simulators .banner-content > * {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#banner-simulators[simulator-count="0"] .found-simulator,
|
||||
#banner-simulators:not([simulator-count="0"]) .no-simulator {
|
||||
display: none;
|
||||
}
|
||||
|
@ -46,7 +46,8 @@
|
||||
.ruleview-warning {
|
||||
background: url("chrome://browser/skin/devtools/alerticon-warning.png");
|
||||
-moz-margin-start: 5px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 13px;
|
||||
height: 12px;
|
||||
}
|
||||
|
@ -21,10 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=470804
|
||||
Passing a null targetURL to checkLoadURIWithPrincipal shouldn't crash
|
||||
**/
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
const nsIScriptSecurityManager = Components.interfaces.nsIScriptSecurityManager;
|
||||
var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(nsIScriptSecurityManager);
|
||||
const nsIScriptSecurityManager = SpecialPowers.Ci.nsIScriptSecurityManager;
|
||||
var secMan = SpecialPowers.Services.scriptSecurityManager;
|
||||
var principal = SpecialPowers.wrap(document).nodePrincipal;
|
||||
isnot(principal, undefined, "Should have a principal");
|
||||
isnot(principal, null, "Should have a non-null principal");
|
||||
|
@ -30,12 +30,10 @@ function getLoadContext() {
|
||||
}
|
||||
|
||||
function testCopyPaste (isXHTML) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var suppressUnicodeCheckIfHidden = !!isXHTML;
|
||||
var suppressHTMLCheck = !!isXHTML;
|
||||
|
||||
var webnav = window.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
|
||||
var webnav = SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
|
||||
.getInterface(SpecialPowers.Ci.nsIWebNavigation)
|
||||
|
||||
var docShell = webnav.QueryInterface(SpecialPowers.Ci.nsIDocShell);
|
||||
@ -43,8 +41,7 @@ function testCopyPaste (isXHTML) {
|
||||
var documentViewer = docShell.contentViewer
|
||||
.QueryInterface(SpecialPowers.Ci.nsIContentViewerEdit);
|
||||
|
||||
var clipboard = SpecialPowers.Cc["@mozilla.org/widget/clipboard;1"]
|
||||
.getService(SpecialPowers.Ci.nsIClipboard);
|
||||
var clipboard = SpecialPowers.Services.clipboard;
|
||||
|
||||
var textarea = SpecialPowers.wrap(document.getElementById('input'));
|
||||
|
||||
@ -88,14 +85,14 @@ function testCopyPaste (isXHTML) {
|
||||
transferable.init(getLoadContext());
|
||||
transferable.addDataFlavor(mime);
|
||||
clipboard.getData(transferable, 1);
|
||||
var data = {};
|
||||
var data = SpecialPowers.createBlankObject();
|
||||
transferable.getTransferData(mime, data, {}) ;
|
||||
return data;
|
||||
}
|
||||
function testClipboardValue(mime, expected) {
|
||||
if (suppressHTMLCheck && mime == "text/html")
|
||||
return null;
|
||||
var data = getClipboardData(mime);
|
||||
var data = SpecialPowers.wrap(getClipboardData(mime));
|
||||
is (data.value == null ? data.value :
|
||||
data.value.QueryInterface(SpecialPowers.Ci.nsISupportsString).data,
|
||||
expected,
|
||||
|
@ -12,11 +12,8 @@ function run() {
|
||||
}
|
||||
|
||||
function forcegc(){
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
Components.utils.forceGC();
|
||||
var wu = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
wu.garbageCollect();
|
||||
SpecialPowers.forceGC();
|
||||
SpecialPowers.gc();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
@ -38,8 +38,6 @@ testDoc2.appendChild(testDoc2.createElement("res"));
|
||||
testDoc2.documentElement.appendChild(testDoc2.createTextNode("text"));
|
||||
is(testDoc2.inputEncoding, null, "wrong encoding");
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var testData = "blahblahblahblahblahblahblaaaaaaaah. blah.";
|
||||
var extensions = [".txt",".png",".jpg",".gif",".xml", "noext"];
|
||||
var fileTypes = ["text/plain", "image/png", "image/jpeg", "image/gif", "text/xml", null];
|
||||
@ -72,14 +70,13 @@ extensions.forEach(
|
||||
testFiles.push(testFile);
|
||||
|
||||
var fileList = document.getElementById('fileList');
|
||||
fileList.value = testFile.path;
|
||||
SpecialPowers.wrap(fileList).value = testFile.path;
|
||||
testDOMFiles.push(fileList.files[0]);
|
||||
}
|
||||
);
|
||||
|
||||
function createFileWithDataExt(fileData, extension) {
|
||||
var dirSvc = SpecialPowers.Cc["@mozilla.org/file/directory_service;1"].getService(SpecialPowers.Ci.nsIProperties);
|
||||
var testFile = dirSvc.get("ProfD", SpecialPowers.Ci.nsIFile);
|
||||
var testFile = SpecialPowers.Services.dirsvc.get("ProfD", SpecialPowers.Ci.nsIFile);
|
||||
testFile.append("testfile" + extension);
|
||||
var outStream = SpecialPowers.Cc["@mozilla.org/network/file-output-stream;1"].createInstance(SpecialPowers.Ci.nsIFileOutputStream);
|
||||
outStream.init(testFile, 0x02 | 0x08 | 0x20, 0666, 0);
|
||||
|
@ -65,8 +65,6 @@ function runNextTest() {
|
||||
if (tests.length > 0) {
|
||||
var test = tests.shift();
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
// Initialize state variables
|
||||
testName = test[0]
|
||||
currentState = 0;
|
||||
|
@ -33,10 +33,7 @@ function runTest() {
|
||||
xhr.open("GET", "test_bug382871.html");
|
||||
xhr.send();
|
||||
xhr = null;
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
SpecialPowers.gc();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
@ -21,14 +21,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=403852
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var dirSvc = SpecialPowers.Cc["@mozilla.org/file/directory_service;1"].getService(SpecialPowers.Ci.nsIProperties);
|
||||
var testFile = dirSvc.get("ProfD", SpecialPowers.Ci.nsIFile);
|
||||
var testFile = SpecialPowers.Services.dirsvc.get("ProfD", SpecialPowers.Ci.nsIFile);
|
||||
testFile.append("prefs.js");
|
||||
|
||||
var fileList = document.getElementById('fileList');
|
||||
fileList.value = testFile.path;
|
||||
SpecialPowers.wrap(fileList).value = testFile.path;
|
||||
|
||||
// Make sure the file is accessible with indexed notation
|
||||
var domFile = fileList.files[0];
|
||||
|
@ -21,16 +21,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=416383
|
||||
/** Test for Bug 416383 **/
|
||||
|
||||
function runTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var testParent = document.getElementById("test_parent");
|
||||
var attrs = testParent.firstChild.attributes;
|
||||
ok(attrs != null, "Element should have attributes!");
|
||||
var attr = testParent.firstChild.getAttributeNode("someattr");
|
||||
ok(attr.value == "foo", "The value of the attribute should be 'foo'!");
|
||||
testParent.removeChild(testParent.firstChild);
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
SpecialPowers.gc();
|
||||
ok(true, "Browser should not crash!")
|
||||
|
||||
}
|
||||
|
@ -23,13 +23,6 @@ SimpleTest.waitForExplicitFinish();
|
||||
var img1loaded = false;
|
||||
var img1errored = false;
|
||||
|
||||
function gc() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
}
|
||||
|
||||
// Our test image
|
||||
function loadTestImage() {
|
||||
var img1 = new Image();
|
||||
@ -46,7 +39,7 @@ function loadTestImage() {
|
||||
loadTestImage();
|
||||
|
||||
// Probably overkill to gc() more than once, but let's be safe
|
||||
gc(); gc(); gc();
|
||||
SpecialPowers.gc(); SpecialPowers.gc(); SpecialPowers.gc();
|
||||
|
||||
function finishTest() {
|
||||
is(img1errored, true, "Image 1 should error");
|
||||
|
@ -19,13 +19,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=448993
|
||||
|
||||
/** Test for Bug 448993 **/
|
||||
|
||||
function gc() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
var a = document.getElementById("a");
|
||||
var b = document.getElementById("b");
|
||||
@ -37,7 +30,7 @@ function runTest() {
|
||||
r.extractContents();
|
||||
var s = document.createRange();
|
||||
s.setEnd(b, 0);
|
||||
gc();
|
||||
SpecialPowers.gc();
|
||||
s.deleteContents();
|
||||
ok(true, "test did not crash");
|
||||
SimpleTest.finish();
|
||||
|
@ -20,14 +20,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=456262
|
||||
/** Test for Bug 456262 **/
|
||||
|
||||
function runTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
document.expando = document;
|
||||
document.documentElement.expando = document;
|
||||
document.documentElement.setAttribute("foo", "bar");
|
||||
document.documentElement.getAttributeNode("foo").expando = document;
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
SpecialPowers.gc();
|
||||
ok(document.expando, "Should have preserved the expando!");
|
||||
ok(document.documentElement.expando, "Should have preserved the expando!");
|
||||
ok(document.documentElement.getAttributeNode('foo').expando,
|
||||
|
@ -20,16 +20,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=459424
|
||||
/** Test for Bug 459424 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var viewer =
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIWebNavigation)
|
||||
.QueryInterface(Components.interfaces.nsIDocShell)
|
||||
.contentViewer
|
||||
.QueryInterface(Components.interfaces.nsIMarkupDocumentViewer);
|
||||
viewer.fullZoom = 2;
|
||||
SpecialPowers.setFullZoom(window, 2);
|
||||
is(true, true, "Gotta test something");
|
||||
viewer.fullZoom = 1;
|
||||
SpecialPowers.setFullZoom(window, 1);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
|
@ -19,19 +19,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=465767
|
||||
|
||||
/** Test for Bug 465767 **/
|
||||
|
||||
function CC() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
var node = document.createElement("div");
|
||||
var e = null;
|
||||
try {
|
||||
document.implementation.createDocument("", "", null).adoptNode(node);
|
||||
CC();
|
||||
SpecialPowers.gc();
|
||||
document.implementation.createDocument("", "", null).adoptNode(node);
|
||||
} catch(ex) {
|
||||
e = ex;
|
||||
|
@ -19,12 +19,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=592829
|
||||
// If we ever change how DOMParser initilization works, just update this code
|
||||
// to create a DOMParser which is not allowed to parse XUL.
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var isXUL = true;
|
||||
try {
|
||||
var x =
|
||||
Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
||||
SpecialPowers.Cc
|
||||
.getService(Components.interfaces.nsIDOMParser)
|
||||
.parseFromString('<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>', "text/xml");
|
||||
isXUL = x.documentElement.namespaceURI ==
|
||||
|
@ -209,10 +209,8 @@ function getFile(name) {
|
||||
basePath = xhr.responseText;
|
||||
}
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var fileList = document.getElementById('fileList');
|
||||
fileList.value = basePath + name;
|
||||
SpecialPowers.wrap(fileList).value = basePath + name;
|
||||
|
||||
return fileList.files[0];
|
||||
}
|
||||
|
@ -90,10 +90,7 @@ function start() {
|
||||
kOS = OS_WINDOWS;
|
||||
|
||||
// code borrowed from browser/modules/test/browser_taskbar_preview.js
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var version = SpecialPowers.Cc['@mozilla.org/system-info;1']
|
||||
.getService(SpecialPowers.Ci.nsIPropertyBag2)
|
||||
.getProperty('version');
|
||||
var version = SpecialPowers.Services.sysinfo.getProperty('version');
|
||||
kOSVersion = parseFloat(version);
|
||||
// Version 6.0 is Vista, 6.1 is 7.
|
||||
} else if (navigator.platform.indexOf('Mac') == 0) {
|
||||
@ -139,7 +136,6 @@ function start() {
|
||||
requestLongerTimeoutLen = 6;
|
||||
|
||||
function getEnv(env) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var envsvc = SpecialPowers.Cc["@mozilla.org/process/environment;1"].getService(SpecialPowers.Ci.nsIEnvironment);
|
||||
var val = envsvc.get(env);
|
||||
if (val == "")
|
||||
|
@ -49,8 +49,7 @@ function isRectContainedInRectFromRegion(rect, region) {
|
||||
}
|
||||
|
||||
function paintListener(e) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
if (isRectContainedInRectFromRegion(buttonRect(), e.clientRects)) {
|
||||
if (isRectContainedInRectFromRegion(buttonRect(), SpecialPowers.wrap(e).clientRects)) {
|
||||
gNeedsPaint = false;
|
||||
currentSnapshot = takeSnapshot();
|
||||
}
|
||||
|
@ -65,8 +65,7 @@ function isRectContainedInRectFromRegion(rect, region) {
|
||||
}
|
||||
|
||||
function paintListener(e) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
if (isRectContainedInRectFromRegion(buttonRect(), e.clientRects)) {
|
||||
if (isRectContainedInRectFromRegion(buttonRect(), SpecialPowers.wrap(e).clientRects)) {
|
||||
gNeedsPaint = false;
|
||||
currentSnapshot = takeSnapshot();
|
||||
}
|
||||
|
@ -52,9 +52,7 @@ function clickHandler(e) {
|
||||
|
||||
function doTest() {
|
||||
window.addEventListener("click", clickHandler, true);
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
utils.sendMouseEvent("mousedown", 1, 1, 0, 1, 0);
|
||||
utils.sendMouseEvent("mouseup", 1, 1, 0, 1, 0);
|
||||
|
||||
|
@ -151,8 +151,7 @@ function runTests() {
|
||||
}
|
||||
|
||||
function dispatchTrusted(t, o) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
t.dispatchEvent(new Event("testevent", o));
|
||||
SpecialPowers.dispatchEvent(window, t, new Event("testevent", o));
|
||||
}
|
||||
|
||||
function testAllListener() {
|
||||
|
@ -20,14 +20,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=450876
|
||||
/** Test for Bug 450876 **/
|
||||
|
||||
function doTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
is(document.activeElement, document.body, "body element should be focused");
|
||||
document.getElementById('a').focus();
|
||||
is(document.activeElement, document.getElementById('a'), "link should have focus");
|
||||
var wu = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
is(document.hasFocus(), true, "document should be focused");
|
||||
wu.sendKeyEvent('keypress', 9, 0, 0);
|
||||
SpecialPowers.DOMWindowUtils.sendKeyEvent('keypress', 9, 0, 0);
|
||||
is(document.activeElement, document.getElementById('a'), "body element should be focused");
|
||||
is(document.hasFocus(), false, "document should not be focused");
|
||||
|
||||
|
@ -23,12 +23,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=456273
|
||||
/** Test for Bug 456273 **/
|
||||
|
||||
function doTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var ev = document.createEvent('KeyEvents');
|
||||
ev.initKeyEvent("keypress", true, true, null, true, false,
|
||||
false, false, 0, "z".charCodeAt(0));
|
||||
document.getElementById('edit456273').dispatchEvent(ev);
|
||||
SpecialPowers.dispatchEvent(window, document.getElementById('edit456273'), ev);
|
||||
|
||||
ok(true, "PASS");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
@ -21,19 +21,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=605242
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
function sendMouseDown(el) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var rect = el.getBoundingClientRect();
|
||||
var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
utils.sendMouseEvent('mousedown', rect.left + 5, rect.top + 5, 0, 1, 0);
|
||||
}
|
||||
|
||||
function sendMouseUp(el) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var rect = el.getBoundingClientRect();
|
||||
var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
utils.sendMouseEvent('mouseup', rect.left + 5, rect.top + 5, 0, 1, 0);
|
||||
}
|
||||
|
||||
|
@ -34,11 +34,6 @@ function invalidEventHandler(e)
|
||||
|
||||
function completeValidityCheck(element, alwaysValid, isBarred)
|
||||
{
|
||||
if (element.type == 'file') {
|
||||
// Need privileges to set a filename with .value.
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
}
|
||||
|
||||
// Check when pattern matches.
|
||||
if (element.type == 'email') {
|
||||
element.pattern = ".*@bar.com";
|
||||
@ -48,7 +43,7 @@ function completeValidityCheck(element, alwaysValid, isBarred)
|
||||
element.value = "http://mozilla.com";
|
||||
} else {
|
||||
element.pattern = "foo";
|
||||
element.value = "foo";
|
||||
SpecialPowers.wrap(element).value = "foo";
|
||||
}
|
||||
|
||||
checkValidPattern(element, true, isBarred);
|
||||
@ -63,7 +58,7 @@ function completeValidityCheck(element, alwaysValid, isBarred)
|
||||
element.value = "http://mozilla.org";
|
||||
} else {
|
||||
element.pattern = "foo";
|
||||
element.value = "bar";
|
||||
SpecialPowers.wrap(element).value = "bar";
|
||||
}
|
||||
|
||||
if (!alwaysValid) {
|
||||
|
@ -268,10 +268,8 @@ function checkValidityStateObjectAliveWithoutElement(element)
|
||||
// Then, we make sure it is removed by the garbage collector and we check the
|
||||
// ValidityState default values (it should not crash).
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var v = document.createElement(element).validity;
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils).garbageCollect();
|
||||
SpecialPowers.gc();
|
||||
|
||||
ok(!v.valueMissing,
|
||||
"When the element is not alive, it shouldn't suffer from constraint validation");
|
||||
|
@ -77,9 +77,7 @@ function link123HrefIs(href, testNum) {
|
||||
var gGen;
|
||||
|
||||
function visitedDependentComputedStyle(win, elem, property) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
return utils.getVisitedDependentComputedStyle(elem, "", property);
|
||||
}
|
||||
|
||||
|
@ -25,9 +25,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=406596
|
||||
/** Test for Bug 406596 **/
|
||||
|
||||
function testTabbing(click, focus, selectionOffset) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var wu = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
var wu = SpecialPowers.getDOMWindowUtils(window);
|
||||
|
||||
var elem = document.getElementById(click);
|
||||
var rect = elem.getBoundingClientRect();
|
||||
|
@ -21,9 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=421640
|
||||
/** Test for Bug 421640 **/
|
||||
|
||||
function test(click, focus, nextFocus) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var wu = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
var wu = SpecialPowers.getDOMWindowUtils(window);
|
||||
|
||||
var selection = window.getSelection();
|
||||
var edit = document.getElementById("edit");
|
||||
|
@ -36,15 +36,14 @@ function runTest()
|
||||
range.selectNodeContents(editor);
|
||||
var prevStr = range.toString();
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var docShell =
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIWebNavigation)
|
||||
.QueryInterface(Components.interfaces.nsIDocShell);
|
||||
var docShell = SpecialPowers.wrap(window)
|
||||
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
|
||||
.getInterface(SpecialPowers.Ci.nsIWebNavigation)
|
||||
.QueryInterface(SpecialPowers.Ci.nsIDocShell);
|
||||
var controller =
|
||||
docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsISelectionDisplay)
|
||||
.QueryInterface(Components.interfaces.nsISelectionController);
|
||||
docShell.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
|
||||
.getInterface(SpecialPowers.Ci.nsISelectionDisplay)
|
||||
.QueryInterface(SpecialPowers.Ci.nsISelectionController);
|
||||
var sel = controller.getSelection(controller.SELECTION_NORMAL);
|
||||
sel.collapse(anchorInEditor, 0);
|
||||
synthesizeKey('a', {});
|
||||
|
@ -51,10 +51,7 @@ function checkLinkColor(aElmId, aExpectedColor, aMessage) {
|
||||
// Because link coloring is asynchronous, we wait until we get the right
|
||||
// result, or we will time out (resulting in a failure).
|
||||
function getColor() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var utils = document.defaultView.
|
||||
QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
return utils.getVisitedDependentComputedStyle($(aElmId), "", "color");
|
||||
}
|
||||
while (getColor() != aExpectedColor) {
|
||||
|
@ -152,10 +152,7 @@ function clickImage(aTarget, aX, aY)
|
||||
aTarget.style.left = "0";
|
||||
aTarget.offsetTop;
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var wu = aTarget.ownerDocument.defaultView
|
||||
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
var wu = SpecialPowers.getDOMWindowUtils(aTarget.ownerDocument.defaultView);
|
||||
|
||||
wu.sendMouseEvent('mousedown', aX, aY, 0, 1, 0);
|
||||
wu.sendMouseEvent('mouseup', aX, aY, 0, 0, 0);
|
||||
|
@ -20,10 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=446483
|
||||
/** Test for Bug 446483 **/
|
||||
|
||||
function gc() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
SpecialPowers.gc();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
@ -27,10 +27,7 @@ function testViewport() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
/* Grab Viewport Metadata from the document header. */
|
||||
var iRequester =
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
|
||||
var windowUtils =
|
||||
iRequester.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
var windowUtils = SpecialPowers.getDOMWindowUtils(window);
|
||||
var vpWidth =
|
||||
parseInt(windowUtils.getDocumentMetadata("viewport-width"));
|
||||
var vpHeight =
|
||||
|
@ -120,7 +120,7 @@ interface ScheduledGCCallback : nsISupports
|
||||
/**
|
||||
* interface of Components.utils
|
||||
*/
|
||||
[scriptable, uuid(3b3b5adc-dce0-4d4e-a04e-06b2c8b5acfe)]
|
||||
[scriptable, uuid(c9ccec7a-726c-4479-9438-6f51f6ef4170)]
|
||||
interface nsIXPCComponents_Utils : nsISupports
|
||||
{
|
||||
|
||||
@ -156,6 +156,32 @@ interface nsIXPCComponents_Utils : nsISupports
|
||||
[optional] in jsval filename,
|
||||
[optional] in long lineNo);
|
||||
|
||||
/*
|
||||
* getSandboxMetadata is designed to be called from JavaScript only.
|
||||
*
|
||||
* getSandboxMetadata retrieves the metadata associated with
|
||||
* a sandbox object. It will return undefined if there
|
||||
* is no metadata attached to the sandbox.
|
||||
*
|
||||
* var s = C.u.Sandbox(..., { metadata: "metadata" });
|
||||
* var metadata = C.u.getSandboxMetadata(s);
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
jsval getSandboxMetadata(in jsval sandbox);
|
||||
|
||||
/*
|
||||
* setSandboxMetadata is designed to be called from JavaScript only.
|
||||
*
|
||||
* setSandboxMetadata sets the metadata associated with
|
||||
* a sandbox object.
|
||||
*
|
||||
* Note that the metadata object will be copied before being used.
|
||||
* The copy will be performed using the structured clone algorithm.
|
||||
* Note that this algorithm does not support reflectors and
|
||||
* it will throw if it encounters them.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
void setSandboxMetadata(in jsval sandbox, in jsval metadata);
|
||||
|
||||
/*
|
||||
* import is designed to be called from JavaScript only.
|
||||
|
@ -585,9 +585,11 @@ sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue
|
||||
return JS_ConvertStub(cx, obj, type, vp);
|
||||
}
|
||||
|
||||
#define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
|
||||
|
||||
static const JSClass SandboxClass = {
|
||||
"Sandbox",
|
||||
XPCONNECT_GLOBAL_FLAGS,
|
||||
XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
|
||||
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize,
|
||||
NULL, NULL, NULL, NULL, TraceXPCGlobal
|
||||
@ -1042,6 +1044,8 @@ xpc::CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, Sandb
|
||||
// about:memory may use that information
|
||||
xpc::SetLocationForGlobal(sandbox, options.sandboxName);
|
||||
|
||||
xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
|
||||
|
||||
JS_FireOnNewGlobalObject(cx, sandbox);
|
||||
|
||||
return NS_OK;
|
||||
@ -1339,6 +1343,10 @@ ParseOptionsObject(JSContext *cx, jsval from, SandboxOptions &options)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = GetDOMConstructorsFromOptions(cx, optionsObject, options);
|
||||
|
||||
bool found;
|
||||
rv = GetPropFromOptions(cx, optionsObject,
|
||||
"metadata", &options.metadata, &found);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
@ -1651,3 +1659,40 @@ xpc::NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, boo
|
||||
vp.setObject(*funobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
xpc::GetSandboxMetadata(JSContext *cx, HandleObject sandbox, MutableHandleValue rval)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(IsSandbox(sandbox));
|
||||
|
||||
RootedValue metadata(cx);
|
||||
{
|
||||
JSAutoCompartment ac(cx, sandbox);
|
||||
metadata = JS_GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT);
|
||||
}
|
||||
|
||||
if (!JS_WrapValue(cx, metadata.address()))
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
rval.set(metadata);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
xpc::SetSandboxMetadata(JSContext *cx, HandleObject sandbox, HandleValue metadataArg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(IsSandbox(sandbox));
|
||||
|
||||
RootedValue metadata(cx);
|
||||
|
||||
JSAutoCompartment ac(cx, sandbox);
|
||||
if (!JS_StructuredClone(cx, metadataArg, metadata.address(), NULL, NULL))
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
JS_SetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT, metadata);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2766,6 +2766,46 @@ nsXPCComponents_Utils::EvalInSandbox(const nsAString& source,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::GetSandboxMetadata(const JS::Value &sandboxVal,
|
||||
JSContext *cx, JS::Value *rval)
|
||||
{
|
||||
if (!sandboxVal.isObject())
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
RootedObject sandbox(cx, &sandboxVal.toObject());
|
||||
sandbox = js::CheckedUnwrap(sandbox);
|
||||
if (!sandbox || !xpc::IsSandbox(sandbox))
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
RootedValue metadata(cx);
|
||||
nsresult rv = xpc::GetSandboxMetadata(cx, sandbox, &metadata);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
*rval = metadata;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::SetSandboxMetadata(const JS::Value &sandboxVal,
|
||||
const JS::Value &metadataVal,
|
||||
JSContext *cx)
|
||||
{
|
||||
if (!sandboxVal.isObject())
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
RootedObject sandbox(cx, &sandboxVal.toObject());
|
||||
sandbox = js::CheckedUnwrap(sandbox);
|
||||
if (!sandbox || !xpc::IsSandbox(sandbox))
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
RootedValue metadata(cx, metadataVal);
|
||||
nsresult rv = xpc::SetSandboxMetadata(cx, sandbox, metadata);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* JSObject import (in AUTF8String registryLocation,
|
||||
* [optional] in JSObject targetObj);
|
||||
*/
|
||||
|
@ -3604,6 +3604,7 @@ struct SandboxOptions {
|
||||
, wantExportHelpers(false)
|
||||
, proto(xpc_GetSafeJSContext())
|
||||
, sameZoneAs(xpc_GetSafeJSContext())
|
||||
, metadata(xpc_GetSafeJSContext())
|
||||
{ }
|
||||
|
||||
bool wantXrays;
|
||||
@ -3613,6 +3614,7 @@ struct SandboxOptions {
|
||||
nsCString sandboxName;
|
||||
JS::RootedObject sameZoneAs;
|
||||
DOMConstructors DOMConstructors;
|
||||
JS::RootedValue metadata;
|
||||
};
|
||||
|
||||
JSObject *
|
||||
@ -3647,8 +3649,19 @@ EvalInSandbox(JSContext *cx, JS::HandleObject sandbox, const nsAString& source,
|
||||
JSVersion jsVersion, bool returnStringOnly,
|
||||
JS::MutableHandleValue rval);
|
||||
|
||||
// Helper for retrieving metadata stored in a reserved slot. The metadata
|
||||
// is set during the sandbox creation using the "metadata" option.
|
||||
nsresult
|
||||
GetSandboxMetadata(JSContext *cx, JS::HandleObject sandboxArg,
|
||||
JS::MutableHandleValue rval);
|
||||
|
||||
nsresult
|
||||
SetSandboxMetadata(JSContext *cx, JS::HandleObject sandboxArg,
|
||||
JS::HandleValue metadata);
|
||||
|
||||
} /* namespace xpc */
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
// Inlined utilities.
|
||||
|
||||
|
45
js/xpconnect/tests/unit/test_sandbox_metadata.js
Normal file
45
js/xpconnect/tests/unit/test_sandbox_metadata.js
Normal file
@ -0,0 +1,45 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* See https://bugzilla.mozilla.org/show_bug.cgi?id=898559 */
|
||||
|
||||
function run_test()
|
||||
{
|
||||
let sandbox = Components.utils.Sandbox("http://www.blah.com", {
|
||||
metadata: "test metadata"
|
||||
});
|
||||
|
||||
do_check_eq(Components.utils.getSandboxMetadata(sandbox), "test metadata");
|
||||
|
||||
let sandbox = Components.utils.Sandbox("http://www.blah.com", {
|
||||
metadata: { foopy: { bar: 2 }, baz: "hi" }
|
||||
});
|
||||
|
||||
let metadata = Components.utils.getSandboxMetadata(sandbox);
|
||||
do_check_eq(metadata.baz, "hi");
|
||||
do_check_eq(metadata.foopy.bar, 2);
|
||||
metadata.baz = "foo";
|
||||
|
||||
metadata = Components.utils.getSandboxMetadata(sandbox);
|
||||
do_check_eq(metadata.baz, "foo");
|
||||
|
||||
metadata = { foo: "bar" };
|
||||
Components.utils.setSandboxMetadata(sandbox, metadata);
|
||||
metadata.foo = "baz";
|
||||
metadata = Components.utils.getSandboxMetadata(sandbox);
|
||||
do_check_eq(metadata.foo, "bar");
|
||||
|
||||
let thrown = false;
|
||||
let reflector = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
|
||||
try {
|
||||
Components.utils.setSandboxMetadata(sandbox, { foo: reflector });
|
||||
} catch(e) {
|
||||
thrown = true;
|
||||
}
|
||||
|
||||
do_check_eq(thrown, true);
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ fail-if = os == "android"
|
||||
[test_allowedDomains.js]
|
||||
[test_allowedDomainsXHR.js]
|
||||
[test_nuke_sandbox.js]
|
||||
[test_sandbox_metadata.js]
|
||||
[test_exportFunction.js]
|
||||
[test_textDecoder.js]
|
||||
[test_watchdog_enable.js]
|
||||
|
@ -955,13 +955,12 @@ public class GeckoAppShell
|
||||
|
||||
@GeneratableAndroidBridgeTarget(stubName = "GetMimeTypeFromExtensionsWrapper")
|
||||
static String getMimeTypeFromExtensions(String aFileExt) {
|
||||
MimeTypeMap mtm = MimeTypeMap.getSingleton();
|
||||
StringTokenizer st = new StringTokenizer(aFileExt, ".,; ");
|
||||
String type = null;
|
||||
String subType = null;
|
||||
while (st.hasMoreElements()) {
|
||||
String ext = st.nextToken();
|
||||
String mt = mtm.getMimeTypeFromExtension(ext);
|
||||
String mt = getMimeTypeFromExtension(ext);
|
||||
if (mt == null)
|
||||
continue;
|
||||
int slash = mt.indexOf('/');
|
||||
@ -1746,6 +1745,14 @@ public class GeckoAppShell
|
||||
|
||||
@GeneratableAndroidBridgeTarget
|
||||
public static void scanMedia(String aFile, String aMimeType) {
|
||||
// If the platform didn't give us a mimetype, try to guess one from the filename
|
||||
if (TextUtils.isEmpty(aMimeType)) {
|
||||
int extPosition = aFile.lastIndexOf(".");
|
||||
if (extPosition > 0 && extPosition < aFile.length() - 1) {
|
||||
aMimeType = getMimeTypeFromExtension(aFile.substring(extPosition+1));
|
||||
}
|
||||
}
|
||||
|
||||
Context context = getContext();
|
||||
GeckoMediaScannerClient.startScan(context, aFile, aMimeType);
|
||||
}
|
||||
@ -1781,10 +1788,14 @@ public class GeckoAppShell
|
||||
}
|
||||
}
|
||||
|
||||
private static String getMimeTypeFromExtension(String ext) {
|
||||
final MimeTypeMap mtm = MimeTypeMap.getSingleton();
|
||||
return mtm.getMimeTypeFromExtension(ext);
|
||||
}
|
||||
|
||||
private static Drawable getDrawableForExtension(PackageManager pm, String aExt) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
MimeTypeMap mtm = MimeTypeMap.getSingleton();
|
||||
String mimeType = mtm.getMimeTypeFromExtension(aExt);
|
||||
final String mimeType = getMimeTypeFromExtension(aExt);
|
||||
if (mimeType != null && mimeType.length() > 0)
|
||||
intent.setType(mimeType);
|
||||
else
|
||||
|
@ -68,7 +68,7 @@ public final class GeckoProfile {
|
||||
}
|
||||
|
||||
GeckoProfile guest = GeckoProfile.getGuestProfile(context);
|
||||
// if the guest profile exists and is locked, return it
|
||||
// if the guest profile is locked, return it
|
||||
if (guest != null && guest.locked()) {
|
||||
return guest;
|
||||
}
|
||||
@ -231,14 +231,21 @@ public final class GeckoProfile {
|
||||
return mLocked == LockState.LOCKED;
|
||||
}
|
||||
|
||||
File lockFile = new File(getDir(), LOCK_FILE_NAME);
|
||||
boolean res = lockFile.exists();
|
||||
mLocked = res ? LockState.LOCKED : LockState.UNLOCKED;
|
||||
return res;
|
||||
// Don't use getDir() as it will create a dir if none exists
|
||||
if (mDir != null && mDir.exists()) {
|
||||
File lockFile = new File(mDir, LOCK_FILE_NAME);
|
||||
boolean res = lockFile.exists();
|
||||
mLocked = res ? LockState.LOCKED : LockState.UNLOCKED;
|
||||
} else {
|
||||
mLocked = LockState.UNLOCKED;
|
||||
}
|
||||
|
||||
return mLocked == LockState.LOCKED;
|
||||
}
|
||||
|
||||
public boolean lock() {
|
||||
try {
|
||||
// If this dir doesn't exist getDir will create it for us
|
||||
File lockFile = new File(getDir(), LOCK_FILE_NAME);
|
||||
boolean result = lockFile.createNewFile();
|
||||
if (result) {
|
||||
@ -255,8 +262,13 @@ public final class GeckoProfile {
|
||||
}
|
||||
|
||||
public boolean unlock() {
|
||||
// Don't use getDir() as it will create a dir
|
||||
if (mDir == null || !mDir.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
File lockFile = new File(getDir(), LOCK_FILE_NAME);
|
||||
File lockFile = new File(mDir, LOCK_FILE_NAME);
|
||||
boolean result = delete(lockFile);
|
||||
if (result) {
|
||||
mLocked = LockState.UNLOCKED;
|
||||
|
@ -689,7 +689,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
||||
Log.d(LOG_TAG, "Ignoring search without location.");
|
||||
return;
|
||||
}
|
||||
recordSearch(message.getString("identifier"), message.getString("location"));
|
||||
recordSearch(message.optString("identifier", null), message.getString("location"));
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -479,7 +479,7 @@ pref("devtools.debugger.force-local", true);
|
||||
// Display a prompt when a new connection starts to accept/reject it
|
||||
pref("devtools.debugger.prompt-connection", true);
|
||||
// Temporary setting to enable webapps actors
|
||||
pref("devtools.debugger.enable-content-actors", false);
|
||||
pref("devtools.debugger.enable-content-actors", true);
|
||||
|
||||
// view source
|
||||
pref("view_source.syntax_highlight", true);
|
||||
|
52
netwerk/test/unit/test_about_networking.js
Normal file
52
netwerk/test/unit/test_about_networking.js
Normal file
@ -0,0 +1,52 @@
|
||||
/* -*- Mode: Javasript; indent-tab-mode: nil; js-indent-level: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
let gDashboard = Cc['@mozilla.org/network/dashboard;1']
|
||||
.getService(Ci.nsIDashboard);
|
||||
|
||||
const RESOLVE_DISABLE_IPV6 = (1 << 5);
|
||||
|
||||
add_test(function test_http() {
|
||||
gDashboard.requestHttpConnections(function(data) {
|
||||
do_check_neq(data.host.indexOf("example.com"), -1);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_dns() {
|
||||
gDashboard.requestDNSInfo(function(data) {
|
||||
do_check_neq(data.hostname.indexOf("example.com"), -1);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_sockets() {
|
||||
let dns = Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService);
|
||||
let record = dns.resolve("example.com", RESOLVE_DISABLE_IPV6);
|
||||
let answer = record.getNextAddrAsString();
|
||||
|
||||
gDashboard.requestSockets(function(data) {
|
||||
do_check_neq(data.host.indexOf(answer), -1);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
let ioService = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
let uri = ioService.newURI("http://example.com", null, null);
|
||||
let channel = ioService.newChannelFromURI(uri);
|
||||
|
||||
channel.open();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
@ -246,3 +246,4 @@ skip-if = os == "android"
|
||||
[test_tldservice_nextsubdomain.js]
|
||||
[test_about_protocol.js]
|
||||
[test_bug856978.js]
|
||||
[test_about_networking.js]
|
||||
|
@ -816,6 +816,10 @@ Download.prototype = {
|
||||
serializable.error = { message: this.error.message };
|
||||
}
|
||||
|
||||
if (this.startTime) {
|
||||
serializable.startTime = this.startTime.toJSON();
|
||||
}
|
||||
|
||||
// These are serialized unless they are false, null, or empty strings.
|
||||
for (let property of kSerializableDownloadProperties) {
|
||||
if (property != "error" && this[property]) {
|
||||
@ -853,7 +857,6 @@ const kSerializableDownloadProperties = [
|
||||
"succeeded",
|
||||
"canceled",
|
||||
"error",
|
||||
"startTime",
|
||||
"totalBytes",
|
||||
"hasPartialData",
|
||||
"tryToKeepPartialData",
|
||||
@ -901,6 +904,13 @@ Download.fromSerializable = function (aSerializable) {
|
||||
}
|
||||
download.saver.download = download;
|
||||
|
||||
if ("startTime" in aSerializable) {
|
||||
let time = aSerializable.startTime.getTime
|
||||
? aSerializable.startTime.getTime()
|
||||
: aSerializable.startTime;
|
||||
download.startTime = new Date(time);
|
||||
}
|
||||
|
||||
for (let property of kSerializableDownloadProperties) {
|
||||
if (property in aSerializable) {
|
||||
download[property] = aSerializable[property];
|
||||
|
@ -106,7 +106,7 @@ this.DownloadImport.prototype = {
|
||||
|
||||
let autoResume = false;
|
||||
try {
|
||||
autoResume = row.getResultByName("autoResume");
|
||||
autoResume = (row.getResultByName("autoResume") == 1);
|
||||
} catch (ex) {
|
||||
// autoResume wasn't present in schema version 7
|
||||
}
|
||||
@ -153,7 +153,7 @@ this.DownloadImport.prototype = {
|
||||
type: "copy",
|
||||
entityID: entityID
|
||||
},
|
||||
startTime: startTime,
|
||||
startTime: new Date(startTime / 1000),
|
||||
totalBytes: maxBytes,
|
||||
hasPartialData: !!tempPath,
|
||||
tryToKeepPartialData: true,
|
||||
|
@ -65,31 +65,6 @@ function promiseDownloadMidway(aDownload) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a download to finish, in case it has not finished already.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object to wait upon.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the download has finished successfully.
|
||||
* @rejects JavaScript exception if the download failed.
|
||||
*/
|
||||
function promiseDownloadStopped(aDownload) {
|
||||
if (!aDownload.stopped) {
|
||||
// The download is in progress, wait for the current attempt to finish and
|
||||
// report any errors that may occur.
|
||||
return aDownload.start();
|
||||
}
|
||||
|
||||
if (aDownload.succeeded) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// The download failed or was canceled.
|
||||
return Promise.reject(aDownload.error || new Error("Download canceled."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and starts a new download, configured to keep partial data, and
|
||||
* returns only when the first part of "interruptible_resumable.txt" has been
|
||||
@ -1624,6 +1599,25 @@ add_task(function test_contentType() {
|
||||
do_check_eq("text/plain", download.contentType);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that the serialization/deserialization of the startTime Date
|
||||
* object works correctly.
|
||||
*/
|
||||
add_task(function test_toSerializable_startTime()
|
||||
{
|
||||
let download1 = yield promiseStartDownload(httpUrl("source.txt"));
|
||||
yield promiseDownloadStopped(download1);
|
||||
|
||||
let serializable = download1.toSerializable();
|
||||
let reserialized = JSON.parse(JSON.stringify(serializable));
|
||||
|
||||
let download2 = yield Downloads.createDownload(reserialized);
|
||||
|
||||
do_check_eq(download1.startTime.constructor.name, "Date");
|
||||
do_check_eq(download2.startTime.constructor.name, "Date");
|
||||
do_check_eq(download1.startTime.toJSON(), download2.startTime.toJSON());
|
||||
});
|
||||
|
||||
/**
|
||||
* This test will call the platform specific operations within
|
||||
* DownloadPlatform::DownloadDone. While there is no test to verify the
|
||||
|
@ -464,6 +464,31 @@ function promiseStartExternalHelperAppServiceDownload(aSourceUrl) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a download to finish, in case it has not finished already.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object to wait upon.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the download has finished successfully.
|
||||
* @rejects JavaScript exception if the download failed.
|
||||
*/
|
||||
function promiseDownloadStopped(aDownload) {
|
||||
if (!aDownload.stopped) {
|
||||
// The download is in progress, wait for the current attempt to finish and
|
||||
// report any errors that may occur.
|
||||
return aDownload.start();
|
||||
}
|
||||
|
||||
if (aDownload.succeeded) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// The download failed or was canceled.
|
||||
return Promise.reject(aDownload.error || new Error("Download canceled."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new public DownloadList object.
|
||||
*
|
||||
|
701
toolkit/components/jsdownloads/test/unit/test_DownloadImport.js
Normal file
701
toolkit/components/jsdownloads/test/unit/test_DownloadImport.js
Normal file
@ -0,0 +1,701 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the DownloadImport object.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
|
||||
"resource://gre/modules/Sqlite.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadImport",
|
||||
"resource://gre/modules/DownloadImport.jsm");
|
||||
|
||||
// Importable states
|
||||
const DOWNLOAD_NOTSTARTED = -1;
|
||||
const DOWNLOAD_DOWNLOADING = 0;
|
||||
const DOWNLOAD_PAUSED = 4;
|
||||
const DOWNLOAD_QUEUED = 5;
|
||||
|
||||
// Non importable states
|
||||
const DOWNLOAD_FAILED = 2;
|
||||
const DOWNLOAD_CANCELED = 3;
|
||||
const DOWNLOAD_BLOCKED_PARENTAL = 6;
|
||||
const DOWNLOAD_SCANNING = 7;
|
||||
const DOWNLOAD_DIRTY = 8;
|
||||
const DOWNLOAD_BLOCKED_POLICY = 9;
|
||||
|
||||
// The TEST_DATA_TAINTED const is a version of TEST_DATA_SHORT in which the
|
||||
// beginning of the data was changed (with the TEST_DATA_REPLACEMENT value).
|
||||
// We use this to test that the entityID is properly imported and the download
|
||||
// can be resumed from where it was paused.
|
||||
// For simplification purposes, the test requires that TEST_DATA_SHORT and
|
||||
// TEST_DATA_TAINTED have the same length.
|
||||
const TEST_DATA_REPLACEMENT = "-changed- ";
|
||||
const TEST_DATA_TAINTED = TEST_DATA_REPLACEMENT +
|
||||
TEST_DATA_SHORT.substr(TEST_DATA_REPLACEMENT.length);
|
||||
const TEST_DATA_LENGTH = TEST_DATA_SHORT.length;
|
||||
|
||||
// The length of the partial file that we'll write to disk as an existing
|
||||
// ongoing download.
|
||||
const TEST_DATA_PARTIAL_LENGTH = TEST_DATA_REPLACEMENT.length;
|
||||
|
||||
// The value of the "maxBytes" column stored in the DB about the downloads.
|
||||
// It's intentionally different than TEST_DATA_LENGTH to test that each value
|
||||
// is seen when expected.
|
||||
const MAXBYTES_IN_DB = TEST_DATA_LENGTH - 10;
|
||||
|
||||
let gDownloadsRowToImport;
|
||||
let gDownloadsRowNonImportable;
|
||||
|
||||
/**
|
||||
* Creates a database with an empty moz_downloads table and leaves an
|
||||
* open connection to it.
|
||||
*
|
||||
* @param aPath
|
||||
* String containing the path of the database file to be created.
|
||||
* @param aSchemaVersion
|
||||
* Number with the version of the database schema to set.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The open connection to the database.
|
||||
* @rejects If an error occurred during the database creation.
|
||||
*/
|
||||
function promiseEmptyDatabaseConnection({aPath, aSchemaVersion}) {
|
||||
return Task.spawn(function () {
|
||||
let connection = yield Sqlite.openConnection({ path: aPath });
|
||||
|
||||
yield connection.execute("CREATE TABLE moz_downloads ("
|
||||
+ "id INTEGER PRIMARY KEY,"
|
||||
+ "name TEXT,"
|
||||
+ "source TEXT,"
|
||||
+ "target TEXT,"
|
||||
+ "tempPath TEXT,"
|
||||
+ "startTime INTEGER,"
|
||||
+ "endTime INTEGER,"
|
||||
+ "state INTEGER,"
|
||||
+ "referrer TEXT,"
|
||||
+ "entityID TEXT,"
|
||||
+ "currBytes INTEGER NOT NULL DEFAULT 0,"
|
||||
+ "maxBytes INTEGER NOT NULL DEFAULT -1,"
|
||||
+ "mimeType TEXT,"
|
||||
+ "preferredApplication TEXT,"
|
||||
+ "preferredAction INTEGER NOT NULL DEFAULT 0,"
|
||||
+ "autoResume INTEGER NOT NULL DEFAULT 0,"
|
||||
+ "guid TEXT)");
|
||||
|
||||
yield connection.setSchemaVersion(aSchemaVersion);
|
||||
|
||||
throw new Task.Result(connection);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new entry in the database with the given columns' values.
|
||||
*
|
||||
* @param aConnection
|
||||
* The database connection.
|
||||
* @param aDownloadRow
|
||||
* An object representing the values for each column of the row
|
||||
* being inserted.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the operation completes.
|
||||
* @rejects If there's an error inserting the row.
|
||||
*/
|
||||
function promiseInsertRow(aConnection, aDownloadRow) {
|
||||
// We can't use the aDownloadRow obj directly in the execute statement
|
||||
// because the obj bind code in Sqlite.jsm doesn't allow objects
|
||||
// with extra properties beyond those being binded. So we might as well
|
||||
// use an array as it is simpler.
|
||||
let values = [
|
||||
aDownloadRow.source, aDownloadRow.target, aDownloadRow.tempPath,
|
||||
aDownloadRow.startTime.getTime() * 1000, aDownloadRow.state,
|
||||
aDownloadRow.referrer, aDownloadRow.entityID, aDownloadRow.maxBytes,
|
||||
aDownloadRow.mimeType, aDownloadRow.preferredApplication,
|
||||
aDownloadRow.preferredAction, aDownloadRow.autoResume
|
||||
];
|
||||
|
||||
return aConnection.execute("INSERT INTO moz_downloads ("
|
||||
+ "name, source, target, tempPath, startTime,"
|
||||
+ "endTime, state, referrer, entityID, currBytes,"
|
||||
+ "maxBytes, mimeType, preferredApplication,"
|
||||
+ "preferredAction, autoResume, guid)"
|
||||
+ "VALUES ("
|
||||
+ "'', ?, ?, ?, ?, " //name,
|
||||
+ "0, ?, ?, ?, 0, " //endTime, currBytes
|
||||
+ " ?, ?, ?, " //
|
||||
+ " ?, ?, '')", //and guid are not imported
|
||||
values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the number of rows in the moz_downloads table of the
|
||||
* database.
|
||||
*
|
||||
* @param aConnection
|
||||
* The database connection.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves With the number of rows.
|
||||
* @rejects Never.
|
||||
*/
|
||||
function promiseTableCount(aConnection) {
|
||||
return aConnection.execute("SELECT COUNT(*) FROM moz_downloads")
|
||||
.then(res => res[0].getResultByName("COUNT(*)"))
|
||||
.then(null, Cu.reportError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Briefly opens a network channel to a given URL to retrieve
|
||||
* the entityID of this url, as generated by the network code.
|
||||
*
|
||||
* @param aUrl
|
||||
* The URL to retrieve the entityID.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The EntityID of the given URL.
|
||||
* @rejects When there's a problem accessing the URL.
|
||||
*/
|
||||
function promiseEntityID(aUrl) {
|
||||
let deferred = Promise.defer();
|
||||
let entityID = "";
|
||||
let channel = NetUtil.newChannel(NetUtil.newURI(aUrl));
|
||||
|
||||
channel.asyncOpen({
|
||||
onStartRequest: function (aRequest) {
|
||||
if (aRequest instanceof Ci.nsIResumableChannel) {
|
||||
entityID = aRequest.entityID;
|
||||
}
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
},
|
||||
|
||||
onStopRequest: function (aRequest, aContext, aStatusCode) {
|
||||
if (aStatusCode == Cr.NS_BINDING_ABORTED) {
|
||||
deferred.resolve(entityID);
|
||||
} else {
|
||||
deferred.reject("Unexpected status code received");
|
||||
}
|
||||
},
|
||||
|
||||
onDataAvailable: function () {}
|
||||
}, null);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a file path to a temporary writeable download target, in the
|
||||
* correct format as expected to be stored in the downloads database,
|
||||
* which is file:///absolute/path/to/file
|
||||
*
|
||||
* @param aLeafName
|
||||
* A hint leaf name for the file.
|
||||
*
|
||||
* @return String The path to the download target.
|
||||
*/
|
||||
function getDownloadTarget(aLeafName) {
|
||||
return NetUtil.newURI(getTempFile(aLeafName)).spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a temporary partial file to use as an in-progress
|
||||
* download. The file is written to disk with a part of the total expected
|
||||
* download content pre-written.
|
||||
*
|
||||
* @param aLeafName
|
||||
* A hint leaf name for the file.
|
||||
* @param aTainted
|
||||
* A boolean value. When true, the partial content of the file
|
||||
* will be different from the expected content of the original source
|
||||
* file. See the declaration of TEST_DATA_TAINTED for more information.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the operation completes, and returns a string with the path
|
||||
* to the generated file.
|
||||
* @rejects If there's an error writing the file.
|
||||
*/
|
||||
function getPartialFile(aLeafName, aTainted = false) {
|
||||
let tempDownload = getTempFile(aLeafName);
|
||||
let partialContent = aTainted
|
||||
? TEST_DATA_TAINTED.substr(0, TEST_DATA_PARTIAL_LENGTH)
|
||||
: TEST_DATA_SHORT.substr(0, TEST_DATA_PARTIAL_LENGTH);
|
||||
|
||||
return OS.File.writeAtomic(tempDownload.path,
|
||||
partialContent,
|
||||
{ bytes: TEST_DATA_PARTIAL_LENGTH })
|
||||
.then(() => tempDownload.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Date object to be used as the startTime for the download rows
|
||||
* in the DB. A date that is obviously different from the current time is
|
||||
* generated to make sure this stored data and a `new Date()` can't collide.
|
||||
*
|
||||
* @param aOffset
|
||||
* A offset from the base generated date is used to differentiate each
|
||||
* row in the database.
|
||||
*
|
||||
* @return A Date object.
|
||||
*/
|
||||
function getStartTime(aOffset) {
|
||||
return new Date(1000000 + (aOffset * 10000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs various checks on an imported Download object to make sure
|
||||
* all properties are properly set as expected from the import procedure.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object to be checked.
|
||||
* @param aDownloadRow
|
||||
* An object that represents a row from the original database table,
|
||||
* with extra properties describing expected values that are not
|
||||
* explictly part of the database.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the operation completes
|
||||
* @rejects Never
|
||||
*/
|
||||
function checkDownload(aDownload, aDownloadRow) {
|
||||
return Task.spawn(function() {
|
||||
do_check_eq(aDownload.source.url, aDownloadRow.source);
|
||||
do_check_eq(aDownload.source.referrer, aDownloadRow.referrer);
|
||||
|
||||
do_check_eq(aDownload.target.path,
|
||||
NetUtil.newURI(aDownloadRow.target)
|
||||
.QueryInterface(Ci.nsIFileURL).file.path);
|
||||
|
||||
do_check_eq(aDownload.target.partFilePath, aDownloadRow.tempPath);
|
||||
|
||||
if (aDownloadRow.expectedResume) {
|
||||
do_check_true(!aDownload.stopped || aDownload.succeeded);
|
||||
yield promiseDownloadStopped(aDownload);
|
||||
|
||||
do_check_true(aDownload.succeeded);
|
||||
do_check_eq(aDownload.progress, 100);
|
||||
// If the download has resumed, a new startTime will be set.
|
||||
// By calling toJSON we're also testing that startTime is a Date object.
|
||||
do_check_neq(aDownload.startTime.toJSON(),
|
||||
aDownloadRow.startTime.toJSON());
|
||||
} else {
|
||||
do_check_false(aDownload.succeeded);
|
||||
do_check_eq(aDownload.startTime.toJSON(),
|
||||
aDownloadRow.startTime.toJSON());
|
||||
}
|
||||
|
||||
do_check_eq(aDownload.stopped, true);
|
||||
|
||||
let serializedSaver = aDownload.saver.toSerializable();
|
||||
if (typeof(serializedSaver) == "object") {
|
||||
do_check_eq(serializedSaver.type, "copy");
|
||||
} else {
|
||||
do_check_eq(serializedSaver, "copy");
|
||||
}
|
||||
|
||||
if (aDownloadRow.entityID) {
|
||||
do_check_eq(aDownload.saver.entityID, aDownloadRow.entityID);
|
||||
}
|
||||
|
||||
do_check_eq(aDownload.currentBytes, aDownloadRow.expectedCurrentBytes);
|
||||
do_check_eq(aDownload.totalBytes, aDownloadRow.expectedTotalBytes);
|
||||
|
||||
if (aDownloadRow.expectedContent) {
|
||||
let fileToCheck = aDownloadRow.expectedResume
|
||||
? aDownload.target.path
|
||||
: aDownload.target.partFilePath;
|
||||
yield promiseVerifyContents(fileToCheck, aDownloadRow.expectedContent);
|
||||
}
|
||||
|
||||
do_check_eq(aDownload.contentType, aDownloadRow.expectedContentType);
|
||||
do_check_eq(aDownload.launcherPath, aDownloadRow.preferredApplication);
|
||||
|
||||
do_check_eq(aDownload.launchWhenSucceeded,
|
||||
aDownloadRow.preferredAction != Ci.nsIMIMEInfo.saveToDisk);
|
||||
});
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Preparation tasks
|
||||
|
||||
/**
|
||||
* Prepares the list of downloads to be added to the database that should
|
||||
* be imported by the import procedure.
|
||||
*/
|
||||
add_task(function prepareDownloadsToImport() {
|
||||
|
||||
let sourceUrl = httpUrl("source.txt");
|
||||
let sourceEntityId = yield promiseEntityID(sourceUrl);
|
||||
|
||||
gDownloadsRowToImport = [
|
||||
// Paused download with autoResume and a partial file. By
|
||||
// setting the correct entityID the download can resume from
|
||||
// where it stopped, and to test that this works properly we
|
||||
// intentionally set different data in the beginning of the
|
||||
// partial file to make sure it was not replaced.
|
||||
{
|
||||
source: sourceUrl,
|
||||
target: getDownloadTarget("inprogress1.txt"),
|
||||
tempPath: yield getPartialFile("inprogress1.txt.part", true),
|
||||
startTime: getStartTime(1),
|
||||
state: DOWNLOAD_PAUSED,
|
||||
referrer: httpUrl("referrer1"),
|
||||
entityID: sourceEntityId,
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "mimeType1",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication1",
|
||||
autoResume: 1,
|
||||
|
||||
// Even though the information stored in the DB said
|
||||
// maxBytes was MAXBYTES_IN_DB, the download turned out to be
|
||||
// a different length. Here we make sure the totalBytes property
|
||||
// was correctly set with the actual value. The same consideration
|
||||
// applies to the contentType.
|
||||
expectedCurrentBytes: TEST_DATA_LENGTH,
|
||||
expectedTotalBytes: TEST_DATA_LENGTH,
|
||||
expectedResume: true,
|
||||
expectedContentType: "text/plain",
|
||||
expectedContent: TEST_DATA_TAINTED,
|
||||
},
|
||||
|
||||
// Paused download with autoResume and a partial file,
|
||||
// but missing entityID. This means that the download will
|
||||
// start from beginning, and the entire original content of the
|
||||
// source file should replace the different data that was stored
|
||||
// in the partial file.
|
||||
{
|
||||
source: sourceUrl,
|
||||
target: getDownloadTarget("inprogress2.txt"),
|
||||
tempPath: yield getPartialFile("inprogress2.txt.part", true),
|
||||
startTime: getStartTime(2),
|
||||
state: DOWNLOAD_PAUSED,
|
||||
referrer: httpUrl("referrer2"),
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "mimeType2",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication2",
|
||||
autoResume: 1,
|
||||
|
||||
expectedCurrentBytes: TEST_DATA_LENGTH,
|
||||
expectedTotalBytes: TEST_DATA_LENGTH,
|
||||
expectedResume: true,
|
||||
expectedContentType: "text/plain",
|
||||
expectedContent: TEST_DATA_SHORT
|
||||
},
|
||||
|
||||
// Paused download with no autoResume and a partial file.
|
||||
{
|
||||
source: sourceUrl,
|
||||
target: getDownloadTarget("inprogress3.txt"),
|
||||
tempPath: yield getPartialFile("inprogress3.txt.part"),
|
||||
startTime: getStartTime(3),
|
||||
state: DOWNLOAD_PAUSED,
|
||||
referrer: httpUrl("referrer3"),
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "mimeType3",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication3",
|
||||
autoResume: 0,
|
||||
|
||||
// Since this download has not been resumed, the actual data
|
||||
// about its total size and content type is not known.
|
||||
// Therefore, we're going by the information imported from the DB.
|
||||
expectedCurrentBytes: TEST_DATA_PARTIAL_LENGTH,
|
||||
expectedTotalBytes: MAXBYTES_IN_DB,
|
||||
expectedResume: false,
|
||||
expectedContentType: "mimeType3",
|
||||
expectedContent: TEST_DATA_SHORT.substr(0, TEST_DATA_PARTIAL_LENGTH),
|
||||
},
|
||||
|
||||
// Paused download with autoResume and no partial file.
|
||||
{
|
||||
source: sourceUrl,
|
||||
target: getDownloadTarget("inprogress4.txt"),
|
||||
tempPath: "",
|
||||
startTime: getStartTime(4),
|
||||
state: DOWNLOAD_PAUSED,
|
||||
referrer: httpUrl("referrer4"),
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "text/plain",
|
||||
preferredAction: Ci.nsIMIMEInfo.useHelperApp,
|
||||
preferredApplication: "prerredApplication4",
|
||||
autoResume: 1,
|
||||
|
||||
expectedCurrentBytes: TEST_DATA_LENGTH,
|
||||
expectedTotalBytes: TEST_DATA_LENGTH,
|
||||
expectedResume: true,
|
||||
expectedContentType: "text/plain",
|
||||
expectedContent: TEST_DATA_SHORT
|
||||
},
|
||||
|
||||
// Paused download with no autoResume and no partial file.
|
||||
{
|
||||
source: sourceUrl,
|
||||
target: getDownloadTarget("inprogress5.txt"),
|
||||
tempPath: "",
|
||||
startTime: getStartTime(5),
|
||||
state: DOWNLOAD_PAUSED,
|
||||
referrer: httpUrl("referrer4"),
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "text/plain",
|
||||
preferredAction: Ci.nsIMIMEInfo.useSystemDefault,
|
||||
preferredApplication: "prerredApplication5",
|
||||
autoResume: 0,
|
||||
|
||||
expectedCurrentBytes: 0,
|
||||
expectedTotalBytes: MAXBYTES_IN_DB,
|
||||
expectedResume: false,
|
||||
expectedContentType: "text/plain",
|
||||
},
|
||||
|
||||
// Queued download with no autoResume and no partial file.
|
||||
// Even though autoResume=0, queued downloads always autoResume.
|
||||
{
|
||||
source: sourceUrl,
|
||||
target: getDownloadTarget("inprogress6.txt"),
|
||||
tempPath: "",
|
||||
startTime: getStartTime(6),
|
||||
state: DOWNLOAD_QUEUED,
|
||||
referrer: httpUrl("referrer6"),
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "text/plain",
|
||||
preferredAction: Ci.nsIMIMEInfo.useHelperApp,
|
||||
preferredApplication: "prerredApplication6",
|
||||
autoResume: 0,
|
||||
|
||||
expectedCurrentBytes: TEST_DATA_LENGTH,
|
||||
expectedTotalBytes: TEST_DATA_LENGTH,
|
||||
expectedResume: true,
|
||||
expectedContentType: "text/plain",
|
||||
expectedContent: TEST_DATA_SHORT
|
||||
},
|
||||
|
||||
// Notstarted download with no autoResume and no partial file.
|
||||
// Even though autoResume=0, notstarted downloads always autoResume.
|
||||
{
|
||||
source: sourceUrl,
|
||||
target: getDownloadTarget("inprogress7.txt"),
|
||||
tempPath: "",
|
||||
startTime: getStartTime(7),
|
||||
state: DOWNLOAD_NOTSTARTED,
|
||||
referrer: httpUrl("referrer7"),
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "text/plain",
|
||||
preferredAction: Ci.nsIMIMEInfo.useHelperApp,
|
||||
preferredApplication: "prerredApplication7",
|
||||
autoResume: 0,
|
||||
|
||||
expectedCurrentBytes: TEST_DATA_LENGTH,
|
||||
expectedTotalBytes: TEST_DATA_LENGTH,
|
||||
expectedResume: true,
|
||||
expectedContentType: "text/plain",
|
||||
expectedContent: TEST_DATA_SHORT
|
||||
},
|
||||
|
||||
// Downloading download with no autoResume and a partial file.
|
||||
// Even though autoResume=0, downloading downloads always autoResume.
|
||||
{
|
||||
source: sourceUrl,
|
||||
target: getDownloadTarget("inprogress8.txt"),
|
||||
tempPath: yield getPartialFile("inprogress8.txt.part", true),
|
||||
startTime: getStartTime(8),
|
||||
state: DOWNLOAD_DOWNLOADING,
|
||||
referrer: httpUrl("referrer8"),
|
||||
entityID: sourceEntityId,
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "text/plain",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication8",
|
||||
autoResume: 0,
|
||||
|
||||
expectedCurrentBytes: TEST_DATA_LENGTH,
|
||||
expectedTotalBytes: TEST_DATA_LENGTH,
|
||||
expectedResume: true,
|
||||
expectedContentType: "text/plain",
|
||||
expectedContent: TEST_DATA_TAINTED
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
/**
|
||||
* Prepares the list of downloads to be added to the database that should
|
||||
* *not* be imported by the import procedure.
|
||||
*/
|
||||
add_task(function prepareNonImportableDownloads()
|
||||
{
|
||||
gDownloadsRowNonImportable = [
|
||||
// Download with no source (should never happen in normal circumstances).
|
||||
{
|
||||
source: "",
|
||||
target: "nonimportable1.txt",
|
||||
tempPath: "",
|
||||
startTime: getStartTime(1),
|
||||
state: DOWNLOAD_PAUSED,
|
||||
referrer: "",
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "mimeType1",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication1",
|
||||
autoResume: 1
|
||||
},
|
||||
|
||||
// state = DOWNLOAD_FAILED
|
||||
{
|
||||
source: httpUrl("source.txt"),
|
||||
target: "nonimportable2.txt",
|
||||
tempPath: "",
|
||||
startTime: getStartTime(2),
|
||||
state: DOWNLOAD_FAILED,
|
||||
referrer: "",
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "mimeType2",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication2",
|
||||
autoResume: 1
|
||||
},
|
||||
|
||||
// state = DOWNLOAD_CANCELED
|
||||
{
|
||||
source: httpUrl("source.txt"),
|
||||
target: "nonimportable3.txt",
|
||||
tempPath: "",
|
||||
startTime: getStartTime(3),
|
||||
state: DOWNLOAD_CANCELED,
|
||||
referrer: "",
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "mimeType3",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication3",
|
||||
autoResume: 1
|
||||
},
|
||||
|
||||
// state = DOWNLOAD_BLOCKED_PARENTAL
|
||||
{
|
||||
source: httpUrl("source.txt"),
|
||||
target: "nonimportable4.txt",
|
||||
tempPath: "",
|
||||
startTime: getStartTime(4),
|
||||
state: DOWNLOAD_BLOCKED_PARENTAL,
|
||||
referrer: "",
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "mimeType4",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication4",
|
||||
autoResume: 1
|
||||
},
|
||||
|
||||
// state = DOWNLOAD_SCANNING
|
||||
{
|
||||
source: httpUrl("source.txt"),
|
||||
target: "nonimportable5.txt",
|
||||
tempPath: "",
|
||||
startTime: getStartTime(5),
|
||||
state: DOWNLOAD_SCANNING,
|
||||
referrer: "",
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "mimeType5",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication5",
|
||||
autoResume: 1
|
||||
},
|
||||
|
||||
// state = DOWNLOAD_DIRTY
|
||||
{
|
||||
source: httpUrl("source.txt"),
|
||||
target: "nonimportable6.txt",
|
||||
tempPath: "",
|
||||
startTime: getStartTime(6),
|
||||
state: DOWNLOAD_DIRTY,
|
||||
referrer: "",
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "mimeType6",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication6",
|
||||
autoResume: 1
|
||||
},
|
||||
|
||||
// state = DOWNLOAD_BLOCKED_POLICY
|
||||
{
|
||||
source: httpUrl("source.txt"),
|
||||
target: "nonimportable7.txt",
|
||||
tempPath: "",
|
||||
startTime: getStartTime(7),
|
||||
state: DOWNLOAD_BLOCKED_POLICY,
|
||||
referrer: "",
|
||||
entityID: "",
|
||||
maxBytes: MAXBYTES_IN_DB,
|
||||
mimeType: "mimeType7",
|
||||
preferredAction: Ci.nsIMIMEInfo.saveToDisk,
|
||||
preferredApplication: "prerredApplication7",
|
||||
autoResume: 1
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test
|
||||
|
||||
/**
|
||||
* Creates a temporary Sqlite database with download data and perform an
|
||||
* import of that data to the new Downloads API to verify that the import
|
||||
* worked correctly.
|
||||
*/
|
||||
add_task(function test_downloadImport()
|
||||
{
|
||||
let connection = null;
|
||||
let downloadsSqlite = getTempFile("downloads.sqlite").path;
|
||||
|
||||
try {
|
||||
// Set up the database.
|
||||
connection = yield promiseEmptyDatabaseConnection({
|
||||
aPath: downloadsSqlite,
|
||||
aSchemaVersion: 9
|
||||
});
|
||||
|
||||
// Insert both the importable and non-importable
|
||||
// downloads together.
|
||||
for (let downloadRow of gDownloadsRowToImport) {
|
||||
yield promiseInsertRow(connection, downloadRow);
|
||||
}
|
||||
|
||||
for (let downloadRow of gDownloadsRowNonImportable) {
|
||||
yield promiseInsertRow(connection, downloadRow);
|
||||
}
|
||||
|
||||
// Check that every item was inserted.
|
||||
do_check_eq((yield promiseTableCount(connection)),
|
||||
gDownloadsRowToImport.length +
|
||||
gDownloadsRowNonImportable.length);
|
||||
} finally {
|
||||
// Close the connection so that DownloadImport can open it.
|
||||
yield connection.close();
|
||||
}
|
||||
|
||||
// Import items.
|
||||
let list = yield promiseNewDownloadList();
|
||||
yield new DownloadImport(list, downloadsSqlite).import();
|
||||
let items = yield list.getAll();
|
||||
|
||||
do_check_eq(items.length, gDownloadsRowToImport.length);
|
||||
|
||||
for (let i = 0; i < gDownloadsRowToImport.length; i++) {
|
||||
yield checkDownload(items[i], gDownloadsRowToImport[i]);
|
||||
}
|
||||
})
|
@ -3,6 +3,7 @@ head = head.js
|
||||
tail = tail.js
|
||||
|
||||
[test_DownloadCore.js]
|
||||
[test_DownloadImport.js]
|
||||
[test_DownloadIntegration.js]
|
||||
[test_DownloadLegacy.js]
|
||||
[test_DownloadList.js]
|
||||
|
@ -11,6 +11,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
echo.sjs \
|
||||
worker_xhr.js \
|
||||
browser_frameworker.js \
|
||||
browser_frameworker_sandbox.js \
|
||||
worker_relative.js \
|
||||
relative_import.js \
|
||||
browser_workerAPI.js \
|
||||
|
@ -2,6 +2,9 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests message ports and semantics of the frameworker which aren't
|
||||
// directly related to the sandbox. See also browser_frameworker_sandbox.js.
|
||||
|
||||
function makeWorkerUrl(runner) {
|
||||
let prefix = "http://example.com/browser/toolkit/components/social/test/browser/echo.sjs?";
|
||||
if (typeof runner == "function") {
|
||||
@ -136,263 +139,6 @@ let tests = {
|
||||
worker.port.postMessage({topic: "hello", data: [1,2,3]});
|
||||
},
|
||||
|
||||
testArrayUsingBuffer: function(cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "go") {
|
||||
let buffer = new ArrayBuffer(10);
|
||||
// this one has always worked in the past, but worth checking anyway...
|
||||
if (new Uint8Array(buffer).length != 10) {
|
||||
port.postMessage({topic: "result", reason: "first length was not 10"});
|
||||
return;
|
||||
}
|
||||
let reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
if (new Uint8Array(buffer).length != 10) {
|
||||
port.postMessage({topic: "result", reason: "length in onload handler was not 10"});
|
||||
return;
|
||||
}
|
||||
// all seems good!
|
||||
port.postMessage({topic: "result", reason: "ok"});
|
||||
}
|
||||
let blob = new Blob([buffer], {type: "binary"});
|
||||
reader.readAsArrayBuffer(blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testArray");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "result") {
|
||||
is(e.data.reason, "ok", "check the array worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "go"});
|
||||
},
|
||||
|
||||
testArrayUsingReader: function(cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "go") {
|
||||
let buffer = new ArrayBuffer(10);
|
||||
let reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
try {
|
||||
if (new Uint8Array(reader.result).length != 10) {
|
||||
port.postMessage({topic: "result", reason: "length in onload handler was not 10"});
|
||||
return;
|
||||
}
|
||||
// all seems good!
|
||||
port.postMessage({topic: "result", reason: "ok"});
|
||||
} catch (ex) {
|
||||
port.postMessage({topic: "result", reason: ex.toString()});
|
||||
}
|
||||
}
|
||||
let blob = new Blob([buffer], {type: "binary"});
|
||||
reader.readAsArrayBuffer(blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testArray");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "result") {
|
||||
is(e.data.reason, "ok", "check the array worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "go"});
|
||||
},
|
||||
|
||||
testXHR: function(cbnext) {
|
||||
// NOTE: this url MUST be in the same origin as worker_xhr.js fetches from!
|
||||
let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_xhr.js";
|
||||
let worker = getFrameWorkerHandle(url, undefined, "testXHR");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check the xhr test worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testLocalStorage: function(cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
try {
|
||||
localStorage.setItem("foo", "1");
|
||||
} catch(e) {
|
||||
port.postMessage({topic: "done", result: "FAILED to set localStorage, " + e.toString() });
|
||||
return;
|
||||
}
|
||||
|
||||
var ok;
|
||||
try {
|
||||
ok = localStorage["foo"] == 1;
|
||||
} catch (e) {
|
||||
port.postMessage({topic: "done", result: "FAILED to read localStorage, " + e.toString() });
|
||||
return;
|
||||
}
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testLocalStorage", null, true);
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check the localStorage test worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testNoLocalStorage: function(cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
try {
|
||||
localStorage.setItem("foo", "1");
|
||||
} catch(e) {
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
return;
|
||||
}
|
||||
|
||||
port.postMessage({topic: "done", result: "FAILED because localStorage was exposed" });
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testNoLocalStorage");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check that retrieving localStorage fails by default");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testBase64: function (cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
var ok = false;
|
||||
try {
|
||||
ok = btoa("1234") == "MTIzNA==";
|
||||
} catch(e) {
|
||||
port.postMessage({topic: "done", result: "FAILED to call btoa, " + e.toString() });
|
||||
return;
|
||||
}
|
||||
if (!ok) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling btoa"});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ok = atob("NDMyMQ==") == "4321";
|
||||
} catch (e) {
|
||||
port.postMessage({topic: "done", result: "FAILED to call atob, " + e.toString() });
|
||||
return;
|
||||
}
|
||||
if (!ok) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling atob"});
|
||||
return;
|
||||
}
|
||||
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testBase64");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check the atob/btoa test worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testTimeouts: function (cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
|
||||
var timeout;
|
||||
try {
|
||||
timeout = setTimeout(function () {
|
||||
port.postMessage({topic: "done", result: "FAILED cancelled timeout was called"});
|
||||
}, 100);
|
||||
} catch (ex) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling setTimeout: " + ex});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
clearTimeout(timeout);
|
||||
} catch (ex) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling clearTimeout: " + ex});
|
||||
return;
|
||||
}
|
||||
|
||||
var counter = 0;
|
||||
try {
|
||||
timeout = setInterval(function () {
|
||||
if (++counter == 2) {
|
||||
clearInterval(timeout);
|
||||
setTimeout(function () {
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
return;
|
||||
}, 0);
|
||||
}
|
||||
}, 100);
|
||||
} catch (ex) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling setInterval: " + ex});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testTimeouts");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check that timeouts worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testWebSocket: function (cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
|
||||
try {
|
||||
var exampleSocket = new WebSocket("ws://mochi.test:8888/socketserver");
|
||||
} catch (e) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling WebSocket constructor: " + e});
|
||||
return;
|
||||
}
|
||||
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testWebSocket");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check that websockets worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testSameOriginImport: function(cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
@ -422,7 +168,6 @@ let tests = {
|
||||
worker.port.postMessage({topic: "ping"})
|
||||
},
|
||||
|
||||
|
||||
testRelativeImport: function(cbnext) {
|
||||
let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_relative.js";
|
||||
let worker = getFrameWorkerHandle(url, undefined, "testSameOriginImport");
|
||||
@ -633,68 +378,4 @@ let tests = {
|
||||
}
|
||||
worker.port.postMessage({topic: "get-ready"});
|
||||
},
|
||||
|
||||
testEventSource: function(cbnext) {
|
||||
let worker = getFrameWorkerHandle("https://example.com/browser/toolkit/components/social/test/browser/worker_eventsource.js", undefined, "testEventSource");
|
||||
worker.port.onmessage = function(e) {
|
||||
let m = e.data;
|
||||
if (m.topic == "eventSourceTest") {
|
||||
if (m.result.ok != undefined)
|
||||
ok(m.result.ok, e.data.result.msg);
|
||||
if (m.result.is != undefined)
|
||||
is(m.result.is, m.result.match, m.result.msg);
|
||||
if (m.result.info != undefined)
|
||||
info(m.result.info);
|
||||
} else if (e.data.topic == "pong") {
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "ping"})
|
||||
},
|
||||
|
||||
|
||||
testIndexedDB: function(cbnext) {
|
||||
let worker = getFrameWorkerHandle("https://example.com/browser/toolkit/components/social/test/browser/worker_social.js", undefined, "testIndexedDB");
|
||||
worker.port.onmessage = function(e) {
|
||||
let m = e.data;
|
||||
if (m.topic == "social.indexeddb-result") {
|
||||
is(m.data.result, "ok", "created indexeddb");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "test-indexeddb-create"})
|
||||
},
|
||||
|
||||
testSubworker: function(cbnext) {
|
||||
// the main "frameworker"...
|
||||
let mainworker = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "go") {
|
||||
let suburl = e.data.data;
|
||||
let worker = new Worker(suburl);
|
||||
worker.onmessage = function(sube) {
|
||||
port.postMessage({topic: "sub-message", data: sube.data});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The "subworker" that is actually a real, bona-fide worker.
|
||||
let subworker = function() {
|
||||
postMessage("hello");
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(mainworker), undefined, "testSubWorker");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "sub-message" && e.data.data == "hello") {
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "go", data: makeWorkerUrl(subworker)});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,347 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file tests features made available to the frameworker via the sandbox.
|
||||
// For other frameworker tests, see browser_frameworker.js
|
||||
|
||||
function makeWorkerUrl(runner) {
|
||||
let prefix = "http://example.com/browser/toolkit/components/social/test/browser/echo.sjs?";
|
||||
if (typeof runner == "function") {
|
||||
runner = "var run=" + runner.toSource() + ";run();";
|
||||
}
|
||||
return prefix + encodeURI(runner);
|
||||
}
|
||||
|
||||
var getFrameWorkerHandle;
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let scope = {};
|
||||
Cu.import("resource://gre/modules/FrameWorker.jsm", scope);
|
||||
getFrameWorkerHandle = scope.getFrameWorkerHandle;
|
||||
|
||||
runTests(tests);
|
||||
}
|
||||
|
||||
let tests = {
|
||||
testArrayUsingBuffer: function(cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "go") {
|
||||
let buffer = new ArrayBuffer(10);
|
||||
// this one has always worked in the past, but worth checking anyway...
|
||||
if (new Uint8Array(buffer).length != 10) {
|
||||
port.postMessage({topic: "result", reason: "first length was not 10"});
|
||||
return;
|
||||
}
|
||||
let reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
if (new Uint8Array(buffer).length != 10) {
|
||||
port.postMessage({topic: "result", reason: "length in onload handler was not 10"});
|
||||
return;
|
||||
}
|
||||
// all seems good!
|
||||
port.postMessage({topic: "result", reason: "ok"});
|
||||
}
|
||||
let blob = new Blob([buffer], {type: "binary"});
|
||||
reader.readAsArrayBuffer(blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testArray");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "result") {
|
||||
is(e.data.reason, "ok", "check the array worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "go"});
|
||||
},
|
||||
|
||||
testArrayUsingReader: function(cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "go") {
|
||||
let buffer = new ArrayBuffer(10);
|
||||
let reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
try {
|
||||
if (new Uint8Array(reader.result).length != 10) {
|
||||
port.postMessage({topic: "result", reason: "length in onload handler was not 10"});
|
||||
return;
|
||||
}
|
||||
// all seems good!
|
||||
port.postMessage({topic: "result", reason: "ok"});
|
||||
} catch (ex) {
|
||||
port.postMessage({topic: "result", reason: ex.toString()});
|
||||
}
|
||||
}
|
||||
let blob = new Blob([buffer], {type: "binary"});
|
||||
reader.readAsArrayBuffer(blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testArray");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "result") {
|
||||
is(e.data.reason, "ok", "check the array worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "go"});
|
||||
},
|
||||
|
||||
testXHR: function(cbnext) {
|
||||
// NOTE: this url MUST be in the same origin as worker_xhr.js fetches from!
|
||||
let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_xhr.js";
|
||||
let worker = getFrameWorkerHandle(url, undefined, "testXHR");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check the xhr test worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testLocalStorage: function(cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
try {
|
||||
localStorage.setItem("foo", "1");
|
||||
} catch(e) {
|
||||
port.postMessage({topic: "done", result: "FAILED to set localStorage, " + e.toString() });
|
||||
return;
|
||||
}
|
||||
|
||||
var ok;
|
||||
try {
|
||||
ok = localStorage["foo"] == 1;
|
||||
} catch (e) {
|
||||
port.postMessage({topic: "done", result: "FAILED to read localStorage, " + e.toString() });
|
||||
return;
|
||||
}
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testLocalStorage", null, true);
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check the localStorage test worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testNoLocalStorage: function(cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
try {
|
||||
localStorage.setItem("foo", "1");
|
||||
} catch(e) {
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
return;
|
||||
}
|
||||
|
||||
port.postMessage({topic: "done", result: "FAILED because localStorage was exposed" });
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testNoLocalStorage");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check that retrieving localStorage fails by default");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testBase64: function (cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
var ok = false;
|
||||
try {
|
||||
ok = btoa("1234") == "MTIzNA==";
|
||||
} catch(e) {
|
||||
port.postMessage({topic: "done", result: "FAILED to call btoa, " + e.toString() });
|
||||
return;
|
||||
}
|
||||
if (!ok) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling btoa"});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ok = atob("NDMyMQ==") == "4321";
|
||||
} catch (e) {
|
||||
port.postMessage({topic: "done", result: "FAILED to call atob, " + e.toString() });
|
||||
return;
|
||||
}
|
||||
if (!ok) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling atob"});
|
||||
return;
|
||||
}
|
||||
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testBase64");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check the atob/btoa test worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testTimeouts: function (cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
|
||||
var timeout;
|
||||
try {
|
||||
timeout = setTimeout(function () {
|
||||
port.postMessage({topic: "done", result: "FAILED cancelled timeout was called"});
|
||||
}, 100);
|
||||
} catch (ex) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling setTimeout: " + ex});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
clearTimeout(timeout);
|
||||
} catch (ex) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling clearTimeout: " + ex});
|
||||
return;
|
||||
}
|
||||
|
||||
var counter = 0;
|
||||
try {
|
||||
timeout = setInterval(function () {
|
||||
if (++counter == 2) {
|
||||
clearInterval(timeout);
|
||||
setTimeout(function () {
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
return;
|
||||
}, 0);
|
||||
}
|
||||
}, 100);
|
||||
} catch (ex) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling setInterval: " + ex});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testTimeouts");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check that timeouts worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testWebSocket: function (cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
|
||||
try {
|
||||
var exampleSocket = new WebSocket("ws://mochi.test:8888/socketserver");
|
||||
} catch (e) {
|
||||
port.postMessage({topic: "done", result: "FAILED calling WebSocket constructor: " + e});
|
||||
return;
|
||||
}
|
||||
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testWebSocket");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check that websockets worked");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testEventSource: function(cbnext) {
|
||||
let worker = getFrameWorkerHandle("https://example.com/browser/toolkit/components/social/test/browser/worker_eventsource.js", undefined, "testEventSource");
|
||||
worker.port.onmessage = function(e) {
|
||||
let m = e.data;
|
||||
if (m.topic == "eventSourceTest") {
|
||||
if (m.result.ok != undefined)
|
||||
ok(m.result.ok, e.data.result.msg);
|
||||
if (m.result.is != undefined)
|
||||
is(m.result.is, m.result.match, m.result.msg);
|
||||
if (m.result.info != undefined)
|
||||
info(m.result.info);
|
||||
} else if (e.data.topic == "pong") {
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "ping"})
|
||||
},
|
||||
|
||||
testIndexedDB: function(cbnext) {
|
||||
let worker = getFrameWorkerHandle("https://example.com/browser/toolkit/components/social/test/browser/worker_social.js", undefined, "testIndexedDB");
|
||||
worker.port.onmessage = function(e) {
|
||||
let m = e.data;
|
||||
if (m.topic == "social.indexeddb-result") {
|
||||
is(m.data.result, "ok", "created indexeddb");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "test-indexeddb-create"})
|
||||
},
|
||||
|
||||
testSubworker: function(cbnext) {
|
||||
// the main "frameworker"...
|
||||
let mainworker = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "go") {
|
||||
let suburl = e.data.data;
|
||||
let worker = new Worker(suburl);
|
||||
worker.onmessage = function(sube) {
|
||||
port.postMessage({topic: "sub-message", data: sube.data});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The "subworker" that is actually a real, bona-fide worker.
|
||||
let subworker = function() {
|
||||
postMessage("hello");
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(mainworker), undefined, "testSubWorker");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "sub-message" && e.data.data == "hello") {
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "go", data: makeWorkerUrl(subworker)});
|
||||
}
|
||||
}
|
@ -363,6 +363,9 @@ nsAppStartup::Quit(uint32_t aMode)
|
||||
}
|
||||
|
||||
if (mRestart) {
|
||||
// Mark the next startup as a restart.
|
||||
PR_SetEnv("MOZ_APP_RESTART=1");
|
||||
|
||||
/* Firefox-restarts reuse the process so regular process start-time isn't
|
||||
a useful indicator of startup time anymore. */
|
||||
TimeStamp::RecordProcessRestart();
|
||||
@ -522,6 +525,19 @@ nsAppStartup::GetRestarting(bool *aResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAppStartup::GetWasRestarted(bool *aResult)
|
||||
{
|
||||
char *mozAppRestart = PR_GetEnv("MOZ_APP_RESTART");
|
||||
|
||||
/* When calling PR_SetEnv() with an empty value the existing variable may
|
||||
* be unset or set to the empty string depending on the underlying platform
|
||||
* thus we have to check if the variable is present and not empty. */
|
||||
*aResult = mozAppRestart && (strcmp(mozAppRestart, "") != 0);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAppStartup::SetInterrupted(bool aInterrupted)
|
||||
{
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
interface nsICmdLineService;
|
||||
|
||||
[scriptable, uuid(380618f8-479a-435b-b58e-7398ab937531)]
|
||||
[scriptable, uuid(744d6ec0-115f-11e3-9c94-68fd99890b3c)]
|
||||
interface nsIAppStartup : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -140,6 +140,12 @@ interface nsIAppStartup : nsISupports
|
||||
*/
|
||||
readonly attribute boolean restarting;
|
||||
|
||||
/**
|
||||
* True if this is the startup following restart, i.e. if the application
|
||||
* was restarted using quit(eRestart*).
|
||||
*/
|
||||
readonly attribute boolean wasRestarted;
|
||||
|
||||
/**
|
||||
* Returns an object with main, process, firstPaint, sessionRestored properties.
|
||||
* Properties may not be available depending on platform or application
|
||||
|
@ -362,9 +362,6 @@ Capture.prototype = {
|
||||
// notify, since it calls destroy, which cancels the timeout timer and
|
||||
// removes the didCapture message listener.
|
||||
|
||||
this.captureCallback(this);
|
||||
this.destroy();
|
||||
|
||||
if (data && data.telemetry) {
|
||||
// Telemetry is currently disabled in the content process (bug 680508).
|
||||
for (let id in data.telemetry) {
|
||||
@ -372,7 +369,9 @@ Capture.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
let callOnDones = function callOnDonesFn() {
|
||||
let done = () => {
|
||||
this.captureCallback(this);
|
||||
this.destroy();
|
||||
for (let callback of this.doneCallbacks) {
|
||||
try {
|
||||
callback.call(this.options, this.url);
|
||||
@ -381,15 +380,15 @@ Capture.prototype = {
|
||||
Cu.reportError(err);
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
if (!data) {
|
||||
callOnDones();
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
PageThumbs._store(this.url, data.finalURL, data.imageData, data.wasErrorResponse)
|
||||
.then(callOnDones);
|
||||
.then(done, done);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -169,3 +169,7 @@ MOCHITEST_CHROME_FILES += $(filter disabled-temporarily, test_cursorsnap.xul) \
|
||||
window_cursorsnap_dialog.xul \
|
||||
window_cursorsnap_wizard.xul
|
||||
endif
|
||||
|
||||
MOCHITEST_CHROME_FILES += test_about_networking.html \
|
||||
file_about_networking_wsh.py
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
from mod_pywebsocket import msgutil
|
||||
|
||||
def web_socket_do_extra_handshake(request):
|
||||
pass
|
||||
|
||||
def web_socket_transfer_data(request):
|
||||
while not request.client_terminated:
|
||||
msgutil.receive_message(request)
|
||||
|
52
toolkit/content/tests/chrome/test_about_networking.html
Normal file
52
toolkit/content/tests/chrome/test_about_networking.html
Normal file
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=912103
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug </title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
var dashboard = Cc['@mozilla.org/network/dashboard;1']
|
||||
.getService(Ci.nsIDashboard);
|
||||
dashboard.enableLogging = true;
|
||||
|
||||
var wsURI = "ws://mochi.test:8888/chrome/toolkit/content/tests/chrome/file_about_networking";
|
||||
var websocket = new WebSocket(wsURI);
|
||||
|
||||
websocket.addEventListener("open", function() {
|
||||
dashboard.requestWebsocketConnections(function(data) {
|
||||
isnot(data.hostport.indexOf("mochi.test:8888"), -1,
|
||||
"tested websocket entry not found");
|
||||
websocket.close();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function run() {
|
||||
window.removeEventListener("DOMContentLoaded", run);
|
||||
runTest();
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=912103">Mozilla Bug </a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -55,6 +55,7 @@ var BuiltinProvider = {
|
||||
"devtools": "resource:///modules/devtools",
|
||||
"devtools/server": "resource://gre/modules/devtools/server",
|
||||
"devtools/toolkit/webconsole": "resource://gre/modules/devtools/toolkit/webconsole",
|
||||
"devtools/app-actor-front": "resource://gre/modules/devtools/app-actor-front.js",
|
||||
"devtools/styleinspector/css-logic": "resource://gre/modules/devtools/styleinspector/css-logic",
|
||||
"devtools/client": "resource://gre/modules/devtools/client",
|
||||
|
||||
@ -95,6 +96,7 @@ var SrcdirProvider = {
|
||||
let toolkitURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools"));
|
||||
let serverURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools", "server"));
|
||||
let webconsoleURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools", "webconsole"));
|
||||
let appActorURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools", "apps", "app-actor-front.js"));
|
||||
let cssLogicURI = this.fileURI(OS.Path.join(toolkitURI, "styleinspector", "css-logic"));
|
||||
let clientURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools", "client"));
|
||||
let mainURI = this.fileURI(OS.Path.join(srcdir, "browser", "devtools", "main.js"));
|
||||
@ -107,6 +109,7 @@ var SrcdirProvider = {
|
||||
"": "resource://gre/modules/commonjs/",
|
||||
"devtools/server": serverURI,
|
||||
"devtools/toolkit/webconsole": webconsoleURI,
|
||||
"devtools/app-actor-front": appActorURI,
|
||||
"devtools/client": clientURI,
|
||||
"devtools": devtoolsURI,
|
||||
"devtools/styleinspector/css-logic": cssLogicURI,
|
||||
|
33
toolkit/devtools/apps/Simulator.jsm
Normal file
33
toolkit/devtools/apps/Simulator.jsm
Normal file
@ -0,0 +1,33 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource:///modules/devtools/shared/event-emitter.js");
|
||||
|
||||
const EXPORTED_SYMBOLS = ["Simulator"];
|
||||
|
||||
const Simulator = {
|
||||
_simulators: {},
|
||||
|
||||
register: function (version, simulator) {
|
||||
this._simulators[version] = simulator;
|
||||
this.emit("register");
|
||||
},
|
||||
|
||||
unregister: function (version) {
|
||||
delete this._simulators[version];
|
||||
this.emit("unregister");
|
||||
},
|
||||
|
||||
availableVersions: function () {
|
||||
return Object.keys(this._simulators).sort();
|
||||
},
|
||||
|
||||
getByVersion: function (version) {
|
||||
return this._simulators[version];
|
||||
}
|
||||
};
|
||||
|
||||
EventEmitter.decorate(Simulator);
|
216
toolkit/devtools/apps/app-actor-front.js
Normal file
216
toolkit/devtools/apps/app-actor-front.js
Normal file
@ -0,0 +1,216 @@
|
||||
const {Ci, Cc, Cu, Cr} = require("chrome");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
|
||||
const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
const promise = require("sdk/core/promise");
|
||||
|
||||
// XXX: bug 912476 make this module a real protocol.js front
|
||||
// by converting webapps actor to protocol.js
|
||||
|
||||
const PR_USEC_PER_MSEC = 1000;
|
||||
const PR_RDWR = 0x04;
|
||||
const PR_CREATE_FILE = 0x08;
|
||||
const PR_TRUNCATE = 0x20;
|
||||
|
||||
const CHUNK_SIZE = 10000;
|
||||
|
||||
function addDirToZip(writer, dir, basePath) {
|
||||
let files = dir.directoryEntries;
|
||||
|
||||
while (files.hasMoreElements()) {
|
||||
let file = files.getNext().QueryInterface(Ci.nsIFile);
|
||||
|
||||
if (file.isHidden() ||
|
||||
file.isSymlink() ||
|
||||
file.isSpecial() ||
|
||||
file.equals(writer.file))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
writer.addEntryDirectory(basePath + file.leafName + "/",
|
||||
file.lastModifiedTime * PR_USEC_PER_MSEC,
|
||||
true);
|
||||
addDirToZip(writer, file, basePath + file.leafName + "/");
|
||||
} else {
|
||||
writer.addEntryFile(basePath + file.leafName,
|
||||
Ci.nsIZipWriter.COMPRESSION_DEFAULT,
|
||||
file,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an XPConnect result code to its name and message.
|
||||
* We have to extract them from an exception per bug 637307 comment 5.
|
||||
*/
|
||||
function getResultTest(code) {
|
||||
let regexp =
|
||||
/^\[Exception... "(.*)" nsresult: "0x[0-9a-fA-F]* \((.*)\)" location: ".*" data: .*\]$/;
|
||||
let ex = Cc["@mozilla.org/js/xpc/Exception;1"].
|
||||
createInstance(Ci.nsIXPCException);
|
||||
ex.initialize(null, code, null, null, null, null);
|
||||
let [, message, name] = regexp.exec(ex.toString());
|
||||
return { name: name, message: message };
|
||||
}
|
||||
|
||||
function zipDirectory(zipFile, dirToArchive) {
|
||||
let deferred = promise.defer();
|
||||
let writer = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
|
||||
writer.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
|
||||
|
||||
this.addDirToZip(writer, dirToArchive, "");
|
||||
|
||||
writer.processQueue({
|
||||
onStartRequest: function onStartRequest(request, context) {},
|
||||
onStopRequest: (request, context, status) => {
|
||||
if (status == Cr.NS_OK) {
|
||||
writer.close();
|
||||
deferred.resolve(zipFile);
|
||||
}
|
||||
else {
|
||||
let { name, message } = getResultText(status);
|
||||
deferred.reject(name + ": " + message);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function uploadPackage(client, webappsActor, packageFile) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let request = {
|
||||
to: webappsActor,
|
||||
type: "uploadPackage"
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
openFile(res.actor);
|
||||
});
|
||||
|
||||
function openFile(actor) {
|
||||
OS.File.open(packageFile.path)
|
||||
.then(function (file) {
|
||||
uploadChunk(actor, file);
|
||||
});
|
||||
}
|
||||
function uploadChunk(actor, file) {
|
||||
file.read(CHUNK_SIZE)
|
||||
.then(function (bytes) {
|
||||
// To work around the fact that JSON.stringify translates the typed
|
||||
// array to object, we are encoding the typed array here into a string
|
||||
let chunk = String.fromCharCode.apply(null, bytes);
|
||||
|
||||
let request = {
|
||||
to: actor,
|
||||
type: "chunk",
|
||||
chunk: chunk
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
if (bytes.length == CHUNK_SIZE) {
|
||||
uploadChunk(actor, file);
|
||||
} else {
|
||||
file.close().then(function () {
|
||||
endsUpload(actor);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function endsUpload(actor) {
|
||||
let request = {
|
||||
to: actor,
|
||||
type: "done"
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
deferred.resolve(actor);
|
||||
});
|
||||
}
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function removeServerTemporaryFile(client, fileActor) {
|
||||
let request = {
|
||||
to: fileActor,
|
||||
type: "remove"
|
||||
};
|
||||
client.request(request, function (aResponse) {
|
||||
console.error("Failed removing server side temporary package file", aResponse);
|
||||
});
|
||||
}
|
||||
|
||||
function installPackaged(client, webappsActor, packagePath, appId) {
|
||||
let deferred = promise.defer();
|
||||
let file = FileUtils.File(packagePath);
|
||||
let packagePromise;
|
||||
if (file.isDirectory()) {
|
||||
let tmpZipFile = FileUtils.getDir("TmpD", [], true);
|
||||
tmpZipFile.append("application.zip");
|
||||
tmpZipFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
|
||||
packagePromise = zipDirectory(tmpZipFile, file)
|
||||
} else {
|
||||
packagePromise = promise.resolve(file);
|
||||
}
|
||||
packagePromise.then((zipFile) => {
|
||||
uploadPackage(client, webappsActor, zipFile)
|
||||
.then((fileActor) => {
|
||||
let request = {
|
||||
to: webappsActor,
|
||||
type: "install",
|
||||
appId: appId,
|
||||
upload: fileActor
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
// If the install method immediatly fails,
|
||||
// reject immediatly the installPackaged promise.
|
||||
// Otherwise, wait for webappsEvent for completion
|
||||
if (res.error) {
|
||||
deferred.reject(res);
|
||||
}
|
||||
});
|
||||
client.addOneTimeListener("webappsEvent", function (aState, aType, aPacket) {
|
||||
if ("error" in aType)
|
||||
deferred.reject({error: aType.error, message: aType.message});
|
||||
else
|
||||
deferred.resolve({appId: aType.appId});
|
||||
});
|
||||
// Ensure deleting the temporary package file, but only if that a temporary
|
||||
// package created when we pass a directory as `packagePath`
|
||||
if (zipFile != file)
|
||||
zipFile.remove(false);
|
||||
// In case of success or error, ensure deleting the temporary package file
|
||||
// also created on the device, but only once install request is done
|
||||
deferred.promise.then(removeServerTemporaryFile, removeServerTemporaryFile);
|
||||
});
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
exports.installPackaged = installPackaged;
|
||||
|
||||
function installHosted(client, webappsActor, appId, metadata, manifest) {
|
||||
let deferred = promise.defer();
|
||||
let request = {
|
||||
to: webappsActor,
|
||||
type: "install",
|
||||
appId: appId,
|
||||
metadata: metadata,
|
||||
manifest: manifest
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
if (res.error) {
|
||||
deferred.reject(res);
|
||||
}
|
||||
});
|
||||
client.addOneTimeListener("webappsEvent", function (aState, aType, aPacket) {
|
||||
if ("error" in aType)
|
||||
deferred.reject({error: aType.error, message: aType.message});
|
||||
else
|
||||
deferred.resolve({appId: aType.appId});
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
exports.installHosted = installHosted;
|
||||
|
@ -5,3 +5,9 @@
|
||||
|
||||
TEST_DIRS += ['tests']
|
||||
|
||||
JS_MODULES_PATH = 'modules/devtools'
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'Simulator.jsm',
|
||||
'app-actor-front.js'
|
||||
]
|
||||
|
@ -98,12 +98,13 @@ function do_get_webappsdir() {
|
||||
var webappsDir = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
webappsDir.append("test_webapps");
|
||||
if (!webappsDir.exists())
|
||||
webappsDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
|
||||
webappsDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("755", 8));
|
||||
|
||||
var coreAppsDir = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
coreAppsDir.append("test_coreapps");
|
||||
if (!coreAppsDir.exists())
|
||||
coreAppsDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
|
||||
coreAppsDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("755", 8));
|
||||
var tmpDir = Services.dirsvc.get("TmpD", Ci.nsILocalFile);
|
||||
|
||||
// Register our own provider for the profile directory.
|
||||
// It will return our special docshell profile directory.
|
||||
@ -116,6 +117,7 @@ function do_get_webappsdir() {
|
||||
else if (prop == "coreAppsDir") {
|
||||
return coreAppsDir.clone();
|
||||
}
|
||||
return tmpDir.clone();
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
},
|
||||
QueryInterface: function(iid) {
|
||||
|
@ -2,6 +2,9 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const {require} = devtools;
|
||||
const {installHosted, installPackaged} = require("devtools/app-actor-front");
|
||||
|
||||
let gAppId = "actor-test";
|
||||
const APP_ORIGIN = "app://" + gAppId;
|
||||
@ -177,105 +180,35 @@ add_test(function testUninstall() {
|
||||
});
|
||||
|
||||
add_test(function testFileUploadInstall() {
|
||||
function createUpload() {
|
||||
let request = {
|
||||
type: "uploadPackage"
|
||||
};
|
||||
webappActorRequest(request, function (aResponse) {
|
||||
getPackageContent(aResponse.actor);
|
||||
});
|
||||
}
|
||||
function getPackageContent(uploadActor) {
|
||||
let packageFile = do_get_file("data/app.zip");
|
||||
OS.File.read(packageFile.path)
|
||||
.then(function (bytes) {
|
||||
// To work around the fact that JSON.stringify translates the typed
|
||||
// array to object, we are encoding the typed array here into a string
|
||||
let content = String.fromCharCode.apply(null, bytes);
|
||||
uploadChunk(uploadActor, content);
|
||||
});
|
||||
}
|
||||
function uploadChunk(uploadActor, content) {
|
||||
let request = {
|
||||
to: uploadActor,
|
||||
type: "chunk",
|
||||
chunk: content
|
||||
};
|
||||
gClient.request(request, function (aResponse) {
|
||||
endsUpload(uploadActor);
|
||||
});
|
||||
}
|
||||
function endsUpload(uploadActor, content) {
|
||||
let request = {
|
||||
to: uploadActor,
|
||||
type: "done"
|
||||
};
|
||||
gClient.request(request, function (aResponse) {
|
||||
installApp(uploadActor);
|
||||
});
|
||||
}
|
||||
function installApp(uploadActor) {
|
||||
let request = {type: "install", appId: gAppId, upload: uploadActor};
|
||||
webappActorRequest(request, function (aResponse) {
|
||||
do_check_eq(aResponse.appId, gAppId);
|
||||
});
|
||||
gClient.addOneTimeListener("webappsEvent", function listener(aState, aType, aPacket) {
|
||||
do_check_eq(aType.appId, gAppId);
|
||||
if ("error" in aType) {
|
||||
do_print("Error: " + aType.error);
|
||||
}
|
||||
if ("message" in aType) {
|
||||
do_print("Error message: " + aType.message);
|
||||
}
|
||||
do_check_eq("error" in aType, false);
|
||||
|
||||
removeUpload(uploadActor);
|
||||
});
|
||||
}
|
||||
function removeUpload(uploadActor, content) {
|
||||
let request = {
|
||||
to: uploadActor,
|
||||
type: "remove"
|
||||
};
|
||||
gClient.request(request, function (aResponse) {
|
||||
let packageFile = do_get_file("data/app.zip");
|
||||
installPackaged(gClient, gActor, packageFile.path, gAppId)
|
||||
.then(function ({ appId }) {
|
||||
do_check_eq(appId, gAppId);
|
||||
run_next_test();
|
||||
}, function (e) {
|
||||
do_throw("Failed install uploaded packaged app: " + e.error + ": " + e.message);
|
||||
});
|
||||
}
|
||||
createUpload();
|
||||
});
|
||||
|
||||
add_test(function testInstallHosted() {
|
||||
gAppId = "hosted-app";
|
||||
let request = {
|
||||
type: "install",
|
||||
appId: gAppId,
|
||||
manifest: {
|
||||
name: "My hosted app"
|
||||
},
|
||||
metadata: {
|
||||
origin: "http://foo.com",
|
||||
installOrigin: "http://metadata.foo.com",
|
||||
manifestURL: "http://foo.com/metadata/manifest.webapp"
|
||||
}
|
||||
let metadata = {
|
||||
origin: "http://foo.com",
|
||||
installOrigin: "http://metadata.foo.com",
|
||||
manifestURL: "http://foo.com/metadata/manifest.webapp"
|
||||
};
|
||||
webappActorRequest(request, function (aResponse) {
|
||||
do_check_eq(aResponse.appId, gAppId);
|
||||
});
|
||||
|
||||
// The install request is asynchronous and send back an event to tell
|
||||
// if the installation succeed or failed
|
||||
gClient.addOneTimeListener("webappsEvent", function listener(aState, aType, aPacket) {
|
||||
do_check_eq(aType.appId, gAppId);
|
||||
if ("error" in aType) {
|
||||
do_print("Error: " + aType.error);
|
||||
let manifest = {
|
||||
name: "My hosted app"
|
||||
};
|
||||
installHosted(gClient, gActor, gAppId, metadata, manifest).then(
|
||||
function ({ appId }) {
|
||||
do_check_eq(appId, gAppId);
|
||||
run_next_test();
|
||||
},
|
||||
function (e) {
|
||||
do_throw("Failed installing hosted app: " + e.error + ": " + e.message);
|
||||
}
|
||||
if ("message" in aType) {
|
||||
do_print("Error message: " + aType.message);
|
||||
}
|
||||
do_check_eq("error" in aType, false);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
add_test(function testCheckHostedApp() {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user