diff --git a/addon-sdk/Makefile.in b/addon-sdk/Makefile.in
index 841a8a67c20c..f5d610991f17 100644
--- a/addon-sdk/Makefile.in
+++ b/addon-sdk/Makefile.in
@@ -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)
diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml
index 767abffab051..152fd2c190ad 100644
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -15,7 +15,7 @@
-
+
@@ -134,10 +134,10 @@
-
+
-
+
diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index fe3ae517fdac..149f6972377e 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index b5625ff256dc..a4b0a49b3d64 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml
index d5d74c094049..e9e24457b85b 100644
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml
index 38acddcc7e09..5f7922987343 100644
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index fe3ae517fdac..149f6972377e 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml
index 4b1e940614b9..595641e59ede 100644
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml
index c8313c95c3db..95190e086480 100644
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index be1f9d10232e..26e66934deeb 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
{
"git": {
- "git_revision": "943c8b4039f59b08ba100390e164a076a20c892e",
+ "git_revision": "2b87ee8e7e2ec30a9851b6b59a899006a98767ab",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
- "revision": "c8f9d4bbb0ab4ddfb272c0ee955e640b9d122b54",
+ "revision": "a3f060d46f5e179164fe9435f30878ebdf58eb49",
"repo_path": "integration/gaia-central"
}
diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml
index bae58fecbafb..c75828674f89 100644
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml
index 2503143a7c99..835fad350620 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 6cd5197d449c..419adde1f6fd 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -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);
diff --git a/browser/components/readinglist/test/xpcshell/test_scheduler.js b/browser/components/readinglist/test/xpcshell/test_scheduler.js
index bb258a4720d2..8b2eb0333669 100644
--- a/browser/components/readinglist/test/xpcshell/test_scheduler.js
+++ b/browser/components/readinglist/test/xpcshell/test_scheduler.js
@@ -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);
}
}
diff --git a/browser/devtools/performance/modules/front.js b/browser/devtools/performance/modules/front.js
index c28d3743f422..108f0421612c 100644
--- a/browser/devtools/performance/modules/front.js
+++ b/browser/devtools/performance/modules/front.js
@@ -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;
}),
diff --git a/browser/devtools/performance/performance-controller.js b/browser/devtools/performance/performance-controller.js
index 2d5c667a5ea7..2a2e3113b651 100644
--- a/browser/devtools/performance/performance-controller.js
+++ b/browser/devtools/performance/performance-controller.js
@@ -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);
diff --git a/browser/devtools/performance/test/browser.ini b/browser/devtools/performance/test/browser.ini
index 95e62ea65628..38a647845c42 100644
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -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]
diff --git a/browser/devtools/performance/test/browser_perf-options-allocations.js b/browser/devtools/performance/test/browser_perf-options-allocations.js
new file mode 100644
index 000000000000..713493c53e07
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-options-allocations.js
@@ -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();
+}
diff --git a/browser/devtools/shared/test/browser.ini b/browser/devtools/shared/test/browser.ini
index 096238c21858..89cf5dd3b671 100644
--- a/browser/devtools/shared/test/browser.ini
+++ b/browser/devtools/shared/test/browser.ini
@@ -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]
diff --git a/browser/devtools/shared/test/browser_prefs.js b/browser/devtools/shared/test/browser_prefs-01.js
similarity index 100%
rename from browser/devtools/shared/test/browser_prefs.js
rename to browser/devtools/shared/test/browser_prefs-01.js
diff --git a/browser/devtools/shared/test/browser_prefs-02.js b/browser/devtools/shared/test/browser_prefs-02.js
new file mode 100644
index 000000000000..0099aa959032
--- /dev/null
+++ b/browser/devtools/shared/test/browser_prefs-02.js
@@ -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();
+}
diff --git a/browser/devtools/shared/timeline/marker-details.js b/browser/devtools/shared/timeline/marker-details.js
index 8fbdc9f232a0..235a5825f93e 100644
--- a/browser/devtools/shared/timeline/marker-details.js
+++ b/browser/devtools/shared/timeline/marker-details.js
@@ -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);
}
diff --git a/browser/devtools/shared/widgets/ViewHelpers.jsm b/browser/devtools/shared/widgets/ViewHelpers.jsm
index 9d19563a6ea3..8cd990b6215d 100644
--- a/browser/devtools/shared/widgets/ViewHelpers.jsm
+++ b/browser/devtools/shared/widgets/ViewHelpers.jsm
@@ -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)),
diff --git a/browser/devtools/webide/content/jar.mn b/browser/devtools/webide/content/jar.mn
index 2f7c45eeb35f..37d46612300b 100644
--- a/browser/devtools/webide/content/jar.mn
+++ b/browser/devtools/webide/content/jar.mn
@@ -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)
diff --git a/browser/devtools/webide/content/project-listing.js b/browser/devtools/webide/content/project-listing.js
new file mode 100644
index 000000000000..76828c77f072
--- /dev/null
+++ b/browser/devtools/webide/content/project-listing.js
@@ -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();
+}
diff --git a/browser/devtools/webide/content/project-listing.xhtml b/browser/devtools/webide/content/project-listing.xhtml
new file mode 100644
index 000000000000..2d11b891027a
--- /dev/null
+++ b/browser/devtools/webide/content/project-listing.xhtml
@@ -0,0 +1,33 @@
+
+
+
+
+
+ %webideDTD;
+]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/devtools/webide/content/project-panel.js b/browser/devtools/webide/content/project-panel.js
new file mode 100644
index 000000000000..544bd6f2f7d5
--- /dev/null
+++ b/browser/devtools/webide/content/project-panel.js
@@ -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;
+ }
+};
diff --git a/browser/devtools/webide/content/webide.js b/browser/devtools/webide/content/webide.js
index 67c147eec25f..e2f3284ea3e7 100644
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -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() {
diff --git a/browser/devtools/webide/content/webide.xul b/browser/devtools/webide/content/webide.xul
index 387d4d63b67e..859570a439ab 100644
--- a/browser/devtools/webide/content/webide.xul
+++ b/browser/devtools/webide/content/webide.xul
@@ -12,6 +12,7 @@
+
+
@@ -150,7 +152,7 @@
-
+
@@ -191,18 +193,26 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/devtools/webide/modules/app-manager.js b/browser/devtools/webide/modules/app-manager.js
index 4fe0f2bb9deb..3906c2036ca3 100644
--- a/browser/devtools/webide/modules/app-manager.js
+++ b/browser/devtools/webide/modules/app-manager.js
@@ -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) {
diff --git a/browser/devtools/webide/modules/project-list.js b/browser/devtools/webide/modules/project-list.js
new file mode 100644
index 000000000000..b4929092f094
--- /dev/null
+++ b/browser/devtools/webide/modules/project-list.js
@@ -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;
+ }
+};
diff --git a/browser/devtools/webide/moz.build b/browser/devtools/webide/moz.build
index 20a072793a96..9f076ef463e6 100644
--- a/browser/devtools/webide/moz.build
+++ b/browser/devtools/webide/moz.build
@@ -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',
diff --git a/browser/devtools/webide/test/browser_tabs.js b/browser/devtools/webide/test/browser_tabs.js
index 81ff64c2c7bf..d00e15a188d3 100644
--- a/browser/devtools/webide/test/browser_tabs.js
+++ b/browser/devtools/webide/test/browser_tabs.js
@@ -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];
diff --git a/browser/devtools/webide/test/head.js b/browser/devtools/webide/test/head.js
index a69c2ef855c1..898f8bb95232 100644
--- a/browser/devtools/webide/test/head.js
+++ b/browser/devtools/webide/test/head.js
@@ -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) {
diff --git a/browser/devtools/webide/test/sidebars/browser.ini b/browser/devtools/webide/test/sidebars/browser.ini
new file mode 100644
index 000000000000..0f9f0e78f523
--- /dev/null
+++ b/browser/devtools/webide/test/sidebars/browser.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+subsuite = devtools
+support-files =
+ ../head.js
diff --git a/browser/devtools/webide/test/sidebars/chrome.ini b/browser/devtools/webide/test/sidebars/chrome.ini
new file mode 100644
index 000000000000..ee85858ab239
--- /dev/null
+++ b/browser/devtools/webide/test/sidebars/chrome.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+ ../app/index.html
+ ../app/manifest.webapp
+ ../head.js
+
+[test_duplicate_import.html]
+[test_import.html]
diff --git a/browser/devtools/webide/test/sidebars/test_duplicate_import.html b/browser/devtools/webide/test/sidebars/test_duplicate_import.html
new file mode 100644
index 000000000000..278efe6f4ac5
--- /dev/null
+++ b/browser/devtools/webide/test/sidebars/test_duplicate_import.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/devtools/webide/test/sidebars/test_import.html b/browser/devtools/webide/test/sidebars/test_import.html
new file mode 100644
index 000000000000..d0a0e3d9a667
--- /dev/null
+++ b/browser/devtools/webide/test/sidebars/test_import.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/devtools/webide/test/sidebars/test_runtime.html b/browser/devtools/webide/test/sidebars/test_runtime.html
new file mode 100644
index 000000000000..4958f7984928
--- /dev/null
+++ b/browser/devtools/webide/test/sidebars/test_runtime.html
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/devtools/webide/test/test_build.html b/browser/devtools/webide/test/test_build.html
index 3892b9037dc8..260698a0c215 100644
--- a/browser/devtools/webide/test/test_build.html
+++ b/browser/devtools/webide/test/test_build.html
@@ -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;
diff --git a/browser/devtools/webide/test/test_duplicate_import.html b/browser/devtools/webide/test/test_duplicate_import.html
index 1efcbabe7dd2..7bb032d88157 100644
--- a/browser/devtools/webide/test/test_duplicate_import.html
+++ b/browser/devtools/webide/test/test_duplicate_import.html
@@ -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.");
diff --git a/browser/devtools/webide/test/test_import.html b/browser/devtools/webide/test/test_import.html
index 01b4bf8c78c3..df60c2243f71 100644
--- a/browser/devtools/webide/test/test_import.html
+++ b/browser/devtools/webide/test/test_import.html
@@ -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();
diff --git a/browser/devtools/webide/test/test_manifestUpdate.html b/browser/devtools/webide/test/test_manifestUpdate.html
index ba74e04d5c3c..73649ce7f7b8 100644
--- a/browser/devtools/webide/test/test_manifestUpdate.html
+++ b/browser/devtools/webide/test/test_manifestUpdate.html
@@ -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;
diff --git a/browser/devtools/webide/test/test_newapp.html b/browser/devtools/webide/test/test_newapp.html
index 29c6db8b652c..dedcbbf6645b 100644
--- a/browser/devtools/webide/test/test_newapp.html
+++ b/browser/devtools/webide/test/test_newapp.html
@@ -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
diff --git a/browser/devtools/webide/themes/jar.mn b/browser/devtools/webide/themes/jar.mn
index 804f47f6a85b..11350d8184f5 100644
--- a/browser/devtools/webide/themes/jar.mn
+++ b/browser/devtools/webide/themes/jar.mn
@@ -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)
diff --git a/browser/devtools/webide/themes/project-listing.css b/browser/devtools/webide/themes/project-listing.css
new file mode 100644
index 000000000000..5a5eed2088f7
--- /dev/null
+++ b/browser/devtools/webide/themes/project-listing.css
@@ -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;
+}
diff --git a/browser/devtools/webide/themes/webide.css b/browser/devtools/webide/themes/webide.css
index 76074c2c40a6..96c4cfea5283 100644
--- a/browser/devtools/webide/themes/webide.css
+++ b/browser/devtools/webide/themes/webide.css
@@ -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;
}
diff --git a/browser/devtools/webide/webide-prefs.js b/browser/devtools/webide/webide-prefs.js
index 6e21bec8b141..fe3716bc5fe6 100644
--- a/browser/devtools/webide/webide-prefs.js
+++ b/browser/devtools/webide/webide-prefs.js
@@ -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);
diff --git a/browser/modules/DirectoryLinksProvider.jsm b/browser/modules/DirectoryLinksProvider.jsm
index 9385c0921c9e..c61688a28518 100644
--- a/browser/modules/DirectoryLinksProvider.jsm
+++ b/browser/modules/DirectoryLinksProvider.jsm
@@ -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 [];
diff --git a/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js b/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js
index 72c28953fd43..0ba8c96540ca 100644
--- a/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js
+++ b/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js
@@ -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;
diff --git a/dom/mobileconnection/tests/marionette/test_mobile_signal_strength.js b/dom/mobileconnection/tests/marionette/test_mobile_signal_strength.js
index 2245246c0132..f2b12f67f6e3 100644
--- a/dom/mobileconnection/tests/marionette/test_mobile_signal_strength.js
+++ b/dom/mobileconnection/tests/marionette/test_mobile_signal_strength.js
@@ -20,18 +20,6 @@ const TEST_DATA = [
relSignalStrength: null
}
},
- // Valid rxlev with max value.
- {
- input: {
- rxlev: 63,
- rsrp: 65535,
- rssnr: 65535
- },
- expect: {
- signalStrength: -48,
- relSignalStrength: 100
- }
- },
// Valid rxlev.
{
input: {
@@ -40,7 +28,7 @@ const TEST_DATA = [
rssnr: 65535
},
expect: {
- signalStrength: -99,
+ signalStrength: null,
relSignalStrength: 100
}
},
@@ -52,9 +40,57 @@ const TEST_DATA = [
rssnr: 65535
},
expect: {
- signalStrength: -111,
+ signalStrength: null,
relSignalStrength: 0
}
+ },
+ // Valid rxlev with max value.
+ {
+ input: {
+ rxlev: 63,
+ rsrp: 65535,
+ rssnr: 65535
+ },
+ expect: {
+ signalStrength: null,
+ relSignalStrength: 100
+ }
+ },
+ // Valid rsrp.
+ {
+ input: {
+ rxlev: 31,
+ rsrp: 50,
+ rssnr: 65535
+ },
+ expect: {
+ signalStrength: 50,
+ relSignalStrength: 100
+ }
+ },
+ // Valid rssnr.
+ {
+ input: {
+ rxlev: 31,
+ rsrp: 65535,
+ rssnr: 100
+ },
+ expect: {
+ signalStrength: null,
+ relSignalStrength: 81
+ }
+ },
+ // Valid rsrp and rssnr.
+ {
+ input: {
+ rxlev: 31,
+ rsrp: 100,
+ rssnr: 30
+ },
+ expect: {
+ signalStrength: 100,
+ relSignalStrength: 37
+ }
}
];
diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js
index 636d9fc37a54..d0c064efcfe1 100644
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -47,6 +47,9 @@ let RILQUIRKS_RADIO_OFF_WO_CARD =
let RILQUIRKS_HAVE_IPV6 =
libcutils.property_get("ro.moz.ril.ipv6", "false") == "true";
+let RILQUIRKS_SIGNAL_EXTRA_INT32 =
+ libcutils.property_get("ro.moz.ril.signal_extra_int", "false") == "true";
+
const RADIOINTERFACELAYER_CID =
Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
const RADIOINTERFACE_CID =
@@ -1512,7 +1515,8 @@ WorkerMessenger.prototype = {
sendStkProfileDownload:
libcutils.property_get("ro.moz.ril.send_stk_profile_dl", "false") == "true",
dataRegistrationOnDemand: RILQUIRKS_DATA_REGISTRATION_ON_DEMAND,
- subscriptionControl: RILQUIRKS_SUBSCRIPTION_CONTROL
+ subscriptionControl: RILQUIRKS_SUBSCRIPTION_CONTROL,
+ signalExtraInt: RILQUIRKS_SIGNAL_EXTRA_INT32
}
};
diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js
index 145a0e0527cc..8ed70f9ff010 100644
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -82,6 +82,8 @@ let RILQUIRKS_DATA_REGISTRATION_ON_DEMAND;
// Ril quirk to control the uicc/data subscription.
let RILQUIRKS_SUBSCRIPTION_CONTROL;
+let RILQUIRKS_SIGNAL_EXTRA_INT32;
+
const TELEPHONY_REQUESTS = [
REQUEST_GET_CURRENT_CALLS,
REQUEST_ANSWER,
@@ -3376,15 +3378,12 @@ RilObject.prototype = {
*
* @return The object of signal strength info.
* Or null if invalid signal input.
+ *
+ * TODO: Bug 982013: reconsider the format of signal strength APIs for
+ * GSM/CDMA/LTE to expose details, such as rsrp and rsnnr,
+ * individually.
*/
_processLteSignal: function(signal) {
- // Valid values are 0-63 as defined in TS 27.007 clause 8.69.
- if (signal.lteSignalStrength === undefined ||
- signal.lteSignalStrength < 0 ||
- signal.lteSignalStrength > 63) {
- return null;
- }
-
let info = {
voice: {
signalStrength: null,
@@ -3396,16 +3395,51 @@ RilObject.prototype = {
}
};
- // TODO: Bug 982013: reconsider signalStrength/relSignalStrength APIs for
- // GSM/CDMA/LTE, and take rsrp/rssnr into account for LTE case then.
- let signalStrength = -111 + signal.lteSignalStrength;
+ // Referring to AOSP, use lteRSRP for signalStrength in dBm.
+ let signalStrength = (signal.lteRSRP === undefined || signal.lteRSRP === 0x7FFFFFFF) ?
+ null : signal.lteRSRP;
info.voice.signalStrength = info.data.signalStrength = signalStrength;
- // 0 and 12 are referred to AOSP's implementation. These values are not
- // constants and can be customized based on different requirements.
- let signalLevel = this._processSignalLevel(signal.lteSignalStrength, 0, 12);
- info.voice.relSignalStrength = info.data.relSignalStrength = signalLevel;
- return info;
+ // Referring to AOSP, first determine signalLevel based on RSRP and RSSNR,
+ // then on lteSignalStrength if RSRP and RSSNR are invalid.
+ let rsrpLevel = -1;
+ let rssnrLevel = -1;
+ if (signal.lteRSRP !== undefined &&
+ signal.lteRSRP !== 0x7FFFFFFF &&
+ signal.lteRSRP >= 44 &&
+ signal.lteRSRP <= 140) {
+ rsrpLevel = this._processSignalLevel(signal.lteRSRP * -1, -115, -85);
+ }
+
+ if (signal.lteRSSNR !== undefined &&
+ signal.lteRSSNR !== 0x7FFFFFFF &&
+ signal.lteRSSNR >= -200 &&
+ signal.lteRSSNR <= 300) {
+ rssnrLevel = this._processSignalLevel(signal.lteRSSNR, -30, 130);
+ }
+
+ if (rsrpLevel !== -1 && rssnrLevel !== -1) {
+ info.voice.relSignalStrength = info.data.relSignalStrength =
+ Math.min(rsrpLevel, rssnrLevel);
+ return info;
+ }
+
+ let level = Math.max(rsrpLevel, rssnrLevel);
+ if (level !== -1) {
+ info.voice.relSignalStrength = info.data.relSignalStrength = level;
+ return info;
+ }
+
+ // Valid values are 0-63 as defined in TS 27.007 clause 8.69.
+ if (signal.lteSignalStrength !== undefined &&
+ signal.lteSignalStrength >= 0 &&
+ signal.lteSignalStrength <= 63) {
+ level = this._processSignalLevel(signal.lteSignalStrength, 0, 12);
+ info.voice.relSignalStrength = info.data.relSignalStrength = level;
+ return info;
+ }
+
+ return null;
},
_processSignalStrength: function(signal) {
@@ -5323,15 +5357,18 @@ RilObject.prototype[REQUEST_SIGNAL_STRENGTH] = function REQUEST_SIGNAL_STRENGTH(
}
let Buf = this.context.Buf;
- let signal = {
- gsmSignalStrength: Buf.readInt32(),
- gsmBitErrorRate: Buf.readInt32(),
- cdmaDBM: Buf.readInt32(),
- cdmaECIO: Buf.readInt32(),
- evdoDBM: Buf.readInt32(),
- evdoECIO: Buf.readInt32(),
- evdoSNR: Buf.readInt32()
- };
+ let signal = {};
+
+ signal.gsmSignalStrength = Buf.readInt32();
+ signal.gsmBitErrorRate = Buf.readInt32();
+ if (RILQUIRKS_SIGNAL_EXTRA_INT32) {
+ Buf.readInt32();
+ }
+ signal.cdmaDBM = Buf.readInt32();
+ signal.cdmaECIO = Buf.readInt32();
+ signal.evdoDBM = Buf.readInt32();
+ signal.evdoECIO = Buf.readInt32();
+ signal.evdoSNR = Buf.readInt32();
if (!this.v5Legacy) {
signal.lteSignalStrength = Buf.readInt32();
@@ -16093,6 +16130,7 @@ let ContextPool = {
RILQUIRKS_SEND_STK_PROFILE_DOWNLOAD = quirks.sendStkProfileDownload;
RILQUIRKS_DATA_REGISTRATION_ON_DEMAND = quirks.dataRegistrationOnDemand;
RILQUIRKS_SUBSCRIPTION_CONTROL = quirks.subscriptionControl;
+ RILQUIRKS_SIGNAL_EXTRA_INT32 = quirks.signalExtraInt;
},
setDebugFlag: function(aOptions) {
diff --git a/mobile/android/base/home/ReadingListPanel.java b/mobile/android/base/home/ReadingListPanel.java
index c8524d33e612..9a826596a23b 100644
--- a/mobile/android/base/home/ReadingListPanel.java
+++ b/mobile/android/base/home/ReadingListPanel.java
@@ -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);
diff --git a/mobile/android/base/util/HardwareUtils.java b/mobile/android/base/util/HardwareUtils.java
index cd54b4d59de0..c9a2598cac65 100644
--- a/mobile/android/base/util/HardwareUtils.java
+++ b/mobile/android/base/util/HardwareUtils.java
@@ -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;
+ }
}
diff --git a/mobile/android/themes/core/aboutReader.css b/mobile/android/themes/core/aboutReader.css
index 2303aee409cc..2edd6b67c8b8 100644
--- a/mobile/android/themes/core/aboutReader.css
+++ b/mobile/android/themes/core/aboutReader.css
@@ -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;
}
diff --git a/services/healthreport/docs/dataformat.rst b/services/healthreport/docs/dataformat.rst
index bdf0edb08699..2d341fde3fae 100644
--- a/services/healthreport/docs/dataformat.rst
+++ b/services/healthreport/docs/dataformat.rst
@@ -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,
+ }
diff --git a/toolkit/components/passwordmgr/LoginManagerParent.jsm b/toolkit/components/passwordmgr/LoginManagerParent.jsm
index 9fa46e573da9..817e738af997 100644
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm
@@ -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,
diff --git a/toolkit/components/passwordmgr/moz.build b/toolkit/components/passwordmgr/moz.build
index 670d8edb607a..0cf3db0bca21 100644
--- a/toolkit/components/passwordmgr/moz.build
+++ b/toolkit/components/passwordmgr/moz.build
@@ -31,6 +31,10 @@ EXTRA_PP_COMPONENTS += [
'passwordmgr.manifest',
]
+EXTRA_PP_JS_MODULES += [
+ 'LoginManagerParent.jsm',
+]
+
EXTRA_JS_MODULES += [
'InsecurePasswordUtils.jsm',
'LoginHelper.jsm',
diff --git a/toolkit/components/passwordmgr/passwordmgr.manifest b/toolkit/components/passwordmgr/passwordmgr.manifest
index 9a3709d42e8d..1d3d3c367a6e 100644
--- a/toolkit/components/passwordmgr/passwordmgr.manifest
+++ b/toolkit/components/passwordmgr/passwordmgr.manifest
@@ -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
\ No newline at end of file
diff --git a/toolkit/components/reader/ReaderWorker.js b/toolkit/components/reader/ReaderWorker.js
index dc3253c816e8..69f48f17e736 100644
--- a/toolkit/components/reader/ReaderWorker.js
+++ b/toolkit/components/reader/ReaderWorker.js
@@ -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));
diff --git a/toolkit/components/reader/content/aboutReader.html b/toolkit/components/reader/content/aboutReader.html
index dc6cbc67138b..8fa9719743c2 100644
--- a/toolkit/components/reader/content/aboutReader.html
+++ b/toolkit/components/reader/content/aboutReader.html
@@ -11,7 +11,7 @@
-