merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-03-11 12:58:02 +01:00
commit 2550f09581
50 changed files with 1312 additions and 356 deletions

View File

@ -18,7 +18,7 @@ sinclude $(topsrcdir)/config/rules.mk
# This can switch to just zipping the files when native jetpacks land
$(TESTADDONS)/%.xpi: FORCE $(call mkdir_deps,$(CURDIR)/$(TESTADDONS)) $(ADDONSRC)/%
$(PYTHON) $(srcdir)/source/bin/cfx xpi --pkgdir=$(lastword $^) --output-file=$@
$(PYTHON) $(srcdir)/source/bin/cfx xpi --no-strip-xpi --pkgdir=$(lastword $^) --output-file=$@
#libs:: $(ADDONS)

View File

@ -1461,6 +1461,8 @@ pref("devtools.profiler.ui.show-platform-data", false);
pref("devtools.profiler.ui.show-idle-blocks", true);
// The default Performance UI settings
pref("devtools.performance.memory.sample-probability", "0.05");
pref("devtools.performance.memory.max-log-length", 2147483647); // Math.pow(2,31) - 1
pref("devtools.performance.timeline.hidden-markers", "[]");
pref("devtools.performance.ui.invert-call-tree", true);
pref("devtools.performance.ui.invert-flame-graph", false);

View File

@ -30,7 +30,7 @@ function createScheduler(options) {
// avoid typos in the test and other footguns in the options.
let allowedOptions = ["expectedDelay", "expectNewTimer", "syncFunction"];
for (let key of Object.keys(options)) {
if (!allowedOptions.includes(key)) {
if (allowedOptions.indexOf(key) == -1) {
throw new Error("Invalid option " + key);
}
}

View File

@ -329,7 +329,7 @@ PerformanceFront.prototype = {
return 0;
}
yield this._request("memory", "attach");
let memoryStartTime = yield this._request("memory", "startRecordingAllocations");
let memoryStartTime = yield this._request("memory", "startRecordingAllocations", options);
yield this._pullAllocationSites();
return memoryStartTime;
}),

View File

@ -182,7 +182,9 @@ let PerformanceController = {
// ToolbarView, so that they may be accessible via the "gear" menu.
// Every other pref should be registered here.
this._nonBooleanPrefs = new ViewHelpers.Prefs("devtools.performance", {
"hidden-markers": ["Json", "timeline.hidden-markers"]
"hidden-markers": ["Json", "timeline.hidden-markers"],
"memory-sample-probability": ["Float", "memory.sample-probability"],
"memory-max-log-length": ["Int", "memory.max-log-length"]
});
this._nonBooleanPrefs.registerObserver();
@ -262,11 +264,13 @@ let PerformanceController = {
let withMemory = this.getOption("enable-memory");
let withTicks = this.getOption("enable-framerate");
let withAllocations = this.getOption("enable-memory");
let probability = this.getPref("memory-sample-probability");
let maxLogLength = this.getPref("memory-max-log-length");
let recording = this._createRecording({ withMemory, withTicks, withAllocations });
let recording = this._createRecording({ withMemory, withTicks, withAllocations, probability, maxLogLength });
this.emit(EVENTS.RECORDING_WILL_START, recording);
yield recording.startRecording({ withTicks, withMemory, withAllocations });
yield recording.startRecording({ withMemory, withTicks, withAllocations, probability, maxLogLength });
this.emit(EVENTS.RECORDING_STARTED, recording);
this.setCurrentRecording(recording);

View File

@ -54,6 +54,7 @@ support-files =
[browser_perf-options-enable-memory-01.js]
[browser_perf-options-enable-memory-02.js]
[browser_perf-options-enable-framerate.js]
[browser_perf-options-allocations.js]
[browser_perf-overview-render-01.js]
[browser_perf-overview-render-02.js]
[browser_perf-overview-render-03.js]

View File

@ -0,0 +1,33 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that setting the `devtools.performance.memory.` prefs propagate to the memory actor.
*/
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, PerformanceController, $, gFront } = panel.panelWin;
Services.prefs.setBoolPref(MEMORY_PREF, true);
let originalProbability = Services.prefs.getCharPref("devtools.performance.memory.sample-probability");
let originalLogLength = Services.prefs.getIntPref("devtools.performance.memory.max-log-length");
Services.prefs.setCharPref("devtools.performance.memory.sample-probability", "0.213");
Services.prefs.setIntPref("devtools.performance.memory.max-log-length", 777777);
yield startRecording(panel);
let { probability, maxLogLength } = yield gFront._request("memory", "getAllocationsSettings");
yield stopRecording(panel);
is(probability, 0.213, "allocations probability option is set on memory actor");
is(maxLogLength, 777777, "allocations max log length option is set on memory actor");
Services.prefs.setBoolPref(MEMORY_PREF, false);
Services.prefs.setCharPref("devtools.performance.memory.sample-probability", originalProbability);
Services.prefs.setIntPref("devtools.performance.memory.max-log-length", originalLogLength);
yield teardown(panel);
finish();
}

View File

@ -58,7 +58,8 @@ skip-if = e10s # Layouthelpers test should not run in a content page.
[browser_options-view-01.js]
[browser_outputparser.js]
skip-if = e10s # Test intermittently fails with e10s. Bug 1124162.
[browser_prefs.js]
[browser_prefs-01.js]
[browser_prefs-02.js]
[browser_require_basic.js]
[browser_spectrum.js]
[browser_theme.js]

View File

@ -0,0 +1,39 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that ViewHelpers.Prefs work properly with custom types of Float and Json.
let {ViewHelpers} = Cu.import("resource:///modules/devtools/ViewHelpers.jsm", {});
function test() {
let originalJson = Services.prefs.getCharPref("devtools.performance.timeline.hidden-markers");
let originalFloat = Services.prefs.getCharPref("devtools.performance.memory.sample-probability");
let Prefs = new ViewHelpers.Prefs("devtools.performance", {
"float": ["Float", "memory.sample-probability"],
"json": ["Json", "timeline.hidden-markers"]
});
Prefs.registerObserver();
// Float
Services.prefs.setCharPref("devtools.performance.timeline.hidden-markers", "{\"a\":1}");
is(Prefs.json.a, 1, "The JSON pref value is correctly casted on get.");
Prefs.json = { b: 2 };
is(Prefs.json.a, undefined, "The JSON pref value is correctly casted on set (1).");
is(Prefs.json.b, 2, "The JSON pref value is correctly casted on set (2).");
// Float
Services.prefs.setCharPref("devtools.performance.memory.sample-probability", "3.14");
is(Prefs.float, 3.14, "The float pref value is correctly casted on get.");
Prefs.float = 6.28;
is(Prefs.float, 6.28, "The float pref value is correctly casted on set.");
Prefs.unregisterObserver();
Services.prefs.setCharPref("devtools.performance.timeline.hidden-markers", originalJson);
Services.prefs.setCharPref("devtools.performance.memory.sample-probability", originalFloat);
finish();
}

View File

