Remove WebIDE devtools component.
parent
1123af8b9c
commit
c52cc0239a
|
@ -38,7 +38,6 @@ const kPrefCustomizationState = "browser.uiCustomization.state";
|
|||
const kPrefCustomizationAutoAdd = "browser.uiCustomization.autoAdd";
|
||||
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
|
||||
const kPrefDrawInTitlebar = "browser.tabs.drawInTitlebar";
|
||||
const kPrefWebIDEInNavbar = "devtools.webide.widget.inNavbarByDefault";
|
||||
|
||||
const kExpectedWindowURL = "chrome://browser/content/browser.xul";
|
||||
|
||||
|
@ -230,10 +229,6 @@ var CustomizableUIInternal = {
|
|||
navbarPlacements.splice(2, 0, "developer-button");
|
||||
}
|
||||
|
||||
if (Services.prefs.getBoolPref(kPrefWebIDEInNavbar)) {
|
||||
navbarPlacements.push("webide-button");
|
||||
}
|
||||
|
||||
// Place this last, when createWidget is called for pocket, it will
|
||||
// append to the toolbar.
|
||||
if (Services.prefs.getPrefType("extensions.pocket.enabled") != Services.prefs.PREF_INVALID &&
|
||||
|
|
|
@ -355,8 +355,6 @@
|
|||
@RESPATH@/browser/components/nsSetDefaultBrowser.js
|
||||
@RESPATH@/browser/components/devtools-startup.manifest
|
||||
@RESPATH@/browser/components/devtools-startup.js
|
||||
@RESPATH@/browser/components/webideCli.js
|
||||
@RESPATH@/browser/components/webideComponents.manifest
|
||||
@RESPATH@/browser/components/browser-newtab.xpt
|
||||
@RESPATH@/browser/components/aboutNewTabService.js
|
||||
@RESPATH@/browser/components/NewTabComponents.manifest
|
||||
|
@ -610,11 +608,6 @@
|
|||
@RESPATH@/browser/chrome/icons/default/default48.png
|
||||
#endif
|
||||
|
||||
; [Webide Files]
|
||||
@RESPATH@/browser/chrome/webide@JAREXT@
|
||||
@RESPATH@/browser/chrome/webide.manifest
|
||||
@RESPATH@/browser/@PREF_DIR@/webide-prefs.js
|
||||
|
||||
; DevTools
|
||||
@RESPATH@/browser/chrome/devtools@JAREXT@
|
||||
@RESPATH@/browser/chrome/devtools.manifest
|
||||
|
|
|
@ -210,11 +210,6 @@
|
|||
#endif
|
||||
|
||||
#ifdef MOZ_DEVTOOLS
|
||||
; [Webide Files]
|
||||
@RESPATH@/browser/chrome/webide@JAREXT@
|
||||
@RESPATH@/browser/chrome/webide.manifest
|
||||
@RESPATH@/browser/@PREF_DIR@/webide-prefs.js
|
||||
|
||||
; DevTools
|
||||
@RESPATH@/browser/chrome/devtools@JAREXT@
|
||||
@RESPATH@/browser/chrome/devtools.manifest
|
||||
|
|
|
@ -123,23 +123,6 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
|
|||
win.DeveloperToolbar.show(false).catch(console.error);
|
||||
}
|
||||
|
||||
// Enable WebIDE?
|
||||
let webIDEEnabled = Services.prefs.getBoolPref("devtools.webide.enabled");
|
||||
idEls = [
|
||||
"appmenu_webide",
|
||||
"menu_webide"
|
||||
];
|
||||
idEls.forEach(function (idEl) {
|
||||
toggleMenuItem(idEl, webIDEEnabled);
|
||||
});
|
||||
|
||||
let showWebIDEWidget = Services.prefs.getBoolPref("devtools.webide.widget.enabled");
|
||||
if (webIDEEnabled && showWebIDEWidget) {
|
||||
gDevToolsBrowser.installWebIDEWidget();
|
||||
} else {
|
||||
gDevToolsBrowser.uninstallWebIDEWidget();
|
||||
}
|
||||
|
||||
// Enable Browser Toolbox?
|
||||
let chromeEnabled = Services.prefs.getBoolPref("devtools.chrome.enabled");
|
||||
let devtoolsRemoteEnabled = Services.prefs.getBoolPref("devtools.debugger.remote-enabled");
|
||||
|
|
|
@ -126,16 +126,9 @@ let gDevToolsBrowserMethods = [
|
|||
// Used by browser-sets.inc, command
|
||||
"openConnectScreen",
|
||||
|
||||
// Used by browser-sets.inc, command
|
||||
// itself, webide widget
|
||||
"openWebIDE",
|
||||
|
||||
// Used by browser-sets.inc, command
|
||||
"openContentProcessToolbox",
|
||||
|
||||
// Used by webide.js
|
||||
"moveWebIDEWidgetInNavbar",
|
||||
|
||||
// Used by browser.js
|
||||
"registerBrowserWindow",
|
||||
|
||||
|
@ -146,10 +139,6 @@ let gDevToolsBrowserMethods = [
|
|||
"forgetBrowserWindow"
|
||||
];
|
||||
this.gDevToolsBrowser = {
|
||||
// Used by webide.js
|
||||
get isWebIDEInitialized() {
|
||||
return browser.isWebIDEInitialized;
|
||||
},
|
||||
// Used by a test (should be removed)
|
||||
get _trackedBrowserWindows() {
|
||||
return browser._trackedBrowserWindows;
|
||||
|
|
|
@ -54,11 +54,6 @@ devToolbarMenu.accesskey = v
|
|||
devToolbarMenu.key = VK_F2
|
||||
devToolbarMenu.keytext = F2
|
||||
|
||||
webide.label = WebIDE
|
||||
webide.accesskey = W
|
||||
webide.key = VK_F8
|
||||
webide.keytext = F8
|
||||
|
||||
devToolboxMenuItem.label = Toggle Tools
|
||||
devToolboxMenuItem.accesskey = T
|
||||
devToolboxMenuItem.key = I
|
||||
|
|
|
@ -1,222 +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/. -->
|
||||
|
||||
<!ENTITY % brandDTD
|
||||
SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%brandDTD;
|
||||
|
||||
<!ENTITY windowTitle "&brandShortName; WebIDE">
|
||||
|
||||
<!ENTITY projectMenu_label "Project">
|
||||
<!ENTITY projectMenu_accesskey "P">
|
||||
<!ENTITY projectMenu_newApp_label "New App…">
|
||||
<!ENTITY projectMenu_newApp_accesskey "N">
|
||||
<!ENTITY projectMenu_importPackagedApp_label "Open Packaged App…">
|
||||
<!ENTITY projectMenu_importPackagedApp_accesskey "P">
|
||||
<!ENTITY projectMenu_importHostedApp_label "Open Hosted App…">
|
||||
<!ENTITY projectMenu_importHostedApp_accesskey "H">
|
||||
<!ENTITY projectMenu_selectApp_label "Open App…">
|
||||
<!ENTITY projectMenu_selectApp_accesskey "O">
|
||||
<!ENTITY projectMenu_play_label "Install and Run">
|
||||
<!ENTITY projectMenu_play_accesskey "I">
|
||||
<!ENTITY projectMenu_stop_label "Stop App">
|
||||
<!ENTITY projectMenu_stop_accesskey "S">
|
||||
<!ENTITY projectMenu_debug_label "Debug App">
|
||||
<!ENTITY projectMenu_debug_accesskey "D">
|
||||
<!ENTITY projectMenu_remove_label "Remove Project">
|
||||
<!ENTITY projectMenu_remove_accesskey "R">
|
||||
<!ENTITY projectMenu_showPrefs_label "Preferences">
|
||||
<!ENTITY projectMenu_showPrefs_accesskey "e">
|
||||
<!ENTITY projectMenu_manageComponents_label "Manage Extra Components">
|
||||
<!ENTITY projectMenu_manageComponents_accesskey "M">
|
||||
<!ENTITY projectMenu_refreshTabs_label "Refresh Tabs">
|
||||
|
||||
<!ENTITY runtimeMenu_label "Runtime">
|
||||
<!ENTITY runtimeMenu_accesskey "R">
|
||||
<!ENTITY runtimeMenu_disconnect_label "Disconnect">
|
||||
<!ENTITY runtimeMenu_disconnect_accesskey "D">
|
||||
<!ENTITY runtimeMenu_showPermissionTable_label "Permissions Table">
|
||||
<!ENTITY runtimeMenu_showPermissionTable_accesskey "P">
|
||||
<!ENTITY runtimeMenu_takeScreenshot_label "Screenshot">
|
||||
<!ENTITY runtimeMenu_takeScreenshot_accesskey "S">
|
||||
<!ENTITY runtimeMenu_showDetails_label "Runtime Info">
|
||||
<!ENTITY runtimeMenu_showDetails_accesskey "E">
|
||||
<!ENTITY runtimeMenu_showMonitor_label "Monitor">
|
||||
<!ENTITY runtimeMenu_showMonitor_accesskey "M">
|
||||
<!ENTITY runtimeMenu_showDevicePrefs_label "Device Preferences">
|
||||
<!ENTITY runtimeMenu_showDevicePrefs_accesskey "D">
|
||||
<!ENTITY runtimeMenu_showSettings_label "Device Settings">
|
||||
<!ENTITY runtimeMenu_showSettings_accesskey "s">
|
||||
|
||||
<!ENTITY viewMenu_label "View">
|
||||
<!ENTITY viewMenu_accesskey "V">
|
||||
<!ENTITY viewMenu_toggleEditor_label "Toggle Editor">
|
||||
<!ENTITY viewMenu_toggleEditor_accesskey "E">
|
||||
<!ENTITY viewMenu_zoomin_label "Zoom In">
|
||||
<!ENTITY viewMenu_zoomin_accesskey "I">
|
||||
<!ENTITY viewMenu_zoomout_label "Zoom Out">
|
||||
<!ENTITY viewMenu_zoomout_accesskey "O">
|
||||
<!ENTITY viewMenu_resetzoom_label "Reset Zoom">
|
||||
<!ENTITY viewMenu_resetzoom_accesskey "R">
|
||||
|
||||
<!ENTITY projectButton_label "Open App">
|
||||
<!ENTITY runtimeButton_label "Select Runtime">
|
||||
|
||||
<!-- We try to repicate browser' bindings: -->
|
||||
<!-- quit app -->
|
||||
<!ENTITY key_quit "W">
|
||||
<!-- open menu -->
|
||||
<!ENTITY key_showProjectPanel "O">
|
||||
<!-- reload app -->
|
||||
<!ENTITY key_play "R">
|
||||
<!-- show toolbox -->
|
||||
<!ENTITY key_toggleToolbox "VK_F12">
|
||||
<!-- toggle sidebar -->
|
||||
<!ENTITY key_toggleEditor "B">
|
||||
<!-- zoom -->
|
||||
<!ENTITY key_zoomin "+">
|
||||
<!ENTITY key_zoomin2 "=">
|
||||
<!ENTITY key_zoomout "-">
|
||||
<!ENTITY key_resetzoom "0">
|
||||
|
||||
<!ENTITY projectPanel_myProjects "My Projects">
|
||||
<!ENTITY projectPanel_runtimeApps "Runtime Apps">
|
||||
<!ENTITY projectPanel_tabs "Tabs">
|
||||
<!ENTITY runtimePanel_usb "USB Devices">
|
||||
<!ENTITY runtimePanel_wifi "Wi-Fi Devices">
|
||||
<!ENTITY runtimePanel_simulator "Simulators">
|
||||
<!ENTITY runtimePanel_other "Other">
|
||||
<!ENTITY runtimePanel_installsimulator "Install Simulator">
|
||||
<!ENTITY runtimePanel_noadbhelper "Install ADB Helper">
|
||||
<!ENTITY runtimePanel_nousbdevice "Can’t see your device?">
|
||||
<!ENTITY runtimePanel_refreshDevices_label "Refresh Devices">
|
||||
|
||||
<!-- Lense -->
|
||||
<!ENTITY details_valid_header "valid">
|
||||
<!ENTITY details_warning_header "warnings">
|
||||
<!ENTITY details_error_header "errors">
|
||||
<!ENTITY details_description "Description">
|
||||
<!ENTITY details_location "Location">
|
||||
<!ENTITY details_manifestURL "App ID">
|
||||
<!ENTITY details_removeProject_button "Remove Project">
|
||||
<!ENTITY details_showPrepackageLog_button "Show Pre-package Log">
|
||||
|
||||
<!-- New App -->
|
||||
<!ENTITY newAppWindowTitle "New App">
|
||||
<!ENTITY newAppHeader "Select template">
|
||||
<!ENTITY newAppLoadingTemplate "Loading templates…">
|
||||
<!ENTITY newAppProjectName "Project Name:">
|
||||
|
||||
|
||||
<!-- Decks -->
|
||||
|
||||
<!ENTITY deck_close "Close">
|
||||
|
||||
<!-- Addons -->
|
||||
<!ENTITY addons_title "Extra Components">
|
||||
<!ENTITY addons_aboutaddons "Open Add-ons Manager">
|
||||
|
||||
<!-- Prefs -->
|
||||
<!ENTITY prefs_title "Preferences">
|
||||
<!ENTITY prefs_editor_title "Editor">
|
||||
<!ENTITY prefs_general_title "General">
|
||||
<!ENTITY prefs_restore "Restore Defaults">
|
||||
<!ENTITY prefs_manage_components "Manage Extra Components">
|
||||
<!ENTITY prefs_options_autoconnectruntime "Reconnect to previous runtime">
|
||||
<!ENTITY prefs_options_autoconnectruntime_tooltip "Reconnect to previous runtime when WebIDE starts">
|
||||
<!ENTITY prefs_options_rememberlastproject "Remember last project">
|
||||
<!ENTITY prefs_options_rememberlastproject_tooltip "Restore previous project when WebIDE starts">
|
||||
<!ENTITY prefs_options_templatesurl "Templates URL">
|
||||
<!ENTITY prefs_options_templatesurl_tooltip "Index of available templates">
|
||||
<!ENTITY prefs_options_showeditor "Show editor">
|
||||
<!ENTITY prefs_options_showeditor_tooltip "Show internal editor">
|
||||
<!ENTITY prefs_options_tabsize "Tab size">
|
||||
<!ENTITY prefs_options_expandtab "Soft tabs">
|
||||
<!ENTITY prefs_options_expandtab_tooltip "Use spaces instead of the tab character">
|
||||
<!ENTITY prefs_options_detectindentation "Autoindent">
|
||||
<!ENTITY prefs_options_detectindentation_tooltip "Guess indentation based on source content">
|
||||
<!ENTITY prefs_options_autocomplete "Autocomplete">
|
||||
<!ENTITY prefs_options_autocomplete_tooltip "Enable code autocompletion">
|
||||
<!ENTITY prefs_options_autoclosebrackets "Autoclose brackets">
|
||||
<!ENTITY prefs_options_autoclosebrackets_tooltip "Automatically insert closing brackets">
|
||||
<!ENTITY prefs_options_keybindings "Keybindings">
|
||||
<!ENTITY prefs_options_keybindings_default "Default">
|
||||
<!ENTITY prefs_options_autosavefiles "Autosave files">
|
||||
<!ENTITY prefs_options_autosavefiles_tooltip "Automatically save edited files before running project">
|
||||
|
||||
<!-- Permissions Table -->
|
||||
<!ENTITY permissionstable_title "Permissions Table">
|
||||
<!ENTITY permissionstable_name_header "Name">
|
||||
|
||||
<!-- Runtime Details -->
|
||||
<!ENTITY runtimedetails_title "Runtime Info">
|
||||
<!ENTITY runtimedetails_adbIsRoot "ADB is root: ">
|
||||
<!ENTITY runtimedetails_summonADBRoot "root device">
|
||||
<!ENTITY runtimedetails_ADBRootWarning "(requires unlocked bootloader)">
|
||||
<!ENTITY runtimedetails_unrestrictedPrivileges "Unrestricted DevTools privileges: ">
|
||||
<!ENTITY runtimedetails_requestPrivileges "request higher privileges">
|
||||
<!ENTITY runtimedetails_privilegesWarning "(Will reboot device. Requires root access.)">
|
||||
|
||||
<!-- Device Preferences and Settings -->
|
||||
<!ENTITY device_typeboolean "Boolean">
|
||||
<!ENTITY device_typenumber "Integer">
|
||||
<!ENTITY device_typestring "String">
|
||||
<!ENTITY device_typeobject "Object">
|
||||
<!ENTITY device_typenone "Select a type">
|
||||
|
||||
<!-- Device Preferences -->
|
||||
<!ENTITY devicepreference_title "Device Preferences">
|
||||
<!ENTITY devicepreference_search "Search preferences">
|
||||
<!ENTITY devicepreference_newname "New preference name">
|
||||
<!ENTITY devicepreference_newtext "Preference value">
|
||||
<!ENTITY devicepreference_addnew "Add new preference">
|
||||
|
||||
<!-- Device Settings -->
|
||||
<!ENTITY devicesetting_title "Device Settings">
|
||||
<!ENTITY devicesetting_search "Search settings">
|
||||
<!ENTITY devicesetting_newname "New setting name">
|
||||
<!ENTITY devicesetting_newtext "Setting value">
|
||||
<!ENTITY devicesetting_addnew "Add new setting">
|
||||
|
||||
<!-- Monitor -->
|
||||
<!ENTITY monitor_title "Monitor">
|
||||
<!ENTITY monitor_help "Help">
|
||||
|
||||
<!-- WiFi Authentication -->
|
||||
<!-- LOCALIZATION NOTE (wifi_auth_header): The header displayed on the dialog
|
||||
that instructs the user to transfer an authentication token to the
|
||||
server. -->
|
||||
<!ENTITY wifi_auth_header "Client Identification">
|
||||
<!-- LOCALIZATION NOTE (wifi_auth_scan_request): Instructions requesting the
|
||||
user to transfer authentication info by scanning a QR code. -->
|
||||
<!ENTITY wifi_auth_scan_request "The endpoint you are connecting to needs more information to authenticate this connection. Please scan the QR code below via the prompt on your other device.">
|
||||
<!-- LOCALIZATION NOTE (wifi_auth_no_scanner): Link text to assist users with
|
||||
devices that can't scan a QR code. -->
|
||||
<!ENTITY wifi_auth_no_scanner "No QR scanner prompt?">
|
||||
<!-- LOCALIZATION NOTE (wifi_auth_yes_scanner): Link text to assist users with
|
||||
devices that can scan a QR code. -->
|
||||
<!ENTITY wifi_auth_yes_scanner "Have a QR scanner prompt?">
|
||||
<!-- LOCALIZATION NOTE (wifi_auth_token_request): Instructions requesting the
|
||||
user to transfer authentication info by transferring a token. -->
|
||||
<!ENTITY wifi_auth_token_request "If your other device asks for a token instead of scanning a QR code, please copy the value below to the other device:">
|
||||
<!ENTITY wifi_auth_qr_size_note "If the QR code appears too small for the connection to be successfully established, try zooming or enlarging the window.">
|
||||
|
||||
<!-- Logs panel -->
|
||||
<!ENTITY logs_title "Pre-packaging Command Logs">
|
||||
|
||||
<!-- Simulator Options -->
|
||||
<!ENTITY simulator_title "Simulator Options">
|
||||
<!ENTITY simulator_remove "Delete Simulator">
|
||||
<!ENTITY simulator_reset "Restore Defaults">
|
||||
<!ENTITY simulator_name "Name">
|
||||
<!ENTITY simulator_software "Software">
|
||||
<!ENTITY simulator_version "Version">
|
||||
<!ENTITY simulator_profile "Profile">
|
||||
<!ENTITY simulator_hardware "Hardware">
|
||||
<!ENTITY simulator_device "Device">
|
||||
<!ENTITY simulator_screenSize "Screen">
|
||||
<!ENTITY simulator_pixelRatio "Pixel Ratio">
|
||||
<!ENTITY simulator_tv_data "TV Simulation">
|
||||
<!ENTITY simulator_tv_data_open "Config Data">
|
||||
<!ENTITY simulator_tv_data_open_button "Open Config Directory…">
|
|
@ -1,92 +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/.
|
||||
|
||||
title_noApp=WebIDE
|
||||
title_app=WebIDE: %S
|
||||
|
||||
runtimeButton_label=Select Runtime
|
||||
projectButton_label=Open App
|
||||
|
||||
mainProcess_label=Main Process
|
||||
|
||||
local_runtime=Local Runtime
|
||||
remote_runtime=Remote Runtime
|
||||
remote_runtime_promptTitle=Remote Runtime
|
||||
remote_runtime_promptMessage=hostname:port
|
||||
|
||||
importPackagedApp_title=Select Directory
|
||||
importHostedApp_title=Open Hosted App
|
||||
importHostedApp_header=Enter Manifest URL
|
||||
|
||||
selectCustomBinary_title=Select custom B2G binary
|
||||
selectCustomProfile_title=Select custom Gaia profile
|
||||
|
||||
notification_showTroubleShooting_label=Troubleshooting
|
||||
notification_showTroubleShooting_accesskey=T
|
||||
|
||||
# LOCALIZATION NOTE (project_tab_loading): This is shown as a temporary tab
|
||||
# title for browser tab projects when the tab is still loading.
|
||||
project_tab_loading=Loading…
|
||||
|
||||
# These messages appear in a notification box when an error occur.
|
||||
|
||||
error_cantInstallNotFullyConnected=Can’t install project. Not fully connected.
|
||||
error_cantInstallValidationErrors=Can’t install project. Validation errors.
|
||||
error_listRunningApps=Can’t get app list from device
|
||||
|
||||
# Variable: name of the operation (in english)
|
||||
error_operationTimeout=Operation timed out: %1$S
|
||||
error_operationFail=Operation failed: %1$S
|
||||
|
||||
# Variable: app name
|
||||
error_cantConnectToApp=Can’t connect to app: %1$S
|
||||
|
||||
# Variable: error message (in english)
|
||||
error_cantFetchAddonsJSON=Can’t fetch the add-on list: %S
|
||||
|
||||
error_appProjectsLoadFailed=Unable to load project list. This can occur if you’ve used this profile with a newer version of the browser.
|
||||
error_folderCreationFailed=Unable to create project folder in the selected directory.
|
||||
|
||||
# Variable: runtime app build ID (looks like this %Y%M%D format) and firefox build ID (same format)
|
||||
error_runtimeVersionTooRecent=The connected runtime has a more recent build date (%1$S) than your desktop browser (%2$S) does. This is an unsupported setup and may cause DevTools to fail. Please update the browser.
|
||||
|
||||
addons_stable=stable
|
||||
addons_unstable=unstable
|
||||
# LOCALIZATION NOTE (addons_simulator_label): This label is shown as the name of
|
||||
# a given simulator version in the "Manage Simulators" pane. %1$S: Firefox OS
|
||||
# version in the simulator, ex. 1.3. %2$S: Simulator stability label, ex.
|
||||
# "stable" or "unstable".
|
||||
addons_simulator_label=Firefox OS %1$S Simulator (%2$S)
|
||||
addons_install_button=install
|
||||
addons_uninstall_button=uninstall
|
||||
addons_adb_label=ADB Helper Add-on
|
||||
addons_adapters_label=Tools Adapters Add-on
|
||||
addons_adb_warning=USB devices won’t be detected without this add-on
|
||||
addons_status_unknown=?
|
||||
addons_status_installed=Installed
|
||||
addons_status_uninstalled=Not Installed
|
||||
addons_status_preparing=preparing
|
||||
addons_status_downloading=downloading
|
||||
addons_status_installing=installing
|
||||
|
||||
runtimedetails_checkno=no
|
||||
runtimedetails_checkyes=yes
|
||||
runtimedetails_checkunknown=unknown (requires ADB Helper 0.4.0 or later)
|
||||
runtimedetails_notUSBDevice=Not a USB device
|
||||
|
||||
# Validation status
|
||||
status_tooltip=Validation status: %1$S
|
||||
status_valid=VALID
|
||||
status_warning=WARNINGS
|
||||
status_error=ERRORS
|
||||
status_unknown=UNKNOWN
|
||||
|
||||
# Device preferences and settings
|
||||
device_reset_default=Reset to default
|
||||
|
||||
# Simulator options
|
||||
simulator_custom_device=Custom
|
||||
simulator_custom_binary=Custom B2G binary…
|
||||
simulator_custom_profile=Custom Gaia profile…
|
||||
simulator_default_profile=Use default
|
|
@ -86,17 +86,6 @@ exports.menuitems = [
|
|||
},
|
||||
checkbox: true
|
||||
},
|
||||
{ id: "menu_webide",
|
||||
l10nKey: "webide",
|
||||
disabled: true,
|
||||
oncommand() {
|
||||
gDevToolsBrowser.openWebIDE();
|
||||
},
|
||||
key: {
|
||||
id: "webide",
|
||||
modifiers: "shift"
|
||||
}
|
||||
},
|
||||
{ id: "menu_browserToolbox",
|
||||
l10nKey: "browserToolboxMenu",
|
||||
disabled: true,
|
||||
|
|
|
@ -34,7 +34,6 @@ DIRS += [
|
|||
'themes',
|
||||
'webaudioeditor',
|
||||
'webconsole',
|
||||
'webide',
|
||||
]
|
||||
|
||||
# Shim old theme paths used by DevTools add-ons
|
||||
|
|
|
@ -21,9 +21,6 @@ pref("devtools.loader.hotreload", false);
|
|||
pref("devtools.toolbar.enabled", true);
|
||||
pref("devtools.toolbar.visible", false);
|
||||
|
||||
// Enable DevTools WebIDE by default
|
||||
pref("devtools.webide.enabled", true);
|
||||
|
||||
// Toolbox preferences
|
||||
pref("devtools.toolbox.footer.height", 250);
|
||||
pref("devtools.toolbox.sidebar.width", 500);
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* 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 { Class } = require("sdk/core/heritage");
|
||||
const promise = require("promise");
|
||||
const { ItchEditor } = require("devtools/client/projecteditor/lib/editors");
|
||||
|
||||
var AppProjectEditor = Class({
|
||||
extends: ItchEditor,
|
||||
|
||||
hidesToolbar: true,
|
||||
|
||||
initialize: function (host) {
|
||||
ItchEditor.prototype.initialize.apply(this, arguments);
|
||||
this.appended = promise.resolve();
|
||||
this.host = host;
|
||||
this.label = "app-manager";
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.elt.remove();
|
||||
this.elt = null;
|
||||
},
|
||||
|
||||
load: function (resource) {
|
||||
let {appManagerOpts} = this.host.project;
|
||||
|
||||
// Only load the frame the first time it is selected
|
||||
if (!this.iframe || this.iframe.getAttribute("src") !== appManagerOpts.projectOverviewURL) {
|
||||
|
||||
this.elt.textContent = "";
|
||||
let iframe = this.iframe = this.elt.ownerDocument.createElement("iframe");
|
||||
let iframeLoaded = this.iframeLoaded = promise.defer();
|
||||
|
||||
iframe.addEventListener("load", function onLoad() {
|
||||
iframe.removeEventListener("load", onLoad);
|
||||
iframeLoaded.resolve();
|
||||
});
|
||||
|
||||
iframe.setAttribute("flex", "1");
|
||||
iframe.setAttribute("src", appManagerOpts.projectOverviewURL);
|
||||
this.elt.appendChild(iframe);
|
||||
|
||||
}
|
||||
|
||||
promise.all([this.iframeLoaded.promise, this.appended]).then(() => {
|
||||
this.emit("load");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
exports.AppProjectEditor = AppProjectEditor;
|
|
@ -1,10 +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(
|
||||
'app-project-editor.js',
|
||||
'plugin.js',
|
||||
)
|
|
@ -1,77 +0,0 @@
|
|||
const { Cu } = require("chrome");
|
||||
const { Class } = require("sdk/core/heritage");
|
||||
const { EventTarget } = require("sdk/event/target");
|
||||
const { emit } = require("sdk/event/core");
|
||||
const promise = require("promise");
|
||||
var { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
|
||||
const { AppProjectEditor } = require("./app-project-editor");
|
||||
const OPTION_URL = "chrome://devtools/skin/images/tool-options.svg";
|
||||
const Services = require("Services");
|
||||
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
|
||||
|
||||
var AppManagerRenderer = Class({
|
||||
extends: Plugin,
|
||||
|
||||
isAppManagerProject: function () {
|
||||
return !!this.host.project.appManagerOpts;
|
||||
},
|
||||
editorForResource: function (resource) {
|
||||
if (!resource.parent && this.isAppManagerProject()) {
|
||||
return AppProjectEditor;
|
||||
}
|
||||
},
|
||||
getUI: function (parent) {
|
||||
let doc = parent.ownerDocument;
|
||||
if (parent.childElementCount == 0) {
|
||||
let image = doc.createElement("image");
|
||||
let optionImage = doc.createElement("image");
|
||||
let flexElement = doc.createElement("div");
|
||||
let nameLabel = doc.createElement("span");
|
||||
let statusElement = doc.createElement("div");
|
||||
|
||||
image.className = "project-image";
|
||||
optionImage.className = "project-options";
|
||||
optionImage.setAttribute("src", OPTION_URL);
|
||||
nameLabel.className = "project-name-label";
|
||||
statusElement.className = "project-status";
|
||||
flexElement.className = "project-flex";
|
||||
|
||||
parent.appendChild(image);
|
||||
parent.appendChild(nameLabel);
|
||||
parent.appendChild(flexElement);
|
||||
parent.appendChild(statusElement);
|
||||
parent.appendChild(optionImage);
|
||||
}
|
||||
|
||||
return {
|
||||
image: parent.querySelector(".project-image"),
|
||||
nameLabel: parent.querySelector(".project-name-label"),
|
||||
statusElement: parent.querySelector(".project-status")
|
||||
};
|
||||
},
|
||||
onAnnotate: function (resource, editor, elt) {
|
||||
if (resource.parent || !this.isAppManagerProject()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let {appManagerOpts} = this.host.project;
|
||||
let doc = elt.ownerDocument;
|
||||
|
||||
let {image, nameLabel, statusElement} = this.getUI(elt);
|
||||
let name = appManagerOpts.name || resource.basename;
|
||||
let url = appManagerOpts.iconUrl || "icon-sample.png";
|
||||
let status = appManagerOpts.validationStatus || "unknown";
|
||||
let tooltip = Strings.formatStringFromName("status_tooltip",
|
||||
[Strings.GetStringFromName("status_" + status)], 1);
|
||||
|
||||
nameLabel.textContent = name;
|
||||
image.setAttribute("src", url);
|
||||
statusElement.setAttribute("status", status);
|
||||
statusElement.setAttribute("tooltiptext", tooltip);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
exports.AppManagerRenderer = AppManagerRenderer;
|
||||
registerPlugin(AppManagerRenderer);
|
|
@ -5,7 +5,6 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += [
|
||||
'app-manager',
|
||||
'delete',
|
||||
'dirty',
|
||||
'image-view',
|
||||
|
|
|
@ -31,7 +31,6 @@ require("devtools/client/projecteditor/lib/plugins/new/new");
|
|||
require("devtools/client/projecteditor/lib/plugins/rename/rename");
|
||||
require("devtools/client/projecteditor/lib/plugins/save/save");
|
||||
require("devtools/client/projecteditor/lib/plugins/image-view/plugin");
|
||||
require("devtools/client/projecteditor/lib/plugins/app-manager/plugin");
|
||||
require("devtools/client/projecteditor/lib/plugins/status-bar/plugin");
|
||||
|
||||
// Uncomment to enable logging.
|
||||
|
|
|
@ -177,23 +177,6 @@ Telemetry.prototype = {
|
|||
histogram: "DEVTOOLS_ABOUTDEBUGGING_OPENED_COUNT",
|
||||
timerHistogram: "DEVTOOLS_ABOUTDEBUGGING_TIME_ACTIVE_SECONDS"
|
||||
},
|
||||
webide: {
|
||||
histogram: "DEVTOOLS_WEBIDE_OPENED_COUNT",
|
||||
timerHistogram: "DEVTOOLS_WEBIDE_TIME_ACTIVE_SECONDS"
|
||||
},
|
||||
webideProjectEditor: {
|
||||
histogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_OPENED_COUNT",
|
||||
timerHistogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_TIME_ACTIVE_SECONDS"
|
||||
},
|
||||
webideProjectEditorSave: {
|
||||
histogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_SAVE_COUNT",
|
||||
},
|
||||
webideNewProject: {
|
||||
histogram: "DEVTOOLS_WEBIDE_NEW_PROJECT_COUNT",
|
||||
},
|
||||
webideImportProject: {
|
||||
histogram: "DEVTOOLS_WEBIDE_IMPORT_PROJECT_COUNT",
|
||||
},
|
||||
custom: {
|
||||
histogram: "DEVTOOLS_CUSTOM_OPENED_COUNT",
|
||||
timerHistogram: "DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS"
|
||||
|
|
|
@ -1,10 +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/.
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'webideCli.js',
|
||||
'webideComponents.manifest',
|
||||
]
|
|
@ -1,58 +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 = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
|
||||
|
||||
/**
|
||||
* Handles --webide command line option.
|
||||
*/
|
||||
|
||||
function webideCli() { }
|
||||
|
||||
webideCli.prototype = {
|
||||
handle: function (cmdLine) {
|
||||
if (!cmdLine.handleFlag("webide", false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If --webide is used remotely, we don't want to open
|
||||
// a new tab.
|
||||
//
|
||||
// If --webide is used for a new Firefox instance, we
|
||||
// want to open webide only.
|
||||
cmdLine.preventDefault = true;
|
||||
|
||||
let win = Services.wm.getMostRecentWindow("devtools:webide");
|
||||
if (win) {
|
||||
win.focus();
|
||||
} else {
|
||||
win = Services.ww.openWindow(null,
|
||||
"chrome://webide/content/",
|
||||
"webide",
|
||||
"chrome,centerscreen,resizable,dialog=no",
|
||||
null);
|
||||
}
|
||||
|
||||
if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
|
||||
// If this is a new Firefox instance, and because we will only start
|
||||
// webide, we need to notify "sessionstore-windows-restored" to trigger
|
||||
// addons registration (for simulators and adb helper).
|
||||
Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
|
||||
}
|
||||
},
|
||||
|
||||
helpInfo: "",
|
||||
|
||||
classID: Components.ID("{79b7b44e-de5e-4e4c-b7a2-044003c615d9}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([webideCli]);
|
|
@ -1,4 +0,0 @@
|
|||
# webide components
|
||||
component {79b7b44e-de5e-4e4c-b7a2-044003c615d9} webideCli.js
|
||||
contract @mozilla.org/browser/webide-clh;1 {79b7b44e-de5e-4e4c-b7a2-044003c615d9}
|
||||
category command-line-handler a-webide @mozilla.org/browser/webide-clh;1
|
|
@ -1,135 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const Services = require("Services");
|
||||
const {gDevTools} = require("devtools/client/framework/devtools");
|
||||
const {GetAvailableAddons, ForgetAddonsList} = require("devtools/client/webide/modules/addons");
|
||||
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
document.querySelector("#aboutaddons").onclick = function () {
|
||||
let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
|
||||
if (browserWin && browserWin.BrowserOpenAddonsMgr) {
|
||||
browserWin.BrowserOpenAddonsMgr("addons://list/extension");
|
||||
}
|
||||
};
|
||||
document.querySelector("#close").onclick = CloseUI;
|
||||
GetAvailableAddons().then(BuildUI, (e) => {
|
||||
console.error(e);
|
||||
window.alert(Strings.formatStringFromName("error_cantFetchAddonsJSON", [e], 1));
|
||||
});
|
||||
}, true);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
ForgetAddonsList();
|
||||
}, true);
|
||||
|
||||
function CloseUI() {
|
||||
window.parent.UI.openProject();
|
||||
}
|
||||
|
||||
function BuildUI(addons) {
|
||||
BuildItem(addons.adb, "adb");
|
||||
BuildItem(addons.adapters, "adapters");
|
||||
for (let addon of addons.simulators) {
|
||||
BuildItem(addon, "simulator");
|
||||
}
|
||||
}
|
||||
|
||||
function BuildItem(addon, type) {
|
||||
|
||||
function onAddonUpdate(event, arg) {
|
||||
switch (event) {
|
||||
case "update":
|
||||
progress.removeAttribute("value");
|
||||
li.setAttribute("status", addon.status);
|
||||
status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
|
||||
break;
|
||||
case "failure":
|
||||
window.parent.UI.reportError("error_operationFail", arg);
|
||||
break;
|
||||
case "progress":
|
||||
if (arg == -1) {
|
||||
progress.removeAttribute("value");
|
||||
} else {
|
||||
progress.value = arg;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let events = ["update", "failure", "progress"];
|
||||
for (let e of events) {
|
||||
addon.on(e, onAddonUpdate);
|
||||
}
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
for (let e of events) {
|
||||
addon.off(e, onAddonUpdate);
|
||||
}
|
||||
});
|
||||
|
||||
let li = document.createElement("li");
|
||||
li.setAttribute("status", addon.status);
|
||||
|
||||
let name = document.createElement("span");
|
||||
name.className = "name";
|
||||
|
||||
switch (type) {
|
||||
case "adb":
|
||||
li.setAttribute("addon", type);
|
||||
name.textContent = Strings.GetStringFromName("addons_adb_label");
|
||||
break;
|
||||
case "adapters":
|
||||
li.setAttribute("addon", type);
|
||||
try {
|
||||
name.textContent = Strings.GetStringFromName("addons_adapters_label");
|
||||
} catch (e) {
|
||||
// This code (bug 1081093) will be backported to Aurora, which doesn't
|
||||
// contain this string.
|
||||
name.textContent = "Tools Adapters Add-on";
|
||||
}
|
||||
break;
|
||||
case "simulator":
|
||||
li.setAttribute("addon", "simulator-" + addon.version);
|
||||
let stability = Strings.GetStringFromName("addons_" + addon.stability);
|
||||
name.textContent = Strings.formatStringFromName("addons_simulator_label", [addon.version, stability], 2);
|
||||
break;
|
||||
}
|
||||
|
||||
li.appendChild(name);
|
||||
|
||||
let status = document.createElement("span");
|
||||
status.className = "status";
|
||||
status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
|
||||
li.appendChild(status);
|
||||
|
||||
let installButton = document.createElement("button");
|
||||
installButton.className = "install-button";
|
||||
installButton.onclick = () => addon.install();
|
||||
installButton.textContent = Strings.GetStringFromName("addons_install_button");
|
||||
li.appendChild(installButton);
|
||||
|
||||
let uninstallButton = document.createElement("button");
|
||||
uninstallButton.className = "uninstall-button";
|
||||
uninstallButton.onclick = () => addon.uninstall();
|
||||
uninstallButton.textContent = Strings.GetStringFromName("addons_uninstall_button");
|
||||
li.appendChild(uninstallButton);
|
||||
|
||||
let progress = document.createElement("progress");
|
||||
li.appendChild(progress);
|
||||
|
||||
if (type == "adb") {
|
||||
let warning = document.createElement("p");
|
||||
warning.textContent = Strings.GetStringFromName("addons_adb_warning");
|
||||
warning.className = "warning";
|
||||
li.appendChild(warning);
|
||||
}
|
||||
|
||||
document.querySelector("ul").appendChild(li);
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/addons.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/addons.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a id="aboutaddons">&addons_aboutaddons;</a>
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
<h1>&addons_title;</h1>
|
||||
|
||||
<ul></ul>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,139 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const Services = require("Services");
|
||||
const {AppManager} = require("devtools/client/webide/modules/app-manager");
|
||||
const {ProjectBuilding} = require("devtools/client/webide/modules/build");
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
document.addEventListener("visibilitychange", updateUI, true);
|
||||
AppManager.on("app-manager-update", onAppManagerUpdate);
|
||||
updateUI();
|
||||
}, true);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
AppManager.off("app-manager-update", onAppManagerUpdate);
|
||||
}, true);
|
||||
|
||||
function onAppManagerUpdate(event, what, details) {
|
||||
if (what == "project" ||
|
||||
what == "project-validated") {
|
||||
updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
function resetUI() {
|
||||
document.querySelector("#toolbar").classList.add("hidden");
|
||||
document.querySelector("#type").classList.add("hidden");
|
||||
document.querySelector("#descriptionHeader").classList.add("hidden");
|
||||
document.querySelector("#manifestURLHeader").classList.add("hidden");
|
||||
document.querySelector("#locationHeader").classList.add("hidden");
|
||||
|
||||
document.body.className = "";
|
||||
document.querySelector("#icon").src = "";
|
||||
document.querySelector("h1").textContent = "";
|
||||
document.querySelector("#description").textContent = "";
|
||||
document.querySelector("#type").textContent = "";
|
||||
document.querySelector("#manifestURL").textContent = "";
|
||||
document.querySelector("#location").textContent = "";
|
||||
|
||||
document.querySelector("#prePackageLog").hidden = true;
|
||||
|
||||
document.querySelector("#errorslist").innerHTML = "";
|
||||
document.querySelector("#warningslist").innerHTML = "";
|
||||
|
||||
}
|
||||
|
||||
function updateUI() {
|
||||
resetUI();
|
||||
|
||||
let project = AppManager.selectedProject;
|
||||
if (!project) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (project.type != "runtimeApp" && project.type != "mainProcess") {
|
||||
document.querySelector("#toolbar").classList.remove("hidden");
|
||||
document.querySelector("#locationHeader").classList.remove("hidden");
|
||||
document.querySelector("#location").textContent = project.location;
|
||||
}
|
||||
|
||||
document.body.className = project.validationStatus;
|
||||
document.querySelector("#icon").src = project.icon;
|
||||
document.querySelector("h1").textContent = project.name;
|
||||
|
||||
let manifest;
|
||||
if (project.type == "runtimeApp") {
|
||||
manifest = project.app.manifest;
|
||||
} else {
|
||||
manifest = project.manifest;
|
||||
}
|
||||
|
||||
if (manifest) {
|
||||
if (manifest.description) {
|
||||
document.querySelector("#descriptionHeader").classList.remove("hidden");
|
||||
document.querySelector("#description").textContent = manifest.description;
|
||||
}
|
||||
|
||||
document.querySelector("#type").classList.remove("hidden");
|
||||
|
||||
if (project.type == "runtimeApp") {
|
||||
let manifestURL = AppManager.getProjectManifestURL(project);
|
||||
document.querySelector("#type").textContent = manifest.type || "web";
|
||||
document.querySelector("#manifestURLHeader").classList.remove("hidden");
|
||||
document.querySelector("#manifestURL").textContent = manifestURL;
|
||||
} else if (project.type == "mainProcess") {
|
||||
document.querySelector("#type").textContent = project.name;
|
||||
} else {
|
||||
document.querySelector("#type").textContent = project.type + " " + (manifest.type || "web");
|
||||
}
|
||||
|
||||
if (project.type == "packaged") {
|
||||
let manifestURL = AppManager.getProjectManifestURL(project);
|
||||
if (manifestURL) {
|
||||
document.querySelector("#manifestURLHeader").classList.remove("hidden");
|
||||
document.querySelector("#manifestURL").textContent = manifestURL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (project.type != "runtimeApp" && project.type != "mainProcess") {
|
||||
ProjectBuilding.hasPrepackage(project).then(hasPrepackage => {
|
||||
document.querySelector("#prePackageLog").hidden = !hasPrepackage;
|
||||
});
|
||||
}
|
||||
|
||||
let errorsNode = document.querySelector("#errorslist");
|
||||
let warningsNode = document.querySelector("#warningslist");
|
||||
|
||||
if (project.errors) {
|
||||
for (let e of project.errors) {
|
||||
let li = document.createElement("li");
|
||||
li.textContent = e;
|
||||
errorsNode.appendChild(li);
|
||||
}
|
||||
}
|
||||
|
||||
if (project.warnings) {
|
||||
for (let w of project.warnings) {
|
||||
let li = document.createElement("li");
|
||||
li.textContent = w;
|
||||
warningsNode.appendChild(li);
|
||||
}
|
||||
}
|
||||
|
||||
AppManager.update("details");
|
||||
}
|
||||
|
||||
function showPrepackageLog() {
|
||||
window.top.UI.selectDeckPanel("logs");
|
||||
}
|
||||
|
||||
function removeProject() {
|
||||
AppManager.removeSelectedProject();
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/details.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/details.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="toolbar">
|
||||
<button onclick="removeProject()">&details_removeProject_button;</button>
|
||||
<p id="validation_status">
|
||||
<span class="valid">&details_valid_header;</span>
|
||||
<span class="warning">&details_warning_header;</span>
|
||||
<span class="error">&details_error_header;</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<header>
|
||||
<img id="icon"></img>
|
||||
<div>
|
||||
<h1></h1>
|
||||
<p id="type"></p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<h3 id="descriptionHeader">&details_description;</h3>
|
||||
<p id="description"></p>
|
||||
|
||||
<h3 id="locationHeader">&details_location;</h3>
|
||||
<p id="location"></p>
|
||||
|
||||
<h3 id="manifestURLHeader">&details_manifestURL;</h3>
|
||||
<p id="manifestURL"></p>
|
||||
|
||||
<button id="prePackageLog" onclick="showPrepackageLog()" hidden="true">&details_showPrepackageLog_button;</button>
|
||||
</main>
|
||||
|
||||
<ul class="validation_messages" id="errorslist"></ul>
|
||||
<ul class="validation_messages" id="warningslist"></ul>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,81 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {AppManager} = require("devtools/client/webide/modules/app-manager");
|
||||
const {Connection} = require("devtools/shared/client/connection-manager");
|
||||
const ConfigView = require("devtools/client/webide/modules/config-view");
|
||||
|
||||
var configView = new ConfigView(window);
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
AppManager.on("app-manager-update", OnAppManagerUpdate);
|
||||
document.getElementById("close").onclick = CloseUI;
|
||||
document.getElementById("device-fields").onchange = UpdateField;
|
||||
document.getElementById("device-fields").onclick = CheckReset;
|
||||
document.getElementById("search-bar").onkeyup = document.getElementById("search-bar").onclick = SearchField;
|
||||
document.getElementById("custom-value").onclick = UpdateNewField;
|
||||
document.getElementById("custom-value-type").onchange = ClearNewFields;
|
||||
document.getElementById("add-custom-field").onkeyup = CheckNewFieldSubmit;
|
||||
BuildUI();
|
||||
}, true);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
AppManager.off("app-manager-update", OnAppManagerUpdate);
|
||||
});
|
||||
|
||||
function CloseUI() {
|
||||
window.parent.UI.openProject();
|
||||
}
|
||||
|
||||
function OnAppManagerUpdate(event, what) {
|
||||
if (what == "connection" || what == "runtime-global-actors") {
|
||||
BuildUI();
|
||||
}
|
||||
}
|
||||
|
||||
function CheckNewFieldSubmit(event) {
|
||||
configView.checkNewFieldSubmit(event);
|
||||
}
|
||||
|
||||
function UpdateNewField() {
|
||||
configView.updateNewField();
|
||||
}
|
||||
|
||||
function ClearNewFields() {
|
||||
configView.clearNewFields();
|
||||
}
|
||||
|
||||
function CheckReset(event) {
|
||||
configView.checkReset(event);
|
||||
}
|
||||
|
||||
function UpdateField(event) {
|
||||
configView.updateField(event);
|
||||
}
|
||||
|
||||
function SearchField(event) {
|
||||
configView.search(event);
|
||||
}
|
||||
|
||||
var getAllPrefs; // Used by tests
|
||||
function BuildUI() {
|
||||
configView.resetTable();
|
||||
|
||||
if (AppManager.connection &&
|
||||
AppManager.connection.status == Connection.Status.CONNECTED &&
|
||||
AppManager.preferenceFront) {
|
||||
configView.front = AppManager.preferenceFront;
|
||||
configView.kind = "Pref";
|
||||
configView.includeTypeName = true;
|
||||
|
||||
getAllPrefs = AppManager.preferenceFront.getAllPrefs()
|
||||
.then(json => configView.generateDisplay(json));
|
||||
} else {
|
||||
CloseUI();
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/config-view.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/devicepreferences.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div id="controls">
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
<h1>&devicepreference_title;</h1>
|
||||
<div id="search">
|
||||
<input type="text" id="search-bar" placeholder="&devicepreference_search;"/>
|
||||
</div>
|
||||
</header>
|
||||
<table id="device-fields">
|
||||
<tr id="add-custom-field">
|
||||
<td>
|
||||
<select id="custom-value-type">
|
||||
<option value="" selected="selected">&device_typenone;</option>
|
||||
<option value="boolean">&device_typeboolean;</option>
|
||||
<option value="number">&device_typenumber;</option>
|
||||
<option value="string">&device_typestring;</option>
|
||||
</select>
|
||||
<input type="text" id="custom-value-name" placeholder="&devicepreference_newname;"/>
|
||||
</td>
|
||||
<td class="custom-input">
|
||||
<input type="text" id="custom-value-text" placeholder="&devicepreference_newtext;"/>
|
||||
</td>
|
||||
<td>
|
||||
<button id="custom-value" class="new-editable">&devicepreference_addnew;</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -1,81 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {AppManager} = require("devtools/client/webide/modules/app-manager");
|
||||
const {Connection} = require("devtools/shared/client/connection-manager");
|
||||
const ConfigView = require("devtools/client/webide/modules/config-view");
|
||||
|
||||
var configView = new ConfigView(window);
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
AppManager.on("app-manager-update", OnAppManagerUpdate);
|
||||
document.getElementById("close").onclick = CloseUI;
|
||||
document.getElementById("device-fields").onchange = UpdateField;
|
||||
document.getElementById("device-fields").onclick = CheckReset;
|
||||
document.getElementById("search-bar").onkeyup = document.getElementById("search-bar").onclick = SearchField;
|
||||
document.getElementById("custom-value").onclick = UpdateNewField;
|
||||
document.getElementById("custom-value-type").onchange = ClearNewFields;
|
||||
document.getElementById("add-custom-field").onkeyup = CheckNewFieldSubmit;
|
||||
BuildUI();
|
||||
}, true);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
AppManager.off("app-manager-update", OnAppManagerUpdate);
|
||||
});
|
||||
|
||||
function CloseUI() {
|
||||
window.parent.UI.openProject();
|
||||
}
|
||||
|
||||
function OnAppManagerUpdate(event, what) {
|
||||
if (what == "connection" || what == "runtime-global-actors") {
|
||||
BuildUI();
|
||||
}
|
||||
}
|
||||
|
||||
function CheckNewFieldSubmit(event) {
|
||||
configView.checkNewFieldSubmit(event);
|
||||
}
|
||||
|
||||
function UpdateNewField() {
|
||||
configView.updateNewField();
|
||||
}
|
||||
|
||||
function ClearNewFields() {
|
||||
configView.clearNewFields();
|
||||
}
|
||||
|
||||
function CheckReset(event) {
|
||||
configView.checkReset(event);
|
||||
}
|
||||
|
||||
function UpdateField(event) {
|
||||
configView.updateField(event);
|
||||
}
|
||||
|
||||
function SearchField(event) {
|
||||
configView.search(event);
|
||||
}
|
||||
|
||||
var getAllSettings; // Used by tests
|
||||
function BuildUI() {
|
||||
configView.resetTable();
|
||||
|
||||
if (AppManager.connection &&
|
||||
AppManager.connection.status == Connection.Status.CONNECTED &&
|
||||
AppManager.settingsFront) {
|
||||
configView.front = AppManager.settingsFront;
|
||||
configView.kind = "Setting";
|
||||
configView.includeTypeName = false;
|
||||
|
||||
getAllSettings = AppManager.settingsFront.getAllSettings()
|
||||
.then(json => configView.generateDisplay(json));
|
||||
} else {
|
||||
CloseUI();
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/config-view.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/devicesettings.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div id="controls">
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
<h1>&devicesetting_title;</h1>
|
||||
<div id="search">
|
||||
<input type="text" id="search-bar" placeholder="&devicesetting_search;"/>
|
||||
</div>
|
||||
</header>
|
||||
<table id="device-fields">
|
||||
<tr id="add-custom-field">
|
||||
<td>
|
||||
<select id="custom-value-type">
|
||||
<option value="" selected="selected">&device_typenone;</option>
|
||||
<option value="boolean">&device_typeboolean;</option>
|
||||
<option value="number">&device_typenumber;</option>
|
||||
<option value="string">&device_typestring;</option>
|
||||
<option value="object">&device_typeobject;</option>
|
||||
</select>
|
||||
<input type="text" id="custom-value-name" placeholder="&devicesetting_newname;"/>
|
||||
</td>
|
||||
<td class="custom-input">
|
||||
<input type="text" id="custom-value-text" placeholder="&devicesetting_newtext;"/>
|
||||
</td>
|
||||
<td>
|
||||
<button id="custom-value" class="new-editable">&devicesetting_addnew;</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -1,38 +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/.
|
||||
|
||||
webide.jar:
|
||||
% content webide %content/
|
||||
content/webide.xul (webide.xul)
|
||||
content/webide.js (webide.js)
|
||||
content/newapp.xul (newapp.xul)
|
||||
content/newapp.js (newapp.js)
|
||||
content/details.xhtml (details.xhtml)
|
||||
content/details.js (details.js)
|
||||
content/addons.js (addons.js)
|
||||
content/addons.xhtml (addons.xhtml)
|
||||
content/permissionstable.js (permissionstable.js)
|
||||
content/permissionstable.xhtml (permissionstable.xhtml)
|
||||
content/runtimedetails.js (runtimedetails.js)
|
||||
content/runtimedetails.xhtml (runtimedetails.xhtml)
|
||||
content/prefs.js (prefs.js)
|
||||
content/prefs.xhtml (prefs.xhtml)
|
||||
content/monitor.xhtml (monitor.xhtml)
|
||||
content/monitor.js (monitor.js)
|
||||
content/devicepreferences.js (devicepreferences.js)
|
||||
content/devicepreferences.xhtml (devicepreferences.xhtml)
|
||||
content/devicesettings.js (devicesettings.js)
|
||||
content/devicesettings.xhtml (devicesettings.xhtml)
|
||||
content/wifi-auth.js (wifi-auth.js)
|
||||
content/wifi-auth.xhtml (wifi-auth.xhtml)
|
||||
content/logs.xhtml (logs.xhtml)
|
||||
content/logs.js (logs.js)
|
||||
content/project-listing.xhtml (project-listing.xhtml)
|
||||
content/project-listing.js (project-listing.js)
|
||||
content/project-panel.js (project-panel.js)
|
||||
content/runtime-panel.js (runtime-panel.js)
|
||||
content/runtime-listing.xhtml (runtime-listing.xhtml)
|
||||
content/runtime-listing.js (runtime-listing.js)
|
||||
content/simulator.js (simulator.js)
|
||||
content/simulator.xhtml (simulator.xhtml)
|
|
@ -1,70 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {AppManager} = require("devtools/client/webide/modules/app-manager");
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
|
||||
Logs.init();
|
||||
});
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
|
||||
Logs.uninit();
|
||||
});
|
||||
|
||||
const Logs = {
|
||||
init: function () {
|
||||
this.list = document.getElementById("logs");
|
||||
|
||||
Logs.onAppManagerUpdate = Logs.onAppManagerUpdate.bind(this);
|
||||
AppManager.on("app-manager-update", Logs.onAppManagerUpdate);
|
||||
|
||||
document.getElementById("close").onclick = Logs.close.bind(this);
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
AppManager.off("app-manager-update", Logs.onAppManagerUpdate);
|
||||
},
|
||||
|
||||
onAppManagerUpdate: function (event, what, details) {
|
||||
switch (what) {
|
||||
case "pre-package":
|
||||
this.prePackageLog(details);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
close: function () {
|
||||
window.parent.UI.openProject();
|
||||
},
|
||||
|
||||
prePackageLog: function (msg, details) {
|
||||
if (msg == "start") {
|
||||
this.clear();
|
||||
} else if (msg == "succeed") {
|
||||
setTimeout(function () {
|
||||
Logs.close();
|
||||
}, 1000);
|
||||
} else if (msg == "failed") {
|
||||
this.log(details);
|
||||
} else {
|
||||
this.log(msg);
|
||||
}
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this.list.innerHTML = "";
|
||||
},
|
||||
|
||||
log: function (msg) {
|
||||
let line = document.createElement("li");
|
||||
line.textContent = msg;
|
||||
this.list.appendChild(line);
|
||||
}
|
||||
};
|
|
@ -1,33 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="resource://devtools/client/themes/common.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/logs.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"></script>
|
||||
<script type="application/javascript;version=1.8" src="logs.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
<h1>&logs_title;</h1>
|
||||
|
||||
<ul id="logs" class="devtools-monospace">
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,741 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const Services = require("Services");
|
||||
const {AppManager} = require("devtools/client/webide/modules/app-manager");
|
||||
const {AppActorFront} = require("devtools/shared/apps/app-actor-front");
|
||||
const {Connection} = require("devtools/shared/client/connection-manager");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
window.addEventListener("resize", Monitor.resize);
|
||||
window.addEventListener("unload", Monitor.unload);
|
||||
|
||||
document.querySelector("#close").onclick = () => {
|
||||
window.parent.UI.openProject();
|
||||
};
|
||||
|
||||
Monitor.load();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* The Monitor is a WebIDE tool used to display any kind of time-based data in
|
||||
* the form of graphs.
|
||||
*
|
||||
* The data can come from a Firefox OS device, simulator, or from a WebSockets
|
||||
* server running locally.
|
||||
*
|
||||
* The format of a data update is typically an object like:
|
||||
*
|
||||
* { graph: 'mygraph', curve: 'mycurve', value: 42, time: 1234 }
|
||||
*
|
||||
* or an array of such objects. For more details on the data format, see the
|
||||
* `Graph.update(data)` method.
|
||||
*/
|
||||
var Monitor = {
|
||||
|
||||
apps: new Map(),
|
||||
graphs: new Map(),
|
||||
front: null,
|
||||
socket: null,
|
||||
wstimeout: null,
|
||||
b2ginfo: false,
|
||||
b2gtimeout: null,
|
||||
|
||||
/**
|
||||
* Add new data to the graphs, create a new graph if necessary.
|
||||
*/
|
||||
update: function (data, fallback) {
|
||||
if (Array.isArray(data)) {
|
||||
data.forEach(d => Monitor.update(d, fallback));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Monitor.b2ginfo && data.graph === "USS") {
|
||||
// If we're polling b2g-info, ignore USS updates from the device's
|
||||
// USSAgents (see Monitor.pollB2GInfo()).
|
||||
return;
|
||||
}
|
||||
|
||||
if (fallback) {
|
||||
for (let key in fallback) {
|
||||
if (!data[key]) {
|
||||
data[key] = fallback[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let graph = Monitor.graphs.get(data.graph);
|
||||
if (!graph) {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("graph");
|
||||
document.body.appendChild(element);
|
||||
|
||||
graph = new Graph(data.graph, element);
|
||||
Monitor.resize(); // a scrollbar might have dis/reappeared
|
||||
Monitor.graphs.set(data.graph, graph);
|
||||
}
|
||||
graph.update(data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the Monitor.
|
||||
*/
|
||||
load: function () {
|
||||
AppManager.on("app-manager-update", Monitor.onAppManagerUpdate);
|
||||
Monitor.connectToRuntime();
|
||||
Monitor.connectToWebSocket();
|
||||
},
|
||||
|
||||
/**
|
||||
* Clean up the Monitor.
|
||||
*/
|
||||
unload: function () {
|
||||
AppManager.off("app-manager-update", Monitor.onAppManagerUpdate);
|
||||
Monitor.disconnectFromRuntime();
|
||||
Monitor.disconnectFromWebSocket();
|
||||
},
|
||||
|
||||
/**
|
||||
* Resize all the graphs.
|
||||
*/
|
||||
resize: function () {
|
||||
for (let graph of Monitor.graphs.values()) {
|
||||
graph.resize();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When WebIDE connects to a new runtime, start its data forwarders.
|
||||
*/
|
||||
onAppManagerUpdate: function (event, what, details) {
|
||||
switch (what) {
|
||||
case "runtime-global-actors":
|
||||
Monitor.connectToRuntime();
|
||||
break;
|
||||
case "connection":
|
||||
if (AppManager.connection.status == Connection.Status.DISCONNECTED) {
|
||||
Monitor.disconnectFromRuntime();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Use an AppActorFront on a runtime to watch track its apps.
|
||||
*/
|
||||
connectToRuntime: function () {
|
||||
Monitor.pollB2GInfo();
|
||||
let client = AppManager.connection && AppManager.connection.client;
|
||||
let resp = AppManager._listTabsResponse;
|
||||
if (client && resp && !Monitor.front) {
|
||||
Monitor.front = new AppActorFront(client, resp);
|
||||
Monitor.front.watchApps(Monitor.onRuntimeAppEvent);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy our AppActorFront.
|
||||
*/
|
||||
disconnectFromRuntime: function () {
|
||||
Monitor.unpollB2GInfo();
|
||||
if (Monitor.front) {
|
||||
Monitor.front.unwatchApps(Monitor.onRuntimeAppEvent);
|
||||
Monitor.front = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Try connecting to a local websockets server and accept updates from it.
|
||||
*/
|
||||
connectToWebSocket: function () {
|
||||
let webSocketURL = Services.prefs.getCharPref("devtools.webide.monitorWebSocketURL");
|
||||
try {
|
||||
Monitor.socket = new WebSocket(webSocketURL);
|
||||
Monitor.socket.onmessage = function (event) {
|
||||
Monitor.update(JSON.parse(event.data));
|
||||
};
|
||||
Monitor.socket.onclose = function () {
|
||||
Monitor.wstimeout = setTimeout(Monitor.connectToWebsocket, 1000);
|
||||
};
|
||||
} catch (e) {
|
||||
Monitor.wstimeout = setTimeout(Monitor.connectToWebsocket, 1000);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Used when cleaning up.
|
||||
*/
|
||||
disconnectFromWebSocket: function () {
|
||||
clearTimeout(Monitor.wstimeout);
|
||||
if (Monitor.socket) {
|
||||
Monitor.socket.onclose = () => {};
|
||||
Monitor.socket.close();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When an app starts on the runtime, start a monitor actor for its process.
|
||||
*/
|
||||
onRuntimeAppEvent: function (type, app) {
|
||||
if (type !== "appOpen" && type !== "appClose") {
|
||||
return;
|
||||
}
|
||||
|
||||
let client = AppManager.connection.client;
|
||||
app.getForm().then(form => {
|
||||
if (type === "appOpen") {
|
||||
app.monitorClient = new MonitorClient(client, form);
|
||||
app.monitorClient.start();
|
||||
app.monitorClient.on("update", Monitor.onRuntimeUpdate);
|
||||
Monitor.apps.set(form.monitorActor, app);
|
||||
} else {
|
||||
let app = Monitor.apps.get(form.monitorActor);
|
||||
if (app) {
|
||||
app.monitorClient.stop(() => app.monitorClient.destroy());
|
||||
Monitor.apps.delete(form.monitorActor);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Accept data updates from the monitor actors of a runtime.
|
||||
*/
|
||||
onRuntimeUpdate: function (type, packet) {
|
||||
let fallback = {}, app = Monitor.apps.get(packet.from);
|
||||
if (app) {
|
||||
fallback.curve = app.manifest.name;
|
||||
}
|
||||
Monitor.update(packet.data, fallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Bug 1047355: If possible, parsing the output of `b2g-info` has several
|
||||
* benefits over bug 1037465's multi-process USSAgent approach, notably:
|
||||
* - Works for older Firefox OS devices (pre-2.1),
|
||||
* - Doesn't need certified-apps debugging,
|
||||
* - Polling time is synchronized for all processes.
|
||||
* TODO: After bug 1043324 lands, consider removing this hack.
|
||||
*/
|
||||
pollB2GInfo: function () {
|
||||
if (AppManager.selectedRuntime) {
|
||||
let device = AppManager.selectedRuntime.device;
|
||||
if (device && device.shell) {
|
||||
device.shell("b2g-info").then(s => {
|
||||
let lines = s.split("\n");
|
||||
let line = "";
|
||||
|
||||
// Find the header row to locate NAME and USS, looks like:
|
||||
// ' NAME PID NICE USS PSS RSS VSIZE OOM_ADJ USER '.
|
||||
while (line.indexOf("NAME") < 0) {
|
||||
if (lines.length < 1) {
|
||||
// Something is wrong with this output, don't trust b2g-info.
|
||||
Monitor.unpollB2GInfo();
|
||||
return;
|
||||
}
|
||||
line = lines.shift();
|
||||
}
|
||||
let namelength = line.indexOf("NAME") + "NAME".length;
|
||||
let ussindex = line.slice(namelength).split(/\s+/).indexOf("USS");
|
||||
|
||||
// Get the NAME and USS in each following line, looks like:
|
||||
// 'Homescreen 375 18 12.6 16.3 27.1 67.8 4 app_375'.
|
||||
while (lines.length > 0 && lines[0].length > namelength) {
|
||||
line = lines.shift();
|
||||
let name = line.slice(0, namelength);
|
||||
let uss = line.slice(namelength).split(/\s+/)[ussindex];
|
||||
Monitor.update({
|
||||
curve: name.trim(),
|
||||
value: 1024 * 1024 * parseFloat(uss) // Convert MB to bytes.
|
||||
}, {
|
||||
// Note: We use the fallback object to set the graph name to 'USS'
|
||||
// so that Monitor.update() can ignore USSAgent updates.
|
||||
graph: "USS"
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Monitor.b2ginfo = true;
|
||||
Monitor.b2gtimeout = setTimeout(Monitor.pollB2GInfo, 350);
|
||||
},
|
||||
|
||||
/**
|
||||
* Polling b2g-info doesn't work or is no longer needed.
|
||||
*/
|
||||
unpollB2GInfo: function () {
|
||||
clearTimeout(Monitor.b2gtimeout);
|
||||
Monitor.b2ginfo = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A MonitorClient is used as an actor client of a runtime's monitor actors,
|
||||
* receiving its updates.
|
||||
*/
|
||||
function MonitorClient(client, form) {
|
||||
this.client = client;
|
||||
this.actor = form.monitorActor;
|
||||
this.events = ["update"];
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
this.client.registerClient(this);
|
||||
}
|
||||
MonitorClient.prototype.destroy = function () {
|
||||
this.client.unregisterClient(this);
|
||||
};
|
||||
MonitorClient.prototype.start = function () {
|
||||
this.client.request({
|
||||
to: this.actor,
|
||||
type: "start"
|
||||
});
|
||||
};
|
||||
MonitorClient.prototype.stop = function (callback) {
|
||||
this.client.request({
|
||||
to: this.actor,
|
||||
type: "stop"
|
||||
}, callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A Graph populates a container DOM element with an SVG graph and a legend.
|
||||
*/
|
||||
function Graph(name, element) {
|
||||
this.name = name;
|
||||
this.element = element;
|
||||
this.curves = new Map();
|
||||
this.events = new Map();
|
||||
this.ignored = new Set();
|
||||
this.enabled = true;
|
||||
this.request = null;
|
||||
|
||||
this.x = d3.time.scale();
|
||||
this.y = d3.scale.linear();
|
||||
|
||||
this.xaxis = d3.svg.axis().scale(this.x).orient("bottom");
|
||||
this.yaxis = d3.svg.axis().scale(this.y).orient("left");
|
||||
|
||||
this.xformat = d3.time.format("%I:%M:%S");
|
||||
this.yformat = this.formatter(1);
|
||||
this.yaxis.tickFormat(this.formatter(0));
|
||||
|
||||
this.line = d3.svg.line().interpolate("linear")
|
||||
.x(function (d) { return this.x(d.time); })
|
||||
.y(function (d) { return this.y(d.value); });
|
||||
|
||||
this.color = d3.scale.category10();
|
||||
|
||||
this.svg = d3.select(element).append("svg").append("g")
|
||||
.attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")");
|
||||
|
||||
this.xelement = this.svg.append("g").attr("class", "x axis").call(this.xaxis);
|
||||
this.yelement = this.svg.append("g").attr("class", "y axis").call(this.yaxis);
|
||||
|
||||
// RULERS on axes
|
||||
let xruler = this.xruler = this.svg.select(".x.axis").append("g").attr("class", "x ruler");
|
||||
xruler.append("line").attr("y2", 6);
|
||||
xruler.append("line").attr("stroke-dasharray", "1,1");
|
||||
xruler.append("text").attr("y", 9).attr("dy", ".71em");
|
||||
|
||||
let yruler = this.yruler = this.svg.select(".y.axis").append("g").attr("class", "y ruler");
|
||||
yruler.append("line").attr("x2", -6);
|
||||
yruler.append("line").attr("stroke-dasharray", "1,1");
|
||||
yruler.append("text").attr("x", -9).attr("dy", ".32em");
|
||||
|
||||
let self = this;
|
||||
|
||||
d3.select(element).select("svg")
|
||||
.on("mousemove", function () {
|
||||
let mouse = d3.mouse(this);
|
||||
self.mousex = mouse[0] - self.margin.left,
|
||||
self.mousey = mouse[1] - self.margin.top;
|
||||
|
||||
xruler.attr("transform", "translate(" + self.mousex + ",0)");
|
||||
yruler.attr("transform", "translate(0," + self.mousey + ")");
|
||||
});
|
||||
/* .on('mouseout', function() {
|
||||
self.xruler.attr('transform', 'translate(-500,0)');
|
||||
self.yruler.attr('transform', 'translate(0,-500)');
|
||||
});*/
|
||||
this.mousex = this.mousey = -500;
|
||||
|
||||
let sidebar = d3.select(this.element).append("div").attr("class", "sidebar");
|
||||
let title = sidebar.append("label").attr("class", "graph-title");
|
||||
|
||||
title.append("input")
|
||||
.attr("type", "checkbox")
|
||||
.attr("checked", "true")
|
||||
.on("click", function () { self.toggle(); });
|
||||
title.append("span").text(this.name);
|
||||
|
||||
this.legend = sidebar.append("div").attr("class", "legend");
|
||||
|
||||
this.resize = this.resize.bind(this);
|
||||
this.render = this.render.bind(this);
|
||||
this.averages = this.averages.bind(this);
|
||||
|
||||
setInterval(this.averages, 1000);
|
||||
|
||||
this.resize();
|
||||
}
|
||||
|
||||
Graph.prototype = {
|
||||
|
||||
/**
|
||||
* These margin are used to properly position the SVG graph items inside the
|
||||
* container element.
|
||||
*/
|
||||
margin: {
|
||||
top: 10,
|
||||
right: 150,
|
||||
bottom: 20,
|
||||
left: 50
|
||||
},
|
||||
|
||||
/**
|
||||
* A Graph can be collapsed by the user.
|
||||
*/
|
||||
toggle: function () {
|
||||
if (this.enabled) {
|
||||
this.element.classList.add("disabled");
|
||||
this.enabled = false;
|
||||
} else {
|
||||
this.element.classList.remove("disabled");
|
||||
this.enabled = true;
|
||||
}
|
||||
Monitor.resize();
|
||||
},
|
||||
|
||||
/**
|
||||
* If the container element is resized (e.g. because the window was resized or
|
||||
* a scrollbar dis/appeared), the graph needs to be resized as well.
|
||||
*/
|
||||
resize: function () {
|
||||
let style = getComputedStyle(this.element),
|
||||
height = parseFloat(style.height) - this.margin.top - this.margin.bottom,
|
||||
width = parseFloat(style.width) - this.margin.left - this.margin.right;
|
||||
|
||||
d3.select(this.element).select("svg")
|
||||
.attr("width", width + this.margin.left)
|
||||
.attr("height", height + this.margin.top + this.margin.bottom);
|
||||
|
||||
this.x.range([0, width]);
|
||||
this.y.range([height, 0]);
|
||||
|
||||
this.xelement.attr("transform", "translate(0," + height + ")");
|
||||
this.xruler.select("line[stroke-dasharray]").attr("y2", -height);
|
||||
this.yruler.select("line[stroke-dasharray]").attr("x2", width);
|
||||
},
|
||||
|
||||
/**
|
||||
* If the domain of the Graph's data changes (on the time axis and/or on the
|
||||
* value axis), the axes' domains need to be updated and the graph items need
|
||||
* to be rescaled in order to represent all the data.
|
||||
*/
|
||||
rescale: function () {
|
||||
let gettime = v => { return v.time; },
|
||||
getvalue = v => { return v.value; },
|
||||
ignored = c => { return this.ignored.has(c.id); };
|
||||
|
||||
let xmin = null, xmax = null, ymin = null, ymax = null;
|
||||
for (let curve of this.curves.values()) {
|
||||
if (ignored(curve)) {
|
||||
continue;
|
||||
}
|
||||
if (xmax == null || curve.xmax > xmax) {
|
||||
xmax = curve.xmax;
|
||||
}
|
||||
if (xmin == null || curve.xmin < xmin) {
|
||||
xmin = curve.xmin;
|
||||
}
|
||||
if (ymax == null || curve.ymax > ymax) {
|
||||
ymax = curve.ymax;
|
||||
}
|
||||
if (ymin == null || curve.ymin < ymin) {
|
||||
ymin = curve.ymin;
|
||||
}
|
||||
}
|
||||
for (let event of this.events.values()) {
|
||||
if (ignored(event)) {
|
||||
continue;
|
||||
}
|
||||
if (xmax == null || event.xmax > xmax) {
|
||||
xmax = event.xmax;
|
||||
}
|
||||
if (xmin == null || event.xmin < xmin) {
|
||||
xmin = event.xmin;
|
||||
}
|
||||
}
|
||||
|
||||
let oldxdomain = this.x.domain();
|
||||
if (xmin != null && xmax != null) {
|
||||
this.x.domain([xmin, xmax]);
|
||||
let newxdomain = this.x.domain();
|
||||
if (newxdomain[0] !== oldxdomain[0] || newxdomain[1] !== oldxdomain[1]) {
|
||||
this.xelement.call(this.xaxis);
|
||||
}
|
||||
}
|
||||
|
||||
let oldydomain = this.y.domain();
|
||||
if (ymin != null && ymax != null) {
|
||||
this.y.domain([ymin, ymax]).nice();
|
||||
let newydomain = this.y.domain();
|
||||
if (newydomain[0] !== oldydomain[0] || newydomain[1] !== oldydomain[1]) {
|
||||
this.yelement.call(this.yaxis);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add new values to the graph.
|
||||
*/
|
||||
update: function (data) {
|
||||
delete data.graph;
|
||||
|
||||
let time = data.time || Date.now();
|
||||
delete data.time;
|
||||
|
||||
let curve = data.curve;
|
||||
delete data.curve;
|
||||
|
||||
// Single curve value, e.g. { curve: 'memory', value: 42, time: 1234 }.
|
||||
if ("value" in data) {
|
||||
this.push(this.curves, curve, [{time: time, value: data.value}]);
|
||||
delete data.value;
|
||||
}
|
||||
|
||||
// Several curve values, e.g. { curve: 'memory', values: [{value: 42, time: 1234}] }.
|
||||
if ("values" in data) {
|
||||
this.push(this.curves, curve, data.values);
|
||||
delete data.values;
|
||||
}
|
||||
|
||||
// Punctual event, e.g. { event: 'gc', time: 1234 },
|
||||
// event with duration, e.g. { event: 'jank', duration: 425, time: 1234 }.
|
||||
if ("event" in data) {
|
||||
this.push(this.events, data.event, [{time: time, value: data.duration}]);
|
||||
delete data.event;
|
||||
delete data.duration;
|
||||
}
|
||||
|
||||
// Remaining keys are curves, e.g. { time: 1234, memory: 42, battery: 13, temperature: 45 }.
|
||||
for (let key in data) {
|
||||
this.push(this.curves, key, [{time: time, value: data[key]}]);
|
||||
}
|
||||
|
||||
// If no render is currently pending, request one.
|
||||
if (this.enabled && !this.request) {
|
||||
this.request = requestAnimationFrame(this.render);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Insert new data into the graph's data structures.
|
||||
*/
|
||||
push: function (collection, id, values) {
|
||||
|
||||
// Note: collection is either `this.curves` or `this.events`.
|
||||
let item = collection.get(id);
|
||||
if (!item) {
|
||||
item = { id: id, values: [], xmin: null, xmax: null, ymin: 0, ymax: null, average: 0 };
|
||||
collection.set(id, item);
|
||||
}
|
||||
|
||||
for (let v of values) {
|
||||
let time = new Date(v.time), value = +v.value;
|
||||
// Update the curve/event's domain values.
|
||||
if (item.xmax == null || time > item.xmax) {
|
||||
item.xmax = time;
|
||||
}
|
||||
if (item.xmin == null || time < item.xmin) {
|
||||
item.xmin = time;
|
||||
}
|
||||
if (item.ymax == null || value > item.ymax) {
|
||||
item.ymax = value;
|
||||
}
|
||||
if (item.ymin == null || value < item.ymin) {
|
||||
item.ymin = value;
|
||||
}
|
||||
// Note: A curve's average is not computed here. Call `graph.averages()`.
|
||||
item.values.push({ time: time, value: value });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the SVG graph with curves, events, crosshair and legend.
|
||||
*/
|
||||
render: function () {
|
||||
this.request = null;
|
||||
this.rescale();
|
||||
|
||||
|
||||
// DATA
|
||||
|
||||
let self = this,
|
||||
getid = d => { return d.id; },
|
||||
gettime = d => { return d.time.getTime(); },
|
||||
getline = d => { return self.line(d.values); },
|
||||
getcolor = d => { return self.color(d.id); },
|
||||
getvalues = d => { return d.values; },
|
||||
ignored = d => { return self.ignored.has(d.id); };
|
||||
|
||||
// Convert our maps to arrays for d3.
|
||||
let curvedata = [...this.curves.values()],
|
||||
eventdata = [...this.events.values()],
|
||||
data = curvedata.concat(eventdata);
|
||||
|
||||
|
||||
// CURVES
|
||||
|
||||
// Map curve data to curve elements.
|
||||
let curves = this.svg.selectAll(".curve").data(curvedata, getid);
|
||||
|
||||
// Create new curves (no element corresponding to the data).
|
||||
curves.enter().append("g").attr("class", "curve").append("path")
|
||||
.style("stroke", getcolor);
|
||||
|
||||
// Delete old curves (elements corresponding to data not present anymore).
|
||||
curves.exit().remove();
|
||||
|
||||
// Update all curves from data.
|
||||
this.svg.selectAll(".curve").select("path")
|
||||
.attr("d", d => { return ignored(d) ? "" : getline(d); });
|
||||
|
||||
let height = parseFloat(getComputedStyle(this.element).height) - this.margin.top - this.margin.bottom;
|
||||
|
||||
|
||||
// EVENTS
|
||||
|
||||
// Map event data to event elements.
|
||||
let events = this.svg.selectAll(".event-slot").data(eventdata, getid);
|
||||
|
||||
// Create new events.
|
||||
events.enter().append("g").attr("class", "event-slot");
|
||||
|
||||
// Remove old events.
|
||||
events.exit().remove();
|
||||
|
||||
// Get all occurences of an event, and map its data to them.
|
||||
let lines = this.svg.selectAll(".event-slot")
|
||||
.style("stroke", d => { return ignored(d) ? "none" : getcolor(d); })
|
||||
.selectAll(".event")
|
||||
.data(getvalues, gettime);
|
||||
|
||||
// Create new event occurrence.
|
||||
lines.enter().append("line").attr("class", "event").attr("y2", height);
|
||||
|
||||
// Delete old event occurrence.
|
||||
lines.exit().remove();
|
||||
|
||||
// Update all event occurrences from data.
|
||||
this.svg.selectAll(".event")
|
||||
.attr("transform", d => { return "translate(" + self.x(d.time) + ",0)"; });
|
||||
|
||||
|
||||
// CROSSHAIR
|
||||
|
||||
// TODO select curves and events, intersect with curves and show values/hovers
|
||||
// e.g. look like http://code.shutterstock.com/rickshaw/examples/lines.html
|
||||
|
||||
// Update crosshair labels on each axis.
|
||||
this.xruler.select("text").text(self.xformat(self.x.invert(self.mousex)));
|
||||
this.yruler.select("text").text(self.yformat(self.y.invert(self.mousey)));
|
||||
|
||||
|
||||
// LEGEND
|
||||
|
||||
// Map data to legend elements.
|
||||
let legends = this.legend.selectAll("label").data(data, getid);
|
||||
|
||||
// Update averages.
|
||||
legends.attr("title", c => { return "Average: " + self.yformat(c.average); });
|
||||
|
||||
// Create new legends.
|
||||
let newlegend = legends.enter().append("label");
|
||||
newlegend.append("input").attr("type", "checkbox").attr("checked", "true").on("click", function (c) {
|
||||
if (ignored(c)) {
|
||||
this.parentElement.classList.remove("disabled");
|
||||
self.ignored.delete(c.id);
|
||||
} else {
|
||||
this.parentElement.classList.add("disabled");
|
||||
self.ignored.add(c.id);
|
||||
}
|
||||
self.update({}); // if no re-render is pending, request one.
|
||||
});
|
||||
newlegend.append("span").attr("class", "legend-color").style("background-color", getcolor);
|
||||
newlegend.append("span").attr("class", "legend-id").text(getid);
|
||||
|
||||
// Delete old legends.
|
||||
legends.exit().remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a SI value formatter with a given precision.
|
||||
*/
|
||||
formatter: function (decimals) {
|
||||
return value => {
|
||||
// Don't use sub-unit SI prefixes (milli, micro, etc.).
|
||||
if (Math.abs(value) < 1) return value.toFixed(decimals);
|
||||
// SI prefix, e.g. 1234567 will give '1.2M' at precision 1.
|
||||
let prefix = d3.formatPrefix(value);
|
||||
return prefix.scale(value).toFixed(decimals) + prefix.symbol;
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Compute the average of each time series.
|
||||
*/
|
||||
averages: function () {
|
||||
for (let c of this.curves.values()) {
|
||||
let length = c.values.length;
|
||||
if (length > 0) {
|
||||
let total = 0;
|
||||
c.values.forEach(v => total += v.value);
|
||||
c.average = (total / length);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Bisect a time serie to find the data point immediately left of `time`.
|
||||
*/
|
||||
bisectTime: d3.bisector(d => d.time).left,
|
||||
|
||||
/**
|
||||
* Get all curve values at a given time.
|
||||
*/
|
||||
valuesAt: function (time) {
|
||||
let values = { time: time };
|
||||
|
||||
for (let id of this.curves.keys()) {
|
||||
let curve = this.curves.get(id);
|
||||
|
||||
// Find the closest value just before `time`.
|
||||
let i = this.bisectTime(curve.values, time);
|
||||
if (i < 0) {
|
||||
// Curve starts after `time`, use first value.
|
||||
values[id] = curve.values[0].value;
|
||||
} else if (i > curve.values.length - 2) {
|
||||
// Curve ends before `time`, use last value.
|
||||
values[id] = curve.values[curve.values.length - 1].value;
|
||||
} else {
|
||||
// Curve has two values around `time`, interpolate.
|
||||
let v1 = curve.values[i],
|
||||
v2 = curve.values[i + 1],
|
||||
delta = (time - v1.time) / (v2.time - v1.time);
|
||||
values[id] = v1.value + (v2.value - v1.time) * delta;
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
};
|
|
@ -1,31 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/monitor.css" type="text/css"/>
|
||||
<script src="chrome://devtools/content/shared/vendor/d3.js"></script>
|
||||
<script type="application/javascript;version=1.8" src="monitor.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a href="https://developer.mozilla.org/docs/Tools/WebIDE/Monitor" target="_blank">&monitor_help;</a>
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
<h1>&monitor_title;</h1>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,7 +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/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
|
@ -1,175 +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 Cc = Components.classes;
|
||||
var Cu = Components.utils;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const Services = require("Services");
|
||||
const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
|
||||
const {AppProjects} = require("devtools/client/webide/modules/app-projects");
|
||||
const {AppManager} = require("devtools/client/webide/modules/app-manager");
|
||||
const {getJSON} = require("devtools/client/shared/getjson");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ZipUtils", "resource://gre/modules/ZipUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
|
||||
|
||||
const TEMPLATES_URL = "devtools.webide.templatesURL";
|
||||
|
||||
var gTemplateList = null;
|
||||
|
||||
// See bug 989619
|
||||
console.log = console.log.bind(console);
|
||||
console.warn = console.warn.bind(console);
|
||||
console.error = console.error.bind(console);
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
let projectNameNode = document.querySelector("#project-name");
|
||||
projectNameNode.addEventListener("input", canValidate, true);
|
||||
getTemplatesJSON();
|
||||
}, true);
|
||||
|
||||
function getTemplatesJSON() {
|
||||
getJSON(TEMPLATES_URL).then(list => {
|
||||
if (!Array.isArray(list)) {
|
||||
throw new Error("JSON response not an array");
|
||||
}
|
||||
if (list.length == 0) {
|
||||
throw new Error("JSON response is an empty array");
|
||||
}
|
||||
gTemplateList = list;
|
||||
let templatelistNode = document.querySelector("#templatelist");
|
||||
templatelistNode.innerHTML = "";
|
||||
for (let template of list) {
|
||||
let richlistitemNode = document.createElement("richlistitem");
|
||||
let imageNode = document.createElement("image");
|
||||
imageNode.setAttribute("src", template.icon);
|
||||
let labelNode = document.createElement("label");
|
||||
labelNode.setAttribute("value", template.name);
|
||||
let descriptionNode = document.createElement("description");
|
||||
descriptionNode.textContent = template.description;
|
||||
let vboxNode = document.createElement("vbox");
|
||||
vboxNode.setAttribute("flex", "1");
|
||||
richlistitemNode.appendChild(imageNode);
|
||||
vboxNode.appendChild(labelNode);
|
||||
vboxNode.appendChild(descriptionNode);
|
||||
richlistitemNode.appendChild(vboxNode);
|
||||
templatelistNode.appendChild(richlistitemNode);
|
||||
}
|
||||
templatelistNode.selectedIndex = 0;
|
||||
|
||||
/* Chrome mochitest support */
|
||||
let testOptions = window.arguments[0].testOptions;
|
||||
if (testOptions) {
|
||||
templatelistNode.selectedIndex = testOptions.index;
|
||||
document.querySelector("#project-name").value = testOptions.name;
|
||||
doOK();
|
||||
}
|
||||
}, (e) => {
|
||||
failAndBail("Can't download app templates: " + e);
|
||||
});
|
||||
}
|
||||
|
||||
function failAndBail(msg) {
|
||||
let promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
|
||||
promptService.alert(window, "error", msg);
|
||||
window.close();
|
||||
}
|
||||
|
||||
function canValidate() {
|
||||
let projectNameNode = document.querySelector("#project-name");
|
||||
let dialogNode = document.querySelector("dialog");
|
||||
if (projectNameNode.value.length > 0) {
|
||||
dialogNode.removeAttribute("buttondisabledaccept");
|
||||
} else {
|
||||
dialogNode.setAttribute("buttondisabledaccept", "true");
|
||||
}
|
||||
}
|
||||
|
||||
function doOK() {
|
||||
let projectName = document.querySelector("#project-name").value;
|
||||
|
||||
if (!projectName) {
|
||||
console.error("No project name");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gTemplateList) {
|
||||
console.error("No template index");
|
||||
return false;
|
||||
}
|
||||
|
||||
let templatelistNode = document.querySelector("#templatelist");
|
||||
if (templatelistNode.selectedIndex < 0) {
|
||||
console.error("No template selected");
|
||||
return false;
|
||||
}
|
||||
|
||||
let folder;
|
||||
|
||||
/* Chrome mochitest support */
|
||||
let testOptions = window.arguments[0].testOptions;
|
||||
if (testOptions) {
|
||||
folder = testOptions.folder;
|
||||
} else {
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
fp.init(window, "Select directory where to create app directory", Ci.nsIFilePicker.modeGetFolder);
|
||||
let res = fp.show();
|
||||
if (res == Ci.nsIFilePicker.returnCancel) {
|
||||
console.error("No directory selected");
|
||||
return false;
|
||||
}
|
||||
folder = fp.file;
|
||||
}
|
||||
|
||||
// Create subfolder with fs-friendly name of project
|
||||
let subfolder = projectName.replace(/[\\/:*?"<>|]/g, "").toLowerCase();
|
||||
let win = Services.wm.getMostRecentWindow("devtools:webide");
|
||||
folder.append(subfolder);
|
||||
|
||||
try {
|
||||
folder.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
} catch (e) {
|
||||
win.UI.reportError("error_folderCreationFailed");
|
||||
window.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Download boilerplate zip
|
||||
let template = gTemplateList[templatelistNode.selectedIndex];
|
||||
let source = template.file;
|
||||
let target = folder.clone();
|
||||
target.append(subfolder + ".zip");
|
||||
|
||||
let bail = (e) => {
|
||||
console.error(e);
|
||||
window.close();
|
||||
};
|
||||
|
||||
Downloads.fetch(source, target).then(() => {
|
||||
ZipUtils.extractFiles(target, folder);
|
||||
target.remove(false);
|
||||
AppProjects.addPackaged(folder).then((project) => {
|
||||
window.arguments[0].location = project.location;
|
||||
AppManager.validateAndUpdateProject(project).then(() => {
|
||||
if (project.manifest) {
|
||||
project.manifest.name = projectName;
|
||||
AppManager.writeManifest(project).then(() => {
|
||||
AppManager.validateAndUpdateProject(project).then(
|
||||
() => {window.close();}, bail);
|
||||
}, bail);
|
||||
} else {
|
||||
bail("Manifest not found");
|
||||
}
|
||||
}, bail);
|
||||
}, bail);
|
||||
}, bail);
|
||||
|
||||
return false;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
<?xml version="1.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/. -->
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||
<?xml-stylesheet href="chrome://webide/skin/newapp.css"?>
|
||||
|
||||
<dialog id="webide:newapp" title="&newAppWindowTitle;"
|
||||
width="600" height="400"
|
||||
buttons="accept,cancel"
|
||||
ondialogaccept="return doOK();"
|
||||
buttondisabledaccept="true"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript" src="newapp.js"></script>
|
||||
<label class="header-name" value="&newAppHeader;"/>
|
||||
|
||||
<richlistbox id="templatelist" flex="1">
|
||||
<description>&newAppLoadingTemplate;</description>
|
||||
</richlistbox>
|
||||
<vbox>
|
||||
<label class="header-name" control="project-name" value="&newAppProjectName;"/>
|
||||
<textbox id="project-name"/>
|
||||
</vbox>
|
||||
|
||||
</dialog>
|
|
@ -1,78 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const Services = require("Services");
|
||||
const {AppManager} = require("devtools/client/webide/modules/app-manager");
|
||||
const {Connection} = require("devtools/shared/client/connection-manager");
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
document.querySelector("#close").onclick = CloseUI;
|
||||
AppManager.on("app-manager-update", OnAppManagerUpdate);
|
||||
BuildUI();
|
||||
}, true);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
AppManager.off("app-manager-update", OnAppManagerUpdate);
|
||||
});
|
||||
|
||||
function CloseUI() {
|
||||
window.parent.UI.openProject();
|
||||
}
|
||||
|
||||
function OnAppManagerUpdate(event, what) {
|
||||
if (what == "connection" || what == "runtime-global-actors") {
|
||||
BuildUI();
|
||||
}
|
||||
}
|
||||
|
||||
function generateFields(json) {
|
||||
let table = document.querySelector("table");
|
||||
let permissionsTable = json.rawPermissionsTable;
|
||||
for (let name in permissionsTable) {
|
||||
let tr = document.createElement("tr");
|
||||
tr.className = "line";
|
||||
let td = document.createElement("td");
|
||||
td.textContent = name;
|
||||
tr.appendChild(td);
|
||||
for (let type of ["app", "privileged", "certified"]) {
|
||||
let td = document.createElement("td");
|
||||
if (permissionsTable[name][type] == json.ALLOW_ACTION) {
|
||||
td.textContent = "✓";
|
||||
td.className = "permallow";
|
||||
}
|
||||
if (permissionsTable[name][type] == json.PROMPT_ACTION) {
|
||||
td.textContent = "!";
|
||||
td.className = "permprompt";
|
||||
}
|
||||
if (permissionsTable[name][type] == json.DENY_ACTION) {
|
||||
td.textContent = "✕";
|
||||
td.className = "permdeny";
|
||||
}
|
||||
tr.appendChild(td);
|
||||
}
|
||||
table.appendChild(tr);
|
||||
}
|
||||
}
|
||||
|
||||
var getRawPermissionsTablePromise; // Used by tests
|
||||
function BuildUI() {
|
||||
let table = document.querySelector("table");
|
||||
let lines = table.querySelectorAll(".line");
|
||||
for (let line of lines) {
|
||||
line.remove();
|
||||
}
|
||||
|
||||
if (AppManager.connection &&
|
||||
AppManager.connection.status == Connection.Status.CONNECTED &&
|
||||
AppManager.deviceFront) {
|
||||
getRawPermissionsTablePromise = AppManager.deviceFront.getRawPermissionsTable()
|
||||
.then(json => generateFields(json));
|
||||
} else {
|
||||
CloseUI();
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/permissionstable.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/permissionstable.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
<h1>&permissionstable_title;</h1>
|
||||
|
||||
<table class="permissionstable">
|
||||
<tr>
|
||||
<th>&permissionstable_name_header;</th>
|
||||
<th>type:web</th>
|
||||
<th>type:privileged</th>
|
||||
<th>type:certified</th>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -1,108 +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 Cu = Components.utils;
|
||||
const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
|
||||
// Listen to preference changes
|
||||
let inputs = document.querySelectorAll("[data-pref]");
|
||||
for (let i of inputs) {
|
||||
let pref = i.dataset.pref;
|
||||
Services.prefs.addObserver(pref, FillForm, false);
|
||||
i.addEventListener("change", SaveForm, false);
|
||||
}
|
||||
|
||||
// Buttons
|
||||
document.querySelector("#close").onclick = CloseUI;
|
||||
document.querySelector("#restore").onclick = RestoreDefaults;
|
||||
document.querySelector("#manageComponents").onclick = ShowAddons;
|
||||
|
||||
// Initialize the controls
|
||||
FillForm();
|
||||
|
||||
}, true);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
let inputs = document.querySelectorAll("[data-pref]");
|
||||
for (let i of inputs) {
|
||||
let pref = i.dataset.pref;
|
||||
i.removeEventListener("change", SaveForm, false);
|
||||
Services.prefs.removeObserver(pref, FillForm, false);
|
||||
}
|
||||
}, true);
|
||||
|
||||
function CloseUI() {
|
||||
window.parent.UI.openProject();
|
||||
}
|
||||
|
||||
function ShowAddons() {
|
||||
window.parent.Cmds.showAddons();
|
||||
}
|
||||
|
||||
function FillForm() {
|
||||
let inputs = document.querySelectorAll("[data-pref]");
|
||||
for (let i of inputs) {
|
||||
let pref = i.dataset.pref;
|
||||
let val = GetPref(pref);
|
||||
if (i.type == "checkbox") {
|
||||
i.checked = val;
|
||||
} else {
|
||||
i.value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function SaveForm(e) {
|
||||
let inputs = document.querySelectorAll("[data-pref]");
|
||||
for (let i of inputs) {
|
||||
let pref = i.dataset.pref;
|
||||
if (i.type == "checkbox") {
|
||||
SetPref(pref, i.checked);
|
||||
} else {
|
||||
SetPref(pref, i.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function GetPref(name) {
|
||||
let type = Services.prefs.getPrefType(name);
|
||||
switch (type) {
|
||||
case Services.prefs.PREF_STRING:
|
||||
return Services.prefs.getCharPref(name);
|
||||
case Services.prefs.PREF_INT:
|
||||
return Services.prefs.getIntPref(name);
|
||||
case Services.prefs.PREF_BOOL:
|
||||
return Services.prefs.getBoolPref(name);
|
||||
default:
|
||||
throw new Error("Unknown type");
|
||||
}
|
||||
}
|
||||
|
||||
function SetPref(name, value) {
|
||||
let type = Services.prefs.getPrefType(name);
|
||||
switch (type) {
|
||||
case Services.prefs.PREF_STRING:
|
||||
return Services.prefs.setCharPref(name, value);
|
||||
case Services.prefs.PREF_INT:
|
||||
return Services.prefs.setIntPref(name, value);
|
||||
case Services.prefs.PREF_BOOL:
|
||||
return Services.prefs.setBoolPref(name, value);
|
||||
default:
|
||||
throw new Error("Unknown type");
|
||||
}
|
||||
}
|
||||
|
||||
function RestoreDefaults() {
|
||||
let inputs = document.querySelectorAll("[data-pref]");
|
||||
for (let i of inputs) {
|
||||
let pref = i.dataset.pref;
|
||||
Services.prefs.clearUserPref(pref);
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/prefs.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a id="restore">&prefs_restore;</a>
|
||||
<a id="manageComponents">&prefs_manage_components;</a>
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
<h1>&prefs_title;</h1>
|
||||
|
||||
<h2>&prefs_general_title;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label title="&prefs_options_showeditor_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.showProjectEditor"/>
|
||||
<span>&prefs_options_showeditor;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_rememberlastproject_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.restoreLastProject"/>
|
||||
<span>&prefs_options_rememberlastproject;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autoconnectruntime_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.autoConnectRuntime"/>
|
||||
<span>&prefs_options_autoconnectruntime;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="text-input" title="&prefs_options_templatesurl_tooltip;">
|
||||
<span>&prefs_options_templatesurl;</span>
|
||||
<input data-pref="devtools.webide.templatesURL"/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>&prefs_editor_title;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label><span>&prefs_options_tabsize;</span>
|
||||
<select data-pref="devtools.editor.tabsize">
|
||||
<option value="2">2</option>
|
||||
<option value="4">4</option>
|
||||
<option value="8">8</option>
|
||||
</select>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_expandtab_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.expandtab"/>
|
||||
<span>&prefs_options_expandtab;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_detectindentation_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.detectindentation"/>
|
||||
<span>&prefs_options_detectindentation;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autocomplete_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.autocomplete"/>
|
||||
<span>&prefs_options_autocomplete;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autoclosebrackets_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.autoclosebrackets"/>
|
||||
<span>&prefs_options_autoclosebrackets;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autosavefiles_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.autosaveFiles"/>
|
||||
<span>&prefs_options_autosavefiles;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label><span>&prefs_options_keybindings;</span>
|
||||
<select data-pref="devtools.editor.keymap">
|
||||
<option value="default">&prefs_options_keybindings_default;</option>
|
||||
<option value="vim">Vim</option>
|
||||
<option value="emacs">Emacs</option>
|
||||
<option value="sublime">Sublime</option>
|
||||
</select>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,42 +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/. */
|
||||
|
||||
/* eslint-env browser */
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const ProjectList = require("devtools/client/webide/modules/project-list");
|
||||
|
||||
var projectList = new ProjectList(window, window.parent);
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad, true);
|
||||
document.getElementById("new-app").onclick = CreateNewApp;
|
||||
document.getElementById("hosted-app").onclick = ImportHostedApp;
|
||||
document.getElementById("packaged-app").onclick = ImportPackagedApp;
|
||||
document.getElementById("refresh-tabs").onclick = RefreshTabs;
|
||||
projectList.update();
|
||||
projectList.updateCommands();
|
||||
}, true);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
projectList.destroy();
|
||||
});
|
||||
|
||||
function RefreshTabs() {
|
||||
projectList.refreshTabs();
|
||||
}
|
||||
|
||||
function CreateNewApp() {
|
||||
projectList.newApp();
|
||||
}
|
||||
|
||||
function ImportHostedApp() {
|
||||
projectList.importHostedApp();
|
||||
}
|
||||
|
||||
function ImportPackagedApp() {
|
||||
projectList.importPackagedApp();
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/panel-listing.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/project-listing.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="project-panel">
|
||||
<div id="project-panel-box">
|
||||
<button class="panel-item project-panel-item-newapp" id="new-app">&projectMenu_newApp_label;</button>
|
||||
<button class="panel-item project-panel-item-openpackaged" id="packaged-app">&projectMenu_importPackagedApp_label;</button>
|
||||
<button class="panel-item project-panel-item-openhosted" id="hosted-app">&projectMenu_importHostedApp_label;</button>
|
||||
<label class="panel-header">&projectPanel_myProjects;</label>
|
||||
<div id="project-panel-projects"></div>
|
||||
<label class="panel-header" id="panel-header-runtimeapps" hidden="true">&projectPanel_runtimeApps;</label>
|
||||
<div id="project-panel-runtimeapps"/>
|
||||
<label class="panel-header" id="panel-header-tabs" hidden="true">&projectPanel_tabs;
|
||||
<button class="project-panel-item-refreshtabs refresh-icon" id="refresh-tabs" title="&projectMenu_refreshTabs_label;"></button>
|
||||
</label>
|
||||
<div id="project-panel-tabs"/>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,11 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var ProjectPanel = {
|
||||
// TODO: Expand function to save toggle state.
|
||||
toggleSidebar: function () {
|
||||
document.querySelector("#project-listing-panel").setAttribute("sidebar-displayed", true);
|
||||
document.querySelector("#project-listing-splitter").setAttribute("sidebar-displayed", true);
|
||||
}
|
||||
};
|
|
@ -1,66 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const RuntimeList = require("devtools/client/webide/modules/runtime-list");
|
||||
|
||||
var runtimeList = new RuntimeList(window, window.parent);
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad, true);
|
||||
document.getElementById("runtime-screenshot").onclick = TakeScreenshot;
|
||||
document.getElementById("runtime-permissions").onclick = ShowPermissionsTable;
|
||||
document.getElementById("runtime-details").onclick = ShowRuntimeDetails;
|
||||
document.getElementById("runtime-disconnect").onclick = DisconnectRuntime;
|
||||
document.getElementById("runtime-preferences").onclick = ShowDevicePreferences;
|
||||
document.getElementById("runtime-settings").onclick = ShowSettings;
|
||||
document.getElementById("runtime-panel-installsimulator").onclick = ShowAddons;
|
||||
document.getElementById("runtime-panel-noadbhelper").onclick = ShowAddons;
|
||||
document.getElementById("runtime-panel-nousbdevice").onclick = ShowTroubleShooting;
|
||||
document.getElementById("refresh-devices").onclick = RefreshScanners;
|
||||
runtimeList.update();
|
||||
runtimeList.updateCommands();
|
||||
}, true);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
runtimeList.destroy();
|
||||
});
|
||||
|
||||
function TakeScreenshot() {
|
||||
runtimeList.takeScreenshot();
|
||||
}
|
||||
|
||||
function ShowRuntimeDetails() {
|
||||
runtimeList.showRuntimeDetails();
|
||||
}
|
||||
|
||||
function ShowPermissionsTable() {
|
||||
runtimeList.showPermissionsTable();
|
||||
}
|
||||
|
||||
function ShowDevicePreferences() {
|
||||
runtimeList.showDevicePreferences();
|
||||
}
|
||||
|
||||
function ShowSettings() {
|
||||
runtimeList.showSettings();
|
||||
}
|
||||
|
||||
function RefreshScanners() {
|
||||
runtimeList.refreshScanners();
|
||||
}
|
||||
|
||||
function DisconnectRuntime() {
|
||||
window.parent.Cmds.disconnectRuntime();
|
||||
}
|
||||
|
||||
function ShowAddons() {
|
||||
runtimeList.showAddons();
|
||||
}
|
||||
|
||||
function ShowTroubleShooting() {
|
||||
runtimeList.showTroubleShooting();
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/panel-listing.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/runtime-listing.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="runtime-panel">
|
||||
<div id="runtime-panel-box">
|
||||
<label class="panel-header">&runtimePanel_usb;
|
||||
<button class="runtime-panel-item-refreshdevices refresh-icon" id="refresh-devices" title="&runtimePanel_refreshDevices_label;"></button>
|
||||
</label>
|
||||
<button class="panel-item" id="runtime-panel-nousbdevice">&runtimePanel_nousbdevice;</button>
|
||||
<button class="panel-item" id="runtime-panel-noadbhelper">&runtimePanel_noadbhelper;</button>
|
||||
<div id="runtime-panel-usb"></div>
|
||||
<label class="panel-header" id="runtime-header-wifi">&runtimePanel_wifi;</label>
|
||||
<div id="runtime-panel-wifi"></div>
|
||||
<label class="panel-header">&runtimePanel_simulator;</label>
|
||||
<div id="runtime-panel-simulator"></div>
|
||||
<button class="panel-item" id="runtime-panel-installsimulator">&runtimePanel_installsimulator;</button>
|
||||
<label class="panel-header">&runtimePanel_other;</label>
|
||||
<div id="runtime-panel-other"></div>
|
||||
<div id="runtime-actions">
|
||||
<button class="panel-item" id="runtime-details">&runtimeMenu_showDetails_label;</button>
|
||||
<button class="panel-item" id="runtime-permissions">&runtimeMenu_showPermissionTable_label;</button>
|
||||
<button class="panel-item" id="runtime-preferences">&runtimeMenu_showDevicePrefs_label;</button>
|
||||
<button class="panel-item" id="runtime-settings">&runtimeMenu_showSettings_label;</button>
|
||||
<button class="panel-item" id="runtime-screenshot">&runtimeMenu_takeScreenshot_label;</button>
|
||||
<button class="panel-item" id="runtime-disconnect">&runtimeMenu_disconnect_label;</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,11 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var RuntimePanel = {
|
||||
// TODO: Expand function to save toggle state.
|
||||
toggleSidebar: function () {
|
||||
document.querySelector("#runtime-listing-panel").setAttribute("sidebar-displayed", true);
|
||||
document.querySelector("#runtime-listing-splitter").setAttribute("sidebar-displayed", true);
|
||||
}
|
||||
};
|
|
@ -1,153 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const Services = require("Services");
|
||||
const {AppManager} = require("devtools/client/webide/modules/app-manager");
|
||||
const {Connection} = require("devtools/shared/client/connection-manager");
|
||||
const {RuntimeTypes} = require("devtools/client/webide/modules/runtimes");
|
||||
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
|
||||
|
||||
const UNRESTRICTED_HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Running_and_debugging_apps#Unrestricted_app_debugging_%28including_certified_apps_main_process_etc.%29";
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
document.querySelector("#close").onclick = CloseUI;
|
||||
document.querySelector("#devtools-check button").onclick = EnableCertApps;
|
||||
document.querySelector("#adb-check button").onclick = RootADB;
|
||||
document.querySelector("#unrestricted-privileges").onclick = function () {
|
||||
window.parent.UI.openInBrowser(UNRESTRICTED_HELP_URL);
|
||||
};
|
||||
AppManager.on("app-manager-update", OnAppManagerUpdate);
|
||||
BuildUI();
|
||||
CheckLockState();
|
||||
}, true);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
AppManager.off("app-manager-update", OnAppManagerUpdate);
|
||||
});
|
||||
|
||||
function CloseUI() {
|
||||
window.parent.UI.openProject();
|
||||
}
|
||||
|
||||
function OnAppManagerUpdate(event, what) {
|
||||
if (what == "connection" || what == "runtime-global-actors") {
|
||||
BuildUI();
|
||||
CheckLockState();
|
||||
}
|
||||
}
|
||||
|
||||
function generateFields(json) {
|
||||
let table = document.querySelector("table");
|
||||
for (let name in json) {
|
||||
let tr = document.createElement("tr");
|
||||
let td = document.createElement("td");
|
||||
td.textContent = name;
|
||||
tr.appendChild(td);
|
||||
td = document.createElement("td");
|
||||
td.textContent = json[name];
|
||||
tr.appendChild(td);
|
||||
table.appendChild(tr);
|
||||
}
|
||||
}
|
||||
|
||||
var getDescriptionPromise; // Used by tests
|
||||
function BuildUI() {
|
||||
let table = document.querySelector("table");
|
||||
table.innerHTML = "";
|
||||
if (AppManager.connection &&
|
||||
AppManager.connection.status == Connection.Status.CONNECTED &&
|
||||
AppManager.deviceFront) {
|
||||
getDescriptionPromise = AppManager.deviceFront.getDescription()
|
||||
.then(json => generateFields(json));
|
||||
} else {
|
||||
CloseUI();
|
||||
}
|
||||
}
|
||||
|
||||
function CheckLockState() {
|
||||
let adbCheckResult = document.querySelector("#adb-check > .yesno");
|
||||
let devtoolsCheckResult = document.querySelector("#devtools-check > .yesno");
|
||||
let flipCertPerfButton = document.querySelector("#devtools-check button");
|
||||
let adbRootButton = document.querySelector("#adb-check button");
|
||||
let flipCertPerfAction = document.querySelector("#devtools-check > .action");
|
||||
let adbRootAction = document.querySelector("#adb-check > .action");
|
||||
|
||||
let sYes = Strings.GetStringFromName("runtimedetails_checkyes");
|
||||
let sNo = Strings.GetStringFromName("runtimedetails_checkno");
|
||||
let sUnknown = Strings.GetStringFromName("runtimedetails_checkunknown");
|
||||
let sNotUSB = Strings.GetStringFromName("runtimedetails_notUSBDevice");
|
||||
|
||||
flipCertPerfButton.setAttribute("disabled", "true");
|
||||
flipCertPerfAction.setAttribute("hidden", "true");
|
||||
adbRootAction.setAttribute("hidden", "true");
|
||||
|
||||
adbCheckResult.textContent = sUnknown;
|
||||
devtoolsCheckResult.textContent = sUnknown;
|
||||
|
||||
if (AppManager.connection &&
|
||||
AppManager.connection.status == Connection.Status.CONNECTED) {
|
||||
|
||||
// ADB check
|
||||
if (AppManager.selectedRuntime.type === RuntimeTypes.USB) {
|
||||
let device = AppManager.selectedRuntime.device;
|
||||
if (device && device.summonRoot) {
|
||||
device.isRoot().then(isRoot => {
|
||||
if (isRoot) {
|
||||
adbCheckResult.textContent = sYes;
|
||||
flipCertPerfButton.removeAttribute("disabled");
|
||||
} else {
|
||||
adbCheckResult.textContent = sNo;
|
||||
adbRootAction.removeAttribute("hidden");
|
||||
}
|
||||
}, e => console.error(e));
|
||||
} else {
|
||||
adbCheckResult.textContent = sUnknown;
|
||||
}
|
||||
} else {
|
||||
adbCheckResult.textContent = sNotUSB;
|
||||
}
|
||||
|
||||
// forbid-certified-apps check
|
||||
try {
|
||||
let prefFront = AppManager.preferenceFront;
|
||||
prefFront.getBoolPref("devtools.debugger.forbid-certified-apps").then(isForbidden => {
|
||||
if (isForbidden) {
|
||||
devtoolsCheckResult.textContent = sNo;
|
||||
flipCertPerfAction.removeAttribute("hidden");
|
||||
} else {
|
||||
devtoolsCheckResult.textContent = sYes;
|
||||
}
|
||||
}, e => console.error(e));
|
||||
} catch (e) {
|
||||
// Exception. pref actor is only accessible if forbird-certified-apps is false
|
||||
devtoolsCheckResult.textContent = sNo;
|
||||
flipCertPerfAction.removeAttribute("hidden");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function EnableCertApps() {
|
||||
let device = AppManager.selectedRuntime.device;
|
||||
// TODO: Remove `network.disable.ipc.security` once bug 1125916 is fixed.
|
||||
device.shell(
|
||||
"stop b2g && " +
|
||||
"cd /data/b2g/mozilla/*.default/ && " +
|
||||
"echo 'user_pref(\"devtools.debugger.forbid-certified-apps\", false);' >> prefs.js && " +
|
||||
"echo 'user_pref(\"dom.apps.developer_mode\", true);' >> prefs.js && " +
|
||||
"echo 'user_pref(\"network.disable.ipc.security\", true);' >> prefs.js && " +
|
||||
"echo 'user_pref(\"dom.webcomponents.enabled\", true);' >> prefs.js && " +
|
||||
"start b2g"
|
||||
);
|
||||
}
|
||||
|
||||
function RootADB() {
|
||||
let device = AppManager.selectedRuntime.device;
|
||||
device.summonRoot().then(CheckLockState, (e) => console.error(e));
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/runtimedetails.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/runtimedetails.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
<h1>&runtimedetails_title;</h1>
|
||||
|
||||
<div id="devicePrivileges">
|
||||
<p id="adb-check">
|
||||
&runtimedetails_adbIsRoot;<span class="yesno"></span>
|
||||
<div class="action">
|
||||
<button>&runtimedetails_summonADBRoot;</button>
|
||||
<em>&runtimedetails_ADBRootWarning;</em>
|
||||
</div>
|
||||
</p>
|
||||
<p id="devtools-check">
|
||||
<a id="unrestricted-privileges">&runtimedetails_unrestrictedPrivileges;</a><span class="yesno"></span>
|
||||
<div class="action">
|
||||
<button>&runtimedetails_requestPrivileges;</button>
|
||||
<em>&runtimedetails_privilegesWarning;</em>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table></table>
|
||||
</body>
|
||||
</html>
|
|
@ -1,352 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const { getDevices, getDeviceString } = require("devtools/client/shared/devices");
|
||||
const { Simulators, Simulator } = require("devtools/client/webide/modules/simulators");
|
||||
const Services = require("Services");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const promise = require("promise");
|
||||
const utils = require("devtools/client/webide/modules/utils");
|
||||
|
||||
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
|
||||
|
||||
var SimulatorEditor = {
|
||||
|
||||
// Available Firefox OS Simulator addons (key: `addon.id`).
|
||||
_addons: {},
|
||||
|
||||
// Available device simulation profiles (key: `device.name`).
|
||||
_devices: {},
|
||||
|
||||
// The names of supported simulation options.
|
||||
_deviceOptions: [],
|
||||
|
||||
// The <form> element used to edit Simulator options.
|
||||
_form: null,
|
||||
|
||||
// The Simulator object being edited.
|
||||
_simulator: null,
|
||||
|
||||
// Generate the dynamic form elements.
|
||||
init() {
|
||||
let promises = [];
|
||||
|
||||
// Grab the <form> element.
|
||||
let form = this._form;
|
||||
if (!form) {
|
||||
// This is the first time we run `init()`, bootstrap some things.
|
||||
form = this._form = document.querySelector("#simulator-editor");
|
||||
form.addEventListener("change", this.update.bind(this));
|
||||
Simulators.on("configure", (e, simulator) => { this.edit(simulator); });
|
||||
// Extract the list of device simulation options we'll support.
|
||||
let deviceFields = form.querySelectorAll("*[data-device]");
|
||||
this._deviceOptions = Array.map(deviceFields, field => field.name);
|
||||
}
|
||||
|
||||
// Append a new <option> to a <select> (or <optgroup>) element.
|
||||
function opt(select, value, text) {
|
||||
let option = document.createElement("option");
|
||||
option.value = value;
|
||||
option.textContent = text;
|
||||
select.appendChild(option);
|
||||
}
|
||||
|
||||
// Generate B2G version selector.
|
||||
promises.push(Simulators.findSimulatorAddons().then(addons => {
|
||||
this._addons = {};
|
||||
form.version.innerHTML = "";
|
||||
form.version.classList.remove("custom");
|
||||
addons.forEach(addon => {
|
||||
this._addons[addon.id] = addon;
|
||||
opt(form.version, addon.id, addon.name);
|
||||
});
|
||||
opt(form.version, "custom", "");
|
||||
opt(form.version, "pick", Strings.GetStringFromName("simulator_custom_binary"));
|
||||
}));
|
||||
|
||||
// Generate profile selector.
|
||||
form.profile.innerHTML = "";
|
||||
form.profile.classList.remove("custom");
|
||||
opt(form.profile, "default", Strings.GetStringFromName("simulator_default_profile"));
|
||||
opt(form.profile, "custom", "");
|
||||
opt(form.profile, "pick", Strings.GetStringFromName("simulator_custom_profile"));
|
||||
|
||||
// Generate example devices list.
|
||||
form.device.innerHTML = "";
|
||||
form.device.classList.remove("custom");
|
||||
opt(form.device, "custom", Strings.GetStringFromName("simulator_custom_device"));
|
||||
promises.push(getDevices().then(devices => {
|
||||
devices.TYPES.forEach(type => {
|
||||
let b2gDevices = devices[type].filter(d => d.firefoxOS);
|
||||
if (b2gDevices.length < 1) {
|
||||
return;
|
||||
}
|
||||
let optgroup = document.createElement("optgroup");
|
||||
optgroup.label = getDeviceString(type);
|
||||
b2gDevices.forEach(device => {
|
||||
this._devices[device.name] = device;
|
||||
opt(optgroup, device.name, device.name);
|
||||
});
|
||||
form.device.appendChild(optgroup);
|
||||
});
|
||||
}));
|
||||
|
||||
return promise.all(promises);
|
||||
},
|
||||
|
||||
// Edit the configuration of an existing Simulator, or create a new one.
|
||||
edit(simulator) {
|
||||
// If no Simulator was given to edit, we're creating a new one.
|
||||
if (!simulator) {
|
||||
simulator = new Simulator(); // Default options.
|
||||
Simulators.add(simulator);
|
||||
}
|
||||
|
||||
this._simulator = null;
|
||||
|
||||
return this.init().then(() => {
|
||||
this._simulator = simulator;
|
||||
|
||||
// Update the form fields.
|
||||
this._form.name.value = simulator.name;
|
||||
|
||||
this.updateVersionSelector();
|
||||
this.updateProfileSelector();
|
||||
this.updateDeviceSelector();
|
||||
this.updateDeviceFields();
|
||||
|
||||
// Change visibility of 'TV Simulator Menu'.
|
||||
let tvSimMenu = document.querySelector("#tv_simulator_menu");
|
||||
tvSimMenu.style.visibility = (this._simulator.type === "television") ?
|
||||
"visible" : "hidden";
|
||||
|
||||
// Trigger any listener waiting for this update
|
||||
let change = document.createEvent("HTMLEvents");
|
||||
change.initEvent("change", true, true);
|
||||
this._form.dispatchEvent(change);
|
||||
});
|
||||
},
|
||||
|
||||
// Open the directory of TV Simulator config.
|
||||
showTVConfigDirectory() {
|
||||
let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
profD.append("extensions");
|
||||
profD.append(this._simulator.addon.id);
|
||||
profD.append("profile");
|
||||
profD.append("dummy");
|
||||
let profileDir = profD.path;
|
||||
|
||||
// Show the profile directory.
|
||||
let nsLocalFile = Components.Constructor("@mozilla.org/file/local;1",
|
||||
"nsILocalFile", "initWithPath");
|
||||
new nsLocalFile(profileDir).reveal();
|
||||
},
|
||||
|
||||
// Close the configuration panel.
|
||||
close() {
|
||||
this._simulator = null;
|
||||
window.parent.UI.openProject();
|
||||
},
|
||||
|
||||
// Restore the simulator to its default configuration.
|
||||
restoreDefaults() {
|
||||
let simulator = this._simulator;
|
||||
this.version = simulator.addon.id;
|
||||
this.profile = "default";
|
||||
simulator.restoreDefaults();
|
||||
Simulators.emitUpdated();
|
||||
return this.edit(simulator);
|
||||
},
|
||||
|
||||
// Delete this simulator.
|
||||
deleteSimulator() {
|
||||
Simulators.remove(this._simulator);
|
||||
this.close();
|
||||
},
|
||||
|
||||
// Select an available option, or set the "custom" option.
|
||||
updateSelector(selector, value) {
|
||||
selector.value = value;
|
||||
if (selector.selectedIndex == -1) {
|
||||
selector.value = "custom";
|
||||
selector.classList.add("custom");
|
||||
selector[selector.selectedIndex].textContent = value;
|
||||
}
|
||||
},
|
||||
|
||||
// VERSION: Can be an installed `addon.id` or a custom binary path.
|
||||
|
||||
get version() {
|
||||
return this._simulator.options.b2gBinary || this._simulator.addon.id;
|
||||
},
|
||||
|
||||
set version(value) {
|
||||
let form = this._form;
|
||||
let simulator = this._simulator;
|
||||
let oldVer = simulator.version;
|
||||
if (this._addons[value]) {
|
||||
// `value` is a simulator addon ID.
|
||||
simulator.addon = this._addons[value];
|
||||
simulator.options.b2gBinary = null;
|
||||
} else {
|
||||
// `value` is a custom binary path.
|
||||
simulator.options.b2gBinary = value;
|
||||
// TODO (Bug 1146531) Indicate that a custom profile is now required.
|
||||
}
|
||||
// If `form.name` contains the old version, update its last occurrence.
|
||||
if (form.name.value.includes(oldVer) && simulator.version !== oldVer) {
|
||||
let regex = new RegExp("(.*)" + oldVer);
|
||||
let name = form.name.value.replace(regex, "$1" + simulator.version);
|
||||
simulator.options.name = form.name.value = Simulators.uniqueName(name);
|
||||
}
|
||||
},
|
||||
|
||||
updateVersionSelector() {
|
||||
this.updateSelector(this._form.version, this.version);
|
||||
},
|
||||
|
||||
// PROFILE. Can be "default" or a custom profile directory path.
|
||||
|
||||
get profile() {
|
||||
return this._simulator.options.gaiaProfile || "default";
|
||||
},
|
||||
|
||||
set profile(value) {
|
||||
this._simulator.options.gaiaProfile = (value == "default" ? null : value);
|
||||
},
|
||||
|
||||
updateProfileSelector() {
|
||||
this.updateSelector(this._form.profile, this.profile);
|
||||
},
|
||||
|
||||
// DEVICE. Can be an existing `device.name` or "custom".
|
||||
|
||||
get device() {
|
||||
let devices = this._devices;
|
||||
let simulator = this._simulator;
|
||||
|
||||
// Search for the name of a device matching current simulator options.
|
||||
for (let name in devices) {
|
||||
let match = true;
|
||||
for (let option of this._deviceOptions) {
|
||||
if (simulator.options[option] === devices[name][option]) {
|
||||
continue;
|
||||
}
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
if (match) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return "custom";
|
||||
},
|
||||
|
||||
set device(name) {
|
||||
let device = this._devices[name];
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
let form = this._form;
|
||||
let simulator = this._simulator;
|
||||
this._deviceOptions.forEach(option => {
|
||||
simulator.options[option] = form[option].value = device[option] || null;
|
||||
});
|
||||
// TODO (Bug 1146531) Indicate when a custom profile is required (e.g. for
|
||||
// tablet, TV…).
|
||||
},
|
||||
|
||||
updateDeviceSelector() {
|
||||
this.updateSelector(this._form.device, this.device);
|
||||
},
|
||||
|
||||
// Erase any current values, trust only the `simulator.options`.
|
||||
updateDeviceFields() {
|
||||
let form = this._form;
|
||||
let simulator = this._simulator;
|
||||
this._deviceOptions.forEach(option => {
|
||||
form[option].value = simulator.options[option];
|
||||
});
|
||||
},
|
||||
|
||||
// Handle a change in our form's fields.
|
||||
update(event) {
|
||||
let simulator = this._simulator;
|
||||
if (!simulator) {
|
||||
return;
|
||||
}
|
||||
let form = this._form;
|
||||
let input = event.target;
|
||||
switch (input.name) {
|
||||
case "name":
|
||||
simulator.options.name = input.value;
|
||||
break;
|
||||
case "version":
|
||||
switch (input.value) {
|
||||
case "pick":
|
||||
let file = utils.getCustomBinary(window);
|
||||
if (file) {
|
||||
this.version = file.path;
|
||||
}
|
||||
// Whatever happens, don't stay on the "pick" option.
|
||||
this.updateVersionSelector();
|
||||
break;
|
||||
case "custom":
|
||||
this.version = input[input.selectedIndex].textContent;
|
||||
break;
|
||||
default:
|
||||
this.version = input.value;
|
||||
}
|
||||
break;
|
||||
case "profile":
|
||||
switch (input.value) {
|
||||
case "pick":
|
||||
let directory = utils.getCustomProfile(window);
|
||||
if (directory) {
|
||||
this.profile = directory.path;
|
||||
}
|
||||
// Whatever happens, don't stay on the "pick" option.
|
||||
this.updateProfileSelector();
|
||||
break;
|
||||
case "custom":
|
||||
this.profile = input[input.selectedIndex].textContent;
|
||||
break;
|
||||
default:
|
||||
this.profile = input.value;
|
||||
}
|
||||
break;
|
||||
case "device":
|
||||
this.device = input.value;
|
||||
break;
|
||||
default:
|
||||
simulator.options[input.name] = input.value || null;
|
||||
this.updateDeviceSelector();
|
||||
}
|
||||
Simulators.emitUpdated();
|
||||
},
|
||||
};
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
document.querySelector("#close").onclick = e => {
|
||||
SimulatorEditor.close();
|
||||
};
|
||||
document.querySelector("#reset").onclick = e => {
|
||||
SimulatorEditor.restoreDefaults();
|
||||
};
|
||||
document.querySelector("#remove").onclick = e => {
|
||||
SimulatorEditor.deleteSimulator();
|
||||
};
|
||||
|
||||
// We just loaded, so we probably missed the first configure request.
|
||||
SimulatorEditor.edit(Simulators._lastConfiguredSimulator);
|
||||
|
||||
document.querySelector("#open-tv-dummy-directory").onclick = e => {
|
||||
SimulatorEditor.showTVConfigDirectory();
|
||||
e.preventDefault();
|
||||
};
|
||||
});
|
|
@ -1,99 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/simulator.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/simulator.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a id="remove" class="hidden">&simulator_remove;</a>
|
||||
<a id="reset">&simulator_reset;</a>
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
<form id="simulator-editor">
|
||||
|
||||
<h1>&simulator_title;</h1>
|
||||
|
||||
<h2>&simulator_software;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_name;</span>
|
||||
<input type="text" name="name"/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_version;</span>
|
||||
<select name="version"/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_profile;</span>
|
||||
<select name="profile"/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>&simulator_hardware;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_device;</span>
|
||||
<select name="device"/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_screenSize;</span>
|
||||
<input name="width" data-device="" type="number"/>
|
||||
<span>×</span>
|
||||
<input name="height" data-device="" type="number"/>
|
||||
</label>
|
||||
</li>
|
||||
<li class="hidden">
|
||||
<label>
|
||||
<span class="label">&simulator_pixelRatio;</span>
|
||||
<input name="pixelRatio" data-device="" type="number" step="0.05"/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- This menu is shown when simulator type is television-->
|
||||
<p id="tv_simulator_menu" style="visibility:hidden;">
|
||||
<h2>&simulator_tv_data;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_tv_data_open;</span>
|
||||
<button id="open-tv-dummy-directory">
|
||||
&simulator_tv_data_open_button;
|
||||
</button>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -1,178 +0,0 @@
|
|||
<?xml version="1.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/. -->
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||
<?xml-stylesheet href="resource://devtools/client/themes/common.css"?>
|
||||
<?xml-stylesheet href="chrome://webide/skin/webide.css"?>
|
||||
|
||||
<window id="webide" onclose="return UI.canCloseProject();"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="&windowTitle;"
|
||||
windowtype="devtools:webide"
|
||||
macanimationtype="document"
|
||||
fullscreenbutton="true"
|
||||
screenX="4" screenY="4"
|
||||
width="800" height="600"
|
||||
persist="screenX screenY width height sizemode">
|
||||
|
||||
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"></script>
|
||||
<script type="application/javascript" src="project-panel.js"></script>
|
||||
<script type="application/javascript" src="runtime-panel.js"></script>
|
||||
<script type="application/javascript" src="webide.js"></script>
|
||||
|
||||
<commandset id="mainCommandSet">
|
||||
<commandset id="editMenuCommands"/>
|
||||
<commandset id="webideCommands">
|
||||
<command id="cmd_quit" oncommand="Cmds.quit()"/>
|
||||
<command id="cmd_newApp" oncommand="Cmds.newApp()" label="&projectMenu_newApp_label;"/>
|
||||
<command id="cmd_importPackagedApp" oncommand="Cmds.importPackagedApp()" label="&projectMenu_importPackagedApp_label;"/>
|
||||
<command id="cmd_importHostedApp" oncommand="Cmds.importHostedApp()" label="&projectMenu_importHostedApp_label;"/>
|
||||
<command id="cmd_showDevicePrefs" label="&runtimeMenu_showDevicePrefs_label;" oncommand="Cmds.showDevicePrefs()"/>
|
||||
<command id="cmd_showSettings" label="&runtimeMenu_showSettings_label;" oncommand="Cmds.showSettings()"/>
|
||||
<command id="cmd_removeProject" oncommand="Cmds.removeProject()" label="&projectMenu_remove_label;"/>
|
||||
<command id="cmd_showProjectPanel" oncommand="Cmds.showProjectPanel()"/>
|
||||
<command id="cmd_showRuntimePanel" oncommand="Cmds.showRuntimePanel()"/>
|
||||
<command id="cmd_disconnectRuntime" oncommand="Cmds.disconnectRuntime()" label="&runtimeMenu_disconnect_label;"/>
|
||||
<command id="cmd_showMonitor" oncommand="Cmds.showMonitor()" label="&runtimeMenu_showMonitor_label;"/>
|
||||
<command id="cmd_showPermissionsTable" oncommand="Cmds.showPermissionsTable()" label="&runtimeMenu_showPermissionTable_label;"/>
|
||||
<command id="cmd_showRuntimeDetails" oncommand="Cmds.showRuntimeDetails()" label="&runtimeMenu_showDetails_label;"/>
|
||||
<command id="cmd_takeScreenshot" oncommand="Cmds.takeScreenshot()" label="&runtimeMenu_takeScreenshot_label;"/>
|
||||
<command id="cmd_toggleEditor" oncommand="Cmds.toggleEditors()" label="&viewMenu_toggleEditor_label;"/>
|
||||
<command id="cmd_showAddons" oncommand="Cmds.showAddons()"/>
|
||||
<command id="cmd_showPrefs" oncommand="Cmds.showPrefs()"/>
|
||||
<command id="cmd_showTroubleShooting" oncommand="Cmds.showTroubleShooting()"/>
|
||||
<command id="cmd_play" oncommand="Cmds.play()"/>
|
||||
<command id="cmd_stop" oncommand="Cmds.stop()" label="&projectMenu_stop_label;"/>
|
||||
<command id="cmd_toggleToolbox" oncommand="Cmds.toggleToolbox()"/>
|
||||
<command id="cmd_zoomin" label="&viewMenu_zoomin_label;" oncommand="Cmds.zoomIn()"/>
|
||||
<command id="cmd_zoomout" label="&viewMenu_zoomout_label;" oncommand="Cmds.zoomOut()"/>
|
||||
<command id="cmd_resetzoom" label="&viewMenu_resetzoom_label;" oncommand="Cmds.resetZoom()"/>
|
||||
</commandset>
|
||||
</commandset>
|
||||
|
||||
<menubar id="main-menubar">
|
||||
<menu id="menu-project" label="&projectMenu_label;" accesskey="&projectMenu_accesskey;">
|
||||
<menupopup id="menu-project-popup">
|
||||
<menuitem command="cmd_newApp" accesskey="&projectMenu_newApp_accesskey;"/>
|
||||
<menuitem command="cmd_importPackagedApp" accesskey="&projectMenu_importPackagedApp_accesskey;"/>
|
||||
<menuitem command="cmd_importHostedApp" accesskey="&projectMenu_importHostedApp_accesskey;"/>
|
||||
<menuitem id="menuitem-show_projectPanel" command="cmd_showProjectPanel" key="key_showProjectPanel" label="&projectMenu_selectApp_label;" accesskey="&projectMenu_selectApp_accesskey;"/>
|
||||
<menuseparator/>
|
||||
<menuitem command="cmd_play" key="key_play" label="&projectMenu_play_label;" accesskey="&projectMenu_play_accesskey;"/>
|
||||
<menuitem command="cmd_stop" accesskey="&projectMenu_stop_accesskey;"/>
|
||||
<menuitem command="cmd_toggleToolbox" key="key_toggleToolbox" label="&projectMenu_debug_label;" accesskey="&projectMenu_debug_accesskey;"/>
|
||||
<menuseparator/>
|
||||
<menuitem command="cmd_removeProject" accesskey="&projectMenu_remove_accesskey;"/>
|
||||
<menuseparator/>
|
||||
<menuitem command="cmd_showPrefs" label="&projectMenu_showPrefs_label;" accesskey="&projectMenu_showPrefs_accesskey;"/>
|
||||
<menuitem command="cmd_showAddons" label="&projectMenu_manageComponents_label;" accesskey="&projectMenu_manageComponents_accesskey;"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
|
||||
<menu id="menu-runtime" label="&runtimeMenu_label;" accesskey="&runtimeMenu_accesskey;">
|
||||
<menupopup id="menu-runtime-popup">
|
||||
<menuitem command="cmd_showMonitor" accesskey="&runtimeMenu_showMonitor_accesskey;"/>
|
||||
<menuitem command="cmd_takeScreenshot" accesskey="&runtimeMenu_takeScreenshot_accesskey;"/>
|
||||
<menuitem command="cmd_showPermissionsTable" accesskey="&runtimeMenu_showPermissionTable_accesskey;"/>
|
||||
<menuitem command="cmd_showRuntimeDetails" accesskey="&runtimeMenu_showDetails_accesskey;"/>
|
||||
<menuitem command="cmd_showDevicePrefs" accesskey="&runtimeMenu_showDevicePrefs_accesskey;"/>
|
||||
<menuitem command="cmd_showSettings" accesskey="&runtimeMenu_showSettings_accesskey;"/>
|
||||
<menuseparator/>
|
||||
<menuitem command="cmd_disconnectRuntime" accesskey="&runtimeMenu_disconnect_accesskey;"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
|
||||
<menu id="menu-view" label="&viewMenu_label;" accesskey="&viewMenu_accesskey;">
|
||||
<menupopup id="menu-ViewPopup">
|
||||
<menuitem command="cmd_toggleEditor" key="key_toggleEditor" accesskey="&viewMenu_toggleEditor_accesskey;"/>
|
||||
<menuseparator/>
|
||||
<menuitem command="cmd_zoomin" key="key_zoomin" accesskey="&viewMenu_zoomin_accesskey;"/>
|
||||
<menuitem command="cmd_zoomout" key="key_zoomout" accesskey="&viewMenu_zoomout_accesskey;"/>
|
||||
<menuitem command="cmd_resetzoom" key="key_resetzoom" accesskey="&viewMenu_resetzoom_accesskey;"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
|
||||
</menubar>
|
||||
|
||||
<keyset id="mainKeyset">
|
||||
<key key="&key_quit;" id="key_quit" command="cmd_quit" modifiers="accel"/>
|
||||
<key key="&key_showProjectPanel;" id="key_showProjectPanel" command="cmd_showProjectPanel" modifiers="accel"/>
|
||||
<key key="&key_play;" id="key_play" command="cmd_play" modifiers="accel"/>
|
||||
<key key="&key_toggleEditor;" id="key_toggleEditor" command="cmd_toggleEditor" modifiers="accel"/>
|
||||
<key keycode="&key_toggleToolbox;" id="key_toggleToolbox" command="cmd_toggleToolbox"/>
|
||||
<key key="&key_zoomin;" id="key_zoomin" command="cmd_zoomin" modifiers="accel"/>
|
||||
<key key="&key_zoomin2;" id="key_zoomin2" command="cmd_zoomin" modifiers="accel"/>
|
||||
<key key="&key_zoomout;" id="key_zoomout" command="cmd_zoomout" modifiers="accel"/>
|
||||
<key key="&key_resetzoom;" id="key_resetzoom" command="cmd_resetzoom" modifiers="accel"/>
|
||||
</keyset>
|
||||
|
||||
<tooltip id="aHTMLTooltip" page="true"/>
|
||||
|
||||
<toolbar id="main-toolbar">
|
||||
|
||||
<vbox flex="1">
|
||||
<hbox id="action-buttons-container" class="busy">
|
||||
<toolbarbutton id="action-button-play" class="action-button" command="cmd_play" tooltiptext="&projectMenu_play_label;"/>
|
||||
<toolbarbutton id="action-button-stop" class="action-button" command="cmd_stop" tooltiptext="&projectMenu_stop_label;"/>
|
||||
<toolbarbutton id="action-button-debug" class="action-button" command="cmd_toggleToolbox" tooltiptext="&projectMenu_debug_label;"/>
|
||||
<hbox id="action-busy" align="center">
|
||||
<html:img id="action-busy-undetermined" src="chrome://webide/skin/throbber.svg"/>
|
||||
<progressmeter id="action-busy-determined"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
|
||||
<hbox id="panel-buttons-container">
|
||||
<spacer flex="1"/>
|
||||
<toolbarbutton id="runtime-panel-button" class="panel-button">
|
||||
<image class="panel-button-image"/>
|
||||
<label class="panel-button-label" value="&runtimeButton_label;"/>
|
||||
</toolbarbutton>
|
||||
</hbox>
|
||||
|
||||
</vbox>
|
||||
</toolbar>
|
||||
|
||||
<notificationbox flex="1" id="notificationbox">
|
||||
<div flex="1" id="deck-panels">
|
||||
<vbox id="project-listing-panel" class="project-listing panel-list" flex="1">
|
||||
<div id="project-listing-wrapper" class="panel-list-wrapper">
|
||||
<iframe id="project-listing-panel-details" flex="1" src="project-listing.xhtml" tooltip="aHTMLTooltip"/>
|
||||
</div>
|
||||
</vbox>
|
||||
<splitter class="devtools-side-splitter" id="project-listing-splitter"/>
|
||||
<deck flex="1" id="deck" selectedIndex="-1">
|
||||
<iframe id="deck-panel-details" flex="1" src="details.xhtml"/>
|
||||
<iframe id="deck-panel-projecteditor" flex="1"/>
|
||||
<iframe id="deck-panel-addons" flex="1" src="addons.xhtml"/>
|
||||
<iframe id="deck-panel-prefs" flex="1" src="prefs.xhtml"/>
|
||||
<iframe id="deck-panel-permissionstable" flex="1" lazysrc="permissionstable.xhtml"/>
|
||||
<iframe id="deck-panel-runtimedetails" flex="1" lazysrc="runtimedetails.xhtml"/>
|
||||
<iframe id="deck-panel-monitor" flex="1" lazysrc="monitor.xhtml"/>
|
||||
<iframe id="deck-panel-devicepreferences" flex="1" lazysrc="devicepreferences.xhtml"/>
|
||||
<iframe id="deck-panel-devicesettings" flex="1" lazysrc="devicesettings.xhtml"/>
|
||||
<iframe id="deck-panel-logs" flex="1" src="logs.xhtml"/>
|
||||
<iframe id="deck-panel-simulator" flex="1" lazysrc="simulator.xhtml"/>
|
||||
</deck>
|
||||
<splitter class="devtools-side-splitter" id="runtime-listing-splitter"/>
|
||||
<vbox id="runtime-listing-panel" class="runtime-listing panel-list" flex="1">
|
||||
<div id="runtime-listing-wrapper" class="panel-list-wrapper">
|
||||
<iframe id="runtime-listing-panel-details" flex="1" src="runtime-listing.xhtml" tooltip="aHTMLTooltip"/>
|
||||
</div>
|
||||
</vbox>
|
||||
</div>
|
||||
<splitter hidden="true" class="devtools-horizontal-splitter" orient="vertical"/>
|
||||
<!-- toolbox iframe will be inserted here -->
|
||||
</notificationbox>
|
||||
|
||||
</window>
|
|
@ -1,44 +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 Cu = Components.utils;
|
||||
const { require } =
|
||||
Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const Services = require("Services");
|
||||
const QR = require("devtools/shared/qrcode/index");
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
document.getElementById("close").onclick = () => window.close();
|
||||
document.getElementById("no-scanner").onclick = showToken;
|
||||
document.getElementById("yes-scanner").onclick = hideToken;
|
||||
buildUI();
|
||||
});
|
||||
|
||||
function buildUI() {
|
||||
let { oob } = window.arguments[0];
|
||||
createQR(oob);
|
||||
createToken(oob);
|
||||
}
|
||||
|
||||
function createQR(oob) {
|
||||
let oobData = JSON.stringify(oob);
|
||||
let imgData = QR.encodeToDataURI(oobData, "L" /* low quality */);
|
||||
document.querySelector("#qr-code img").src = imgData.src;
|
||||
}
|
||||
|
||||
function createToken(oob) {
|
||||
let token = oob.sha256.replace(/:/g, "").toLowerCase() + oob.k;
|
||||
document.querySelector("#token pre").textContent = token;
|
||||
}
|
||||
|
||||
function showToken() {
|
||||
document.querySelector("body").setAttribute("token", "true");
|
||||
}
|
||||
|
||||
function hideToken() {
|
||||
document.querySelector("body").removeAttribute("token");
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html id="devtools:wifi-auth" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/wifi-auth.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/wifi-auth.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
<h3 id="header">&wifi_auth_header;</h3>
|
||||
<div id="scan-request">&wifi_auth_scan_request;</div>
|
||||
|
||||
<div id="qr-code">
|
||||
<div id="qr-code-wrapper">
|
||||
<img/>
|
||||
</div>
|
||||
<a id="no-scanner" class="toggle-scanner">&wifi_auth_no_scanner;</a>
|
||||
<div id="qr-size-note">
|
||||
<h5>&wifi_auth_qr_size_note;</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="token">
|
||||
<div>&wifi_auth_token_request;</div>
|
||||
<pre id="token-value"/>
|
||||
<a id="yes-scanner" class="toggle-scanner">&wifi_auth_yes_scanner;</a>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -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);
|
|
@ -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);
|
|
@ -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;
|
|
@ -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;
|
|
@ -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 + "'");
|
||||
})
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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'
|
||||
)
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
},
|
||||
|
||||
};
|
|
@ -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;
|
|
@ -1,23 +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/.
|
||||
|
||||
DIRS += [
|
||||
'content',
|
||||
'components',
|
||||
'modules',
|
||||
'themes',
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'test/browser.ini'
|
||||
]
|
||||
MOCHITEST_CHROME_MANIFESTS += [
|
||||
'test/chrome.ini'
|
||||
]
|
||||
|
||||
JS_PREFERENCE_PP_FILES += [
|
||||
'webide-prefs.js',
|
||||
]
|
|
@ -1,6 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
// Extend from the shared list of defined globals for mochitests.
|
||||
"extends": "../../../.eslintrc.mochitests.js"
|
||||
};
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"stable": ["1.0", "2.0"],
|
||||
"unstable": ["3.0", "3.0_tv"]
|
||||
}
|
Binary file not shown.
|
@ -1,6 +0,0 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head><title></title></head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"name": "A name (in app directory)",
|
||||
"description": "desc",
|
||||
"launch_path": "/index.html"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
addons/simulators.json
|
||||
doc_tabs.html
|
||||
head.js
|
||||
templates.json
|
||||
|
||||
[browser_tabs.js]
|
||||
skip-if = e10s # Bug 1072167 - browser_tabs.js test fails under e10s
|
||||
[browser_widget.js]
|
|
@ -1,84 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
const TEST_URI = "http://example.com/browser/devtools/client/webide/test/doc_tabs.html";
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
requestCompleteLog();
|
||||
|
||||
Task.spawn(function* () {
|
||||
// Since we test the connections set below, destroy the server in case it
|
||||
// was left open.
|
||||
DebuggerServer.destroy();
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
|
||||
let tab = yield addTab(TEST_URI);
|
||||
|
||||
let win = yield openWebIDE();
|
||||
let docProject = getProjectDocument(win);
|
||||
let docRuntime = getRuntimeDocument(win);
|
||||
|
||||
yield connectToLocal(win, docRuntime);
|
||||
|
||||
is(Object.keys(DebuggerServer._connections).length, 1, "Locally connected");
|
||||
|
||||
yield selectTabProject(win, docProject);
|
||||
|
||||
ok(win.UI.toolboxPromise, "Toolbox promise exists");
|
||||
yield win.UI.toolboxPromise;
|
||||
|
||||
let project = win.AppManager.selectedProject;
|
||||
is(project.location, TEST_URI, "Location is correct");
|
||||
is(project.name, "example.com: Test Tab", "Name is correct");
|
||||
|
||||
// Ensure tab list changes are noticed
|
||||
let tabsNode = docProject.querySelector("#project-panel-tabs");
|
||||
is(tabsNode.querySelectorAll(".panel-item").length, 2, "2 tabs available");
|
||||
yield removeTab(tab);
|
||||
yield waitForUpdate(win, "project");
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
is(tabsNode.querySelectorAll(".panel-item").length, 1, "1 tab available");
|
||||
|
||||
tab = yield addTab(TEST_URI);
|
||||
|
||||
is(tabsNode.querySelectorAll(".panel-item").length, 2, "2 tabs available");
|
||||
|
||||
yield removeTab(tab);
|
||||
|
||||
is(tabsNode.querySelectorAll(".panel-item").length, 2, "2 tabs available");
|
||||
|
||||
docProject.querySelector("#refresh-tabs").click();
|
||||
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
|
||||
is(tabsNode.querySelectorAll(".panel-item").length, 1, "1 tab available");
|
||||
|
||||
yield win.Cmds.disconnectRuntime();
|
||||
yield closeWebIDE(win);
|
||||
|
||||
DebuggerServer.destroy();
|
||||
}).then(finish, handleError);
|
||||
}
|
||||
|
||||
function connectToLocal(win, docRuntime) {
|
||||
let deferred = promise.defer();
|
||||
win.AppManager.connection.once(
|
||||
win.Connection.Events.CONNECTED,
|
||||
() => deferred.resolve());
|
||||
docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function selectTabProject(win, docProject) {
|
||||
return Task.spawn(function* () {
|
||||
yield waitForUpdate(win, "runtime-targets");
|
||||
let tabsNode = docProject.querySelector("#project-panel-tabs");
|
||||
let tabNode = tabsNode.querySelectorAll(".panel-item")[1];
|
||||
let project = waitForUpdate(win, "project");
|
||||
tabNode.click();
|
||||
yield project;
|
||||
});
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue