From 197f4cbaa47e5e8b9b1fb578b10046914eb6486e Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Sat, 13 Jul 2019 02:41:46 +0200 Subject: Remove WebIDE devtools component. This resolves #1123 --- devtools/client/webide/modules/addons.js | 197 ----- devtools/client/webide/modules/app-manager.js | 850 --------------------- devtools/client/webide/modules/app-projects.js | 235 ------ devtools/client/webide/modules/app-validator.js | 292 ------- devtools/client/webide/modules/build.js | 199 ----- devtools/client/webide/modules/config-view.js | 373 --------- devtools/client/webide/modules/moz.build | 21 - devtools/client/webide/modules/project-list.js | 375 --------- devtools/client/webide/modules/runtime-list.js | 207 ----- devtools/client/webide/modules/runtimes.js | 673 ---------------- .../client/webide/modules/simulator-process.js | 325 -------- devtools/client/webide/modules/simulators.js | 368 --------- devtools/client/webide/modules/tab-store.js | 178 ----- devtools/client/webide/modules/utils.js | 68 -- 14 files changed, 4361 deletions(-) delete mode 100644 devtools/client/webide/modules/addons.js delete mode 100644 devtools/client/webide/modules/app-manager.js delete mode 100644 devtools/client/webide/modules/app-projects.js delete mode 100644 devtools/client/webide/modules/app-validator.js delete mode 100644 devtools/client/webide/modules/build.js delete mode 100644 devtools/client/webide/modules/config-view.js delete mode 100644 devtools/client/webide/modules/moz.build delete mode 100644 devtools/client/webide/modules/project-list.js delete mode 100644 devtools/client/webide/modules/runtime-list.js delete mode 100644 devtools/client/webide/modules/runtimes.js delete mode 100644 devtools/client/webide/modules/simulator-process.js delete mode 100644 devtools/client/webide/modules/simulators.js delete mode 100644 devtools/client/webide/modules/tab-store.js delete mode 100644 devtools/client/webide/modules/utils.js (limited to 'devtools/client/webide/modules') diff --git a/devtools/client/webide/modules/addons.js b/devtools/client/webide/modules/addons.js deleted file mode 100644 index 4dc09f1ca..000000000 --- a/devtools/client/webide/modules/addons.js +++ /dev/null @@ -1,197 +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/. */ - -"use strict"; - -const promise = require("promise"); -const {AddonManager} = require("resource://gre/modules/AddonManager.jsm"); -const Services = require("Services"); -const {getJSON} = require("devtools/client/shared/getjson"); -const EventEmitter = require("devtools/shared/event-emitter"); - -const ADDONS_URL = "devtools.webide.addonsURL"; - -var SIMULATOR_LINK = Services.prefs.getCharPref("devtools.webide.simulatorAddonsURL"); -var ADB_LINK = Services.prefs.getCharPref("devtools.webide.adbAddonURL"); -var ADAPTERS_LINK = Services.prefs.getCharPref("devtools.webide.adaptersAddonURL"); -var SIMULATOR_ADDON_ID = Services.prefs.getCharPref("devtools.webide.simulatorAddonID"); -var ADB_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adbAddonID"); -var ADAPTERS_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adaptersAddonID"); - -var platform = Services.appShell.hiddenDOMWindow.navigator.platform; -var OS = ""; -if (platform.indexOf("Win") != -1) { - OS = "win32"; -} else if (platform.indexOf("Mac") != -1) { - OS = "mac64"; -} else if (platform.indexOf("Linux") != -1) { - if (platform.indexOf("x86_64") != -1) { - OS = "linux64"; - } else { - OS = "linux32"; - } -} - -var addonsListener = {}; -addonsListener.onEnabled = -addonsListener.onDisabled = -addonsListener.onInstalled = -addonsListener.onUninstalled = (updatedAddon) => { - GetAvailableAddons().then(addons => { - for (let a of [...addons.simulators, addons.adb, addons.adapters]) { - if (a.addonID == updatedAddon.id) { - a.updateInstallStatus(); - } - } - }); -}; -AddonManager.addAddonListener(addonsListener); - -var GetAvailableAddons_promise = null; -var GetAvailableAddons = exports.GetAvailableAddons = function () { - if (!GetAvailableAddons_promise) { - let deferred = promise.defer(); - GetAvailableAddons_promise = deferred.promise; - let addons = { - simulators: [], - adb: null - }; - getJSON(ADDONS_URL).then(json => { - for (let stability in json) { - for (let version of json[stability]) { - addons.simulators.push(new SimulatorAddon(stability, version)); - } - } - addons.adb = new ADBAddon(); - addons.adapters = new AdaptersAddon(); - deferred.resolve(addons); - }, e => { - GetAvailableAddons_promise = null; - deferred.reject(e); - }); - } - return GetAvailableAddons_promise; -}; - -exports.ForgetAddonsList = function () { - GetAvailableAddons_promise = null; -}; - -function Addon() {} -Addon.prototype = { - _status: "unknown", - set status(value) { - if (this._status != value) { - this._status = value; - this.emit("update"); - } - }, - get status() { - return this._status; - }, - - updateInstallStatus: function () { - AddonManager.getAddonByID(this.addonID, (addon) => { - if (addon && !addon.userDisabled) { - this.status = "installed"; - } else { - this.status = "uninstalled"; - } - }); - }, - - install: function () { - AddonManager.getAddonByID(this.addonID, (addon) => { - if (addon && !addon.userDisabled) { - this.status = "installed"; - return; - } - this.status = "preparing"; - if (addon && addon.userDisabled) { - addon.userDisabled = false; - } else { - AddonManager.getInstallForURL(this.xpiLink, (install) => { - install.addListener(this); - install.install(); - }, "application/x-xpinstall"); - } - }); - }, - - uninstall: function () { - AddonManager.getAddonByID(this.addonID, (addon) => { - addon.uninstall(); - }); - }, - - installFailureHandler: function (install, message) { - this.status = "uninstalled"; - this.emit("failure", message); - }, - - onDownloadStarted: function () { - this.status = "downloading"; - }, - - onInstallStarted: function () { - this.status = "installing"; - }, - - onDownloadProgress: function (install) { - if (install.maxProgress == -1) { - this.emit("progress", -1); - } else { - this.emit("progress", install.progress / install.maxProgress); - } - }, - - onInstallEnded: function ({addon}) { - addon.userDisabled = false; - }, - - onDownloadCancelled: function (install) { - this.installFailureHandler(install, "Download cancelled"); - }, - onDownloadFailed: function (install) { - this.installFailureHandler(install, "Download failed"); - }, - onInstallCancelled: function (install) { - this.installFailureHandler(install, "Install cancelled"); - }, - onInstallFailed: function (install) { - this.installFailureHandler(install, "Install failed"); - }, -}; - -function SimulatorAddon(stability, version) { - EventEmitter.decorate(this); - this.stability = stability; - this.version = version; - // This addon uses the string "linux" for "linux32" - let fixedOS = OS == "linux32" ? "linux" : OS; - this.xpiLink = SIMULATOR_LINK.replace(/#OS#/g, fixedOS) - .replace(/#VERSION#/g, version) - .replace(/#SLASHED_VERSION#/g, version.replace(/\./g, "_")); - this.addonID = SIMULATOR_ADDON_ID.replace(/#SLASHED_VERSION#/g, version.replace(/\./g, "_")); - this.updateInstallStatus(); -} -SimulatorAddon.prototype = Object.create(Addon.prototype); - -function ADBAddon() { - EventEmitter.decorate(this); - // This addon uses the string "linux" for "linux32" - let fixedOS = OS == "linux32" ? "linux" : OS; - this.xpiLink = ADB_LINK.replace(/#OS#/g, fixedOS); - this.addonID = ADB_ADDON_ID; - this.updateInstallStatus(); -} -ADBAddon.prototype = Object.create(Addon.prototype); - -function AdaptersAddon() { - EventEmitter.decorate(this); - this.xpiLink = ADAPTERS_LINK.replace(/#OS#/g, OS); - this.addonID = ADAPTERS_ADDON_ID; - this.updateInstallStatus(); -} -AdaptersAddon.prototype = Object.create(Addon.prototype); diff --git a/devtools/client/webide/modules/app-manager.js b/devtools/client/webide/modules/app-manager.js deleted file mode 100644 index 88dfcdd44..000000000 --- a/devtools/client/webide/modules/app-manager.js +++ /dev/null @@ -1,850 +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/. */ - -const {Cu} = require("chrome"); - -const promise = require("promise"); -const {TargetFactory} = require("devtools/client/framework/target"); -const Services = require("Services"); -const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {}); -const EventEmitter = require("devtools/shared/event-emitter"); -const {TextEncoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", {}); -const {AppProjects} = require("devtools/client/webide/modules/app-projects"); -const TabStore = require("devtools/client/webide/modules/tab-store"); -const {AppValidator} = require("devtools/client/webide/modules/app-validator"); -const {ConnectionManager, Connection} = require("devtools/shared/client/connection-manager"); -const {AppActorFront} = require("devtools/shared/apps/app-actor-front"); -const {getDeviceFront} = require("devtools/shared/fronts/device"); -const {getPreferenceFront} = require("devtools/shared/fronts/preference"); -const {getSettingsFront} = require("devtools/shared/fronts/settings"); -const {Task} = require("devtools/shared/task"); -const {RuntimeScanners, RuntimeTypes} = require("devtools/client/webide/modules/runtimes"); -const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {}); -const Telemetry = require("devtools/client/shared/telemetry"); -const {ProjectBuilding} = require("./build"); - -const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); - -var AppManager = exports.AppManager = { - - DEFAULT_PROJECT_ICON: "chrome://webide/skin/default-app-icon.png", - DEFAULT_PROJECT_NAME: "--", - - _initialized: false, - - init: function () { - if (this._initialized) { - return; - } - this._initialized = true; - - let port = Services.prefs.getIntPref("devtools.debugger.remote-port"); - this.connection = ConnectionManager.createConnection("localhost", port); - this.onConnectionChanged = this.onConnectionChanged.bind(this); - this.connection.on(Connection.Events.STATUS_CHANGED, this.onConnectionChanged); - - this.tabStore = new TabStore(this.connection); - this.onTabList = this.onTabList.bind(this); - this.onTabNavigate = this.onTabNavigate.bind(this); - this.onTabClosed = this.onTabClosed.bind(this); - this.tabStore.on("tab-list", this.onTabList); - this.tabStore.on("navigate", this.onTabNavigate); - this.tabStore.on("closed", this.onTabClosed); - - this._clearRuntimeList(); - this._rebuildRuntimeList = this._rebuildRuntimeList.bind(this); - RuntimeScanners.on("runtime-list-updated", this._rebuildRuntimeList); - RuntimeScanners.enable(); - this._rebuildRuntimeList(); - - this.onInstallProgress = this.onInstallProgress.bind(this); - - this._telemetry = new Telemetry(); - }, - - destroy: function () { - if (!this._initialized) { - return; - } - this._initialized = false; - - this.selectedProject = null; - this.selectedRuntime = null; - RuntimeScanners.off("runtime-list-updated", this._rebuildRuntimeList); - RuntimeScanners.disable(); - this.runtimeList = null; - this.tabStore.off("tab-list", this.onTabList); - this.tabStore.off("navigate", this.onTabNavigate); - this.tabStore.off("closed", this.onTabClosed); - this.tabStore.destroy(); - this.tabStore = null; - this.connection.off(Connection.Events.STATUS_CHANGED, this.onConnectionChanged); - this._listTabsResponse = null; - this.connection.disconnect(); - this.connection = null; - }, - - /** - * This module emits various events when state changes occur. The basic event - * naming scheme is that event "X" means "X has changed" or "X is available". - * Some names are more detailed to clarify their precise meaning. - * - * The events this module may emit include: - * before-project: - * The selected project is about to change. The event includes a special - * |cancel| callback that will abort the project change if desired. - * connection: - * The connection status has changed (connected, disconnected, etc.) - * install-progress: - * A project being installed to a runtime has made further progress. This - * event contains additional details about exactly how far the process is - * when such information is available. - * project: - * The selected project has changed. - * project-started: - * The selected project started running on the connected runtime. - * project-stopped: - * The selected project stopped running on the connected runtime. - * project-removed: - * The selected project was removed from the project list. - * project-validated: - * The selected project just completed validation. As part of validation, - * many pieces of metadata about the project are refreshed, including its - * name, manifest details, etc. - * runtime: - * The selected runtime has changed. - * runtime-apps-icons: - * The list of URLs for the runtime app icons are available. - * runtime-global-actors: - * The list of global actors for the entire runtime (but not actors for a - * specific tab or app) are now available, so we can test for features - * like preferences and settings. - * runtime-details: - * The selected runtime's details have changed, such as its user-visible - * name. - * runtime-list: - * The list of available runtimes has changed, or any of the user-visible - * details (like names) for the non-selected runtimes has changed. - * runtime-telemetry: - * Detailed runtime telemetry has been recorded. Used by tests. - * runtime-targets: - * The list of remote runtime targets available from the currently - * connected runtime (such as tabs or apps) has changed, or any of the - * user-visible details (like names) for the non-selected runtime targets - * has changed. This event includes |type| in the details, to distinguish - * "apps" and "tabs". - */ - update: function (what, details) { - // Anything we want to forward to the UI - this.emit("app-manager-update", what, details); - }, - - reportError: function (l10nProperty, ...l10nArgs) { - let win = Services.wm.getMostRecentWindow("devtools:webide"); - if (win) { - win.UI.reportError(l10nProperty, ...l10nArgs); - } else { - let text; - if (l10nArgs.length > 0) { - text = Strings.formatStringFromName(l10nProperty, l10nArgs, l10nArgs.length); - } else { - text = Strings.GetStringFromName(l10nProperty); - } - console.error(text); - } - }, - - onConnectionChanged: function () { - console.log("Connection status changed: " + this.connection.status); - - if (this.connection.status == Connection.Status.DISCONNECTED) { - this.selectedRuntime = null; - } - - if (!this.connected) { - if (this._appsFront) { - this._appsFront.off("install-progress", this.onInstallProgress); - this._appsFront.unwatchApps(); - this._appsFront = null; - } - this._listTabsResponse = null; - } else { - this.connection.client.listTabs((response) => { - if (response.webappsActor) { - let front = new AppActorFront(this.connection.client, - response); - front.on("install-progress", this.onInstallProgress); - front.watchApps(() => this.checkIfProjectIsRunning()) - .then(() => { - // This can't be done earlier as many operations - // in the apps actor require watchApps to be called - // first. - this._appsFront = front; - this._listTabsResponse = response; - this._recordRuntimeInfo(); - this.update("runtime-global-actors"); - }) - .then(() => { - this.checkIfProjectIsRunning(); - this.update("runtime-targets", { type: "apps" }); - front.fetchIcons().then(() => this.update("runtime-apps-icons")); - }); - } else { - this._listTabsResponse = response; - this._recordRuntimeInfo(); - this.update("runtime-global-actors"); - } - }); - } - - this.update("connection"); - }, - - get connected() { - return this.connection && - this.connection.status == Connection.Status.CONNECTED; - }, - - get apps() { - if (this._appsFront) { - return this._appsFront.apps; - } else { - return new Map(); - } - }, - - onInstallProgress: function (event, details) { - this.update("install-progress", details); - }, - - isProjectRunning: function () { - if (this.selectedProject.type == "mainProcess" || - this.selectedProject.type == "tab") { - return true; - } - - let app = this._getProjectFront(this.selectedProject); - return app && app.running; - }, - - checkIfProjectIsRunning: function () { - if (this.selectedProject) { - if (this.isProjectRunning()) { - this.update("project-started"); - } else { - this.update("project-stopped"); - } - } - }, - - listTabs: function () { - return this.tabStore.listTabs(); - }, - - onTabList: function () { - this.update("runtime-targets", { type: "tabs" }); - }, - - // TODO: Merge this into TabProject as part of project-agnostic work - onTabNavigate: function () { - this.update("runtime-targets", { type: "tabs" }); - if (this.selectedProject.type !== "tab") { - return; - } - let tab = this.selectedProject.app = this.tabStore.selectedTab; - let uri = NetUtil.newURI(tab.url); - // 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 = uri.prePath + "/favicon.ico"; - tab.name = tab.title || Strings.GetStringFromName("project_tab_loading"); - if (uri.scheme.startsWith("http")) { - tab.name = uri.host + ": " + tab.name; - } - this.selectedProject.location = tab.url; - this.selectedProject.name = tab.name; - this.selectedProject.icon = tab.favicon; - this.update("project-validated"); - }, - - onTabClosed: function () { - if (this.selectedProject.type !== "tab") { - return; - } - this.selectedProject = null; - }, - - reloadTab: function () { - if (this.selectedProject && this.selectedProject.type != "tab") { - return promise.reject("tried to reload non-tab project"); - } - return this.getTarget().then(target => { - target.activeTab.reload(); - }, console.error.bind(console)); - }, - - getTarget: function () { - if (this.selectedProject.type == "mainProcess") { - // Fx >=39 exposes a ChromeActor to debug the main process - if (this.connection.client.mainRoot.traits.allowChromeProcess) { - return this.connection.client.getProcess() - .then(aResponse => { - return TargetFactory.forRemoteTab({ - form: aResponse.form, - client: this.connection.client, - chrome: true - }); - }); - } else { - // Fx <39 exposes tab actors on the root actor - return TargetFactory.forRemoteTab({ - form: this._listTabsResponse, - client: this.connection.client, - chrome: true, - isTabActor: false - }); - } - } - - if (this.selectedProject.type == "tab") { - return this.tabStore.getTargetForTab(); - } - - let app = this._getProjectFront(this.selectedProject); - if (!app) { - return promise.reject("Can't find app front for selected project"); - } - - return Task.spawn(function* () { - // Once we asked the app to launch, the app isn't necessary completely loaded. - // launch request only ask the app to launch and immediatly returns. - // We have to keep trying to get app tab actors required to create its target. - - for (let i = 0; i < 10; i++) { - try { - return yield app.getTarget(); - } catch (e) {} - let deferred = promise.defer(); - setTimeout(deferred.resolve, 500); - yield deferred.promise; - } - - AppManager.reportError("error_cantConnectToApp", app.manifest.manifestURL); - throw new Error("can't connect to app"); - }); - }, - - getProjectManifestURL: function (project) { - let manifest = null; - if (project.type == "runtimeApp") { - manifest = project.app.manifestURL; - } - - if (project.type == "hosted") { - manifest = project.location; - } - - if (project.type == "packaged" && project.packagedAppOrigin) { - manifest = "app://" + project.packagedAppOrigin + "/manifest.webapp"; - } - - return manifest; - }, - - _getProjectFront: function (project) { - let manifest = this.getProjectManifestURL(project); - if (manifest && this._appsFront) { - return this._appsFront.apps.get(manifest); - } - return null; - }, - - _selectedProject: null, - set selectedProject(project) { - // A regular comparison doesn't work as we recreate a new object every time - let prev = this._selectedProject; - if (!prev && !project) { - return; - } else if (prev && project && prev.type === project.type) { - let type = project.type; - if (type === "runtimeApp") { - if (prev.app.manifestURL === project.app.manifestURL) { - return; - } - } else if (type === "tab") { - if (prev.app.actor === project.app.actor) { - return; - } - } else if (type === "packaged" || type === "hosted") { - if (prev.location === project.location) { - return; - } - } else if (type === "mainProcess") { - return; - } else { - throw new Error("Unsupported project type: " + type); - } - } - - let cancelled = false; - this.update("before-project", { cancel: () => { cancelled = true; } }); - if (cancelled) { - return; - } - - this._selectedProject = project; - - // Clear out tab store's selected state, if any - this.tabStore.selectedTab = null; - - if (project) { - if (project.type == "packaged" || - project.type == "hosted") { - this.validateAndUpdateProject(project); - } - if (project.type == "tab") { - this.tabStore.selectedTab = project.app; - } - } - - this.update("project"); - this.checkIfProjectIsRunning(); - }, - get selectedProject() { - return this._selectedProject; - }, - - 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; - } - - yield AppProjects.remove(location); - AppManager.update("project-removed"); - }), - - packageProject: Task.async(function* (project) { - if (!project) { - return; - } - if (project.type == "packaged" || - project.type == "hosted") { - yield ProjectBuilding.build({ - project: project, - logger: this.update.bind(this, "pre-package") - }); - } - }), - - _selectedRuntime: null, - set selectedRuntime(value) { - this._selectedRuntime = value; - if (!value && this.selectedProject && - (this.selectedProject.type == "mainProcess" || - this.selectedProject.type == "runtimeApp" || - this.selectedProject.type == "tab")) { - this.selectedProject = null; - } - this.update("runtime"); - }, - - get selectedRuntime() { - return this._selectedRuntime; - }, - - connectToRuntime: function (runtime) { - - if (this.connected && this.selectedRuntime === runtime) { - // Already connected - return promise.resolve(); - } - - let deferred = promise.defer(); - - this.disconnectRuntime().then(() => { - this.selectedRuntime = runtime; - - let onConnectedOrDisconnected = () => { - this.connection.off(Connection.Events.CONNECTED, onConnectedOrDisconnected); - this.connection.off(Connection.Events.DISCONNECTED, onConnectedOrDisconnected); - if (this.connected) { - deferred.resolve(); - } else { - deferred.reject(); - } - }; - this.connection.on(Connection.Events.CONNECTED, onConnectedOrDisconnected); - this.connection.on(Connection.Events.DISCONNECTED, onConnectedOrDisconnected); - try { - // Reset the connection's state to defaults - this.connection.resetOptions(); - // Only watch for errors here. Final resolution occurs above, once - // we've reached the CONNECTED state. - this.selectedRuntime.connect(this.connection) - .then(null, e => deferred.reject(e)); - } catch (e) { - deferred.reject(e); - } - }, deferred.reject); - - // Record connection result in telemetry - let logResult = result => { - this._telemetry.log("DEVTOOLS_WEBIDE_CONNECTION_RESULT", result); - if (runtime.type) { - this._telemetry.log("DEVTOOLS_WEBIDE_" + runtime.type + - "_CONNECTION_RESULT", result); - } - }; - deferred.promise.then(() => logResult(true), () => logResult(false)); - - // If successful, record connection time in telemetry - deferred.promise.then(() => { - const timerId = "DEVTOOLS_WEBIDE_CONNECTION_TIME_SECONDS"; - this._telemetry.startTimer(timerId); - this.connection.once(Connection.Events.STATUS_CHANGED, () => { - this._telemetry.stopTimer(timerId); - }); - }).catch(() => { - // Empty rejection handler to silence uncaught rejection warnings - // |connectToRuntime| caller should listen for rejections. - // Bug 1121100 may find a better way to silence these. - }); - - return deferred.promise; - }, - - _recordRuntimeInfo: Task.async(function* () { - if (!this.connected) { - return; - } - let runtime = this.selectedRuntime; - this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_TYPE", - runtime.type || "UNKNOWN", true); - this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_ID", - runtime.id || "unknown", true); - if (!this.deviceFront) { - this.update("runtime-telemetry"); - return; - } - let d = yield this.deviceFront.getDescription(); - this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_PROCESSOR", - d.processor, true); - this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_OS", - d.os, true); - this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_PLATFORM_VERSION", - d.platformversion, true); - this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_APP_TYPE", - d.apptype, true); - this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_VERSION", - d.version, true); - this.update("runtime-telemetry"); - }), - - isMainProcessDebuggable: function () { - // Fx <39 exposes chrome tab actors on RootActor - // Fx >=39 exposes a dedicated actor via getProcess request - return this.connection.client && - this.connection.client.mainRoot && - this.connection.client.mainRoot.traits.allowChromeProcess || - (this._listTabsResponse && - this._listTabsResponse.consoleActor); - }, - - get deviceFront() { - if (!this._listTabsResponse) { - return null; - } - return getDeviceFront(this.connection.client, this._listTabsResponse); - }, - - get preferenceFront() { - if (!this._listTabsResponse) { - return null; - } - return getPreferenceFront(this.connection.client, this._listTabsResponse); - }, - - get settingsFront() { - if (!this._listTabsResponse) { - return null; - } - return getSettingsFront(this.connection.client, this._listTabsResponse); - }, - - disconnectRuntime: function () { - if (!this.connected) { - return promise.resolve(); - } - let deferred = promise.defer(); - this.connection.once(Connection.Events.DISCONNECTED, () => deferred.resolve()); - this.connection.disconnect(); - return deferred.promise; - }, - - launchRuntimeApp: function () { - if (this.selectedProject && this.selectedProject.type != "runtimeApp") { - return promise.reject("attempting to launch a non-runtime app"); - } - let app = this._getProjectFront(this.selectedProject); - return app.launch(); - }, - - launchOrReloadRuntimeApp: function () { - if (this.selectedProject && this.selectedProject.type != "runtimeApp") { - return promise.reject("attempting to launch / reload a non-runtime app"); - } - let app = this._getProjectFront(this.selectedProject); - if (!app.running) { - return app.launch(); - } else { - return app.reload(); - } - }, - - runtimeCanHandleApps: function () { - return !!this._appsFront; - }, - - installAndRunProject: function () { - let project = this.selectedProject; - - if (!project || (project.type != "packaged" && project.type != "hosted")) { - console.error("Can't install project. Unknown type of project."); - return promise.reject("Can't install"); - } - - if (!this._listTabsResponse) { - this.reportError("error_cantInstallNotFullyConnected"); - return promise.reject("Can't install"); - } - - if (!this._appsFront) { - console.error("Runtime doesn't have a webappsActor"); - return promise.reject("Can't install"); - } - - return Task.spawn(function* () { - let self = AppManager; - - // Package and validate project - yield self.packageProject(project); - yield self.validateAndUpdateProject(project); - - if (project.errorsCount > 0) { - self.reportError("error_cantInstallValidationErrors"); - return; - } - - let installPromise; - - if (project.type != "packaged" && project.type != "hosted") { - return promise.reject("Don't know how to install project"); - } - - let response; - if (project.type == "packaged") { - let packageDir = yield ProjectBuilding.getPackageDir(project); - console.log("Installing app from " + packageDir); - - response = yield self._appsFront.installPackaged(packageDir, - project.packagedAppOrigin); - - // If the packaged app specified a custom origin override, - // we need to update the local project origin - project.packagedAppOrigin = response.appId; - // And ensure the indexed db on disk is also updated - AppProjects.update(project); - } - - if (project.type == "hosted") { - let manifestURLObject = Services.io.newURI(project.location, null, null); - let origin = Services.io.newURI(manifestURLObject.prePath, null, null); - let appId = origin.host; - let metadata = { - origin: origin.spec, - manifestURL: project.location - }; - response = yield self._appsFront.installHosted(appId, - metadata, - project.manifest); - } - - // Addons don't have any document to load (yet?) - // So that there is no need to run them, installing is enough - if (project.manifest.manifest_version || project.manifest.role === "addon") { - return; - } - - let {app} = response; - if (!app.running) { - let deferred = promise.defer(); - self.on("app-manager-update", function onUpdate(event, what) { - if (what == "project-started") { - self.off("app-manager-update", onUpdate); - deferred.resolve(); - } - }); - yield app.launch(); - yield deferred.promise; - } else { - yield app.reload(); - } - }); - }, - - stopRunningApp: function () { - let app = this._getProjectFront(this.selectedProject); - return app.close(); - }, - - /* PROJECT VALIDATION */ - - validateAndUpdateProject: function (project) { - if (!project) { - return promise.reject(); - } - - return Task.spawn(function* () { - - let packageDir = yield ProjectBuilding.getPackageDir(project); - let validation = new AppValidator({ - type: project.type, - // Build process may place the manifest in a non-root directory - location: packageDir - }); - - yield validation.validate(); - - if (validation.manifest) { - let manifest = validation.manifest; - let iconPath; - if (manifest.icons) { - let size = Object.keys(manifest.icons).sort((a, b) => b - a)[0]; - if (size) { - iconPath = manifest.icons[size]; - } - } - if (!iconPath) { - project.icon = AppManager.DEFAULT_PROJECT_ICON; - } else { - if (project.type == "hosted") { - let manifestURL = Services.io.newURI(project.location, null, null); - let origin = Services.io.newURI(manifestURL.prePath, null, null); - project.icon = Services.io.newURI(iconPath, null, origin).spec; - } else if (project.type == "packaged") { - let projectFolder = FileUtils.File(packageDir); - let folderURI = Services.io.newFileURI(projectFolder).spec; - project.icon = folderURI + iconPath.replace(/^\/|\\/, ""); - } - } - project.manifest = validation.manifest; - - if ("name" in project.manifest) { - project.name = project.manifest.name; - } else { - project.name = AppManager.DEFAULT_PROJECT_NAME; - } - } else { - project.manifest = null; - project.icon = AppManager.DEFAULT_PROJECT_ICON; - project.name = AppManager.DEFAULT_PROJECT_NAME; - } - - project.validationStatus = "valid"; - - if (validation.warnings.length > 0) { - project.warningsCount = validation.warnings.length; - project.warnings = validation.warnings; - project.validationStatus = "warning"; - } else { - project.warnings = ""; - project.warningsCount = 0; - } - - if (validation.errors.length > 0) { - project.errorsCount = validation.errors.length; - project.errors = validation.errors; - project.validationStatus = "error"; - } else { - project.errors = ""; - project.errorsCount = 0; - } - - if (project.warningsCount && project.errorsCount) { - project.validationStatus = "error warning"; - } - - if (project.type === "hosted" && project.location !== validation.manifestURL) { - yield AppProjects.updateLocation(project, validation.manifestURL); - } else if (AppProjects.get(project.location)) { - yield AppProjects.update(project); - } - - if (AppManager.selectedProject === project) { - AppManager.update("project-validated"); - } - }); - }, - - /* RUNTIME LIST */ - - _clearRuntimeList: function () { - this.runtimeList = { - usb: [], - wifi: [], - simulator: [], - other: [] - }; - }, - - _rebuildRuntimeList: function () { - let runtimes = RuntimeScanners.listRuntimes(); - this._clearRuntimeList(); - - // Reorganize runtimes by type - for (let runtime of runtimes) { - switch (runtime.type) { - case RuntimeTypes.USB: - this.runtimeList.usb.push(runtime); - break; - case RuntimeTypes.WIFI: - this.runtimeList.wifi.push(runtime); - break; - case RuntimeTypes.SIMULATOR: - this.runtimeList.simulator.push(runtime); - break; - default: - this.runtimeList.other.push(runtime); - } - } - - this.update("runtime-details"); - this.update("runtime-list"); - }, - - /* MANIFEST UTILS */ - - writeManifest: function (project) { - if (project.type != "packaged") { - return promise.reject("Not a packaged app"); - } - - if (!project.manifest) { - project.manifest = {}; - } - - let folder = project.location; - let manifestPath = OS.Path.join(folder, "manifest.webapp"); - let text = JSON.stringify(project.manifest, null, 2); - let encoder = new TextEncoder(); - let array = encoder.encode(text); - return OS.File.writeAtomic(manifestPath, array, {tmpPath: manifestPath + ".tmp"}); - }, -}; - -EventEmitter.decorate(AppManager); diff --git a/devtools/client/webide/modules/app-projects.js b/devtools/client/webide/modules/app-projects.js deleted file mode 100644 index 691d09064..000000000 --- a/devtools/client/webide/modules/app-projects.js +++ /dev/null @@ -1,235 +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/. */ - -const {Cc, Ci, Cu, Cr} = require("chrome"); -const promise = require("promise"); - -const EventEmitter = require("devtools/shared/event-emitter"); -const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); -const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {}); - -/** - * IndexedDB wrapper that just save project objects - * - * The only constraint is that project objects have to have - * a unique `location` object. - */ - -const IDB = { - _db: null, - databaseName: "AppProjects", - - open: function () { - let deferred = promise.defer(); - - let request = indexedDB.open(IDB.databaseName, 5); - request.onerror = function (event) { - deferred.reject("Unable to open AppProjects indexedDB: " + - this.error.name + " - " + this.error.message); - }; - request.onupgradeneeded = function (event) { - let db = event.target.result; - db.createObjectStore("projects", { keyPath: "location" }); - }; - - request.onsuccess = function () { - let db = IDB._db = request.result; - let objectStore = db.transaction("projects").objectStore("projects"); - let projects = []; - let toRemove = []; - objectStore.openCursor().onsuccess = function (event) { - let cursor = event.target.result; - if (cursor) { - if (cursor.value.location) { - - // We need to make sure this object has a `.location` property. - // The UI depends on this property. - // This should not be needed as we make sure to register valid - // projects, but in the past (before bug 924568), we might have - // registered invalid objects. - - - // We also want to make sure the location is valid. - // If the location doesn't exist, we remove the project. - - try { - let file = FileUtils.File(cursor.value.location); - if (file.exists()) { - projects.push(cursor.value); - } else { - toRemove.push(cursor.value.location); - } - } catch (e) { - if (e.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH) { - // A URL - projects.push(cursor.value); - } - } - } - cursor.continue(); - } else { - let removePromises = []; - for (let location of toRemove) { - removePromises.push(IDB.remove(location)); - } - promise.all(removePromises).then(() => { - deferred.resolve(projects); - }); - } - }; - }; - - return deferred.promise; - }, - - add: function (project) { - let deferred = promise.defer(); - - if (!project.location) { - // We need to make sure this object has a `.location` property. - deferred.reject("Missing location property on project object."); - } else { - let transaction = IDB._db.transaction(["projects"], "readwrite"); - let objectStore = transaction.objectStore("projects"); - let request = objectStore.add(project); - request.onerror = function (event) { - deferred.reject("Unable to add project to the AppProjects indexedDB: " + - this.error.name + " - " + this.error.message); - }; - request.onsuccess = function () { - deferred.resolve(); - }; - } - - return deferred.promise; - }, - - update: function (project) { - let deferred = promise.defer(); - - var transaction = IDB._db.transaction(["projects"], "readwrite"); - var objectStore = transaction.objectStore("projects"); - var request = objectStore.put(project); - request.onerror = function (event) { - deferred.reject("Unable to update project to the AppProjects indexedDB: " + - this.error.name + " - " + this.error.message); - }; - request.onsuccess = function () { - deferred.resolve(); - }; - - return deferred.promise; - }, - - remove: function (location) { - let deferred = promise.defer(); - - let request = IDB._db.transaction(["projects"], "readwrite") - .objectStore("projects") - .delete(location); - request.onsuccess = function (event) { - deferred.resolve(); - }; - request.onerror = function () { - deferred.reject("Unable to delete project to the AppProjects indexedDB: " + - this.error.name + " - " + this.error.message); - }; - - return deferred.promise; - } -}; - -var loadDeferred = promise.defer(); - -loadDeferred.resolve(IDB.open().then(function (projects) { - AppProjects.projects = projects; - AppProjects.emit("ready", projects); -})); - -const AppProjects = { - load: function () { - return loadDeferred.promise; - }, - - addPackaged: function (folder) { - let file = FileUtils.File(folder.path); - if (!file.exists()) { - return promise.reject("path doesn't exist"); - } - let existingProject = this.get(folder.path); - if (existingProject) { - return promise.reject("Already added"); - } - let project = { - type: "packaged", - location: folder.path, - // We need a unique id, that is the app origin, - // in order to identify the app when being installed on the device. - // The packaged app local path is a valid id, but only on the client. - // This origin will be used to generate the true id of an app: - // its manifest URL. - // If the app ends up specifying an explicit origin in its manifest, - // we will override this random UUID on app install. - packagedAppOrigin: generateUUID().toString().slice(1, -1) - }; - return IDB.add(project).then(() => { - this.projects.push(project); - return project; - }); - }, - - addHosted: function (manifestURL) { - let existingProject = this.get(manifestURL); - if (existingProject) { - return promise.reject("Already added"); - } - let project = { - type: "hosted", - location: manifestURL - }; - return IDB.add(project).then(() => { - this.projects.push(project); - return project; - }); - }, - - update: function (project) { - return IDB.update(project); - }, - - updateLocation: function (project, newLocation) { - return IDB.remove(project.location) - .then(() => { - project.location = newLocation; - return IDB.add(project); - }); - }, - - remove: function (location) { - return IDB.remove(location).then(() => { - for (let i = 0; i < this.projects.length; i++) { - if (this.projects[i].location == location) { - this.projects.splice(i, 1); - return; - } - } - throw new Error("Unable to find project in AppProjects store"); - }); - }, - - get: function (location) { - for (let i = 0; i < this.projects.length; i++) { - if (this.projects[i].location == location) { - return this.projects[i]; - } - } - return null; - }, - - projects: [] -}; - -EventEmitter.decorate(AppProjects); - -exports.AppProjects = AppProjects; diff --git a/devtools/client/webide/modules/app-validator.js b/devtools/client/webide/modules/app-validator.js deleted file mode 100644 index 750720110..000000000 --- a/devtools/client/webide/modules/app-validator.js +++ /dev/null @@ -1,292 +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/. */ -"use strict"; - -var {Ci, Cu, CC} = require("chrome"); -const promise = require("promise"); - -const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {}); -const Services = require("Services"); -const {Task} = require("devtools/shared/task"); -var XMLHttpRequest = CC("@mozilla.org/xmlextras/xmlhttprequest;1"); -var strings = Services.strings.createBundle("chrome://devtools/locale/app-manager.properties"); - -function AppValidator({ type, location }) { - this.type = type; - this.location = location; - this.errors = []; - this.warnings = []; -} - -AppValidator.prototype.error = function (message) { - this.errors.push(message); -}; - -AppValidator.prototype.warning = function (message) { - this.warnings.push(message); -}; - -AppValidator.prototype._getPackagedManifestFile = function () { - let manifestFile = FileUtils.File(this.location); - if (!manifestFile.exists()) { - this.error(strings.GetStringFromName("validator.nonExistingFolder")); - return null; - } - if (!manifestFile.isDirectory()) { - this.error(strings.GetStringFromName("validator.expectProjectFolder")); - return null; - } - - let appManifestFile = manifestFile.clone(); - appManifestFile.append("manifest.webapp"); - - let jsonManifestFile = manifestFile.clone(); - jsonManifestFile.append("manifest.json"); - - let hasAppManifest = appManifestFile.exists() && appManifestFile.isFile(); - let hasJsonManifest = jsonManifestFile.exists() && jsonManifestFile.isFile(); - - if (!hasAppManifest && !hasJsonManifest) { - this.error(strings.GetStringFromName("validator.noManifestFile")); - return null; - } - - return hasAppManifest ? appManifestFile : jsonManifestFile; -}; - -AppValidator.prototype._getPackagedManifestURL = function () { - let manifestFile = this._getPackagedManifestFile(); - if (!manifestFile) { - return null; - } - return Services.io.newFileURI(manifestFile).spec; -}; - -AppValidator.checkManifest = function (manifestURL) { - let deferred = promise.defer(); - let error; - - let req = new XMLHttpRequest(); - req.overrideMimeType("text/plain"); - - try { - req.open("GET", manifestURL, true); - req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING; - } catch (e) { - error = strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1); - deferred.reject(error); - return deferred.promise; - } - - req.onload = function () { - let manifest = null; - try { - manifest = JSON.parse(req.responseText); - } catch (e) { - error = strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2); - deferred.reject(error); - } - - deferred.resolve({manifest, manifestURL}); - }; - - req.onerror = function () { - error = strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2); - deferred.reject(error); - }; - - try { - req.send(null); - } catch (e) { - error = strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2); - deferred.reject(error); - } - - return deferred.promise; -}; - -AppValidator.findManifestAtOrigin = function (manifestURL) { - let fixedManifest = Services.io.newURI(manifestURL, null, null).prePath + "/manifest.webapp"; - return AppValidator.checkManifest(fixedManifest); -}; - -AppValidator.findManifestPath = function (manifestURL) { - let deferred = promise.defer(); - - if (manifestURL.endsWith("manifest.webapp")) { - deferred.reject(); - } else { - let fixedManifest = manifestURL + "/manifest.webapp"; - deferred.resolve(AppValidator.checkManifest(fixedManifest)); - } - - return deferred.promise; -}; - -AppValidator.checkAlternateManifest = function (manifestURL) { - return Task.spawn(function* () { - let result; - try { - result = yield AppValidator.findManifestPath(manifestURL); - } catch (e) { - result = yield AppValidator.findManifestAtOrigin(manifestURL); - } - - return result; - }); -}; - -AppValidator.prototype._fetchManifest = function (manifestURL) { - let deferred = promise.defer(); - this.manifestURL = manifestURL; - - AppValidator.checkManifest(manifestURL) - .then(({manifest, manifestURL}) => { - deferred.resolve(manifest); - }, error => { - AppValidator.checkAlternateManifest(manifestURL) - .then(({manifest, manifestURL}) => { - this.manifestURL = manifestURL; - deferred.resolve(manifest); - }, () => { - this.error(error); - deferred.resolve(null); - }); - }); - - return deferred.promise; -}; - -AppValidator.prototype._getManifest = function () { - let manifestURL; - if (this.type == "packaged") { - manifestURL = this._getPackagedManifestURL(); - if (!manifestURL) - return promise.resolve(null); - } else if (this.type == "hosted") { - manifestURL = this.location; - try { - Services.io.newURI(manifestURL, null, null); - } catch (e) { - this.error(strings.formatStringFromName("validator.invalidHostedManifestURL", [manifestURL, e.message], 2)); - return promise.resolve(null); - } - } else { - this.error(strings.formatStringFromName("validator.invalidProjectType", [this.type], 1)); - return promise.resolve(null); - } - return this._fetchManifest(manifestURL); -}; - -AppValidator.prototype.validateManifest = function (manifest) { - if (!manifest.name) { - this.error(strings.GetStringFromName("validator.missNameManifestProperty")); - } - - if (!manifest.icons || Object.keys(manifest.icons).length === 0) { - this.warning(strings.GetStringFromName("validator.missIconsManifestProperty")); - } else if (!manifest.icons["128"]) { - this.warning(strings.GetStringFromName("validator.missIconMarketplace2")); - } -}; - -AppValidator.prototype._getOriginURL = function () { - if (this.type == "packaged") { - let manifestURL = Services.io.newURI(this.manifestURL, null, null); - return Services.io.newURI(".", null, manifestURL).spec; - } else if (this.type == "hosted") { - return Services.io.newURI(this.location, null, null).prePath; - } -}; - -AppValidator.prototype.validateLaunchPath = function (manifest) { - let deferred = promise.defer(); - // The launch_path field has to start with a `/` - if (manifest.launch_path && manifest.launch_path[0] !== "/") { - this.error(strings.formatStringFromName("validator.nonAbsoluteLaunchPath", [manifest.launch_path], 1)); - deferred.resolve(); - return deferred.promise; - } - let origin = this._getOriginURL(); - let path; - if (this.type == "packaged") { - path = "." + (manifest.launch_path || "/index.html"); - } else if (this.type == "hosted") { - path = manifest.launch_path || "/"; - } - let indexURL; - try { - indexURL = Services.io.newURI(path, null, Services.io.newURI(origin, null, null)).spec; - } catch (e) { - this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [origin + path], 1)); - deferred.resolve(); - return deferred.promise; - } - - let req = new XMLHttpRequest(); - req.overrideMimeType("text/plain"); - try { - req.open("HEAD", indexURL, true); - req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING; - } catch (e) { - this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); - deferred.resolve(); - return deferred.promise; - } - req.onload = () => { - if (req.status >= 400) - this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2)); - deferred.resolve(); - }; - req.onerror = () => { - this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); - deferred.resolve(); - }; - - try { - req.send(null); - } catch (e) { - this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); - deferred.resolve(); - } - - return deferred.promise; -}; - -AppValidator.prototype.validateType = function (manifest) { - let appType = manifest.type || "web"; - if (["web", "privileged", "certified"].indexOf(appType) === -1) { - this.error(strings.formatStringFromName("validator.invalidAppType", [appType], 1)); - } else if (this.type == "hosted" && - ["certified", "privileged"].indexOf(appType) !== -1) { - this.error(strings.formatStringFromName("validator.invalidHostedPriviledges", [appType], 1)); - } - - // certified app are not fully supported on the simulator - if (appType === "certified") { - this.warning(strings.GetStringFromName("validator.noCertifiedSupport")); - } -}; - -AppValidator.prototype.validate = function () { - this.errors = []; - this.warnings = []; - return this._getManifest(). - then((manifest) => { - if (manifest) { - this.manifest = manifest; - - // Skip validations for add-ons - if (manifest.role === "addon" || manifest.manifest_version) { - return promise.resolve(); - } - - this.validateManifest(manifest); - this.validateType(manifest); - return this.validateLaunchPath(manifest); - } - }); -}; - -exports.AppValidator = AppValidator; diff --git a/devtools/client/webide/modules/build.js b/devtools/client/webide/modules/build.js deleted file mode 100644 index 34cbcc0b7..000000000 --- a/devtools/client/webide/modules/build.js +++ /dev/null @@ -1,199 +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/. */ - -const {Cu, Cc, Ci} = require("chrome"); - -const promise = require("promise"); -const { Task } = require("devtools/shared/task"); -const { TextDecoder, OS } = Cu.import("resource://gre/modules/osfile.jsm", {}); -const Subprocess = require("sdk/system/child_process/subprocess"); - -const ProjectBuilding = exports.ProjectBuilding = { - fetchPackageManifest: Task.async(function* (project) { - let manifestPath = OS.Path.join(project.location, "package.json"); - let exists = yield OS.File.exists(manifestPath); - if (!exists) { - // No explicit manifest, try to generate one if possible - return this.generatePackageManifest(project); - } - - let data = yield OS.File.read(manifestPath); - data = new TextDecoder().decode(data); - let manifest; - try { - manifest = JSON.parse(data); - } catch (e) { - throw new Error("Error while reading WebIDE manifest at: '" + manifestPath + - "', invalid JSON: " + e.message); - } - return manifest; - }), - - /** - * For common frameworks in the community, attempt to detect the build - * settings if none are defined. This makes it much easier to get started - * with WebIDE. Later on, perhaps an add-on could define such things for - * different frameworks. - */ - generatePackageManifest: Task.async(function* (project) { - // Cordova - let cordovaConfigPath = OS.Path.join(project.location, "config.xml"); - let exists = yield OS.File.exists(cordovaConfigPath); - if (!exists) { - return; - } - let data = yield OS.File.read(cordovaConfigPath); - data = new TextDecoder().decode(data); - if (data.contains("cordova.apache.org")) { - return { - "webide": { - "prepackage": "cordova prepare", - "packageDir": "./platforms/firefoxos/www" - } - }; - } - }), - - hasPrepackage: Task.async(function* (project) { - let manifest = yield ProjectBuilding.fetchPackageManifest(project); - return manifest && manifest.webide && "prepackage" in manifest.webide; - }), - - // If the app depends on some build step, run it before pushing the app - build: Task.async(function* ({ project, logger }) { - if (!(yield this.hasPrepackage(project))) { - return; - } - - let manifest = yield ProjectBuilding.fetchPackageManifest(project); - - logger("start"); - try { - yield this._build(project, manifest, logger); - logger("succeed"); - } catch (e) { - logger("failed", e); - } - }), - - _build: Task.async(function* (project, manifest, logger) { - // Look for `webide` property - manifest = manifest.webide; - - let command, cwd, args = [], env = []; - - // Copy frequently used env vars - let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); - ["HOME", "PATH"].forEach(key => { - let value = envService.get(key); - if (value) { - env.push(key + "=" + value); - } - }); - - if (typeof (manifest.prepackage) === "string") { - command = manifest.prepackage.replace(/%project%/g, project.location); - } else if (manifest.prepackage.command) { - command = manifest.prepackage.command; - - args = manifest.prepackage.args || []; - args = args.map(a => a.replace(/%project%/g, project.location)); - - env = env.concat(manifest.prepackage.env || []); - env = env.map(a => a.replace(/%project%/g, project.location)); - - if (manifest.prepackage.cwd) { - // Normalize path for Windows support (converts / to \) - let path = OS.Path.normalize(manifest.prepackage.cwd); - // Note that Path.join also support absolute path and argument. - // So that if cwd is absolute, it will return cwd. - let rel = OS.Path.join(project.location, path); - let exists = yield OS.File.exists(rel); - if (exists) { - cwd = rel; - } - } - } else { - throw new Error("pre-package manifest is invalid, missing or invalid " + - "`prepackage` attribute"); - } - - if (!cwd) { - cwd = project.location; - } - - logger("Running pre-package hook '" + command + "' " + - args.join(" ") + - " with ENV=[" + env.join(", ") + "]" + - " at " + cwd); - - // Run the command through a shell command in order to support non absolute - // paths. - // On Windows `ComSpec` env variable is going to refer to cmd.exe, - // Otherwise, on Linux and Mac, SHELL env variable should refer to - // the user chosen shell program. - // (We do not check for OS, as on windows, with cygwin, ComSpec isn't set) - let shell = envService.get("ComSpec") || envService.get("SHELL"); - args.unshift(command); - - // For cmd.exe, we have to pass the `/C` option, - // but for unix shells we need -c. - // That to interpret next argument as a shell command. - if (envService.exists("ComSpec")) { - args.unshift("/C"); - } else { - args.unshift("-c"); - } - - // Subprocess changes CWD, we have to save and restore it. - let originalCwd = yield OS.File.getCurrentDirectory(); - try { - let defer = promise.defer(); - Subprocess.call({ - command: shell, - arguments: args, - environment: env, - workdir: cwd, - - stdout: data => - logger(data), - stderr: data => - logger(data), - - done: result => { - logger("Terminated with error code: " + result.exitCode); - if (result.exitCode == 0) { - defer.resolve(); - } else { - defer.reject("pre-package command failed with error code " + result.exitCode); - } - } - }); - defer.promise.then(() => { - OS.File.setCurrentDirectory(originalCwd); - }); - yield defer.promise; - } catch (e) { - throw new Error("Unable to run pre-package command '" + command + "' " + - args.join(" ") + ":\n" + (e.message || e)); - } - }), - - getPackageDir: Task.async(function* (project) { - let manifest = yield ProjectBuilding.fetchPackageManifest(project); - if (!manifest || !manifest.webide || !manifest.webide.packageDir) { - return project.location; - } - manifest = manifest.webide; - - let packageDir = OS.Path.join(project.location, manifest.packageDir); - // On Windows, replace / by \\ - packageDir = OS.Path.normalize(packageDir); - let exists = yield OS.File.exists(packageDir); - if (exists) { - return packageDir; - } - throw new Error("Unable to resolve application package directory: '" + manifest.packageDir + "'"); - }) -}; diff --git a/devtools/client/webide/modules/config-view.js b/devtools/client/webide/modules/config-view.js deleted file mode 100644 index 5fb07e235..000000000 --- a/devtools/client/webide/modules/config-view.js +++ /dev/null @@ -1,373 +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/. */ - -const {Cu} = require("chrome"); - -const EventEmitter = require("devtools/shared/event-emitter"); -const Services = require("Services"); -const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); - -var ConfigView; - -module.exports = ConfigView = function (window) { - EventEmitter.decorate(this); - this._doc = window.document; - this._keys = []; - return this; -}; - -ConfigView.prototype = { - _renderByType: function (input, name, value, customType) { - value = customType || typeof value; - - switch (value) { - case "boolean": - input.setAttribute("data-type", "boolean"); - input.setAttribute("type", "checkbox"); - break; - case "number": - input.setAttribute("data-type", "number"); - input.setAttribute("type", "number"); - break; - case "object": - input.setAttribute("data-type", "object"); - input.setAttribute("type", "text"); - break; - default: - input.setAttribute("data-type", "string"); - input.setAttribute("type", "text"); - break; - } - return input; - }, - - set front(front) { - this._front = front; - }, - - set keys(keys) { - this._keys = keys; - }, - - get keys() { - return this._keys; - }, - - set kind(kind) { - this._kind = kind; - }, - - set includeTypeName(include) { - this._includeTypeName = include; - }, - - search: function (event) { - if (event.target.value.length) { - let stringMatch = new RegExp(event.target.value, "i"); - - for (let i = 0; i < this._keys.length; i++) { - let key = this._keys[i]; - let row = this._doc.getElementById("row-" + key); - if (key.match(stringMatch)) { - row.classList.remove("hide"); - } else if (row) { - row.classList.add("hide"); - } - } - } else { - var trs = this._doc.getElementById("device-fields").querySelectorAll("tr"); - - for (let i = 0; i < trs.length; i++) { - trs[i].classList.remove("hide"); - } - } - }, - - generateDisplay: function (json) { - let deviceItems = Object.keys(json); - deviceItems.sort(); - this.keys = deviceItems; - for (let i = 0; i < this.keys.length; i++) { - let key = this.keys[i]; - this.generateField(key, json[key].value, json[key].hasUserValue); - } - }, - - generateField: function (name, value, hasUserValue, customType, newRow) { - let table = this._doc.querySelector("table"); - let sResetDefault = Strings.GetStringFromName("device_reset_default"); - - if (this._keys.indexOf(name) === -1) { - this._keys.push(name); - } - - let input = this._doc.createElement("input"); - let tr = this._doc.createElement("tr"); - tr.setAttribute("id", "row-" + name); - tr.classList.add("edit-row"); - let td = this._doc.createElement("td"); - td.classList.add("field-name"); - td.textContent = name; - tr.appendChild(td); - td = this._doc.createElement("td"); - input.classList.add("editable"); - input.setAttribute("id", name); - input = this._renderByType(input, name, value, customType); - - if (customType === "boolean" || input.type === "checkbox") { - input.checked = value; - } else { - if (typeof value === "object") { - value = JSON.stringify(value); - } - input.value = value; - } - - if (!(this._includeTypeName || isNaN(parseInt(value, 10)))) { - input.type = "number"; - } - - td.appendChild(input); - tr.appendChild(td); - td = this._doc.createElement("td"); - td.setAttribute("id", "td-" + name); - - let button = this._doc.createElement("button"); - button.setAttribute("data-id", name); - button.setAttribute("id", "btn-" + name); - button.classList.add("reset"); - button.textContent = sResetDefault; - td.appendChild(button); - - if (!hasUserValue) { - button.classList.add("hide"); - } - - tr.appendChild(td); - - // If this is a new field, add it to the top of the table. - if (newRow) { - let existing = table.querySelector("#" + name); - - if (!existing) { - table.insertBefore(tr, newRow); - } else { - existing.value = value; - } - } else { - table.appendChild(tr); - } - }, - - resetTable: function () { - let table = this._doc.querySelector("table"); - let trs = table.querySelectorAll("tr:not(#add-custom-field)"); - - for (var i = 0; i < trs.length; i++) { - table.removeChild(trs[i]); - } - - return table; - }, - - _getCallType: function (type, name) { - let frontName = "get"; - - if (this._includeTypeName) { - frontName += type; - } - - return this._front[frontName + this._kind](name); - }, - - _setCallType: function (type, name, value) { - let frontName = "set"; - - if (this._includeTypeName) { - frontName += type; - } - - return this._front[frontName + this._kind](name, value); - }, - - _saveByType: function (options) { - let fieldName = options.id; - let inputType = options.type; - let value = options.value; - let input = this._doc.getElementById(fieldName); - - switch (inputType) { - case "boolean": - this._setCallType("Bool", fieldName, input.checked); - break; - case "number": - this._setCallType("Int", fieldName, value); - break; - case "object": - try { - value = JSON.parse(value); - } catch (e) {} - this._setCallType("Object", fieldName, value); - break; - default: - this._setCallType("Char", fieldName, value); - break; - } - }, - - updateField: function (event) { - if (event.target) { - let inputType = event.target.getAttribute("data-type"); - let inputValue = event.target.checked || event.target.value; - - if (event.target.nodeName == "input" && - event.target.validity.valid && - event.target.classList.contains("editable")) { - let id = event.target.id; - if (inputType === "boolean") { - if (event.target.checked) { - inputValue = true; - } else { - inputValue = false; - } - } - - this._saveByType({ - id: id, - type: inputType, - value: inputValue - }); - this._doc.getElementById("btn-" + id).classList.remove("hide"); - } - } - }, - - _resetToDefault: function (name, input, button) { - this._front["clearUser" + this._kind](name); - let dataType = input.getAttribute("data-type"); - let tr = this._doc.getElementById("row-" + name); - - switch (dataType) { - case "boolean": - this._defaultField = this._getCallType("Bool", name); - this._defaultField.then(boolean => { - input.checked = boolean; - }, () => { - input.checked = false; - tr.parentNode.removeChild(tr); - }); - break; - case "number": - this._defaultField = this._getCallType("Int", name); - this._defaultField.then(number => { - input.value = number; - }, () => { - tr.parentNode.removeChild(tr); - }); - break; - case "object": - this._defaultField = this._getCallType("Object", name); - this._defaultField.then(object => { - input.value = JSON.stringify(object); - }, () => { - tr.parentNode.removeChild(tr); - }); - break; - default: - this._defaultField = this._getCallType("Char", name); - this._defaultField.then(string => { - input.value = string; - }, () => { - tr.parentNode.removeChild(tr); - }); - break; - } - - button.classList.add("hide"); - }, - - checkReset: function (event) { - if (event.target.classList.contains("reset")) { - let btnId = event.target.getAttribute("data-id"); - let input = this._doc.getElementById(btnId); - this._resetToDefault(btnId, input, event.target); - } - }, - - updateFieldType: function () { - let table = this._doc.querySelector("table"); - let customValueType = table.querySelector("#custom-value-type").value; - let customTextEl = table.querySelector("#custom-value-text"); - let customText = customTextEl.value; - - if (customValueType.length === 0) { - return false; - } - - switch (customValueType) { - case "boolean": - customTextEl.type = "checkbox"; - customText = customTextEl.checked; - break; - case "number": - customText = parseInt(customText, 10) || 0; - customTextEl.type = "number"; - break; - default: - customTextEl.type = "text"; - break; - } - - return customValueType; - }, - - clearNewFields: function () { - let table = this._doc.querySelector("table"); - let customTextEl = table.querySelector("#custom-value-text"); - if (customTextEl.checked) { - customTextEl.checked = false; - } else { - customTextEl.value = ""; - } - - this.updateFieldType(); - }, - - updateNewField: function () { - let table = this._doc.querySelector("table"); - let customValueType = this.updateFieldType(); - - if (!customValueType) { - return; - } - - let customRow = table.querySelector("tr:nth-of-type(2)"); - let customTextEl = table.querySelector("#custom-value-text"); - let customTextNameEl = table.querySelector("#custom-value-name"); - - if (customTextEl.validity.valid) { - let customText = customTextEl.value; - - if (customValueType === "boolean") { - customText = customTextEl.checked; - } - - let customTextName = customTextNameEl.value.replace(/[^A-Za-z0-9\.\-_]/gi, ""); - this.generateField(customTextName, customText, true, customValueType, customRow); - this._saveByType({ - id: customTextName, - type: customValueType, - value: customText - }); - customTextNameEl.value = ""; - this.clearNewFields(); - } - }, - - checkNewFieldSubmit: function (event) { - if (event.keyCode === 13) { - this._doc.getElementById("custom-value").click(); - } - } -}; diff --git a/devtools/client/webide/modules/moz.build b/devtools/client/webide/modules/moz.build deleted file mode 100644 index c4072b703..000000000 --- a/devtools/client/webide/modules/moz.build +++ /dev/null @@ -1,21 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -DevToolsModules( - 'addons.js', - 'app-manager.js', - 'app-projects.js', - 'app-validator.js', - 'build.js', - 'config-view.js', - 'project-list.js', - 'runtime-list.js', - 'runtimes.js', - 'simulator-process.js', - 'simulators.js', - 'tab-store.js', - 'utils.js' -) diff --git a/devtools/client/webide/modules/project-list.js b/devtools/client/webide/modules/project-list.js deleted file mode 100644 index 10766dd4f..000000000 --- a/devtools/client/webide/modules/project-list.js +++ /dev/null @@ -1,375 +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/. */ - -const {Cu} = require("chrome"); - -const Services = require("Services"); -const {AppProjects} = require("devtools/client/webide/modules/app-projects"); -const {AppManager} = require("devtools/client/webide/modules/app-manager"); -const promise = require("promise"); -const EventEmitter = require("devtools/shared/event-emitter"); -const {Task} = require("devtools/shared/task"); -const utils = require("devtools/client/webide/modules/utils"); -const Telemetry = require("devtools/client/shared/telemetry"); - -const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); - -var ProjectList; - -module.exports = ProjectList = function (win, parentWindow) { - EventEmitter.decorate(this); - this._doc = win.document; - this._UI = parentWindow.UI; - this._parentWindow = parentWindow; - this._telemetry = new Telemetry(); - this._panelNodeEl = "div"; - - this.onWebIDEUpdate = this.onWebIDEUpdate.bind(this); - this._UI.on("webide-update", this.onWebIDEUpdate); - - AppManager.init(); - this.appManagerUpdate = this.appManagerUpdate.bind(this); - AppManager.on("app-manager-update", this.appManagerUpdate); -}; - -ProjectList.prototype = { - get doc() { - return this._doc; - }, - - 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 "project-removed": - case "runtime-apps-icons": - case "runtime-targets": - case "connection": - this.update(details); - break; - case "project": - this.updateCommands(); - this.update(details); - break; - } - }, - - onWebIDEUpdate: function (event, what, details) { - if (what == "busy" || what == "unbusy") { - this.updateCommands(); - } - }, - - /** - * 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; - let self = this; - 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; - - self._telemetry.actionOccurred("webideNewProject"); - }), "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) { - let span = opts.panel.querySelector("span") || this._doc.createElement("span"); - span.textContent = opts.name; - let icon = opts.panel.querySelector("img") || this._doc.createElement("img"); - icon.className = "project-image"; - icon.setAttribute("src", opts.icon); - opts.panel.appendChild(icon); - opts.panel.appendChild(span); - opts.panel.setAttribute("title", opts.name); - }, - - refreshTabs: function () { - if (AppManager.connected) { - return AppManager.listTabs().then(() => { - this.updateTabs(); - }).catch(console.error); - } - }, - - updateTabs: function () { - let tabsHeaderNode = this._doc.querySelector("#panel-header-tabs"); - let tabsNode = this._doc.querySelector("#project-panel-tabs"); - - while (tabsNode.hasChildNodes()) { - tabsNode.firstChild.remove(); - } - - if (!AppManager.connected) { - tabsHeaderNode.setAttribute("hidden", "true"); - return; - } - - let tabs = AppManager.tabStore.tabs; - - tabsHeaderNode.removeAttribute("hidden"); - - 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). - if (url.origin) { - 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 || AppManager.DEFAULT_PROJECT_ICON - }); - panelItemNode.addEventListener("click", () => { - AppManager.selectedProject = { - type: "tab", - app: tab, - icon: tab.favicon || AppManager.DEFAULT_PROJECT_ICON, - location: tab.url, - name: tab.name - }; - }, true); - } - - return promise.resolve(); - }, - - updateApps: function () { - let doc = this._doc; - 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", () => { - 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 || AppManager.DEFAULT_PROJECT_ICON - }); - runtimeAppsNode.appendChild(panelItemNode); - panelItemNode.addEventListener("click", () => { - AppManager.selectedProject = { - type: "runtimeApp", - app: app.manifest, - icon: app.iconURL || AppManager.DEFAULT_PROJECT_ICON, - name: app.manifest.name - }; - }, true); - } - - return promise.resolve(); - }, - - updateCommands: function () { - let doc = this._doc; - let newAppCmd; - let packagedAppCmd; - let hostedAppCmd; - - newAppCmd = doc.querySelector("#new-app"); - packagedAppCmd = doc.querySelector("#packaged-app"); - hostedAppCmd = doc.querySelector("#hosted-app"); - - if (!newAppCmd || !packagedAppCmd || !hostedAppCmd) { - return; - } - - if (this._parentWindow.document.querySelector("window").classList.contains("busy")) { - newAppCmd.setAttribute("disabled", "true"); - packagedAppCmd.setAttribute("disabled", "true"); - hostedAppCmd.setAttribute("disabled", "true"); - return; - } - - newAppCmd.removeAttribute("disabled"); - packagedAppCmd.removeAttribute("disabled"); - hostedAppCmd.removeAttribute("disabled"); - }, - - /** - * Trigger an update of the project and remote runtime list. - * @param options object (optional) - * An |options| object containing a type of |apps| or |tabs| will limit - * what is updated to only those sections. - */ - update: function (options) { - let deferred = promise.defer(); - - if (options && options.type === "apps") { - return this.updateApps(); - } else if (options && options.type === "tabs") { - return this.updateTabs(); - } - - let doc = this._doc; - let projectsNode = doc.querySelector("#project-panel-projects"); - - while (projectsNode.hasChildNodes()) { - projectsNode.firstChild.remove(); - } - - AppProjects.load().then(() => { - let projects = AppProjects.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); - if (!project.validationStatus) { - // 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 - }); - }); - } else { - this._renderProjectItem({ - panel: panelItemNode, - name: project.name || AppManager.DEFAULT_PROJECT_NAME, - icon: project.icon || AppManager.DEFAULT_PROJECT_ICON - }); - } - panelItemNode.addEventListener("click", () => { - AppManager.selectedProject = project; - }, true); - } - - deferred.resolve(); - }, deferred.reject); - - // List remote apps and the main process, if they exist - this.updateApps(); - - // Build the tab list right now, so it's fast... - this.updateTabs(); - - // 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.updateTabs(); - }).catch(console.error); - } - - return deferred.promise; - }, - - destroy: function () { - this._doc = null; - AppManager.off("app-manager-update", this.appManagerUpdate); - this._UI.off("webide-update", this.onWebIDEUpdate); - this._UI = null; - this._parentWindow = null; - this._panelNodeEl = null; - } -}; diff --git a/devtools/client/webide/modules/runtime-list.js b/devtools/client/webide/modules/runtime-list.js deleted file mode 100644 index 295dd1705..000000000 --- a/devtools/client/webide/modules/runtime-list.js +++ /dev/null @@ -1,207 +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/. */ - -"use strict"; - -const Services = require("Services"); -const {AppManager} = require("devtools/client/webide/modules/app-manager"); -const EventEmitter = require("devtools/shared/event-emitter"); -const {RuntimeScanners, WiFiScanner} = require("devtools/client/webide/modules/runtimes"); -const {Devices} = require("resource://devtools/shared/apps/Devices.jsm"); -const {Task} = require("devtools/shared/task"); -const utils = require("devtools/client/webide/modules/utils"); - -const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); - -var RuntimeList; - -module.exports = RuntimeList = function (window, parentWindow) { - EventEmitter.decorate(this); - this._doc = window.document; - this._UI = parentWindow.UI; - this._Cmds = parentWindow.Cmds; - this._parentWindow = parentWindow; - this._panelNodeEl = "button"; - this._panelBoxEl = "div"; - - this.onWebIDEUpdate = this.onWebIDEUpdate.bind(this); - this._UI.on("webide-update", this.onWebIDEUpdate); - - AppManager.init(); - this.appManagerUpdate = this.appManagerUpdate.bind(this); - AppManager.on("app-manager-update", this.appManagerUpdate); -}; - -RuntimeList.prototype = { - get doc() { - return this._doc; - }, - - 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.update(); - break; - case "connection": - case "runtime-global-actors": - this.updateCommands(); - break; - } - }, - - onWebIDEUpdate: function (event, what, details) { - if (what == "busy" || what == "unbusy") { - this.updateCommands(); - } - }, - - takeScreenshot: function () { - this._Cmds.takeScreenshot(); - }, - - showRuntimeDetails: function () { - this._Cmds.showRuntimeDetails(); - }, - - showPermissionsTable: function () { - this._Cmds.showPermissionsTable(); - }, - - showDevicePreferences: function () { - this._Cmds.showDevicePrefs(); - }, - - showSettings: function () { - this._Cmds.showSettings(); - }, - - showTroubleShooting: function () { - this._Cmds.showTroubleShooting(); - }, - - showAddons: function () { - this._Cmds.showAddons(); - }, - - refreshScanners: function () { - RuntimeScanners.scan(); - }, - - updateCommands: function () { - let doc = this._doc; - - // Runtime commands - let screenshotCmd = doc.querySelector("#runtime-screenshot"); - let permissionsCmd = doc.querySelector("#runtime-permissions"); - let detailsCmd = doc.querySelector("#runtime-details"); - let disconnectCmd = doc.querySelector("#runtime-disconnect"); - let devicePrefsCmd = doc.querySelector("#runtime-preferences"); - let settingsCmd = doc.querySelector("#runtime-settings"); - - if (AppManager.connected) { - if (AppManager.deviceFront) { - 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 { - detailsCmd.setAttribute("disabled", "true"); - permissionsCmd.setAttribute("disabled", "true"); - screenshotCmd.setAttribute("disabled", "true"); - disconnectCmd.setAttribute("disabled", "true"); - devicePrefsCmd.setAttribute("disabled", "true"); - settingsCmd.setAttribute("disabled", "true"); - } - }, - - update: function () { - let doc = this._doc; - let wifiHeaderNode = doc.querySelector("#runtime-header-wifi"); - - if (WiFiScanner.allowed) { - wifiHeaderNode.removeAttribute("hidden"); - } else { - wifiHeaderNode.setAttribute("hidden", "true"); - } - - let usbListNode = doc.querySelector("#runtime-panel-usb"); - let wifiListNode = doc.querySelector("#runtime-panel-wifi"); - let simulatorListNode = doc.querySelector("#runtime-panel-simulator"); - let otherListNode = doc.querySelector("#runtime-panel-other"); - let noHelperNode = doc.querySelector("#runtime-panel-noadbhelper"); - let noUSBNode = doc.querySelector("#runtime-panel-nousbdevice"); - - if (Devices.helperAddonInstalled) { - noHelperNode.setAttribute("hidden", "true"); - } else { - noHelperNode.removeAttribute("hidden"); - } - - let runtimeList = AppManager.runtimeList; - - if (!runtimeList) { - return; - } - - if (runtimeList.usb.length === 0 && Devices.helperAddonInstalled) { - noUSBNode.removeAttribute("hidden"); - } else { - noUSBNode.setAttribute("hidden", "true"); - } - - for (let [type, parent] of [ - ["usb", usbListNode], - ["wifi", wifiListNode], - ["simulator", simulatorListNode], - ["other", otherListNode], - ]) { - while (parent.hasChildNodes()) { - parent.firstChild.remove(); - } - for (let runtime of runtimeList[type]) { - let r = runtime; - let panelItemNode = doc.createElement(this._panelBoxEl); - panelItemNode.className = "panel-item-complex"; - - let connectButton = doc.createElement(this._panelNodeEl); - connectButton.className = "panel-item runtime-panel-item-" + type; - connectButton.textContent = r.name; - - connectButton.addEventListener("click", () => { - this._UI.dismissErrorNotification(); - this._UI.connectToRuntime(r); - }, true); - panelItemNode.appendChild(connectButton); - - if (r.configure) { - let configButton = doc.createElement(this._panelNodeEl); - configButton.className = "configure-button"; - configButton.addEventListener("click", r.configure.bind(r), true); - panelItemNode.appendChild(configButton); - } - - parent.appendChild(panelItemNode); - } - } - }, - - destroy: function () { - this._doc = null; - AppManager.off("app-manager-update", this.appManagerUpdate); - this._UI.off("webide-update", this.onWebIDEUpdate); - this._UI = null; - this._Cmds = null; - this._parentWindow = null; - this._panelNodeEl = null; - } -}; diff --git a/devtools/client/webide/modules/runtimes.js b/devtools/client/webide/modules/runtimes.js deleted file mode 100644 index a23337359..000000000 --- a/devtools/client/webide/modules/runtimes.js +++ /dev/null @@ -1,673 +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/. */ - -"use strict"; - -const {Ci} = require("chrome"); -const Services = require("Services"); -const {Devices} = require("resource://devtools/shared/apps/Devices.jsm"); -const {Connection} = require("devtools/shared/client/connection-manager"); -const {DebuggerServer} = require("devtools/server/main"); -const {Simulators} = require("devtools/client/webide/modules/simulators"); -const discovery = require("devtools/shared/discovery/discovery"); -const EventEmitter = require("devtools/shared/event-emitter"); -const promise = require("promise"); -loader.lazyRequireGetter(this, "AuthenticationResult", - "devtools/shared/security/auth", true); -loader.lazyRequireGetter(this, "DevToolsUtils", - "devtools/shared/DevToolsUtils"); - -const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); - -/** - * Runtime and Scanner API - * - * |RuntimeScanners| maintains a set of |Scanner| objects that produce one or - * more |Runtime|s to connect to. Add-ons can extend the set of known runtimes - * by registering additional |Scanner|s that emit them. - * - * Each |Scanner| must support the following API: - * - * enable() - * Bind any event handlers and start any background work the scanner needs to - * maintain an updated set of |Runtime|s. - * Called when the first consumer (such as WebIDE) actively interested in - * maintaining the |Runtime| list enables the registry. - * disable() - * Unbind any event handlers and stop any background work the scanner needs to - * maintain an updated set of |Runtime|s. - * Called when the last consumer (such as WebIDE) actively interested in - * maintaining the |Runtime| list disables the registry. - * emits "runtime-list-updated" - * If the set of runtimes a |Scanner| manages has changed, it must emit this - * event to notify consumers of changes. - * scan() - * Actively refreshes the list of runtimes the scanner knows about. If your - * scanner uses an active scanning approach (as opposed to listening for - * events when changes occur), the bulk of the work would be done here. - * @return Promise - * Should be resolved when scanning is complete. If scanning has no - * well-defined end point, you can resolve immediately, as long as - * update event is emitted later when changes are noticed. - * listRuntimes() - * Return the current list of runtimes known to the |Scanner| instance. - * @return Iterable - * - * Each |Runtime| must support the following API: - * - * |type| field - * The |type| must be one of the values from the |RuntimeTypes| object. This - * is used for Telemetry and to support displaying sets of |Runtime|s - * categorized by type. - * |id| field - * An identifier that is unique in the set of all runtimes with the same - * |type|. WebIDE tries to save the last used runtime via type + id, and - * tries to locate it again in the next session, so this value should attempt - * to be stable across Firefox sessions. - * |name| field - * A user-visible label to identify the runtime that will be displayed in a - * runtime list. - * |prolongedConnection| field - * A boolean value which should be |true| if the connection process is - * expected to take a unknown or large amount of time. A UI may use this as a - * hint to skip timeouts or other time-based code paths. - * connect() - * Configure the passed |connection| object with any settings need to - * successfully connect to the runtime, and call the |connection|'s connect() - * method. - * @param Connection connection - * A |Connection| object from the DevTools |ConnectionManager|. - * @return Promise - * Resolved once you've called the |connection|'s connect() method. - * configure() OPTIONAL - * Show a configuration screen if the runtime is configurable. - */ - -/* SCANNER REGISTRY */ - -var RuntimeScanners = { - - _enabledCount: 0, - _scanners: new Set(), - - get enabled() { - return !!this._enabledCount; - }, - - add(scanner) { - if (this.enabled) { - // Enable any scanner added while globally enabled - this._enableScanner(scanner); - } - this._scanners.add(scanner); - this._emitUpdated(); - }, - - remove(scanner) { - this._scanners.delete(scanner); - if (this.enabled) { - // Disable any scanner removed while globally enabled - this._disableScanner(scanner); - } - this._emitUpdated(); - }, - - has(scanner) { - return this._scanners.has(scanner); - }, - - scan() { - if (!this.enabled) { - return promise.resolve(); - } - - if (this._scanPromise) { - return this._scanPromise; - } - - let promises = []; - - for (let scanner of this._scanners) { - promises.push(scanner.scan()); - } - - this._scanPromise = promise.all(promises); - - // Reset pending promise - this._scanPromise.then(() => { - this._scanPromise = null; - }, () => { - this._scanPromise = null; - }); - - return this._scanPromise; - }, - - listRuntimes: function* () { - for (let scanner of this._scanners) { - for (let runtime of scanner.listRuntimes()) { - yield runtime; - } - } - }, - - _emitUpdated() { - this.emit("runtime-list-updated"); - }, - - enable() { - if (this._enabledCount++ !== 0) { - // Already enabled scanners during a previous call - return; - } - this._emitUpdated = this._emitUpdated.bind(this); - for (let scanner of this._scanners) { - this._enableScanner(scanner); - } - }, - - _enableScanner(scanner) { - scanner.enable(); - scanner.on("runtime-list-updated", this._emitUpdated); - }, - - disable() { - if (--this._enabledCount !== 0) { - // Already disabled scanners during a previous call - return; - } - for (let scanner of this._scanners) { - this._disableScanner(scanner); - } - }, - - _disableScanner(scanner) { - scanner.off("runtime-list-updated", this._emitUpdated); - scanner.disable(); - }, - -}; - -EventEmitter.decorate(RuntimeScanners); - -exports.RuntimeScanners = RuntimeScanners; - -/* SCANNERS */ - -var SimulatorScanner = { - - _runtimes: [], - - enable() { - this._updateRuntimes = this._updateRuntimes.bind(this); - Simulators.on("updated", this._updateRuntimes); - this._updateRuntimes(); - }, - - disable() { - Simulators.off("updated", this._updateRuntimes); - }, - - _emitUpdated() { - this.emit("runtime-list-updated"); - }, - - _updateRuntimes() { - Simulators.findSimulators().then(simulators => { - this._runtimes = []; - for (let simulator of simulators) { - this._runtimes.push(new SimulatorRuntime(simulator)); - } - this._emitUpdated(); - }); - }, - - scan() { - return promise.resolve(); - }, - - listRuntimes: function () { - return this._runtimes; - } - -}; - -EventEmitter.decorate(SimulatorScanner); -RuntimeScanners.add(SimulatorScanner); - -/** - * TODO: Remove this comaptibility layer in the future (bug 1085393) - * This runtime exists to support the ADB Helper add-on below version 0.7.0. - * - * This scanner will list all ADB devices as runtimes, even if they may or may - * not actually connect (since the |DeprecatedUSBRuntime| assumes a Firefox OS - * device). - */ -var DeprecatedAdbScanner = { - - _runtimes: [], - - enable() { - this._updateRuntimes = this._updateRuntimes.bind(this); - Devices.on("register", this._updateRuntimes); - Devices.on("unregister", this._updateRuntimes); - Devices.on("addon-status-updated", this._updateRuntimes); - this._updateRuntimes(); - }, - - disable() { - Devices.off("register", this._updateRuntimes); - Devices.off("unregister", this._updateRuntimes); - Devices.off("addon-status-updated", this._updateRuntimes); - }, - - _emitUpdated() { - this.emit("runtime-list-updated"); - }, - - _updateRuntimes() { - this._runtimes = []; - for (let id of Devices.available()) { - let runtime = new DeprecatedUSBRuntime(id); - this._runtimes.push(runtime); - runtime.updateNameFromADB().then(() => { - this._emitUpdated(); - }, () => {}); - } - this._emitUpdated(); - }, - - scan() { - return promise.resolve(); - }, - - listRuntimes: function () { - return this._runtimes; - } - -}; - -EventEmitter.decorate(DeprecatedAdbScanner); -RuntimeScanners.add(DeprecatedAdbScanner); - -// ADB Helper 0.7.0 and later will replace this scanner on startup -exports.DeprecatedAdbScanner = DeprecatedAdbScanner; - -/** - * This is a lazy ADB scanner shim which only tells the ADB Helper to start and - * stop as needed. The real scanner that lists devices lives in ADB Helper. - * ADB Helper 0.8.0 and later wait until these signals are received before - * starting ADB polling. For earlier versions, they have no effect. - */ -var LazyAdbScanner = { - - enable() { - Devices.emit("adb-start-polling"); - }, - - disable() { - Devices.emit("adb-stop-polling"); - }, - - scan() { - return promise.resolve(); - }, - - listRuntimes: function () { - return []; - } - -}; - -EventEmitter.decorate(LazyAdbScanner); -RuntimeScanners.add(LazyAdbScanner); - -var WiFiScanner = { - - _runtimes: [], - - init() { - this.updateRegistration(); - Services.prefs.addObserver(this.ALLOWED_PREF, this, false); - }, - - enable() { - this._updateRuntimes = this._updateRuntimes.bind(this); - discovery.on("devtools-device-added", this._updateRuntimes); - discovery.on("devtools-device-updated", this._updateRuntimes); - discovery.on("devtools-device-removed", this._updateRuntimes); - this._updateRuntimes(); - }, - - disable() { - discovery.off("devtools-device-added", this._updateRuntimes); - discovery.off("devtools-device-updated", this._updateRuntimes); - discovery.off("devtools-device-removed", this._updateRuntimes); - }, - - _emitUpdated() { - this.emit("runtime-list-updated"); - }, - - _updateRuntimes() { - this._runtimes = []; - for (let device of discovery.getRemoteDevicesWithService("devtools")) { - this._runtimes.push(new WiFiRuntime(device)); - } - this._emitUpdated(); - }, - - scan() { - discovery.scan(); - return promise.resolve(); - }, - - listRuntimes: function () { - return this._runtimes; - }, - - ALLOWED_PREF: "devtools.remote.wifi.scan", - - get allowed() { - return Services.prefs.getBoolPref(this.ALLOWED_PREF); - }, - - updateRegistration() { - if (this.allowed) { - RuntimeScanners.add(WiFiScanner); - } else { - RuntimeScanners.remove(WiFiScanner); - } - this._emitUpdated(); - }, - - observe(subject, topic, data) { - if (data !== WiFiScanner.ALLOWED_PREF) { - return; - } - WiFiScanner.updateRegistration(); - } - -}; - -EventEmitter.decorate(WiFiScanner); -WiFiScanner.init(); - -exports.WiFiScanner = WiFiScanner; - -var StaticScanner = { - enable() {}, - disable() {}, - scan() { return promise.resolve(); }, - listRuntimes() { - let runtimes = [gRemoteRuntime]; - if (Services.prefs.getBoolPref("devtools.webide.enableLocalRuntime")) { - runtimes.push(gLocalRuntime); - } - return runtimes; - } -}; - -EventEmitter.decorate(StaticScanner); -RuntimeScanners.add(StaticScanner); - -/* RUNTIMES */ - -// These type strings are used for logging events to Telemetry. -// You must update Histograms.json if new types are added. -var RuntimeTypes = exports.RuntimeTypes = { - USB: "USB", - WIFI: "WIFI", - SIMULATOR: "SIMULATOR", - REMOTE: "REMOTE", - LOCAL: "LOCAL", - OTHER: "OTHER" -}; - -/** - * TODO: Remove this comaptibility layer in the future (bug 1085393) - * This runtime exists to support the ADB Helper add-on below version 0.7.0. - * - * This runtime assumes it is connecting to a Firefox OS device. - */ -function DeprecatedUSBRuntime(id) { - this._id = id; -} - -DeprecatedUSBRuntime.prototype = { - type: RuntimeTypes.USB, - get device() { - return Devices.getByName(this._id); - }, - connect: function (connection) { - if (!this.device) { - return promise.reject(new Error("Can't find device: " + this.name)); - } - return this.device.connect().then((port) => { - connection.host = "localhost"; - connection.port = port; - connection.connect(); - }); - }, - get id() { - return this._id; - }, - get name() { - return this._productModel || this._id; - }, - updateNameFromADB: function () { - if (this._productModel) { - return promise.reject(); - } - let deferred = promise.defer(); - if (this.device && this.device.shell) { - this.device.shell("getprop ro.product.model").then(stdout => { - this._productModel = stdout; - deferred.resolve(); - }, () => {}); - } else { - this._productModel = null; - deferred.reject(); - } - return deferred.promise; - }, -}; - -// For testing use only -exports._DeprecatedUSBRuntime = DeprecatedUSBRuntime; - -function WiFiRuntime(deviceName) { - this.deviceName = deviceName; -} - -WiFiRuntime.prototype = { - type: RuntimeTypes.WIFI, - // Mark runtime as taking a long time to connect - prolongedConnection: true, - connect: function (connection) { - let service = discovery.getRemoteService("devtools", this.deviceName); - if (!service) { - return promise.reject(new Error("Can't find device: " + this.name)); - } - connection.advertisement = service; - connection.authenticator.sendOOB = this.sendOOB; - // Disable the default connection timeout, since QR scanning can take an - // unknown amount of time. This prevents spurious errors (even after - // eventual success) from being shown. - connection.timeoutDelay = 0; - connection.connect(); - return promise.resolve(); - }, - get id() { - return this.deviceName; - }, - get name() { - return this.deviceName; - }, - - /** - * During OOB_CERT authentication, a notification dialog like this is used to - * to display a token which the user must transfer through some mechanism to the - * server to authenticate the devices. - * - * This implementation presents the token as text for the user to transfer - * manually. For a mobile device, you should override this implementation with - * something more convenient, such as displaying a QR code. - * - * This method receives an object containing: - * @param host string - * The host name or IP address of the debugger server. - * @param port number - * The port number of the debugger server. - * @param cert object (optional) - * The server's cert details. - * @param authResult AuthenticationResult - * Authentication result sent from the server. - * @param oob object (optional) - * The token data to be transferred during OOB_CERT step 8: - * * sha256: hash(ClientCert) - * * k : K(random 128-bit number) - * @return object containing: - * * close: Function to hide the notification - */ - sendOOB(session) { - const WINDOW_ID = "devtools:wifi-auth"; - let { authResult } = session; - // Only show in the PENDING state - if (authResult != AuthenticationResult.PENDING) { - throw new Error("Expected PENDING result, got " + authResult); - } - - // Listen for the window our prompt opens, so we can close it programatically - let promptWindow; - let windowListener = { - onOpenWindow(xulWindow) { - let win = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); - win.addEventListener("load", function listener() { - win.removeEventListener("load", listener, false); - if (win.document.documentElement.getAttribute("id") != WINDOW_ID) { - return; - } - // Found the window - promptWindow = win; - Services.wm.removeListener(windowListener); - }, false); - }, - onCloseWindow() {}, - onWindowTitleChange() {} - }; - Services.wm.addListener(windowListener); - - // |openDialog| is typically a blocking API, so |executeSoon| to get around this - DevToolsUtils.executeSoon(() => { - // Height determines the size of the QR code. Force a minimum size to - // improve scanability. - const MIN_HEIGHT = 600; - let win = Services.wm.getMostRecentWindow("devtools:webide"); - let width = win.outerWidth * 0.8; - let height = Math.max(win.outerHeight * 0.5, MIN_HEIGHT); - win.openDialog("chrome://webide/content/wifi-auth.xhtml", - WINDOW_ID, - "modal=yes,width=" + width + ",height=" + height, session); - }); - - return { - close() { - if (!promptWindow) { - return; - } - promptWindow.close(); - promptWindow = null; - } - }; - } -}; - -// For testing use only -exports._WiFiRuntime = WiFiRuntime; - -function SimulatorRuntime(simulator) { - this.simulator = simulator; -} - -SimulatorRuntime.prototype = { - type: RuntimeTypes.SIMULATOR, - connect: function (connection) { - return this.simulator.launch().then(port => { - connection.host = "localhost"; - connection.port = port; - connection.keepConnecting = true; - connection.once(Connection.Events.DISCONNECTED, e => this.simulator.kill()); - connection.connect(); - }); - }, - configure() { - Simulators.emit("configure", this.simulator); - }, - get id() { - return this.simulator.id; - }, - get name() { - return this.simulator.name; - }, -}; - -// For testing use only -exports._SimulatorRuntime = SimulatorRuntime; - -var gLocalRuntime = { - type: RuntimeTypes.LOCAL, - connect: function (connection) { - if (!DebuggerServer.initialized) { - DebuggerServer.init(); - DebuggerServer.addBrowserActors(); - } - DebuggerServer.allowChromeProcess = true; - connection.host = null; // Force Pipe transport - connection.port = null; - connection.connect(); - return promise.resolve(); - }, - get id() { - return "local"; - }, - get name() { - return Strings.GetStringFromName("local_runtime"); - }, -}; - -// For testing use only -exports._gLocalRuntime = gLocalRuntime; - -var gRemoteRuntime = { - type: RuntimeTypes.REMOTE, - connect: function (connection) { - let win = Services.wm.getMostRecentWindow("devtools:webide"); - if (!win) { - return promise.reject(new Error("No WebIDE window found")); - } - let ret = {value: connection.host + ":" + connection.port}; - let title = Strings.GetStringFromName("remote_runtime_promptTitle"); - let message = Strings.GetStringFromName("remote_runtime_promptMessage"); - let ok = Services.prompt.prompt(win, title, message, ret, null, {}); - let [host, port] = ret.value.split(":"); - if (!ok) { - return promise.reject({canceled: true}); - } - if (!host || !port) { - return promise.reject(new Error("Invalid host or port")); - } - connection.host = host; - connection.port = port; - connection.connect(); - return promise.resolve(); - }, - get name() { - return Strings.GetStringFromName("remote_runtime"); - }, -}; - -// For testing use only -exports._gRemoteRuntime = gRemoteRuntime; diff --git a/devtools/client/webide/modules/simulator-process.js b/devtools/client/webide/modules/simulator-process.js deleted file mode 100644 index 7d0b57cc6..000000000 --- a/devtools/client/webide/modules/simulator-process.js +++ /dev/null @@ -1,325 +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/. - */ - -"use strict"; - -const { Cc, Ci, Cu } = require("chrome"); - -const Environment = require("sdk/system/environment").env; -const EventEmitter = require("devtools/shared/event-emitter"); -const promise = require("promise"); -const Subprocess = require("sdk/system/child_process/subprocess"); -const Services = require("Services"); - -loader.lazyGetter(this, "OS", () => { - const Runtime = require("sdk/system/runtime"); - switch (Runtime.OS) { - case "Darwin": - return "mac64"; - case "Linux": - if (Runtime.XPCOMABI.indexOf("x86_64") === 0) { - return "linux64"; - } else { - return "linux32"; - } - case "WINNT": - return "win32"; - default: - return ""; - } -}); - -function SimulatorProcess() {} -SimulatorProcess.prototype = { - - // Check if B2G is running. - get isRunning() { - return !!this.process; - }, - - // Start the process and connect the debugger client. - run() { - - // Resolve B2G binary. - let b2g = this.b2gBinary; - if (!b2g || !b2g.exists()) { - throw Error("B2G executable not found."); - } - - // Ensure Gaia profile exists. - let gaia = this.gaiaProfile; - if (!gaia || !gaia.exists()) { - throw Error("Gaia profile directory not found."); - } - - this.once("stdout", function () { - if (OS == "mac64") { - console.debug("WORKAROUND run osascript to show b2g-desktop window on OS=='mac64'"); - // Escape double quotes and escape characters for use in AppleScript. - let path = b2g.path.replace(/\\/g, "\\\\").replace(/\"/g, '\\"'); - - Subprocess.call({ - command: "/usr/bin/osascript", - arguments: ["-e", 'tell application "' + path + '" to activate'], - }); - } - }); - - let logHandler = (e, data) => this.log(e, data.trim()); - this.on("stdout", logHandler); - this.on("stderr", logHandler); - this.once("exit", () => { - this.off("stdout", logHandler); - this.off("stderr", logHandler); - }); - - let environment; - if (OS.indexOf("linux") > -1) { - environment = ["TMPDIR=" + Services.dirsvc.get("TmpD", Ci.nsIFile).path]; - ["DISPLAY", "XAUTHORITY"].forEach(key => { - if (key in Environment) { - environment.push(key + "=" + Environment[key]); - } - }); - } - - // Spawn a B2G instance. - this.process = Subprocess.call({ - command: b2g, - arguments: this.args, - environment: environment, - stdout: data => this.emit("stdout", data), - stderr: data => this.emit("stderr", data), - // On B2G instance exit, reset tracked process, remote debugger port and - // shuttingDown flag, then finally emit an exit event. - done: result => { - console.log("B2G terminated with " + result.exitCode); - this.process = null; - this.emit("exit", result.exitCode); - } - }); - }, - - // Request a B2G instance kill. - kill() { - let deferred = promise.defer(); - if (this.process) { - this.once("exit", (e, exitCode) => { - this.shuttingDown = false; - deferred.resolve(exitCode); - }); - if (!this.shuttingDown) { - this.shuttingDown = true; - this.emit("kill", null); - this.process.kill(); - } - return deferred.promise; - } else { - return promise.resolve(undefined); - } - }, - - // Maybe log output messages. - log(level, message) { - if (!Services.prefs.getBoolPref("devtools.webide.logSimulatorOutput")) { - return; - } - if (level === "stderr" || level === "error") { - console.error(message); - return; - } - console.log(message); - }, - - // Compute B2G CLI arguments. - get args() { - let args = []; - - // Gaia profile. - args.push("-profile", this.gaiaProfile.path); - - // Debugger server port. - let port = parseInt(this.options.port); - args.push("-start-debugger-server", "" + port); - - // Screen size. - let width = parseInt(this.options.width); - let height = parseInt(this.options.height); - if (width && height) { - args.push("-screen", width + "x" + height); - } - - // Ignore eventual zombie instances of b2g that are left over. - args.push("-no-remote"); - - // If we are running a simulator based on Mulet, - // we have to override the default chrome URL - // in order to prevent the Browser UI to appear. - if (this.b2gBinary.leafName.includes("firefox")) { - args.push("-chrome", "chrome://b2g/content/shell.html"); - } - - return args; - }, -}; - -EventEmitter.decorate(SimulatorProcess.prototype); - - -function CustomSimulatorProcess(options) { - this.options = options; -} - -var CSPp = CustomSimulatorProcess.prototype = Object.create(SimulatorProcess.prototype); - -// Compute B2G binary file handle. -Object.defineProperty(CSPp, "b2gBinary", { - get: function () { - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); - file.initWithPath(this.options.b2gBinary); - return file; - } -}); - -// Compute Gaia profile file handle. -Object.defineProperty(CSPp, "gaiaProfile", { - get: function () { - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); - file.initWithPath(this.options.gaiaProfile); - return file; - } -}); - -exports.CustomSimulatorProcess = CustomSimulatorProcess; - - -function AddonSimulatorProcess(addon, options) { - this.addon = addon; - this.options = options; -} - -var ASPp = AddonSimulatorProcess.prototype = Object.create(SimulatorProcess.prototype); - -// Compute B2G binary file handle. -Object.defineProperty(ASPp, "b2gBinary", { - get: function () { - let file; - try { - let pref = "extensions." + this.addon.id + ".customRuntime"; - file = Services.prefs.getComplexValue(pref, Ci.nsIFile); - } catch (e) {} - - if (!file) { - file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file; - file.append("b2g"); - let binaries = { - win32: "b2g-bin.exe", - mac64: "B2G.app/Contents/MacOS/b2g-bin", - linux32: "b2g-bin", - linux64: "b2g-bin", - }; - binaries[OS].split("/").forEach(node => file.append(node)); - } - // If the binary doesn't exists, it may be because of a simulator - // based on mulet, which has a different binary name. - if (!file.exists()) { - file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file; - file.append("firefox"); - let binaries = { - win32: "firefox.exe", - mac64: "FirefoxNightly.app/Contents/MacOS/firefox-bin", - linux32: "firefox-bin", - linux64: "firefox-bin", - }; - binaries[OS].split("/").forEach(node => file.append(node)); - } - return file; - } -}); - -// Compute Gaia profile file handle. -Object.defineProperty(ASPp, "gaiaProfile", { - get: function () { - let file; - - // Custom profile from simulator configuration. - if (this.options.gaiaProfile) { - file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); - file.initWithPath(this.options.gaiaProfile); - return file; - } - - // Custom profile from addon prefs. - try { - let pref = "extensions." + this.addon.id + ".gaiaProfile"; - file = Services.prefs.getComplexValue(pref, Ci.nsIFile); - return file; - } catch (e) {} - - // Default profile from addon. - file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file; - file.append("profile"); - return file; - } -}); - -exports.AddonSimulatorProcess = AddonSimulatorProcess; - - -function OldAddonSimulatorProcess(addon, options) { - this.addon = addon; - this.options = options; -} - -var OASPp = OldAddonSimulatorProcess.prototype = Object.create(AddonSimulatorProcess.prototype); - -// Compute B2G binary file handle. -Object.defineProperty(OASPp, "b2gBinary", { - get: function () { - let file; - try { - let pref = "extensions." + this.addon.id + ".customRuntime"; - file = Services.prefs.getComplexValue(pref, Ci.nsIFile); - } catch (e) {} - - if (!file) { - file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file; - let version = this.addon.name.match(/\d+\.\d+/)[0].replace(/\./, "_"); - file.append("resources"); - file.append("fxos_" + version + "_simulator"); - file.append("data"); - file.append(OS == "linux32" ? "linux" : OS); - let binaries = { - win32: "b2g/b2g-bin.exe", - mac64: "B2G.app/Contents/MacOS/b2g-bin", - linux32: "b2g/b2g-bin", - linux64: "b2g/b2g-bin", - }; - binaries[OS].split("/").forEach(node => file.append(node)); - } - return file; - } -}); - -// Compute B2G CLI arguments. -Object.defineProperty(OASPp, "args", { - get: function () { - let args = []; - - // Gaia profile. - args.push("-profile", this.gaiaProfile.path); - - // Debugger server port. - let port = parseInt(this.options.port); - args.push("-dbgport", "" + port); - - // Ignore eventual zombie instances of b2g that are left over. - args.push("-no-remote"); - - return args; - } -}); - -exports.OldAddonSimulatorProcess = OldAddonSimulatorProcess; diff --git a/devtools/client/webide/modules/simulators.js b/devtools/client/webide/modules/simulators.js deleted file mode 100644 index f09df9e05..000000000 --- a/devtools/client/webide/modules/simulators.js +++ /dev/null @@ -1,368 +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/. */ - -"use strict"; - -const { AddonManager } = require("resource://gre/modules/AddonManager.jsm"); -const { Task } = require("devtools/shared/task"); -loader.lazyRequireGetter(this, "ConnectionManager", "devtools/shared/client/connection-manager", true); -loader.lazyRequireGetter(this, "AddonSimulatorProcess", "devtools/client/webide/modules/simulator-process", true); -loader.lazyRequireGetter(this, "OldAddonSimulatorProcess", "devtools/client/webide/modules/simulator-process", true); -loader.lazyRequireGetter(this, "CustomSimulatorProcess", "devtools/client/webide/modules/simulator-process", true); -const asyncStorage = require("devtools/shared/async-storage"); -const EventEmitter = require("devtools/shared/event-emitter"); -const promise = require("promise"); -const Services = require("Services"); - -const SimulatorRegExp = new RegExp(Services.prefs.getCharPref("devtools.webide.simulatorAddonRegExp")); -const LocaleCompare = (a, b) => { - return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); -}; - -var Simulators = { - - // The list of simulator configurations. - _simulators: [], - - /** - * Load a previously saved list of configurations (only once). - * - * @return Promise. - */ - _load() { - if (this._loadingPromise) { - return this._loadingPromise; - } - - this._loadingPromise = Task.spawn(function* () { - let jobs = []; - - let value = yield asyncStorage.getItem("simulators"); - if (Array.isArray(value)) { - value.forEach(options => { - let simulator = new Simulator(options); - Simulators.add(simulator, true); - - // If the simulator had a reference to an addon, fix it. - if (options.addonID) { - let deferred = promise.defer(); - AddonManager.getAddonByID(options.addonID, addon => { - simulator.addon = addon; - delete simulator.options.addonID; - deferred.resolve(); - }); - jobs.push(deferred.promise); - } - }); - } - - yield promise.all(jobs); - yield Simulators._addUnusedAddons(); - Simulators.emitUpdated(); - return Simulators._simulators; - }); - - return this._loadingPromise; - }, - - /** - * Add default simulators to the list for each new (unused) addon. - * - * @return Promise. - */ - _addUnusedAddons: Task.async(function* () { - let jobs = []; - - let addons = yield Simulators.findSimulatorAddons(); - addons.forEach(addon => { - jobs.push(Simulators.addIfUnusedAddon(addon, true)); - }); - - yield promise.all(jobs); - }), - - /** - * Save the current list of configurations. - * - * @return Promise. - */ - _save: Task.async(function* () { - yield this._load(); - - let value = Simulators._simulators.map(simulator => { - let options = JSON.parse(JSON.stringify(simulator.options)); - if (simulator.addon != null) { - options.addonID = simulator.addon.id; - } - return options; - }); - - yield asyncStorage.setItem("simulators", value); - }), - - /** - * List all available simulators. - * - * @return Promised simulator list. - */ - findSimulators: Task.async(function* () { - yield this._load(); - return Simulators._simulators; - }), - - /** - * List all installed simulator addons. - * - * @return Promised addon list. - */ - findSimulatorAddons() { - let deferred = promise.defer(); - AddonManager.getAllAddons(all => { - let addons = []; - for (let addon of all) { - if (Simulators.isSimulatorAddon(addon)) { - addons.push(addon); - } - } - // Sort simulator addons by name. - addons.sort(LocaleCompare); - deferred.resolve(addons); - }); - return deferred.promise; - }, - - /** - * Add a new simulator for `addon` if no other simulator uses it. - */ - addIfUnusedAddon(addon, silently = false) { - let simulators = this._simulators; - let matching = simulators.filter(s => s.addon && s.addon.id == addon.id); - if (matching.length > 0) { - return promise.resolve(); - } - let options = {}; - options.name = addon.name.replace(" Simulator", ""); - // Some addons specify a simulator type at the end of their version string, - // e.g. "2_5_tv". - let type = this.simulatorAddonVersion(addon).split("_")[2]; - if (type) { - // "tv" is shorthand for type "television". - options.type = (type === "tv" ? "television" : type); - } - return this.add(new Simulator(options, addon), silently); - }, - - // TODO (Bug 1146521) Maybe find a better way to deal with removed addons? - removeIfUsingAddon(addon) { - let simulators = this._simulators; - let remaining = simulators.filter(s => !s.addon || s.addon.id != addon.id); - this._simulators = remaining; - if (remaining.length !== simulators.length) { - this.emitUpdated(); - } - }, - - /** - * Add a new simulator to the list. Caution: `simulator.name` may be modified. - * - * @return Promise to added simulator. - */ - add(simulator, silently = false) { - let simulators = this._simulators; - let uniqueName = this.uniqueName(simulator.options.name); - simulator.options.name = uniqueName; - simulators.push(simulator); - if (!silently) { - this.emitUpdated(); - } - return promise.resolve(simulator); - }, - - /** - * Remove a simulator from the list. - */ - remove(simulator) { - let simulators = this._simulators; - let remaining = simulators.filter(s => s !== simulator); - this._simulators = remaining; - if (remaining.length !== simulators.length) { - this.emitUpdated(); - } - }, - - /** - * Get a unique name for a simulator (may add a suffix, e.g. "MyName (1)"). - */ - uniqueName(name) { - let simulators = this._simulators; - - let names = {}; - simulators.forEach(simulator => names[simulator.name] = true); - - // Strip any previous suffix, add a new suffix if necessary. - let stripped = name.replace(/ \(\d+\)$/, ""); - let unique = stripped; - for (let i = 1; names[unique]; i++) { - unique = stripped + " (" + i + ")"; - } - return unique; - }, - - /** - * Compare an addon's ID against the expected form of a simulator addon ID, - * and try to extract its version if there is a match. - * - * Note: If a simulator addon is recognized, but no version can be extracted - * (e.g. custom RegExp pref value), we return "Unknown" to keep the returned - * value 'truthy'. - */ - simulatorAddonVersion(addon) { - let match = SimulatorRegExp.exec(addon.id); - if (!match) { - return null; - } - let version = match[1]; - return version || "Unknown"; - }, - - /** - * Detect simulator addons, including "unofficial" ones. - */ - isSimulatorAddon(addon) { - return !!this.simulatorAddonVersion(addon); - }, - - emitUpdated() { - this.emit("updated", { length: this._simulators.length }); - this._simulators.sort(LocaleCompare); - this._save(); - }, - - onConfigure(e, simulator) { - this._lastConfiguredSimulator = simulator; - }, - - onInstalled(addon) { - if (this.isSimulatorAddon(addon)) { - this.addIfUnusedAddon(addon); - } - }, - - onEnabled(addon) { - if (this.isSimulatorAddon(addon)) { - this.addIfUnusedAddon(addon); - } - }, - - onDisabled(addon) { - if (this.isSimulatorAddon(addon)) { - this.removeIfUsingAddon(addon); - } - }, - - onUninstalled(addon) { - if (this.isSimulatorAddon(addon)) { - this.removeIfUsingAddon(addon); - } - }, -}; -exports.Simulators = Simulators; -AddonManager.addAddonListener(Simulators); -EventEmitter.decorate(Simulators); -Simulators.on("configure", Simulators.onConfigure.bind(Simulators)); - -function Simulator(options = {}, addon = null) { - this.addon = addon; - this.options = options; - - // Fill `this.options` with default values where needed. - let defaults = this.defaults; - for (let option in defaults) { - if (this.options[option] == null) { - this.options[option] = defaults[option]; - } - } -} -Simulator.prototype = { - - // Default simulation options. - _defaults: { - // Based on the Firefox OS Flame. - phone: { - width: 320, - height: 570, - pixelRatio: 1.5 - }, - // Based on a 720p HD TV. - television: { - width: 1280, - height: 720, - pixelRatio: 1, - } - }, - _defaultType: "phone", - - restoreDefaults() { - let defaults = this.defaults; - let options = this.options; - for (let option in defaults) { - options[option] = defaults[option]; - } - }, - - launch() { - // Close already opened simulation. - if (this.process) { - return this.kill().then(this.launch.bind(this)); - } - - this.options.port = ConnectionManager.getFreeTCPPort(); - - // Choose simulator process type. - if (this.options.b2gBinary) { - // Custom binary. - this.process = new CustomSimulatorProcess(this.options); - } else if (this.version > "1.3") { - // Recent simulator addon. - this.process = new AddonSimulatorProcess(this.addon, this.options); - } else { - // Old simulator addon. - this.process = new OldAddonSimulatorProcess(this.addon, this.options); - } - this.process.run(); - - return promise.resolve(this.options.port); - }, - - kill() { - let process = this.process; - if (!process) { - return promise.resolve(); - } - this.process = null; - return process.kill(); - }, - - get defaults() { - let defaults = this._defaults; - return defaults[this.type] || defaults[this._defaultType]; - }, - - get id() { - return this.name; - }, - - get name() { - return this.options.name; - }, - - get type() { - return this.options.type || this._defaultType; - }, - - get version() { - return this.options.b2gBinary ? "Custom" : this.addon.name.match(/\d+\.\d+/)[0]; - }, -}; -exports.Simulator = Simulator; diff --git a/devtools/client/webide/modules/tab-store.js b/devtools/client/webide/modules/tab-store.js deleted file mode 100644 index 0fed366cc..000000000 --- a/devtools/client/webide/modules/tab-store.js +++ /dev/null @@ -1,178 +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/. */ - -const { Cu } = require("chrome"); - -const { TargetFactory } = require("devtools/client/framework/target"); -const EventEmitter = require("devtools/shared/event-emitter"); -const { Connection } = require("devtools/shared/client/connection-manager"); -const promise = require("promise"); -const { Task } = require("devtools/shared/task"); - -const _knownTabStores = new WeakMap(); - -var TabStore; - -module.exports = TabStore = function (connection) { - // If we already know about this connection, - // let's re-use the existing store. - if (_knownTabStores.has(connection)) { - return _knownTabStores.get(connection); - } - - _knownTabStores.set(connection, this); - - EventEmitter.decorate(this); - - this._resetStore(); - - this.destroy = this.destroy.bind(this); - this._onStatusChanged = this._onStatusChanged.bind(this); - - this._connection = connection; - this._connection.once(Connection.Events.DESTROYED, this.destroy); - this._connection.on(Connection.Events.STATUS_CHANGED, this._onStatusChanged); - this._onTabListChanged = this._onTabListChanged.bind(this); - this._onTabNavigated = this._onTabNavigated.bind(this); - this._onStatusChanged(); - return this; -}; - -TabStore.prototype = { - - destroy: function () { - if (this._connection) { - // While this.destroy is bound using .once() above, that event may not - // have occurred when the TabStore client calls destroy, so we - // manually remove it here. - this._connection.off(Connection.Events.DESTROYED, this.destroy); - this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged); - _knownTabStores.delete(this._connection); - this._connection = null; - } - }, - - _resetStore: function () { - this.response = null; - this.tabs = []; - this._selectedTab = null; - this._selectedTabTargetPromise = null; - }, - - _onStatusChanged: function () { - if (this._connection.status == Connection.Status.CONNECTED) { - // Watch for changes to remote browser tabs - this._connection.client.addListener("tabListChanged", - this._onTabListChanged); - this._connection.client.addListener("tabNavigated", - this._onTabNavigated); - this.listTabs(); - } else { - if (this._connection.client) { - this._connection.client.removeListener("tabListChanged", - this._onTabListChanged); - this._connection.client.removeListener("tabNavigated", - this._onTabNavigated); - } - this._resetStore(); - } - }, - - _onTabListChanged: function () { - this.listTabs().then(() => this.emit("tab-list")) - .catch(console.error); - }, - - _onTabNavigated: function (e, { from, title, url }) { - if (!this._selectedTab || from !== this._selectedTab.actor) { - return; - } - this._selectedTab.url = url; - this._selectedTab.title = title; - this.emit("navigate"); - }, - - listTabs: function () { - if (!this._connection || !this._connection.client) { - return promise.reject(new Error("Can't listTabs, not connected.")); - } - let deferred = promise.defer(); - this._connection.client.listTabs(response => { - if (response.error) { - this._connection.disconnect(); - deferred.reject(response.error); - return; - } - let tabsChanged = JSON.stringify(this.tabs) !== JSON.stringify(response.tabs); - this.response = response; - this.tabs = response.tabs; - this._checkSelectedTab(); - if (tabsChanged) { - this.emit("tab-list"); - } - deferred.resolve(response); - }); - return deferred.promise; - }, - - // TODO: Tab "selection" should really take place by creating a TabProject - // which is the selected project. This should be done as part of the - // project-agnostic work. - _selectedTab: null, - _selectedTabTargetPromise: null, - get selectedTab() { - return this._selectedTab; - }, - set selectedTab(tab) { - if (this._selectedTab === tab) { - return; - } - this._selectedTab = tab; - this._selectedTabTargetPromise = null; - // Attach to the tab to follow navigation events - if (this._selectedTab) { - this.getTargetForTab(); - } - }, - - _checkSelectedTab: function () { - if (!this._selectedTab) { - return; - } - let alive = this.tabs.some(tab => { - return tab.actor === this._selectedTab.actor; - }); - if (!alive) { - this._selectedTab = null; - this._selectedTabTargetPromise = null; - this.emit("closed"); - } - }, - - getTargetForTab: function () { - if (this._selectedTabTargetPromise) { - return this._selectedTabTargetPromise; - } - let store = this; - this._selectedTabTargetPromise = Task.spawn(function* () { - // If you connect to a tab, then detach from it, the root actor may have - // de-listed the actors that belong to the tab. This breaks the toolbox - // if you try to connect to the same tab again. To work around this - // issue, we force a "listTabs" request before connecting to a tab. - yield store.listTabs(); - return TargetFactory.forRemoteTab({ - form: store._selectedTab, - client: store._connection.client, - chrome: false - }); - }); - this._selectedTabTargetPromise.then(target => { - target.once("close", () => { - this._selectedTabTargetPromise = null; - }); - }); - return this._selectedTabTargetPromise; - }, - -}; diff --git a/devtools/client/webide/modules/utils.js b/devtools/client/webide/modules/utils.js deleted file mode 100644 index 7a19c7044..000000000 --- a/devtools/client/webide/modules/utils.js +++ /dev/null @@ -1,68 +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/. */ - -const { Cc, Cu, Ci } = require("chrome"); -const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {}); -const Services = require("Services"); -const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); - -function doesFileExist(location) { - let file = new FileUtils.File(location); - return file.exists(); -} -exports.doesFileExist = doesFileExist; - -function _getFile(location, ...pickerParams) { - if (location) { - return new FileUtils.File(location); - } - let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - fp.init(...pickerParams); - let res = fp.show(); - if (res == Ci.nsIFilePicker.returnCancel) { - return null; - } - return fp.file; -} - -function getCustomBinary(window, location) { - return _getFile(location, window, Strings.GetStringFromName("selectCustomBinary_title"), Ci.nsIFilePicker.modeOpen); -} -exports.getCustomBinary = getCustomBinary; - -function getCustomProfile(window, location) { - return _getFile(location, window, Strings.GetStringFromName("selectCustomProfile_title"), Ci.nsIFilePicker.modeGetFolder); -} -exports.getCustomProfile = getCustomProfile; - -function getPackagedDirectory(window, location) { - return _getFile(location, window, Strings.GetStringFromName("importPackagedApp_title"), Ci.nsIFilePicker.modeGetFolder); -} -exports.getPackagedDirectory = getPackagedDirectory; - -function getHostedURL(window, location) { - let ret = { value: null }; - - if (!location) { - Services.prompt.prompt(window, - Strings.GetStringFromName("importHostedApp_title"), - Strings.GetStringFromName("importHostedApp_header"), - ret, null, {}); - location = ret.value; - } - - if (!location) { - return null; - } - - // Clean location string and add "http://" if missing - location = location.trim(); - try { // Will fail if no scheme - Services.io.extractScheme(location); - } catch (e) { - location = "http://" + location; - } - return location; -} -exports.getHostedURL = getHostedURL; -- cgit v1.2.3