@ -187,6 +187,7 @@ MarkerDetails.prototype = {
if (displayName) {
let functionLabel = this._document.createElement("label");
functionLabel.className = "devtools-monospace";
functionLabel.setAttribute("value", displayName);
hbox.appendChild(functionLabel);
}

View File

@ -390,7 +390,8 @@ ViewHelpers.L10N.prototype = {
* let prefs = new ViewHelpers.Prefs("root.path.to.branch", {
* myIntPref: ["Int", "leaf.path.to.my-int-pref"],
* myCharPref: ["Char", "leaf.path.to.my-char-pref"],
* myJsonPref: ["Json", "leaf.path.to.my-json-pref"]
* myJsonPref: ["Json", "leaf.path.to.my-json-pref"],
* myFloatPref: ["Float", "leaf.path.to.my-float-pref"]
* ...
* });
*
@ -477,8 +478,8 @@ ViewHelpers.Prefs.prototype = {
/**
* Maps a property name to a pref, defining lazy getters and setters.
* Supported types are "Bool", "Char", "Int" and "Json" (which is basically
* just sugar for "Char" using the standard JSON serializer).
* Supported types are "Bool", "Char", "Int", "Float" (sugar around "Char" type and casting),
* and "Json" (which is basically just sugar for "Char" using the standard JSON serializer).
*
* @param string aAccessorName
* @param string aType
@ -494,6 +495,10 @@ ViewHelpers.Prefs.prototype = {
this._map(aAccessorName, "Char", aPrefsRoot, aPrefName, { in: JSON.parse, out: JSON.stringify });
return;
}
if (aType == "Float") {
this._map(aAccessorName, "Char", aPrefsRoot, aPrefName, { in: Number.parseFloat, out: (n) => n + ""});
return;
}
Object.defineProperty(this, aAccessorName, {
get: () => aSerializer.in(this._get(aType, aPrefsRoot, aPrefName)),

View File

@ -28,3 +28,6 @@ webide.jar:
content/wifi-auth.xhtml (wifi-auth.xhtml)
content/logs.xhtml (logs.xhtml)
content/logs.js (logs.js)
content/project-listing.xhtml (project-listing.xhtml)
content/project-listing.js (project-listing.js)
content/project-panel.js (project-panel.js)

View File

@ -0,0 +1,49 @@
/* 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 = Components.utils;
const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
const {AppManager} = require("devtools/webide/app-manager");
const ProjectList = require("devtools/webide/project-list");
let projectList = new ProjectList(window, window.parent);
window.addEventListener("load", function onLoad() {
window.removeEventListener("load", onLoad);
AppManager.on("app-manager-update", onAppManagerUpdate);
document.getElementById("new-app").onclick = CreateNewApp;
document.getElementById("hosted-app").onclick = ImportHostedApp;
document.getElementById("packaged-app").onclick = ImportPackagedApp;
projectList.update();
}, true);
window.addEventListener("unload", function onUnload() {
window.removeEventListener("unload", onUnload);
projectList = null;
AppManager.off("app-manager-update", onAppManagerUpdate);
});
function onAppManagerUpdate(event, what) {
switch (what) {
case "list-tabs-response":
case "runtime-apps-found":
case "project-validated":
case "project-removed":
case "project":
projectList.update();
break;
}
}
function CreateNewApp() {
projectList.newApp();
}
function ImportHostedApp() {
projectList.importHostedApp();
}
function ImportPackagedApp() {
projectList.importPackagedApp();
}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % webideDTD SYSTEM "chrome://browser/locale/devtools/webide.dtd" >
%webideDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf8"/>
<link rel="stylesheet" href="chrome://webide/skin/project-listing.css" type="text/css"/>
<script type="application/javascript;version=1.8" src="chrome://webide/content/project-listing.js"></script>
</head>
<body>
<div id="project-panel">
<div id="project-panel-box">
<button class="panel-item project-panel-item-newapp" id="new-app">&projectMenu_newApp_label;</button>
<button class="panel-item project-panel-item-openpackaged" id="packaged-app">&projectMenu_importPackagedApp_label;</button>
<button class="panel-item project-panel-item-openhosted" id="hosted-app">&projectMenu_importHostedApp_label;</button>
<label class="panel-header">&projectPanel_myProjects;</label>
<div id="project-panel-projects"></div>
<label class="panel-header" id="panel-header-runtimeapps" hidden="true">&projectPanel_runtimeApps;</label>
<div id="project-panel-runtimeapps"/>
<label class="panel-header" id="panel-header-tabs" hidden="true">&projectPanel_tabs;</label>
<div id="project-panel-tabs"/>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,39 @@
/* 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/. */
let ProjectPanel = {
// TODO: Expand function to save toggle state.
toggle: function(sidebarsEnabled, triggerPopup) {
let deferred = promise.defer();
let doc = document;
if (sidebarsEnabled) {
doc.querySelector("#project-listing-panel").setAttribute("sidebar-displayed", true);
doc.querySelector("#project-listing-splitter").setAttribute("sidebar-displayed", true);
deferred.resolve();
} else if (triggerPopup) {
let panelNode = doc.querySelector("#project-panel");
let panelVboxNode = doc.querySelector("#project-panel > #project-panel-box");
let anchorNode = doc.querySelector("#project-panel-button > .panel-button-anchor");
window.setTimeout(() => {
// Open the popup only when the projects are added.
// Not doing it in the next tick can cause mis-calculations
// of the size of the panel.
function onPopupShown() {
panelNode.removeEventListener("popupshown", onPopupShown);
deferred.resolve();
}
panelNode.addEventListener("popupshown", onPopupShown);
panelNode.openPopup(anchorNode);
panelVboxNode.scrollTop = 0;
}, 0);
} else {
deferred.resolve();
}
return deferred.promise;
}
};

View File

@ -24,6 +24,7 @@ const utils = require("devtools/webide/utils");
const Telemetry = require("devtools/shared/telemetry");
const {RuntimeScanners, WiFiScanner} = require("devtools/webide/runtimes");
const {showDoorhanger} = require("devtools/shared/doorhanger");
const ProjectList = require("devtools/webide/project-list");
const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");
@ -51,6 +52,8 @@ window.addEventListener("unload", function onUnload() {
UI.uninit();
});
let projectList;
let UI = {
init: function() {
this._telemetry = new Telemetry();
@ -64,6 +67,9 @@ let UI = {
this.appManagerUpdate = this.appManagerUpdate.bind(this);
AppManager.on("app-manager-update", this.appManagerUpdate);
projectList = new ProjectList(window, window);
ProjectPanel.toggle(projectList.sidebarsEnabled);
this.updateCommands();
this.updateRuntimeList();
@ -72,6 +78,7 @@ let UI = {
AppProjects.load().then(() => {
this.autoSelectProject();
projectList.update();
}, e => {
console.error(e);
this.reportError("error_appProjectsLoadFailed");
@ -115,6 +122,7 @@ let UI = {
window.removeEventListener("focus", this.onfocus, true);
AppManager.off("app-manager-update", this.appManagerUpdate);
AppManager.uninit();
projectList = null;
window.removeEventListener("message", this.onMessage);
this.updateConnectionTelemetry();
this._telemetry.toolClosed("webide");
@ -171,6 +179,7 @@ let UI = {
UI.openProject();
UI.autoStartProject();
UI.saveLastSelectedProject();
projectList.update();
});
return;
case "project-is-not-running":
@ -190,12 +199,17 @@ let UI = {
this.updateCommands();
this.updateProjectButton();
this.updateProjectEditorHeader();
projectList.update();
break;
case "project-removed":
projectList.update();
break;
case "install-progress":
this.updateProgress(Math.round(100 * details.bytesSent / details.totalBytes));
break;
case "runtime-apps-found":
this.autoSelectProject();
projectList.update();
break;
case "pre-package":
this.prePackageLog(details);
@ -225,6 +239,7 @@ let UI = {
}
},
// TODO: remove hidePanel when project layout is complete - Bug 1079347
hidePanels: function() {
let panels = document.querySelectorAll("panel");
for (let p of panels) {
@ -527,14 +542,16 @@ let UI = {
let project = AppManager.selectedProject;
if (!project) {
buttonNode.classList.add("no-project");
labelNode.setAttribute("value", Strings.GetStringFromName("projectButton_label"));
imageNode.removeAttribute("src");
} else {
buttonNode.classList.remove("no-project");
labelNode.setAttribute("value", project.name);
imageNode.setAttribute("src", project.icon);
if (!projectList.sidebarsEnabled) {
if (!project) {
buttonNode.classList.add("no-project");
labelNode.setAttribute("value", Strings.GetStringFromName("projectButton_label"));
imageNode.removeAttribute("src");
} else {
buttonNode.classList.remove("no-project");
labelNode.setAttribute("value", project.name);
imageNode.setAttribute("src", project.icon);
}
}
},
@ -1039,217 +1056,19 @@ let Cmds = {
* }
*/
newApp: function(testOptions) {
return UI.busyUntil(Task.spawn(function* () {
// Open newapp.xul, which will feed ret.location
let ret = {location: null, testOptions: testOptions};
window.openDialog("chrome://webide/content/newapp.xul", "newapp", "chrome,modal", ret);
if (!ret.location)
return;
// Retrieve added project
let project = AppProjects.get(ret.location);
// Select project
AppManager.selectedProject = project;
}), "creating new app");
projectList.newApp(testOptions);
},
importPackagedApp: function(location) {
return UI.busyUntil(Task.spawn(function* () {
let directory = utils.getPackagedDirectory(window, location);
if (!directory) {
// User cancelled directory selection
return;
}
yield UI.importAndSelectApp(directory);
}), "importing packaged app");
projectList.importPackagedApp(location);
},
importHostedApp: function(location) {
return UI.busyUntil(Task.spawn(function* () {
let url = utils.getHostedURL(window, location);
if (!url) {
return;
}
yield UI.importAndSelectApp(url);
}), "importing hosted app");
projectList.importHostedApp(location);
},
showProjectPanel: function() {
let deferred = promise.defer();
let panelNode = document.querySelector("#project-panel");
let panelVboxNode = document.querySelector("#project-panel > vbox");
let anchorNode = document.querySelector("#project-panel-button > .panel-button-anchor");
let projectsNode = document.querySelector("#project-panel-projects");
while (projectsNode.hasChildNodes()) {
projectsNode.firstChild.remove();
}
AppProjects.load().then(() => {
let projects = AppProjects.store.object.projects;
for (let i = 0; i < projects.length; i++) {
let project = projects[i];
let panelItemNode = document.createElement("toolbarbutton");
panelItemNode.className = "panel-item";
projectsNode.appendChild(panelItemNode);
panelItemNode.setAttribute("label", project.name || AppManager.DEFAULT_PROJECT_NAME);
panelItemNode.setAttribute("image", project.icon || AppManager.DEFAULT_PROJECT_ICON);
if (!project.name || !project.icon) {
// The result of the validation process (storing names, icons, …) is not stored in
// the IndexedDB database when App Manager v1 is used.
// We need to run the validation again and update the name and icon of the app.
AppManager.validateAndUpdateProject(project).then(() => {
panelItemNode.setAttribute("label", project.name);
panelItemNode.setAttribute("image", project.icon);
});
}
panelItemNode.addEventListener("click", () => {
UI.hidePanels();
AppManager.selectedProject = project;
}, true);
}
window.setTimeout(() => {
// Open the popup only when the projects are added.
// Not doing it in the next tick can cause mis-calculations
// of the size of the panel.
function onPopupShown() {
panelNode.removeEventListener("popupshown", onPopupShown);
deferred.resolve();
}
panelNode.addEventListener("popupshown", onPopupShown);
panelNode.openPopup(anchorNode);
panelVboxNode.scrollTop = 0;
}, 0);
}, deferred.reject);
let runtimeappsHeaderNode = document.querySelector("#panel-header-runtimeapps");
let sortedApps = [];
for (let [manifestURL, app] of AppManager.apps) {
sortedApps.push(app);
}
sortedApps = sortedApps.sort((a, b) => {
return a.manifest.name > b.manifest.name;
});
let mainProcess = AppManager.isMainProcessDebuggable();
if (AppManager.connected && (sortedApps.length > 0 || mainProcess)) {
runtimeappsHeaderNode.removeAttribute("hidden");
} else {
runtimeappsHeaderNode.setAttribute("hidden", "true");
}
let runtimeAppsNode = document.querySelector("#project-panel-runtimeapps");
while (runtimeAppsNode.hasChildNodes()) {
runtimeAppsNode.firstChild.remove();
}
if (mainProcess) {
let panelItemNode = document.createElement("toolbarbutton");
panelItemNode.className = "panel-item";
panelItemNode.setAttribute("label", Strings.GetStringFromName("mainProcess_label"));
panelItemNode.setAttribute("image", AppManager.DEFAULT_PROJECT_ICON);
runtimeAppsNode.appendChild(panelItemNode);
panelItemNode.addEventListener("click", () => {
UI.hidePanels();
AppManager.selectedProject = {
type: "mainProcess",
name: Strings.GetStringFromName("mainProcess_label"),
icon: AppManager.DEFAULT_PROJECT_ICON
};
}, true);
}
for (let i = 0; i < sortedApps.length; i++) {
let app = sortedApps[i];
let panelItemNode = document.createElement("toolbarbutton");
panelItemNode.className = "panel-item";
panelItemNode.setAttribute("label", app.manifest.name);
panelItemNode.setAttribute("image", app.iconURL);
runtimeAppsNode.appendChild(panelItemNode);
panelItemNode.addEventListener("click", () => {
UI.hidePanels();
AppManager.selectedProject = {
type: "runtimeApp",
app: app.manifest,
icon: app.iconURL,
name: app.manifest.name
};
}, true);
}
// Build the tab list right now, so it's fast...
this._buildProjectPanelTabs();
// But re-list them and rebuild, in case any tabs navigated since the last
// time they were listed.
if (AppManager.connected) {
AppManager.listTabs().then(() => {
this._buildProjectPanelTabs();
}).catch(console.error);
}
return deferred.promise;
},
_buildProjectPanelTabs: function() {
let tabs = AppManager.tabStore.tabs;
let tabsHeaderNode = document.querySelector("#panel-header-tabs");
if (AppManager.connected && tabs.length > 0) {
tabsHeaderNode.removeAttribute("hidden");
} else {
tabsHeaderNode.setAttribute("hidden", "true");
}
let tabsNode = document.querySelector("#project-panel-tabs");
while (tabsNode.hasChildNodes()) {
tabsNode.firstChild.remove();
}
for (let i = 0; i < tabs.length; i++) {
let tab = tabs[i];
let url;
try {
url = new URL(tab.url);
} catch (e) {
// Don't try to handle invalid URLs, especially from Valence.
continue;
}
// Wanted to use nsIFaviconService here, but it only works for visited
// tabs, so that's no help for any remote tabs. Maybe some favicon wizard
// knows how to get high-res favicons easily, or we could offer actor
// support for this (bug 1061654).
tab.favicon = url.origin + "/favicon.ico";
tab.name = tab.title || Strings.GetStringFromName("project_tab_loading");
if (url.protocol.startsWith("http")) {
tab.name = url.hostname + ": " + tab.name;
}
let panelItemNode = document.createElement("toolbarbutton");
panelItemNode.className = "panel-item";
panelItemNode.setAttribute("label", tab.name);
panelItemNode.setAttribute("image", tab.favicon);
tabsNode.appendChild(panelItemNode);
panelItemNode.addEventListener("click", () => {
UI.hidePanels();
AppManager.selectedProject = {
type: "tab",
app: tab,
icon: tab.favicon,
location: tab.url,
name: tab.name
};
}, true);
}
ProjectPanel.toggle(projectList.sidebarsEnabled, true);
},
showRuntimePanel: function() {
@ -1346,7 +1165,7 @@ let Cmds = {
},
removeProject: function() {
return AppManager.removeSelectedProject();
AppManager.removeSelectedProject();
},
toggleEditors: function() {

View File

@ -12,6 +12,7 @@
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/common.css"?>
<?xml-stylesheet href="chrome://webide/skin/webide.css"?>
<window id="webide" onclose="return UI.canCloseProject();"
@ -26,6 +27,7 @@
persist="screenX screenY width height sizemode">
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"></script>
<script type="application/javascript" src="project-panel.js"></script>
<script type="application/javascript" src="webide.js"></script>
<commandset id="mainCommandSet">
@ -150,7 +152,7 @@
<!-- App panel -->
<panel id="project-panel" type="arrow" position="bottomcenter topleft" consumeoutsideclicks="true" animate="false">
<vbox flex="1">
<vbox flex="1" id="project-panel-box">
<toolbarbutton class="panel-item project-panel-item-newapp" command="cmd_newApp"/>
<toolbarbutton class="panel-item project-panel-item-openpackaged" command="cmd_importPackagedApp"/>
<toolbarbutton class="panel-item project-panel-item-openhosted" command="cmd_importHostedApp"/>
@ -191,18 +193,26 @@
</popupset>
<notificationbox flex="1" id="notificationbox">
<deck flex="1" id="deck" selectedIndex="-1">
<iframe id="deck-panel-details" flex="1" src="details.xhtml"/>
<iframe id="deck-panel-projecteditor" flex="1"/>
<iframe id="deck-panel-addons" flex="1" src="addons.xhtml"/>
<iframe id="deck-panel-prefs" flex="1" src="prefs.xhtml"/>
<iframe id="deck-panel-permissionstable" flex="1" lazysrc="permissionstable.xhtml"/>
<iframe id="deck-panel-runtimedetails" flex="1" lazysrc="runtimedetails.xhtml"/>
<iframe id="deck-panel-monitor" flex="1" lazysrc="monitor.xhtml"/>
<iframe id="deck-panel-devicepreferences" flex="1" lazysrc="devicepreferences.xhtml"/>
<iframe id="deck-panel-devicesettings" flex="1" lazysrc="devicesettings.xhtml"/>
<iframe id="deck-panel-logs" flex="1" src="logs.xhtml"/>
</deck>
<hbox flex="1">
<vbox id="project-listing-panel" class="project-listing" flex="1">
<div id="project-listing-wrapper">
<iframe id="project-listing-panel-details" flex="1" src="project-listing.xhtml"/>
</div>
</vbox>
<splitter class="devtools-side-splitter" id="project-listing-splitter"/>
<deck flex="1" id="deck" selectedIndex="-1">
<iframe id="deck-panel-details" flex="1" src="details.xhtml"/>
<iframe id="deck-panel-projecteditor" flex="1"/>
<iframe id="deck-panel-addons" flex="1" src="addons.xhtml"/>
<iframe id="deck-panel-prefs" flex="1" src="prefs.xhtml"/>
<iframe id="deck-panel-permissionstable" flex="1" lazysrc="permissionstable.xhtml"/>
<iframe id="deck-panel-runtimedetails" flex="1" lazysrc="runtimedetails.xhtml"/>
<iframe id="deck-panel-monitor" flex="1" lazysrc="monitor.xhtml"/>
<iframe id="deck-panel-devicepreferences" flex="1" lazysrc="devicepreferences.xhtml"/>
<iframe id="deck-panel-devicesettings" flex="1" lazysrc="devicesettings.xhtml"/>
<iframe id="deck-panel-logs" flex="1" src="logs.xhtml"/>
</deck>
</hbox>
<splitter hidden="true" class="devtools-horizontal-splitter" orient="vertical"/>
<!-- toolbox iframe will be inserted here -->
</notificationbox>

View File

@ -327,15 +327,17 @@ let AppManager = exports.AppManager = {
return this._selectedProject;
},
removeSelectedProject: function() {
removeSelectedProject: Task.async(function*() {
let location = this.selectedProject.location;
AppManager.selectedProject = null;
// If the user cancels the removeProject operation, don't remove the project
if (AppManager.selectedProject != null) {
return;
}
return AppProjects.remove(location);
},
yield AppProjects.remove(location);
AppManager.update("project-removed");
}),
packageProject: Task.async(function*(project) {
if (!project) {

View File

@ -0,0 +1,299 @@
/* 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 {Services} = Cu.import("resource://gre/modules/Services.jsm");
const {AppProjects} = require("devtools/app-manager/app-projects");
const {AppManager} = require("devtools/webide/app-manager");
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const EventEmitter = require("devtools/toolkit/event-emitter");
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
const utils = require("devtools/webide/utils");
const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");
let ProjectList;
module.exports = ProjectList = function(window, parentWindow) {
EventEmitter.decorate(this);
this._doc = window.document;
this._UI = parentWindow.UI;
this._parentWindow = parentWindow;
this._panelNodeEl = "toolbarbutton";
this._sidebarsEnabled = Services.prefs.getBoolPref("devtools.webide.sidebars");
if (this._sidebarsEnabled) {
this._panelNodeEl = "div";
}
return this;
};
ProjectList.prototype = {
get doc() {
return this._doc;
},
get sidebarsEnabled() {
return this._sidebarsEnabled;
},
/**
* testOptions: { chrome mochitest support
* folder: nsIFile, where to store the app
* index: Number, index of the app in the template list
* name: String name of the app
* }
*/
newApp: function(testOptions) {
let parentWindow = this._parentWindow;
return this._UI.busyUntil(Task.spawn(function*() {
// Open newapp.xul, which will feed ret.location
let ret = {location: null, testOptions: testOptions};
parentWindow.openDialog("chrome://webide/content/newapp.xul", "newapp", "chrome,modal", ret);
if (!ret.location)
return;
// Retrieve added project
let project = AppProjects.get(ret.location);
// Select project
AppManager.selectedProject = project;
}), "creating new app");
},
importPackagedApp: function(location) {
let parentWindow = this._parentWindow;
let UI = this._UI;
return UI.busyUntil(Task.spawn(function*() {
let directory = utils.getPackagedDirectory(parentWindow, location);
if (!directory) {
// User cancelled directory selection
return;
}
yield UI.importAndSelectApp(directory);
}), "importing packaged app");
},
importHostedApp: function(location) {
let parentWindow = this._parentWindow;
let UI = this._UI;
return UI.busyUntil(Task.spawn(function*() {
let url = utils.getHostedURL(parentWindow, location);
if (!url) {
return;
}
yield UI.importAndSelectApp(url);
}), "importing hosted app");
},
/**
* opts: {
* panel: Object, currenl project panel node
* name: String, name of the project
* icon: String path of the project icon
* }
*/
_renderProjectItem: function(opts) {
if (this._sidebarsEnabled) {
let span = this._doc.createElement("span");
span.textContent = opts.name;
let icon = this._doc.createElement("img");
icon.className = "project-image";
icon.setAttribute("src", opts.icon);
opts.panel.appendChild(icon);
opts.panel.appendChild(span);
} else {
opts.panel.setAttribute("label", opts.name);
opts.panel.setAttribute("image", opts.icon);
}
},
_buildProjectPanelTabs: function() {
let tabs = AppManager.tabStore.tabs;
let tabsHeaderNode = this._doc.querySelector("#panel-header-tabs");
if (AppManager.connected && tabs.length > 0) {
tabsHeaderNode.removeAttribute("hidden");
} else {
tabsHeaderNode.setAttribute("hidden", "true");
}
let tabsNode = this._doc.querySelector("#project-panel-tabs");
while (tabsNode.hasChildNodes()) {
tabsNode.firstChild.remove();
}
for (let i = 0; i < tabs.length; i++) {
let tab = tabs[i];
let URL = this._parentWindow.URL;
let url;
try {
url = new URL(tab.url);
} catch (e) {
// Don't try to handle invalid URLs, especially from Valence.
continue;
}
// Wanted to use nsIFaviconService here, but it only works for visited
// tabs, so that's no help for any remote tabs. Maybe some favicon wizard
// knows how to get high-res favicons easily, or we could offer actor
// support for this (bug 1061654).
tab.favicon = url.origin + "/favicon.ico";
tab.name = tab.title || Strings.GetStringFromName("project_tab_loading");
if (url.protocol.startsWith("http")) {
tab.name = url.hostname + ": " + tab.name;
}
let panelItemNode = this._doc.createElement(this._panelNodeEl);
panelItemNode.className = "panel-item";
tabsNode.appendChild(panelItemNode);
this._renderProjectItem({
panel: panelItemNode,
name: tab.name,
icon: tab.favicon
});
panelItemNode.addEventListener("click", () => {
if (!this._sidebarsEnabled) {
this._UI.hidePanels();
}
AppManager.selectedProject = {
type: "tab",
app: tab,
icon: tab.favicon,
location: tab.url,
name: tab.name
};
}, true);
}
},
update: function() {
let deferred = promise.defer();
let doc = this._doc;
let panelVboxNode = doc.querySelector("#project-panel > #project-panel-box");
let anchorNode = doc.querySelector("#project-panel-button > .panel-button-anchor");
let projectsNode = doc.querySelector("#project-panel-projects");
while (projectsNode.hasChildNodes()) {
projectsNode.firstChild.remove();
}
AppProjects.load().then(() => {
let projects = AppProjects.store.object.projects;
for (let i = 0; i < projects.length; i++) {
let project = projects[i];
let panelItemNode = doc.createElement(this._panelNodeEl);
panelItemNode.className = "panel-item";
projectsNode.appendChild(panelItemNode);
this._renderProjectItem({
panel: panelItemNode,
name: project.name || AppManager.DEFAULT_PROJECT_NAME,
icon: project.icon|| AppManager.DEFAULT_PROJECT_ICON
});
if (!project.name || !project.icon) {
// The result of the validation process (storing names, icons, …) is not stored in
// the IndexedDB database when App Manager v1 is used.
// We need to run the validation again and update the name and icon of the app.
AppManager.validateAndUpdateProject(project).then(() => {
this._renderProjectItem({
panel: panelItemNode,
name: project.name,
icon: project.icon
});
});
}
panelItemNode.addEventListener("click", () => {
if (!this._sidebarsEnabled) {
this._UI.hidePanels();
}
AppManager.selectedProject = project;
}, true);
}
deferred.resolve();
}, deferred.reject);
let runtimeappsHeaderNode = doc.querySelector("#panel-header-runtimeapps");
let sortedApps = [];
for (let [manifestURL, app] of AppManager.apps) {
sortedApps.push(app);
}
sortedApps = sortedApps.sort((a, b) => {
return a.manifest.name > b.manifest.name;
});
let mainProcess = AppManager.isMainProcessDebuggable();
if (AppManager.connected && (sortedApps.length > 0 || mainProcess)) {
runtimeappsHeaderNode.removeAttribute("hidden");
} else {
runtimeappsHeaderNode.setAttribute("hidden", "true");
}
let runtimeAppsNode = doc.querySelector("#project-panel-runtimeapps");
while (runtimeAppsNode.hasChildNodes()) {
runtimeAppsNode.firstChild.remove();
}
if (mainProcess) {
let panelItemNode = doc.createElement(this._panelNodeEl);
panelItemNode.className = "panel-item";
this._renderProjectItem({
panel: panelItemNode,
name: Strings.GetStringFromName("mainProcess_label"),
icon: AppManager.DEFAULT_PROJECT_ICON
});
runtimeAppsNode.appendChild(panelItemNode);
panelItemNode.addEventListener("click", () => {
if (!this._sidebarsEnabled) {
this._UI.hidePanels();
}
AppManager.selectedProject = {
type: "mainProcess",
name: Strings.GetStringFromName("mainProcess_label"),
icon: AppManager.DEFAULT_PROJECT_ICON
};
}, true);
}
for (let i = 0; i < sortedApps.length; i++) {
let app = sortedApps[i];
let panelItemNode = doc.createElement(this._panelNodeEl);
panelItemNode.className = "panel-item";
this._renderProjectItem({
panel: panelItemNode,
name: app.manifest.name,
icon: app.iconURL
});
runtimeAppsNode.appendChild(panelItemNode);
panelItemNode.addEventListener("click", () => {
if (!this._sidebarsEnabled) {
this._UI.hidePanels();
}
AppManager.selectedProject = {
type: "runtimeApp",
app: app.manifest,
icon: app.iconURL,
name: app.manifest.name
};
}, true);
}
// Build the tab list right now, so it's fast...
this._buildProjectPanelTabs();
// But re-list them and rebuild, in case any tabs navigated since the last
// time they were listed.
if (AppManager.connected) {
AppManager.listTabs().then(() => {
this._buildProjectPanelTabs();
}).catch(console.error);
}
return deferred.promise;
}
};

View File

@ -10,14 +10,21 @@ DIRS += [
'themes',
]
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
BROWSER_CHROME_MANIFESTS += [
'test/browser.ini',
'test/sidebars/browser.ini'
]
MOCHITEST_CHROME_MANIFESTS += [
'test/chrome.ini',
'test/sidebars/chrome.ini'
]
EXTRA_JS_MODULES.devtools.webide += [
'modules/addons.js',
'modules/app-manager.js',
'modules/build.js',
'modules/config-view.js',
'modules/project-list.js',
'modules/remote-resources.js',
'modules/runtimes.js',
'modules/simulator-process.js',

View File

@ -50,7 +50,7 @@ function connectToLocal(win) {
function selectTabProject(win) {
return Task.spawn(function() {
yield win.AppManager.listTabs();
win.Cmds.showProjectPanel();
win.projectList.update();
yield nextTick();
let tabsNode = win.document.querySelector("#project-panel-tabs");
let tabNode = tabsNode.querySelectorAll(".panel-item")[1];

View File

@ -36,6 +36,7 @@ SimpleTest.registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.webide.enableLocalRuntime");
Services.prefs.clearUserPref("devtools.webide.autoinstallADBHelper");
Services.prefs.clearUserPref("devtools.webide.autoinstallFxdtAdapters");
Services.prefs.clearUserPref("devtools.webide.sidebars");
});
function openWebIDE(autoInstallAddons) {

View File

@ -0,0 +1,4 @@
[DEFAULT]
subsuite = devtools
support-files =
../head.js

View File

@ -0,0 +1,8 @@
[DEFAULT]
support-files =
../app/index.html
../app/manifest.webapp
../head.js
[test_duplicate_import.html]
[test_import.html]

View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
<title></title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
<script type="application/javascript;version=1.8" src="../head.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<script type="application/javascript;version=1.8">
window.onload = function() {
SimpleTest.waitForExplicitFinish();
Task.spawn(function*() {
Services.prefs.setBoolPref("devtools.webide.sidebars", true);
let win = yield openWebIDE();
let winIframe = win.document.querySelector("#project-listing-panel-details").contentWindow;
let packagedAppLocation = getTestFilePath("../app");
let hostedAppManifest = TEST_BASE + "hosted_app.manifest";
yield win.AppProjects.load();
is(win.AppProjects.store.object.projects.length, 0, "IDB is empty");
info("to call importPackagedApp(" + packagedAppLocation + ")");
yield win.projectList.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
yield nextTick();
info("to call importHostedApp(" + hostedAppManifest + ")");
yield win.projectList.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
yield nextTick();
info("to call importPackagedApp(" + packagedAppLocation + ") again");
yield win.projectList.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
let project = win.AppManager.selectedProject;
is(project.location, packagedAppLocation, "Correctly reselected existing packaged app.");
yield nextTick();
info("to call importHostedApp(" + hostedAppManifest + ") again");
yield win.projectList.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
is(project.location, hostedAppManifest, "Correctly reselected existing hosted app.");
yield nextTick();
let panelNode = winIframe.document.querySelector("#project-panel");
let items = panelNode.querySelectorAll(".panel-item");
// 3 controls, + 2 projects
is(items.length, 5, "5 projects in panel");
is(items[3].querySelector("span").textContent, "A name (in app directory)", "Panel text is correct");
is(items[4].querySelector("span").textContent, "hosted manifest name property", "Panel text is correct");
yield closeWebIDE(win);
yield removeAllProjects();
SimpleTest.finish();
}).then(null, e => {
ok(false, "Exception: " + e);
SimpleTest.finish();
});
}
</script>
</body>
</html>

View File

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
<title></title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
<script type="application/javascript;version=1.8" src="../head.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<script type="application/javascript;version=1.8">
window.onload = function() {
SimpleTest.waitForExplicitFinish();
Task.spawn(function*() {
Services.prefs.setBoolPref("devtools.webide.sidebars", true);
let win = yield openWebIDE();
let winIframe = win.document.querySelector("#project-listing-panel-details").contentWindow;
let packagedAppLocation = getTestFilePath("../app");
yield win.AppProjects.load();
is(win.AppProjects.store.object.projects.length, 0, "IDB is empty");
info("to call importPackagedApp(" + packagedAppLocation + ")");
ok(!win.UI._busyPromise, "UI is not busy");
yield win.projectList.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
let project = win.AppManager.selectedProject;
is(project.location, packagedAppLocation, "Location is valid");
is(project.name, "A name (in app directory)", "name field has been updated");
is(project.manifest.launch_path, "/index.html", "manifest found. launch_path valid.");
is(project.manifest.description, "desc", "manifest found. description valid");
yield nextTick();
let hostedAppManifest = TEST_BASE + "hosted_app.manifest";
yield win.projectList.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
is(project.location, hostedAppManifest, "Location is valid");
is(project.name, "hosted manifest name property", "name field has been updated");
yield nextTick();
hostedAppManifest = TEST_BASE + "/app";
yield win.projectList.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
ok(project.location.endsWith('manifest.webapp'), "The manifest was found and the project was updated");
let panelNode = winIframe.document.querySelector("#project-panel");
let items = panelNode.querySelectorAll(".panel-item");
// 3 controls, + 2 projects
is(items.length, 6, "6 projects in panel");
is(items[3].querySelector("span").textContent, "A name (in app directory)", "Panel text is correct");
is(items[4].querySelector("span").textContent, "hosted manifest name property", "Panel text is correct");
yield closeWebIDE(win);
yield removeAllProjects();
SimpleTest.finish();
}).then(null, e => {
ok(false, "Exception: " + e);
SimpleTest.finish();
});
}
</script>
</body>
</html>

View File

@ -0,0 +1,145 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
<title></title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
<script type="application/javascript;version=1.8" src="../head.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<script type="application/javascript;version=1.8">
window.onload = function() {
SimpleTest.waitForExplicitFinish();
Services.prefs.setBoolPref("devtools.webide.sidebars", true);
let win;
SimpleTest.registerCleanupFunction(() => {
Task.spawn(function*() {
if (win) {
yield closeWebIDE(win);
}
DebuggerServer.destroy();
yield removeAllProjects();
});
});
Task.spawn(function*() {
function isPlayActive() {
return !win.document.querySelector("#cmd_play").hasAttribute("disabled");
}
function isStopActive() {
return !win.document.querySelector("#cmd_stop").hasAttribute("disabled");
}
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
win = yield openWebIDE();
let winIframe = win.document.querySelector("#project-listing-panel-details").contentWindow;
win.AppManager.runtimeList.usb.push({
connect: function(connection) {
is(connection, win.AppManager.connection, "connection is valid");
connection.host = null; // force connectPipe
connection.connect();
return promise.resolve();
},
get name() {
return "fakeRuntime";
}
});
win.AppManager.update("runtimelist");
let packagedAppLocation = getTestFilePath("app");
yield win.Cmds.importPackagedApp(packagedAppLocation);
let panelNode = winIframe.document.querySelector("#runtime-panel");
let items = panelNode.querySelectorAll(".runtime-panel-item-usb");
is(items.length, 1, "Found one runtime button");
let deferred = promise.defer();
win.AppManager.connection.once(
win.Connection.Events.CONNECTED,
() => deferred.resolve());
items[0].click();
ok(win.document.querySelector("window").className, "busy", "UI is busy");
yield win.UI._busyPromise;
is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
yield waitForUpdate(win, "runtime-apps-found");
ok(isPlayActive(), "play button is enabled 1");
ok(!isStopActive(), "stop button is disabled 1");
let oldProject = win.AppManager.selectedProject;
win.AppManager.selectedProject = null;
yield nextTick();
ok(!isPlayActive(), "play button is disabled 2");
ok(!isStopActive(), "stop button is disabled 2");
win.AppManager._selectedProject = oldProject;
win.UI.updateCommands();
yield nextTick();
ok(isPlayActive(), "play button is enabled 3");
ok(!isStopActive(), "stop button is disabled 3");
yield win.Cmds.disconnectRuntime();
is(Object.keys(DebuggerServer._connections).length, 0, "Disconnected");
ok(win.AppManager.selectedProject, "A project is still selected");
ok(!isPlayActive(), "play button is disabled 4");
ok(!isStopActive(), "stop button is disabled 4");
winIframe.document.querySelectorAll(".runtime-panel-item-other")[1].click();
yield waitForUpdate(win, "runtime-apps-found");
is(Object.keys(DebuggerServer._connections).length, 1, "Locally connected");
ok(win.AppManager.isMainProcessDebuggable(), "Main process available");
// Select main process
yield win.Cmds.showProjectPanel();
SimpleTest.executeSoon(() => {
winIframe.document.querySelectorAll("#project-panel-runtimeapps .panel-item")[0].click();
});
yield waitForUpdate(win, "project");
// Toolbox opens automatically for main process / runtime apps
ok(win.UI.toolboxPromise, "Toolbox promise exists");
yield win.UI.toolboxPromise;
ok(win.UI.toolboxIframe, "Toolbox iframe exists");
yield win.Cmds.disconnectRuntime();
SimpleTest.finish();
});
}
</script>
</body>
</html>

View File

@ -39,7 +39,7 @@
let packagedAppLocation = getTestFilePath("build_app" + testSuffix + "1");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield win.projectList.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "details");
let project = win.AppManager.selectedProject;
@ -76,7 +76,7 @@
// # Now test a full featured package.json
packagedAppLocation = getTestFilePath("build_app" + testSuffix + "2");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield win.projectList.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
@ -95,7 +95,7 @@
is(loggedMessages[3], "succeed", "log messages are correct");
// Switch to the package dir in order to verify the generated webapp.manifest
yield win.Cmds.importPackagedApp(packageDir);
yield win.projectList.importPackagedApp(packageDir);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;

View File

@ -27,17 +27,17 @@
is(win.AppProjects.store.object.projects.length, 0, "IDB is empty");
info("to call importPackagedApp(" + packagedAppLocation + ")");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield win.projectList.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
yield nextTick();
info("to call importHostedApp(" + hostedAppManifest + ")");
yield win.Cmds.importHostedApp(hostedAppManifest);
yield win.projectList.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
yield nextTick();
info("to call importPackagedApp(" + packagedAppLocation + ") again");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield win.projectList.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
let project = win.AppManager.selectedProject;
@ -45,7 +45,7 @@
yield nextTick();
info("to call importHostedApp(" + hostedAppManifest + ") again");
yield win.Cmds.importHostedApp(hostedAppManifest);
yield win.projectList.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
is(project.location, hostedAppManifest, "Correctly reselected existing hosted app.");

View File

@ -18,60 +18,60 @@
window.onload = function() {
SimpleTest.waitForExplicitFinish();
Task.spawn(function* () {
let win = yield openWebIDE();
let packagedAppLocation = getTestFilePath("app");
Task.spawn(function*() {
let win = yield openWebIDE();
let packagedAppLocation = getTestFilePath("app");
yield win.AppProjects.load();
is(win.AppProjects.store.object.projects.length, 0, "IDB is empty");
yield win.AppProjects.load();
is(win.AppProjects.store.object.projects.length, 0, "IDB is empty");
info("to call importPackagedApp(" + packagedAppLocation + ")");
ok(!win.UI._busyPromise, "UI is not busy");
info("to call importPackagedApp(" + packagedAppLocation + ")");
ok(!win.UI._busyPromise, "UI is not busy");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
yield win.projectList.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
let project = win.AppManager.selectedProject;
is(project.location, packagedAppLocation, "Location is valid");
is(project.name, "A name (in app directory)", "name field has been updated");
is(project.manifest.launch_path, "/index.html", "manifest found. launch_path valid.");
is(project.manifest.description, "desc", "manifest found. description valid");
let project = win.AppManager.selectedProject;
is(project.location, packagedAppLocation, "Location is valid");
is(project.name, "A name (in app directory)", "name field has been updated");
is(project.manifest.launch_path, "/index.html", "manifest found. launch_path valid.");
is(project.manifest.description, "desc", "manifest found. description valid");
yield nextTick();
yield nextTick();
let hostedAppManifest = TEST_BASE + "hosted_app.manifest";
yield win.Cmds.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
let hostedAppManifest = TEST_BASE + "hosted_app.manifest";
yield win.projectList.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
is(project.location, hostedAppManifest, "Location is valid");
is(project.name, "hosted manifest name property", "name field has been updated");
project = win.AppManager.selectedProject;
is(project.location, hostedAppManifest, "Location is valid");
is(project.name, "hosted manifest name property", "name field has been updated");
yield nextTick();
yield nextTick();
hostedAppManifest = TEST_BASE + "/app";
yield win.Cmds.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
hostedAppManifest = TEST_BASE + "/app";
yield win.projectList.importHostedApp(hostedAppManifest);
yield waitForUpdate(win, "project-validated");
project = win.AppManager.selectedProject;
ok(project.location.endsWith('manifest.webapp'), "The manifest was found and the project was updated");
project = win.AppManager.selectedProject;
ok(project.location.endsWith('manifest.webapp'), "The manifest was found and the project was updated");
info("opening panel");
yield win.Cmds.showProjectPanel();
info("panel open");
info("opening panel");
yield win.Cmds.showProjectPanel();
info("panel open");
let panelNode = win.document.querySelector("#project-panel");
let items = panelNode.querySelectorAll(".panel-item");
// 3 controls, + 2 projects
is(items.length, 6, "6 projects in panel");
is(items[3].getAttribute("label"), "A name (in app directory)", "Panel label is correct");
is(items[4].getAttribute("label"), "hosted manifest name property", "Panel label is correct");
let panelNode = win.document.querySelector("#project-panel");
let items = panelNode.querySelectorAll(".panel-item");
// 3 controls, + 2 projects
is(items.length, 6, "6 projects in panel");
is(items[3].getAttribute("label"), "A name (in app directory)", "Panel label is correct");
is(items[4].getAttribute("label"), "hosted manifest name property", "Panel label is correct");
yield closeWebIDE(win);
yield closeWebIDE(win);
yield removeAllProjects();
yield removeAllProjects();
SimpleTest.finish();
SimpleTest.finish();
}).then(null, e => {
ok(false, "Exception: " + e);
SimpleTest.finish();

View File

@ -25,13 +25,13 @@
let AppManager = win.AppManager;
function isProjectMarkedAsValid() {
let details = win.frames[0];
let details = win.frames[1];
return !details.document.body.classList.contains("error");
}
let packagedAppLocation = getTestFilePath("app");
yield win.Cmds.importPackagedApp(packagedAppLocation);
yield win.projectList.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "details");
let project = win.AppManager.selectedProject;

View File

@ -21,7 +21,7 @@
Task.spawn(function* () {
let win = yield openWebIDE();
let tmpDir = FileUtils.getDir("TmpD", []);
yield win.Cmds.newApp({
yield win.projectList.newApp({
index: 0,
name: "webideTmpApp",
folder: tmpDir

View File

@ -18,3 +18,4 @@ webide.jar:
skin/config-view.css (config-view.css)
skin/wifi-auth.css (wifi-auth.css)
skin/logs.css (logs.css)
skin/project-listing.css (project-listing.css)

View File

@ -0,0 +1,41 @@
/* 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/. */
html {
font: message-box;
font-size: 12px;
font-weight: normal;
}
.panel-item, label, #project-panel-projects {
display: block;
float: left;
width: 100%;
}
.project-image, .panel-item span {
display: inline-block;
float: left;
line-height: 20px;
}
.project-image {
margin-right: 10px;
max-height: 20px;
}
label {
color: #888;
display: block;
font-size: 12px;
padding: 15px 0 5px;
text-shadow: 1px 1px #fff;
text-transform: uppercase;
}
.panel-item {
cursor: default;
padding: 5px 0;
min-width: 130px;
}

View File

@ -151,6 +151,44 @@ panel > .panel-arrowcontainer > .panel-arrowcontent {
max-width: 400px;
}
#project-listing-panel {
display: none;
position: relative;
max-width: 250px;
overflow: hidden;
}
#project-listing-wrapper {
height: 100%;
width: 100%;
min-width: 100px;
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
#project-listing-panel-details {
height: inherit;
width: 100%;
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
/* TODO: remove once Bug 1079347 is complete */
.project-listing, #project-listing-splitter {
display: none;
}
#project-listing-splitter[sidebar-displayed], #project-listing-panel[sidebar-displayed],
#project-listing-panel[sidebar-displayed] .project-listing {
display: block;
}
.panel-item {
padding: 3px 12px;
margin: 0;
@ -280,14 +318,6 @@ panel > .panel-arrowcontainer > .panel-arrowcontent {
}
.devtools-horizontal-splitter {
-moz-appearance: none;
background-image: none;
background-color: transparent;
border: 0;
border-bottom: 1px solid rgba(118, 121, 125, .5);
min-height: 3px;
height: 3px;
margin-top: -3px;
position: relative;
border-bottom: 1px solid #aaa;
}

View File

@ -32,3 +32,4 @@ pref("devtools.webide.widget.inNavbarByDefault", false);
#endif
pref("devtools.webide.zoom", "1");
pref("devtools.webide.busyTimeout", 10000);
pref("devtools.webide.sidebars", false);

View File

@ -84,6 +84,11 @@ let DirectoryLinksProvider = {
*/
_enhancedLinks: new Map(),
/**
* A mapping from site to a list of related link objects
*/
_relatedLinks: new Map(),
get _observedPrefs() Object.freeze({
enhanced: PREF_NEWTAB_ENHANCED,
linksURL: PREF_DIRECTORY_SOURCE,
@ -187,6 +192,14 @@ let DirectoryLinksProvider = {
}
},
_cacheRelatedLinks: function(link) {
for (let relatedSite of link.related) {
let relatedMap = this._relatedLinks.get(relatedSite) || new Map();
relatedMap.set(link.url, link);
this._relatedLinks.set(relatedSite, relatedMap);
}
},
_fetchAndCacheLinks: function DirectoryLinksProvider_fetchAndCacheLinks(uri) {
// Replace with the same display locale used for selecting links data
uri = uri.replace("%LOCALE%", this.locale);
@ -389,24 +402,33 @@ let DirectoryLinksProvider = {
*/
getLinks: function DirectoryLinksProvider_getLinks(aCallback) {
this._readDirectoryLinksFile().then(rawLinks => {
// Reset the cache of enhanced images for this new set of links
// Reset the cache of related tiles and enhanced images for this new set of links
this._enhancedLinks.clear();
this._relatedLinks.clear();
return rawLinks.filter(link => {
let links = [];
rawLinks.filter(link => {
// Make sure the link url is allowed and images too if they exist
return this.isURLAllowed(link.url, ALLOWED_LINK_SCHEMES) &&
this.isURLAllowed(link.imageURI, ALLOWED_IMAGE_SCHEMES) &&
this.isURLAllowed(link.enhancedImageURI, ALLOWED_IMAGE_SCHEMES);
}).map((link, position) => {
}).forEach((link, position) => {
// Stash the enhanced image for the site
if (link.enhancedImageURI) {
this._enhancedLinks.set(NewTabUtils.extractSite(link.url), link);
}
link.frecency = DIRECTORY_FRECENCY;
link.lastVisitDate = rawLinks.length - position;
return link;
// We cache related tiles here but do not push any of them in the links list yet.
// The decision for which related tile to include will be made separately.
if ("related" == link.type) {
this._cacheRelatedLinks(link);
return;
}
link.frecency = DIRECTORY_FRECENCY;
links.push(link);
});
return links;
}).catch(ex => {
Cu.reportError(ex);
return [];

View File

@ -175,6 +175,71 @@ function run_test() {
});
}
add_task(function test_relatedLinksMap() {
let relatedTile1 = {
url: "http://turbotax.com",
type: "related",
lastVisitDate: 4,
related: [
"taxact.com",
"hrblock.com",
"1040.com",
"taxslayer.com"
]
};
let relatedTile2 = {
url: "http://irs.gov",
type: "related",
lastVisitDate: 3,
related: [
"taxact.com",
"hrblock.com",
"freetaxusa.com",
"taxslayer.com"
]
};
let relatedTile3 = {
url: "http://hrblock.com",
type: "related",
lastVisitDate: 2,
related: [
"taxact.com",
"freetaxusa.com",
"1040.com",
"taxslayer.com"
]
};
let someOtherSite = {url: "http://someothersite.com", title: "Not_A_Related_Site"};
let data = {"en-US": [relatedTile1, relatedTile2, relatedTile3, someOtherSite]};
let dataURI = 'data:application/json,' + JSON.stringify(data);
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
let links = yield fetchData();
// Ensure the related tiles were not considered directory tiles.
do_check_eq(links.length, 1);
let expected_data = [{url: "http://someothersite.com", title: "Not_A_Related_Site", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
isIdentical(links, expected_data);
// Check for correctly saved related tiles data.
expected_data = {
"taxact.com": [relatedTile1, relatedTile2, relatedTile3],
"hrblock.com": [relatedTile1, relatedTile2],
"1040.com": [relatedTile1, relatedTile3],
"taxslayer.com": [relatedTile1, relatedTile2, relatedTile3],
"freetaxusa.com": [relatedTile2, relatedTile3],
};
DirectoryLinksProvider._relatedLinks.forEach((relatedLinks, site) => {
let relatedLinksItr = relatedLinks.values();
for (let link of expected_data[site]) {
isIdentical(relatedLinksItr.next().value, link);
}
})
yield promiseCleanDirectoryLinksProvider();
});
add_task(function test_reportSitesAction() {
yield DirectoryLinksProvider.init();
let deferred, expectedPath, expectedPost;

View File

@ -18,6 +18,7 @@ import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.ReadingListAccessor;
import org.mozilla.gecko.home.HomeContextMenuInfo.RemoveItemType;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.util.HardwareUtils;
import android.content.Context;
import android.database.Cursor;
@ -139,22 +140,26 @@ public class ReadingListPanel extends HomeFragment {
mEmptyView = emptyViewStub.inflate();
final TextView emptyHint = (TextView) mEmptyView.findViewById(R.id.home_empty_hint);
String readingListHint = emptyHint.getText().toString();
if (HardwareUtils.isLowMemoryPlatform()) {
emptyHint.setVisibility(View.GONE);
} else {
String readingListHint = emptyHint.getText().toString();
// Use an ImageSpan to include the reader icon in the "Tip".
int imageSpanIndex = readingListHint.indexOf(MATCH_STRING);
if (imageSpanIndex != -1) {
final ImageSpan readingListIcon = new ImageSpan(getActivity(), R.drawable.reader_cropped, ImageSpan.ALIGN_BOTTOM);
final SpannableStringBuilder hintBuilder = new SpannableStringBuilder(readingListHint);
// Use an ImageSpan to include the reader icon in the "Tip".
int imageSpanIndex = readingListHint.indexOf(MATCH_STRING);
if (imageSpanIndex != -1) {
final ImageSpan readingListIcon = new ImageSpan(getActivity(), R.drawable.reader_cropped, ImageSpan.ALIGN_BOTTOM);
final SpannableStringBuilder hintBuilder = new SpannableStringBuilder(readingListHint);
// Add additional spacing.
hintBuilder.insert(imageSpanIndex + MATCH_STRING.length(), " ");
hintBuilder.insert(imageSpanIndex, " ");
// Add additional spacing.
hintBuilder.insert(imageSpanIndex + MATCH_STRING.length(), " ");
hintBuilder.insert(imageSpanIndex, " ");
// Add icon.
hintBuilder.setSpan(readingListIcon, imageSpanIndex + 1, imageSpanIndex + MATCH_STRING.length() + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
// Add icon.
hintBuilder.setSpan(readingListIcon, imageSpanIndex + 1, imageSpanIndex + MATCH_STRING.length() + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
emptyHint.setText(hintBuilder, TextView.BufferType.SPANNABLE);
emptyHint.setText(hintBuilder, TextView.BufferType.SPANNABLE);
}
}
mList.setEmptyView(mEmptyView);

View File

@ -17,6 +17,14 @@ import android.view.ViewConfiguration;
public final class HardwareUtils {
private static final String LOGTAG = "GeckoHardwareUtils";
// Minimum memory threshold for a device to be considered
// a low memory platform (see isLowMemoryPlatform). This value
// has be in sync with Gecko's equivalent threshold (defined in
// xpcom/base/nsMemoryImpl.cpp) and should only be used in cases
// where we can't depend on Gecko to be up and running e.g. show/hide
// reading list capabilities in HomePager.
private static final int LOW_MEMORY_THRESHOLD_MB = 384;
private static final boolean IS_AMAZON_DEVICE = Build.MANUFACTURER.equalsIgnoreCase("Amazon");
public static final boolean IS_KINDLE_DEVICE = IS_AMAZON_DEVICE &&
(Build.MODEL.equals("Kindle Fire") ||
@ -88,4 +96,17 @@ public final class HardwareUtils {
public static int getMemSize() {
return SysInfo.getMemSize();
}
public static boolean isLowMemoryPlatform() {
final int memSize = getMemSize();
// Fallback to false if we fail to read meminfo
// for some reason.
if (memSize == 0) {
Log.w(LOGTAG, "Could not compute system memory. Falling back to isLowMemoryPlatform = false.");
return false;
}
return memSize < LOW_MEMORY_THRESHOLD_MB;
}
}

View File

@ -117,29 +117,29 @@ body {
margin-bottom: 32px;
}
.light > .header > .domain {
.light > .container > .header > .domain {
color: #ee7600;
border-bottom-color: #d0d0d0;
}
.light > .header > h1 {
.light > .container > .header > h1 {
color: #222222;
}
.light > .header > .credits {
.light > .container > .header > .credits {
color: #898989;
}
.dark > .header > .domain {
.dark > .container > .header > .domain {
color: #ff9400;
border-bottom-color: #777777;
}
.dark > .header > h1 {
.dark > .container > .header > h1 {
color: #eeeeee;
}
.dark > .header > .credits {
.dark > .container > .header > .credits {
color: #aaaaaa;
}
@ -163,17 +163,10 @@ body {
font-weight: normal;
}
.light > .content a,
.light > .content a:visited,
.light > .content a:hover,
.light > .content a:active {
color: #00acff !important;
}
.dark > .content a,
.dark > .content a:visited,
.dark > .content a:hover,
.dark > .content a:active {
.content a,
.content a:visited,
.content a:hover,
.content a:active {
color: #00acff !important;
}
@ -227,15 +220,15 @@ body {
padding-top: 4px !important;
}
.light > .content .caption,
.light > .content .wp-caption-text,
.light > .content figcaption {
.light > .container > .content .caption,
.light > .container > .content .wp-caption-text,
.light > .container > .content figcaption {
color: #898989;
}
.dark > .content .caption,
.dark > .content .wp-caption-text,
.dark > .content figcaption {
.dark > .container > .content .caption,
.dark > .container > .content .wp-caption-text,
.dark > .container > .content figcaption {
color: #aaaaaa;
}
@ -256,12 +249,12 @@ body {
border-left: 2px solid !important;
}
.light > .content blockquote {
.light > .container > .content blockquote {
color: #898989 !important;
border-left-color: #d0d0d0 !important;
}
.dark > .content blockquote {
.dark > .container > .content blockquote {
color: #aaaaaa !important;
border-left-color: #777777 !important;
}

View File

@ -1899,3 +1899,30 @@ Example
"foobar-value"
]
}
org.mozilla.passwordmgr.passwordmgr
-----------------------------------
Daily measurement reporting information about the Password Manager
Version 1
^^^^^^^^^
Property:
numSavedPasswords
number of passwords saved in the Password Manager
enabled
Whether or not the user has disabled the Password Manager in prefernces
Example
^^^^^^^
::
"org.mozilla.passwordmgr.passwordmgr": {
"_v": 1,
"numSavedPasswords": 5,
"enabled": 0,
}

View File

@ -17,7 +17,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "UserAutoCompleteResult",
XPCOMUtils.defineLazyModuleGetter(this, "AutoCompleteE10S",
"resource://gre/modules/AutoCompleteE10S.jsm");
this.EXPORTED_SYMBOLS = [ "LoginManagerParent" ];
this.EXPORTED_SYMBOLS = [ "LoginManagerParent", "PasswordsMetricsProvider" ];
var gDebug;
@ -51,6 +51,61 @@ function log(...pieces) {
Services.console.logStringMessage(message);
}
#ifndef ANDROID
#ifdef MOZ_SERVICES_HEALTHREPORT
XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
"resource://gre/modules/Metrics.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
this.PasswordsMetricsProvider = function() {
Metrics.Provider.call(this);
}
PasswordsMetricsProvider.prototype = Object.freeze({
__proto__: Metrics.Provider.prototype,
name: "org.mozilla.passwordmgr",
measurementTypes: [
PasswordsMeasurement1,
],
pullOnly: true,
collectDailyData: function* () {
return this.storage.enqueueTransaction(this._recordDailyPasswordData.bind(this));
},
_recordDailyPasswordData: function() {
let m = this.getMeasurement(PasswordsMeasurement1.prototype.name,
PasswordsMeasurement1.prototype.version);
let enabled = Services.prefs.getBoolPref("signon.rememberSignons");
yield m.setDailyLastNumeric("enabled", enabled ? 1 : 0);
let loginsCount = Services.logins.countLogins("", "", "");
yield m.setDailyLastNumeric("numSavedPasswords", loginsCount);
},
});
function PasswordsMeasurement1() {
Metrics.Measurement.call(this);
}
PasswordsMeasurement1.prototype = Object.freeze({
__proto__: Metrics.Measurement.prototype,
name: "passwordmgr",
version: 1,
fields: {
enabled: {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC},
numSavedPasswords: {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC},
},
});
#endif
#endif
function prefChanged() {
gDebug = Services.prefs.getBoolPref("signon.debug");
}
@ -151,8 +206,13 @@ var LoginManagerParent = {
}
var logins = Services.logins.findLogins({}, formOrigin, actionOrigin, null);
target.sendAsyncMessage("RemoteLogins:loginsFound",
{ requestId: requestId, logins: logins });
// Convert the array of nsILoginInfo to vanilla JS objects since nsILoginInfo
// doesn't support structured cloning.
var jsLogins = JSON.parse(JSON.stringify(logins));
target.sendAsyncMessage("RemoteLogins:loginsFound", {
requestId: requestId,
logins: jsLogins,
});
const PWMGR_FORM_ACTION_EFFECT = Services.telemetry.getHistogramById("PWMGR_FORM_ACTION_EFFECT");
if (logins.length == 0) {
@ -206,9 +266,13 @@ var LoginManagerParent = {
AutoCompleteE10S.showPopupWithResults(target.ownerDocument.defaultView, rect, result);
}
target.messageManager.sendAsyncMessage("RemoteLogins:loginsAutoCompleted",
{ requestId: requestId,
logins: matchingLogins });
// Convert the array of nsILoginInfo to vanilla JS objects since nsILoginInfo
// doesn't support structured cloning.
var jsLogins = JSON.parse(JSON.stringify(matchingLogins));
target.messageManager.sendAsyncMessage("RemoteLogins:loginsAutoCompleted", {
requestId: requestId,
logins: jsLogins,
});
},
onFormSubmit: function(hostname, formSubmitURL,

View File

@ -31,6 +31,10 @@ EXTRA_PP_COMPONENTS += [
'passwordmgr.manifest',
]
EXTRA_PP_JS_MODULES += [
'LoginManagerParent.jsm',
]
EXTRA_JS_MODULES += [
'InsecurePasswordUtils.jsm',
'LoginHelper.jsm',

View File

@ -15,3 +15,4 @@ contract @mozilla.org/login-manager/storage/json;1 {c00c432d-a0c9-46d7-bef6-9c45
#endif
component {dc6c2976-0f73-4f1f-b9ff-3d72b4e28309} crypto-SDR.js
contract @mozilla.org/login-manager/crypto/SDR;1 {dc6c2976-0f73-4f1f-b9ff-3d72b4e28309}
category healthreport-js-provider-default PasswordsMetricsProvider resource://gre/modules/LoginManagerParent.jsm

View File

@ -9,11 +9,13 @@
*/
importScripts("resource://gre/modules/workers/require.js",
"resource://gre/modules/reader/JSDOMParser.js",
"resource://gre/modules/reader/Readability.js");
"resource://gre/modules/reader/JSDOMParser.js",
"resource://gre/modules/reader/Readability.js");
let PromiseWorker = require("resource://gre/modules/workers/PromiseWorker.js");
const DEBUG = false;
let worker = new PromiseWorker.AbstractWorker();
worker.dispatch = function(method, args = []) {
return Agent[method](...args);
@ -25,7 +27,9 @@ worker.close = function() {
self.close();
};
worker.log = function(...args) {
dump("ReaderWorker: " + args.join(" ") + "\n");
if (DEBUG) {
dump("ReaderWorker: " + args.join(" ") + "\n");
}
};
self.addEventListener("message", msg => worker.handleMessage(msg));

View File

@ -11,7 +11,7 @@
</head>
<body>
<div id="container">
<div id="container" class="container">
<div id="reader-header" class="header">
<a id="reader-domain" class="domain"></a>
<div class="domain-border"></div>

View File

@ -182,6 +182,7 @@ let MemoryActor = protocol.ActorClass({
*/
startRecordingAllocations: method(expectState("attached", function(options = {}) {
this._frameCache.initFrames();
this.dbg.memory.allocationSamplingProbability = options.probability != null
? options.probability
: 1.0;
@ -219,6 +220,23 @@ let MemoryActor = protocol.ActorClass({
}
}),
/**
* Return settings used in `startRecordingAllocations` for `probability`
* and `maxLogLength`. Currently only uses in tests.
*/
getAllocationsSettings: method(expectState("attached", function() {
return {
maxLogLength: this.dbg.memory.maxAllocationsLogLength,
probability: this.dbg.memory.allocationSamplingProbability
};
},
`getting allocations settings`), {
request: {},
response: {
options: RetVal(0, "json")
}
}),
/**
* Get a list of the most recent allocations since the last time we got
* allocations, as well as a summary of all allocations since we've been

View File

@ -99,12 +99,12 @@ body {
color: #0095dd;
}
.light > .header > .domain,
.sepia > .header > .domain {
.light > .container > .header > .domain,
.sepia > .container > .header > .domain {
border-bottom-color: #333333;
}
.dark > .header > .domain {
.dark > .container > .header > .domain {
border-bottom-color: #eeeeee;
}
@ -204,12 +204,12 @@ body {
-moz-padding-start: 16px;
}
.light > .content blockquote,
.sepia > .content blockquote {
.light > .container > .content blockquote,
.sepia > .container > .content blockquote {
-moz-border-start: 2px solid #333333;
}
.dark > .content blockquote {
.dark > .container > .content blockquote {
-moz-border-start: 2px solid #eeeeee;
}