summaryrefslogtreecommitdiffstats
path: root/devtools/client/webide/content/webide.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/webide/content/webide.js')
-rw-r--r--devtools/client/webide/content/webide.js1157
1 files changed, 0 insertions, 1157 deletions
diff --git a/devtools/client/webide/content/webide.js b/devtools/client/webide/content/webide.js
deleted file mode 100644
index c222332e3..000000000
--- a/devtools/client/webide/content/webide.js
+++ /dev/null
@@ -1,1157 +0,0 @@
-/* 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/. */
-
-var Cc = Components.classes;
-var Cu = Components.utils;
-var Ci = Components.interfaces;
-
-const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const {gDevTools} = require("devtools/client/framework/devtools");
-const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser");
-const {Toolbox} = require("devtools/client/framework/toolbox");
-const Services = require("Services");
-const {AppProjects} = require("devtools/client/webide/modules/app-projects");
-const {Connection} = require("devtools/shared/client/connection-manager");
-const {AppManager} = require("devtools/client/webide/modules/app-manager");
-const EventEmitter = require("devtools/shared/event-emitter");
-const promise = require("promise");
-const ProjectEditor = require("devtools/client/projecteditor/lib/projecteditor");
-const {GetAvailableAddons} = require("devtools/client/webide/modules/addons");
-const {getJSON} = require("devtools/client/shared/getjson");
-const utils = require("devtools/client/webide/modules/utils");
-const Telemetry = require("devtools/client/shared/telemetry");
-const {RuntimeScanners} = require("devtools/client/webide/modules/runtimes");
-const {showDoorhanger} = require("devtools/client/shared/doorhanger");
-const {Simulators} = require("devtools/client/webide/modules/simulators");
-const {Task} = require("devtools/shared/task");
-
-const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
-
-const HTML = "http://www.w3.org/1999/xhtml";
-const HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Troubleshooting";
-
-const MAX_ZOOM = 1.4;
-const MIN_ZOOM = 0.6;
-
-const MS_PER_DAY = 86400000;
-
-[["AppManager", AppManager],
- ["AppProjects", AppProjects],
- ["Connection", Connection]].forEach(([key, value]) => {
- Object.defineProperty(this, key, {
- value: value,
- enumerable: true,
- writable: false
- });
- });
-
-// Download remote resources early
-getJSON("devtools.webide.addonsURL");
-getJSON("devtools.webide.templatesURL");
-getJSON("devtools.devices.url");
-
-// See bug 989619
-console.log = console.log.bind(console);
-console.warn = console.warn.bind(console);
-console.error = console.error.bind(console);
-
-window.addEventListener("load", function onLoad() {
- window.removeEventListener("load", onLoad);
- UI.init();
-});
-
-window.addEventListener("unload", function onUnload() {
- window.removeEventListener("unload", onUnload);
- UI.destroy();
-});
-
-var UI = {
- init: function () {
- this._telemetry = new Telemetry();
- this._telemetry.toolOpened("webide");
-
- AppManager.init();
-
- this.appManagerUpdate = this.appManagerUpdate.bind(this);
- AppManager.on("app-manager-update", this.appManagerUpdate);
-
- Cmds.showProjectPanel();
- Cmds.showRuntimePanel();
-
- this.updateCommands();
-
- this.onfocus = this.onfocus.bind(this);
- window.addEventListener("focus", this.onfocus, true);
-
- AppProjects.load().then(() => {
- this.autoSelectProject();
- }, e => {
- console.error(e);
- this.reportError("error_appProjectsLoadFailed");
- });
-
- // Auto install the ADB Addon Helper and Tools Adapters. Only once.
- // If the user decides to uninstall any of this addon, we won't install it again.
- let autoinstallADBHelper = Services.prefs.getBoolPref("devtools.webide.autoinstallADBHelper");
- let autoinstallFxdtAdapters = Services.prefs.getBoolPref("devtools.webide.autoinstallFxdtAdapters");
- if (autoinstallADBHelper) {
- GetAvailableAddons().then(addons => {
- addons.adb.install();
- }, console.error);
- }
- if (autoinstallFxdtAdapters) {
- GetAvailableAddons().then(addons => {
- addons.adapters.install();
- }, console.error);
- }
- Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);
- Services.prefs.setBoolPref("devtools.webide.autoinstallFxdtAdapters", false);
-
- if (Services.prefs.getBoolPref("devtools.webide.widget.autoinstall") &&
- !Services.prefs.getBoolPref("devtools.webide.widget.enabled")) {
- Services.prefs.setBoolPref("devtools.webide.widget.enabled", true);
- gDevToolsBrowser.moveWebIDEWidgetInNavbar();
- }
-
- this.setupDeck();
-
- this.contentViewer = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShell)
- .contentViewer;
- this.contentViewer.fullZoom = Services.prefs.getCharPref("devtools.webide.zoom");
-
- gDevToolsBrowser.isWebIDEInitialized.resolve();
-
- this.configureSimulator = this.configureSimulator.bind(this);
- Simulators.on("configure", this.configureSimulator);
- },
-
- destroy: function () {
- window.removeEventListener("focus", this.onfocus, true);
- AppManager.off("app-manager-update", this.appManagerUpdate);
- AppManager.destroy();
- Simulators.off("configure", this.configureSimulator);
- this.updateConnectionTelemetry();
- this._telemetry.toolClosed("webide");
- this._telemetry.toolClosed("webideProjectEditor");
- this._telemetry.destroy();
- },
-
- canCloseProject: function () {
- if (this.projecteditor) {
- return this.projecteditor.confirmUnsaved();
- }
- return true;
- },
-
- onfocus: function () {
- // Because we can't track the activity in the folder project,
- // we need to validate the project regularly. Let's assume that
- // if a modification happened, it happened when the window was
- // not focused.
- if (AppManager.selectedProject &&
- AppManager.selectedProject.type != "mainProcess" &&
- AppManager.selectedProject.type != "runtimeApp" &&
- AppManager.selectedProject.type != "tab") {
- AppManager.validateAndUpdateProject(AppManager.selectedProject);
- }
-
- // Hook to display promotional Developer Edition doorhanger. Only displayed once.
- // Hooked into the `onfocus` event because sometimes does not work
- // when run at the end of `init`. ¯\(°_o)/¯
- showDoorhanger({ window, type: "deveditionpromo", anchor: document.querySelector("#deck") });
- },
-
- appManagerUpdate: function (event, what, details) {
- // Got a message from app-manager.js
- // See AppManager.update() for descriptions of what these events mean.
- switch (what) {
- case "runtime-list":
- this.autoConnectRuntime();
- break;
- case "connection":
- this.updateRuntimeButton();
- this.updateCommands();
- this.updateConnectionTelemetry();
- break;
- case "before-project":
- if (!this.canCloseProject()) {
- details.cancel();
- }
- break;
- case "project":
- this._updatePromise = Task.spawn(function* () {
- UI.updateTitle();
- yield UI.destroyToolbox();
- UI.updateCommands();
- UI.openProject();
- yield UI.autoStartProject();
- UI.autoOpenToolbox();
- UI.saveLastSelectedProject();
- UI.updateRemoveProjectButton();
- });
- return;
- case "project-started":
- this.updateCommands();
- UI.autoOpenToolbox();
- break;
- case "project-stopped":
- UI.destroyToolbox();
- this.updateCommands();
- break;
- case "runtime-global-actors":
- // Check runtime version only on runtime-global-actors,
- // as we expect to use device actor
- this.checkRuntimeVersion();
- this.updateCommands();
- break;
- case "runtime-details":
- this.updateRuntimeButton();
- break;
- case "runtime":
- this.updateRuntimeButton();
- this.saveLastConnectedRuntime();
- break;
- case "project-validated":
- this.updateTitle();
- this.updateCommands();
- this.updateProjectEditorHeader();
- break;
- case "install-progress":
- this.updateProgress(Math.round(100 * details.bytesSent / details.totalBytes));
- break;
- case "runtime-targets":
- this.autoSelectProject();
- break;
- case "pre-package":
- this.prePackageLog(details);
- break;
- }
- this._updatePromise = promise.resolve();
- },
-
- configureSimulator: function (event, simulator) {
- UI.selectDeckPanel("simulator");
- },
-
- openInBrowser: function (url) {
- // Open a URL in a Firefox window
- let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
- if (mainWindow) {
- mainWindow.openUILinkIn(url, "tab");
- mainWindow.focus()
- } else {
- window.open(url);
- }
- },
-
- updateTitle: function () {
- let project = AppManager.selectedProject;
- if (project) {
- window.document.title = Strings.formatStringFromName("title_app", [project.name], 1);
- } else {
- window.document.title = Strings.GetStringFromName("title_noApp");
- }
- },
-
- /** ******** BUSY UI **********/
-
- _busyTimeout: null,
- _busyOperationDescription: null,
- _busyPromise: null,
-
- updateProgress: function (percent) {
- let progress = document.querySelector("#action-busy-determined");
- progress.mode = "determined";
- progress.value = percent;
- this.setupBusyTimeout();
- },
-
- busy: function () {
- let win = document.querySelector("window");
- win.classList.add("busy");
- win.classList.add("busy-undetermined");
- this.updateCommands();
- this.update("busy");
- },
-
- unbusy: function () {
- let win = document.querySelector("window");
- win.classList.remove("busy");
- win.classList.remove("busy-determined");
- win.classList.remove("busy-undetermined");
- this.updateCommands();
- this.update("unbusy");
- this._busyPromise = null;
- },
-
- setupBusyTimeout: function () {
- this.cancelBusyTimeout();
- this._busyTimeout = setTimeout(() => {
- this.unbusy();
- UI.reportError("error_operationTimeout", this._busyOperationDescription);
- }, Services.prefs.getIntPref("devtools.webide.busyTimeout"));
- },
-
- cancelBusyTimeout: function () {
- clearTimeout(this._busyTimeout);
- },
-
- busyWithProgressUntil: function (promise, operationDescription) {
- let busy = this.busyUntil(promise, operationDescription);
- let win = document.querySelector("window");
- let progress = document.querySelector("#action-busy-determined");
- progress.mode = "undetermined";
- win.classList.add("busy-determined");
- win.classList.remove("busy-undetermined");
- return busy;
- },
-
- busyUntil: function (promise, operationDescription) {
- // Freeze the UI until the promise is resolved. A timeout will unfreeze the
- // UI, just in case the promise never gets resolved.
- this._busyPromise = promise;
- this._busyOperationDescription = operationDescription;
- this.setupBusyTimeout();
- this.busy();
- promise.then(() => {
- this.cancelBusyTimeout();
- this.unbusy();
- }, (e) => {
- let message;
- if (e && e.error && e.message) {
- // Some errors come from fronts that are not based on protocol.js.
- // Errors are not translated to strings.
- message = operationDescription + " (" + e.error + "): " + e.message;
- } else {
- message = operationDescription + (e ? (": " + e) : "");
- }
- this.cancelBusyTimeout();
- let operationCanceled = e && e.canceled;
- if (!operationCanceled) {
- UI.reportError("error_operationFail", message);
- if (e) {
- console.error(e);
- }
- }
- this.unbusy();
- });
- return promise;
- },
-
- reportError: function (l10nProperty, ...l10nArgs) {
- let text;
-
- if (l10nArgs.length > 0) {
- text = Strings.formatStringFromName(l10nProperty, l10nArgs, l10nArgs.length);
- } else {
- text = Strings.GetStringFromName(l10nProperty);
- }
-
- console.error(text);
-
- let buttons = [{
- label: Strings.GetStringFromName("notification_showTroubleShooting_label"),
- accessKey: Strings.GetStringFromName("notification_showTroubleShooting_accesskey"),
- callback: function () {
- Cmds.showTroubleShooting();
- }
- }];
-
- let nbox = document.querySelector("#notificationbox");
- nbox.removeAllNotifications(true);
- nbox.appendNotification(text, "webide:errornotification", null,
- nbox.PRIORITY_WARNING_LOW, buttons);
- },
-
- dismissErrorNotification: function () {
- let nbox = document.querySelector("#notificationbox");
- nbox.removeAllNotifications(true);
- },
-
- /** ******** COMMANDS **********/
-
- /**
- * This module emits various events when state changes occur.
- *
- * The events this module may emit include:
- * busy:
- * The window is currently busy and certain UI functions may be disabled.
- * unbusy:
- * The window is not busy and certain UI functions may be re-enabled.
- */
- update: function (what, details) {
- this.emit("webide-update", what, details);
- },
-
- updateCommands: function () {
- // Action commands
- let playCmd = document.querySelector("#cmd_play");
- let stopCmd = document.querySelector("#cmd_stop");
- let debugCmd = document.querySelector("#cmd_toggleToolbox");
- let playButton = document.querySelector("#action-button-play");
- let projectPanelCmd = document.querySelector("#cmd_showProjectPanel");
-
- if (document.querySelector("window").classList.contains("busy")) {
- playCmd.setAttribute("disabled", "true");
- stopCmd.setAttribute("disabled", "true");
- debugCmd.setAttribute("disabled", "true");
- projectPanelCmd.setAttribute("disabled", "true");
- return;
- }
-
- if (!AppManager.selectedProject || !AppManager.connected) {
- playCmd.setAttribute("disabled", "true");
- stopCmd.setAttribute("disabled", "true");
- debugCmd.setAttribute("disabled", "true");
- } else {
- let isProjectRunning = AppManager.isProjectRunning();
- if (isProjectRunning) {
- playButton.classList.add("reload");
- stopCmd.removeAttribute("disabled");
- debugCmd.removeAttribute("disabled");
- } else {
- playButton.classList.remove("reload");
- stopCmd.setAttribute("disabled", "true");
- debugCmd.setAttribute("disabled", "true");
- }
-
- // If connected and a project is selected
- if (AppManager.selectedProject.type == "runtimeApp") {
- playCmd.removeAttribute("disabled");
- } else if (AppManager.selectedProject.type == "tab") {
- playCmd.removeAttribute("disabled");
- stopCmd.setAttribute("disabled", "true");
- } else if (AppManager.selectedProject.type == "mainProcess") {
- playCmd.setAttribute("disabled", "true");
- stopCmd.setAttribute("disabled", "true");
- } else {
- if (AppManager.selectedProject.errorsCount == 0 &&
- AppManager.runtimeCanHandleApps()) {
- playCmd.removeAttribute("disabled");
- } else {
- playCmd.setAttribute("disabled", "true");
- }
- }
- }
-
- // Runtime commands
- let monitorCmd = document.querySelector("#cmd_showMonitor");
- let screenshotCmd = document.querySelector("#cmd_takeScreenshot");
- let permissionsCmd = document.querySelector("#cmd_showPermissionsTable");
- let detailsCmd = document.querySelector("#cmd_showRuntimeDetails");
- let disconnectCmd = document.querySelector("#cmd_disconnectRuntime");
- let devicePrefsCmd = document.querySelector("#cmd_showDevicePrefs");
- let settingsCmd = document.querySelector("#cmd_showSettings");
-
- if (AppManager.connected) {
- if (AppManager.deviceFront) {
- monitorCmd.removeAttribute("disabled");
- detailsCmd.removeAttribute("disabled");
- permissionsCmd.removeAttribute("disabled");
- screenshotCmd.removeAttribute("disabled");
- }
- if (AppManager.preferenceFront) {
- devicePrefsCmd.removeAttribute("disabled");
- }
- if (AppManager.settingsFront) {
- settingsCmd.removeAttribute("disabled");
- }
- disconnectCmd.removeAttribute("disabled");
- } else {
- monitorCmd.setAttribute("disabled", "true");
- detailsCmd.setAttribute("disabled", "true");
- permissionsCmd.setAttribute("disabled", "true");
- screenshotCmd.setAttribute("disabled", "true");
- disconnectCmd.setAttribute("disabled", "true");
- devicePrefsCmd.setAttribute("disabled", "true");
- settingsCmd.setAttribute("disabled", "true");
- }
-
- let runtimePanelButton = document.querySelector("#runtime-panel-button");
-
- if (AppManager.connected) {
- runtimePanelButton.setAttribute("active", "true");
- runtimePanelButton.removeAttribute("hidden");
- } else {
- runtimePanelButton.removeAttribute("active");
- runtimePanelButton.setAttribute("hidden", "true");
- }
-
- projectPanelCmd.removeAttribute("disabled");
- },
-
- updateRemoveProjectButton: function () {
- // Remove command
- let removeCmdNode = document.querySelector("#cmd_removeProject");
- if (AppManager.selectedProject) {
- removeCmdNode.removeAttribute("disabled");
- } else {
- removeCmdNode.setAttribute("disabled", "true");
- }
- },
-
- /** ******** RUNTIME **********/
-
- get lastConnectedRuntime() {
- return Services.prefs.getCharPref("devtools.webide.lastConnectedRuntime");
- },
-
- set lastConnectedRuntime(runtime) {
- Services.prefs.setCharPref("devtools.webide.lastConnectedRuntime", runtime);
- },
-
- autoConnectRuntime: function () {
- // Automatically reconnect to the previously selected runtime,
- // if available and has an ID and feature is enabled
- if (AppManager.selectedRuntime ||
- !Services.prefs.getBoolPref("devtools.webide.autoConnectRuntime") ||
- !this.lastConnectedRuntime) {
- return;
- }
- let [_, type, id] = this.lastConnectedRuntime.match(/^(\w+):(.+)$/);
-
- type = type.toLowerCase();
-
- // Local connection is mapped to AppManager.runtimeList.other array
- if (type == "local") {
- type = "other";
- }
-
- // We support most runtimes except simulator, that needs to be manually
- // launched
- if (type == "usb" || type == "wifi" || type == "other") {
- for (let runtime of AppManager.runtimeList[type]) {
- // Some runtimes do not expose an id and don't support autoconnect (like
- // remote connection)
- if (runtime.id == id) {
- // Only want one auto-connect attempt, so clear last runtime value
- this.lastConnectedRuntime = "";
- this.connectToRuntime(runtime);
- }
- }
- }
- },
-
- connectToRuntime: function (runtime) {
- let name = runtime.name;
- let promise = AppManager.connectToRuntime(runtime);
- promise.then(() => this.initConnectionTelemetry())
- .catch(() => {
- // Empty rejection handler to silence uncaught rejection warnings
- // |busyUntil| will listen for rejections.
- // Bug 1121100 may find a better way to silence these.
- });
- promise = this.busyUntil(promise, "Connecting to " + name);
- // Stop busy timeout for runtimes that take unknown or long amounts of time
- // to connect.
- if (runtime.prolongedConnection) {
- this.cancelBusyTimeout();
- }
- return promise;
- },
-
- updateRuntimeButton: function () {
- let labelNode = document.querySelector("#runtime-panel-button > .panel-button-label");
- if (!AppManager.selectedRuntime) {
- labelNode.setAttribute("value", Strings.GetStringFromName("runtimeButton_label"));
- } else {
- let name = AppManager.selectedRuntime.name;
- labelNode.setAttribute("value", name);
- }
- },
-
- saveLastConnectedRuntime: function () {
- if (AppManager.selectedRuntime &&
- AppManager.selectedRuntime.id !== undefined) {
- this.lastConnectedRuntime = AppManager.selectedRuntime.type + ":" +
- AppManager.selectedRuntime.id;
- } else {
- this.lastConnectedRuntime = "";
- }
- },
-
- /** ******** ACTIONS **********/
-
- _actionsToLog: new Set(),
-
- /**
- * For each new connection, track whether play and debug were ever used. Only
- * one value is collected for each button, even if they are used multiple
- * times during a connection.
- */
- initConnectionTelemetry: function () {
- this._actionsToLog.add("play");
- this._actionsToLog.add("debug");
- },
-
- /**
- * Action occurred. Log that it happened, and remove it from the loggable
- * set.
- */
- onAction: function (action) {
- if (!this._actionsToLog.has(action)) {
- return;
- }
- this.logActionState(action, true);
- this._actionsToLog.delete(action);
- },
-
- /**
- * Connection status changed or we are shutting down. Record any loggable
- * actions as having not occurred.
- */
- updateConnectionTelemetry: function () {
- for (let action of this._actionsToLog.values()) {
- this.logActionState(action, false);
- }
- this._actionsToLog.clear();
- },
-
- logActionState: function (action, state) {
- let histogramId = "DEVTOOLS_WEBIDE_CONNECTION_" +
- action.toUpperCase() + "_USED";
- this._telemetry.log(histogramId, state);
- },
-
- /** ******** PROJECTS **********/
-
- // ProjectEditor & details screen
-
- destroyProjectEditor: function () {
- if (this.projecteditor) {
- this.projecteditor.destroy();
- this.projecteditor = null;
- }
- },
-
- /**
- * Called when selecting or deselecting the project editor panel.
- */
- onChangeProjectEditorSelected: function () {
- if (this.projecteditor) {
- let panel = document.querySelector("#deck").selectedPanel;
- if (panel && panel.id == "deck-panel-projecteditor") {
- this.projecteditor.menuEnabled = true;
- this._telemetry.toolOpened("webideProjectEditor");
- } else {
- this.projecteditor.menuEnabled = false;
- this._telemetry.toolClosed("webideProjectEditor");
- }
- }
- },
-
- getProjectEditor: function () {
- if (this.projecteditor) {
- return this.projecteditor.loaded;
- }
-
- let projecteditorIframe = document.querySelector("#deck-panel-projecteditor");
- this.projecteditor = ProjectEditor.ProjectEditor(projecteditorIframe, {
- menubar: document.querySelector("#main-menubar"),
- menuindex: 1
- });
- this.projecteditor.on("onEditorSave", () => {
- AppManager.validateAndUpdateProject(AppManager.selectedProject);
- this._telemetry.actionOccurred("webideProjectEditorSave");
- });
- return this.projecteditor.loaded;
- },
-
- updateProjectEditorHeader: function () {
- let project = AppManager.selectedProject;
- if (!project || !this.projecteditor) {
- return;
- }
- let status = project.validationStatus || "unknown";
- if (status == "error warning") {
- status = "error";
- }
- this.getProjectEditor().then((projecteditor) => {
- projecteditor.setProjectToAppPath(project.location, {
- name: project.name,
- iconUrl: project.icon,
- projectOverviewURL: "chrome://webide/content/details.xhtml",
- validationStatus: status
- }).then(null, console.error);
- }, console.error);
- },
-
- isProjectEditorEnabled: function () {
- return Services.prefs.getBoolPref("devtools.webide.showProjectEditor");
- },
-
- openProject: function () {
- let project = AppManager.selectedProject;
-
- // Nothing to show
-
- if (!project) {
- this.resetDeck();
- return;
- }
-
- // Make sure the directory exist before we show Project Editor
-
- let forceDetailsOnly = false;
- if (project.type == "packaged") {
- forceDetailsOnly = !utils.doesFileExist(project.location);
- }
-
- // Show only the details screen
-
- if (project.type != "packaged" ||
- !this.isProjectEditorEnabled() ||
- forceDetailsOnly) {
- this.selectDeckPanel("details");
- return;
- }
-
- // Show ProjectEditor
-
- this.getProjectEditor().then(() => {
- this.updateProjectEditorHeader();
- }, console.error);
-
- this.selectDeckPanel("projecteditor");
- },
-
- autoStartProject: Task.async(function* () {
- let project = AppManager.selectedProject;
-
- if (!project) {
- return;
- }
- if (!(project.type == "runtimeApp" ||
- project.type == "mainProcess" ||
- project.type == "tab")) {
- return; // For something that is not an editable app, we're done.
- }
-
- // Do not force opening apps that are already running, as they may have
- // some activity being opened and don't want to dismiss them.
- if (project.type == "runtimeApp" && !AppManager.isProjectRunning()) {
- yield UI.busyUntil(AppManager.launchRuntimeApp(), "running app");
- }
- }),
-
- autoOpenToolbox: Task.async(function* () {
- let project = AppManager.selectedProject;
-
- if (!project) {
- return;
- }
- if (!(project.type == "runtimeApp" ||
- project.type == "mainProcess" ||
- project.type == "tab")) {
- return; // For something that is not an editable app, we're done.
- }
-
- yield UI.createToolbox();
- }),
-
- importAndSelectApp: Task.async(function* (source) {
- let isPackaged = !!source.path;
- let project;
- try {
- project = yield AppProjects[isPackaged ? "addPackaged" : "addHosted"](source);
- } catch (e) {
- if (e === "Already added") {
- // Select project that's already been added,
- // and allow it to be revalidated and selected
- project = AppProjects.get(isPackaged ? source.path : source);
- } else {
- throw e;
- }
- }
-
- // Select project
- AppManager.selectedProject = project;
-
- this._telemetry.actionOccurred("webideImportProject");
- }),
-
- // Remember the last selected project on the runtime
- saveLastSelectedProject: function () {
- let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject");
- if (!shouldRestore) {
- return;
- }
-
- // Ignore unselection of project on runtime disconnection
- if (!AppManager.connected) {
- return;
- }
-
- let project = "", type = "";
- let selected = AppManager.selectedProject;
- if (selected) {
- if (selected.type == "runtimeApp") {
- type = "runtimeApp";
- project = selected.app.manifestURL;
- } else if (selected.type == "mainProcess") {
- type = "mainProcess";
- } else if (selected.type == "packaged" ||
- selected.type == "hosted") {
- type = "local";
- project = selected.location;
- }
- }
- if (type) {
- Services.prefs.setCharPref("devtools.webide.lastSelectedProject",
- type + ":" + project);
- } else {
- Services.prefs.clearUserPref("devtools.webide.lastSelectedProject");
- }
- },
-
- autoSelectProject: function () {
- if (AppManager.selectedProject) {
- return;
- }
- let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject");
- if (!shouldRestore) {
- return;
- }
- let pref = Services.prefs.getCharPref("devtools.webide.lastSelectedProject");
- if (!pref) {
- return;
- }
- let m = pref.match(/^(\w+):(.*)$/);
- if (!m) {
- return;
- }
- let [_, type, project] = m;
-
- if (type == "local") {
- let lastProject = AppProjects.get(project);
- if (lastProject) {
- AppManager.selectedProject = lastProject;
- }
- }
-
- // For other project types, we need to be connected to the runtime
- if (!AppManager.connected) {
- return;
- }
-
- if (type == "mainProcess" && AppManager.isMainProcessDebuggable()) {
- AppManager.selectedProject = {
- type: "mainProcess",
- name: Strings.GetStringFromName("mainProcess_label"),
- icon: AppManager.DEFAULT_PROJECT_ICON
- };
- } else if (type == "runtimeApp") {
- let app = AppManager.apps.get(project);
- if (app) {
- AppManager.selectedProject = {
- type: "runtimeApp",
- app: app.manifest,
- icon: app.iconURL,
- name: app.manifest.name
- };
- }
- }
- },
-
- /** ******** DECK **********/
-
- setupDeck: function () {
- let iframes = document.querySelectorAll("#deck > iframe");
- for (let iframe of iframes) {
- iframe.tooltip = "aHTMLTooltip";
- }
- },
-
- resetFocus: function () {
- document.commandDispatcher.focusedElement = document.documentElement;
- },
-
- selectDeckPanel: function (id) {
- let deck = document.querySelector("#deck");
- if (deck.selectedPanel && deck.selectedPanel.id === "deck-panel-" + id) {
- // This panel is already displayed.
- return;
- }
- this.resetFocus();
- let panel = deck.querySelector("#deck-panel-" + id);
- let lazysrc = panel.getAttribute("lazysrc");
- if (lazysrc) {
- panel.removeAttribute("lazysrc");
- panel.setAttribute("src", lazysrc);
- }
- deck.selectedPanel = panel;
- this.onChangeProjectEditorSelected();
- },
-
- resetDeck: function () {
- this.resetFocus();
- let deck = document.querySelector("#deck");
- deck.selectedPanel = null;
- this.onChangeProjectEditorSelected();
- },
-
- buildIDToDate(buildID) {
- let fields = buildID.match(/(\d{4})(\d{2})(\d{2})/);
- // Date expects 0 - 11 for months
- return new Date(fields[1], Number.parseInt(fields[2]) - 1, fields[3]);
- },
-
- checkRuntimeVersion: Task.async(function* () {
- if (AppManager.connected && AppManager.deviceFront) {
- let desc = yield AppManager.deviceFront.getDescription();
- // Compare device and firefox build IDs
- // and only compare by day (strip hours/minutes) to prevent
- // warning against builds of the same day.
- let deviceID = desc.appbuildid.substr(0, 8);
- let localID = Services.appinfo.appBuildID.substr(0, 8);
- let deviceDate = this.buildIDToDate(deviceID);
- let localDate = this.buildIDToDate(localID);
- // Allow device to be newer by up to a week. This accommodates those with
- // local device builds, since their devices will almost always be newer
- // than the client.
- if (deviceDate - localDate > 7 * MS_PER_DAY) {
- this.reportError("error_runtimeVersionTooRecent", deviceID, localID);
- }
- }
- }),
-
- /** ******** TOOLBOX **********/
-
- /**
- * There are many ways to close a toolbox:
- * * Close button inside the toolbox
- * * Toggle toolbox wrench in WebIDE
- * * Disconnect the current runtime gracefully
- * * Yank cord out of device
- * * Close or crash the app/tab
- * We can't know for sure which one was used here, so reset the
- * |toolboxPromise| since someone must be destroying it to reach here,
- * and call our own close method.
- */
- _onToolboxClosed: function (promise, iframe) {
- // Only save toolbox size, disable wrench button, workaround focus issue...
- // if we are closing the last toolbox:
- // - toolboxPromise is nullified by destroyToolbox and is still null here
- // if no other toolbox has been opened in between,
- // - having two distinct promise means we are receiving closed event
- // for a previous, non-current, toolbox.
- if (!this.toolboxPromise || this.toolboxPromise === promise) {
- this.toolboxPromise = null;
- this.resetFocus();
- Services.prefs.setIntPref("devtools.toolbox.footer.height", iframe.height);
-
- let splitter = document.querySelector(".devtools-horizontal-splitter");
- splitter.setAttribute("hidden", "true");
- document.querySelector("#action-button-debug").removeAttribute("active");
- }
- // We have to destroy the iframe, otherwise, the keybindings of webide don't work
- // properly anymore.
- iframe.remove();
- },
-
- destroyToolbox: function () {
- // Only have a live toolbox if |this.toolboxPromise| exists
- if (this.toolboxPromise) {
- let toolboxPromise = this.toolboxPromise;
- this.toolboxPromise = null;
- return toolboxPromise.then(toolbox => toolbox.destroy());
- }
- return promise.resolve();
- },
-
- createToolbox: function () {
- // If |this.toolboxPromise| exists, there is already a live toolbox
- if (this.toolboxPromise) {
- return this.toolboxPromise;
- }
-
- let iframe = document.createElement("iframe");
- iframe.id = "toolbox";
-
- // Compute a uid on the iframe in order to identify toolbox iframe
- // when receiving toolbox-close event
- iframe.uid = new Date().getTime();
-
- let height = Services.prefs.getIntPref("devtools.toolbox.footer.height");
- iframe.height = height;
-
- let promise = this.toolboxPromise = AppManager.getTarget().then(target => {
- return this._showToolbox(target, iframe);
- }).then(toolbox => {
- // Destroy the toolbox on WebIDE side before
- // toolbox.destroy's promise resolves.
- toolbox.once("destroyed", this._onToolboxClosed.bind(this, promise, iframe));
- return toolbox;
- }, console.error);
-
- return this.busyUntil(this.toolboxPromise, "opening toolbox");
- },
-
- _showToolbox: function (target, iframe) {
- let splitter = document.querySelector(".devtools-horizontal-splitter");
- splitter.removeAttribute("hidden");
-
- document.querySelector("notificationbox").insertBefore(iframe, splitter.nextSibling);
- let host = Toolbox.HostType.CUSTOM;
- let options = { customIframe: iframe, zoom: false, uid: iframe.uid };
-
- document.querySelector("#action-button-debug").setAttribute("active", "true");
-
- return gDevTools.showToolbox(target, null, host, options);
- },
-
- prePackageLog: function (msg) {
- if (msg == "start") {
- UI.selectDeckPanel("logs");
- }
- }
-};
-
-EventEmitter.decorate(UI);
-
-var Cmds = {
- quit: function () {
- if (UI.canCloseProject()) {
- window.close();
- }
- },
-
- showProjectPanel: function () {
- ProjectPanel.toggleSidebar();
- return promise.resolve();
- },
-
- showRuntimePanel: function () {
- RuntimeScanners.scan();
- RuntimePanel.toggleSidebar();
- },
-
- disconnectRuntime: function () {
- let disconnecting = Task.spawn(function* () {
- yield UI.destroyToolbox();
- yield AppManager.disconnectRuntime();
- });
- return UI.busyUntil(disconnecting, "disconnecting from runtime");
- },
-
- takeScreenshot: function () {
- let url = AppManager.deviceFront.screenshotToDataURL();
- return UI.busyUntil(url.then(longstr => {
- return longstr.string().then(dataURL => {
- longstr.release().then(null, console.error);
- UI.openInBrowser(dataURL);
- });
- }), "taking screenshot");
- },
-
- showPermissionsTable: function () {
- UI.selectDeckPanel("permissionstable");
- },
-
- showRuntimeDetails: function () {
- UI.selectDeckPanel("runtimedetails");
- },
-
- showDevicePrefs: function () {
- UI.selectDeckPanel("devicepreferences");
- },
-
- showSettings: function () {
- UI.selectDeckPanel("devicesettings");
- },
-
- showMonitor: function () {
- UI.selectDeckPanel("monitor");
- },
-
- play: Task.async(function* () {
- let busy;
- switch (AppManager.selectedProject.type) {
- case "packaged":
- let autosave =
- Services.prefs.getBoolPref("devtools.webide.autosaveFiles");
- if (autosave && UI.projecteditor) {
- yield UI.projecteditor.saveAllFiles();
- }
- busy = UI.busyWithProgressUntil(AppManager.installAndRunProject(),
- "installing and running app");
- break;
- case "hosted":
- busy = UI.busyUntil(AppManager.installAndRunProject(),
- "installing and running app");
- break;
- case "runtimeApp":
- busy = UI.busyUntil(AppManager.launchOrReloadRuntimeApp(), "launching / reloading app");
- break;
- case "tab":
- busy = UI.busyUntil(AppManager.reloadTab(), "reloading tab");
- break;
- }
- if (!busy) {
- return promise.reject();
- }
- UI.onAction("play");
- return busy;
- }),
-
- stop: function () {
- return UI.busyUntil(AppManager.stopRunningApp(), "stopping app");
- },
-
- toggleToolbox: function () {
- UI.onAction("debug");
- if (UI.toolboxPromise) {
- UI.destroyToolbox();
- return promise.resolve();
- } else {
- return UI.createToolbox();
- }
- },
-
- removeProject: function () {
- AppManager.removeSelectedProject();
- },
-
- toggleEditors: function () {
- let isNowEnabled = !UI.isProjectEditorEnabled();
- Services.prefs.setBoolPref("devtools.webide.showProjectEditor", isNowEnabled);
- if (!isNowEnabled) {
- UI.destroyProjectEditor();
- }
- UI.openProject();
- },
-
- showTroubleShooting: function () {
- UI.openInBrowser(HELP_URL);
- },
-
- showAddons: function () {
- UI.selectDeckPanel("addons");
- },
-
- showPrefs: function () {
- UI.selectDeckPanel("prefs");
- },
-
- zoomIn: function () {
- if (UI.contentViewer.fullZoom < MAX_ZOOM) {
- UI.contentViewer.fullZoom += 0.1;
- Services.prefs.setCharPref("devtools.webide.zoom", UI.contentViewer.fullZoom);
- }
- },
-
- zoomOut: function () {
- if (UI.contentViewer.fullZoom > MIN_ZOOM) {
- UI.contentViewer.fullZoom -= 0.1;
- Services.prefs.setCharPref("devtools.webide.zoom", UI.contentViewer.fullZoom);
- }
- },
-
- resetZoom: function () {
- UI.contentViewer.fullZoom = 1;
- Services.prefs.setCharPref("devtools.webide.zoom", 1);
- }
-};