Bug 912447 - [app manager] land the app manager front end. r=poirot.alex r=mratcliffe

This commit is contained in:
Paul Rouget 2013-09-05 15:15:37 +02:00
parent 3680175e2a
commit 6cd7501f9e
30 changed files with 2530 additions and 5 deletions

View File

@ -0,0 +1,99 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Cu = Components.utils;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/gDevTools.jsm");
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {require} = devtools;
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
const ConnectionStore = require("devtools/app-manager/connection-store");
const DeviceStore = require("devtools/app-manager/device-store");
let UI = {
init: function() {
this.useFloatingScrollbarsIfNeeded();
let connections = ConnectionManager.connections;
if (connections.length > 0) {
let hash = window.location.hash;
if (hash) {
let res = (/cid=([^&]+)/).exec(hash)
if (res) {
let [,cid] = res;
this.connection = connections.filter((({uid}) => uid == cid))[0];
}
}
if (!this.connection) {
// We take the first connection available.
this.connection = connections[0];
}
} else {
let host = Services.prefs.getCharPref("devtools.debugger.remote-host");
let port = Services.prefs.getIntPref("devtools.debugger.remote-port");
this.connection = ConnectionManager.createConnection(host, port);
}
window.location.hash = "cid=" + this.connection.uid;
window.parent.postMessage(JSON.stringify({name:"connection",cid:this.connection.uid}), "*");
this.store = Utils.mergeStores({
"device": new DeviceStore(this.connection),
"connection": new ConnectionStore(this.connection),
});
let pre = document.querySelector("#logs > pre");
pre.textContent = this.connection.logs;
pre.scrollTop = pre.scrollTopMax;
this.connection.on(Connection.Events.NEW_LOG, (event, str) => {
pre.textContent += "\n" + str;
pre.scrollTop = pre.scrollTopMax;
});
this.template = new Template(document.body, this.store, Utils.l10n);
this.template.start();
},
useFloatingScrollbarsIfNeeded: function() {
if (Services.appinfo.OS == "Darwin") {
return;
}
let scrollbarsUrl = Services.io.newURI("chrome://browser/skin/devtools/floating-scrollbars-light.css", null, null);
let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
winUtils.loadSheet(scrollbarsUrl, winUtils.AGENT_SHEET);
let computedStyle = window.getComputedStyle(document.documentElement);
if (computedStyle) { // Force a reflow to take the new css into account
let display = computedStyle.display; // Save display value
document.documentElement.style.display = "none";
window.getComputedStyle(document.documentElement).display; // Flush
document.documentElement.style.display = display; // Restore
}
},
disconnect: function() {
this.connection.disconnect();
},
connect: function() {
this.connection.connect();
},
editConnectionParameters: function() {
document.body.classList.add("edit-connection");
document.querySelector("input.host").focus();
},
saveConnectionInfo: function() {
document.body.classList.remove("edit-connection");
document.querySelector("#connect-button").focus();
let host = document.querySelector("input.host").value;
let port = document.querySelector("input.port").value;
this.connection.port = port;
this.connection.host = host;
Services.prefs.setCharPref("devtools.debugger.remote-host", host);
Services.prefs.setIntPref("devtools.debugger.remote-port", port);
},
}

View File

@ -0,0 +1,98 @@
<?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 % appMgrDTD SYSTEM "chrome://browser/locale/devtools/app-manager.dtd" >
%appMgrDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf8"/>
<link rel="stylesheet" href="chrome://browser/skin/devtools/app-manager/connection-footer.css" type="text/css"/>
</head>
<body onload="UI.init()">
<div id="connection-footer" template='{"type":"attribute","path":"connection.status","name":"status"}'>
<div id="banners-and-logs">
<!-- Connected -->
<div id="banner-connected" class="banner">
<div class="connected-indicator"></div>
<div id="status" class="banner-box">
<div class="banner-content">
<span template='{"type":"localizedContent","property":"connection.connectedToDevice","paths":["device.description.name"]}'></span>
<button class="action-cancel" onclick="UI.disconnect()">&connection.disconnect;</button>
</div>
</div>
</div>
<!-- Disconnected -->
<div id="banner-disconnected" class="banner">
<div class="connected-indicator"></div>
<div class="banner-box">
<div class="banner-content">
<span>&connection.notConnected;</span>
<button class="action-primary left" onclick="UI.connect()" id="connect-button" template='{"type":"localizedContent","property":"connection.connectTo","paths":["connection.host","connection.port"]}'></button>
<button class="right" onclick="UI.editConnectionParameters()">&connection.changeHostAndPort;</button>
<div id="start-simulator-box" template='{"type":"attribute","path":"simulators.versions.length","name":"simulators-count"}'>
<span>&connection.or;</span>
<button id="start-simulator-button" class="action-primary" onclick="UI.startSimulator()">&connection.startSimulator;</button>
</div>
</div>
</div>
</div>
<!-- Connecting -->
<div id="banner-connecting" class="banner">
<div class="connected-indicator"></div>
<div id="status" class="banner-box">
<div class="banner-content">
<span>&connection.connecting;</span>
<button class="action-cancel" onclick="UI.disconnect()">&connection.cancel;</button>
</div>
</div>
</div>
<!-- Disconnecting -->
<div id="banner-disconnecting" class="banner">
<div class="connected-indicator"></div>
<div id="status" class="banner-box">
<div class="banner-content">
<span>&connection.disconnecting;</span>
</div>
</div>
</div>
<!-- Editing -->
<div id="banner-editing" class="banner">
<div class="connected-indicator"></div>
<div class="banner-box">
<div class="banner-content">
<form onsubmit="UI.saveConnectionInfo()">
<input class="host" template='{"type":"attribute","path":"connection.host","name":"value"}'></input>
<input class="port" pattern="\d+" template='{"type":"attribute","path":"connection.port","name":"value"}' type="number"></input>
<button type="submit">&connection.saveConnectionInfo;</button>
</form>
</div>
</div>
</div>
<!-- Logs -->
<div id="banner-logs">
<div id="logs" class="banner-box">
<pre></pre>
</div>
</div>
</div>
</div>
</body>
<script type="application/javascript;version=1.8" src="utils.js"></script>
<script type="application/javascript;version=1.8" src="template.js"></script>
<script type="application/javascript;version=1.8" src="connection-footer.js"></script>
</html>

View File

@ -0,0 +1,211 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/gDevTools.jsm");
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {require} = devtools;
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
const {getDeviceFront} = require("devtools/server/actors/device");
const DeviceStore = require("devtools/app-manager/device-store");
const WebappsStore = require("devtools/app-manager/webapps-store");
const promise = require("sdk/core/promise");
window.addEventListener("message", function(event) {
try {
let message = JSON.parse(event.data);
if (message.name == "connection") {
let cid = parseInt(message.cid);
for (let c of ConnectionManager.connections) {
if (c.uid == cid) {
UI.connection = c;
UI.onNewConnection();
break;
}
}
}
} catch(e) {
Cu.reportError(e);
}
}, false);
let UI = {
init: function() {
this.showFooterIfNeeded();
this._onConnectionStatusChange = this._onConnectionStatusChange.bind(this);
this.setTab("apps");
if (this.connection) {
this.onNewConnection();
} else {
this.hide();
}
},
showFooterIfNeeded: function() {
let footer = document.querySelector("#connection-footer");
if (window.parent == window) {
// We're alone. Let's add a footer.
footer.removeAttribute("hidden");
footer.src = "chrome://browser/content/devtools/app-manager/connection-footer.xhtml";
} else {
footer.setAttribute("hidden", "true");
}
},
hide: function() {
document.body.classList.add("notconnected");
},
show: function() {
document.body.classList.remove("notconnected");
},
onNewConnection: function() {
this.connection.on(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange);
this.store = Utils.mergeStores({
"device": new DeviceStore(this.connection),
"apps": new WebappsStore(this.connection),
});
this.template = new Template(document.body, this.store, Utils.l10n);
this.template.start();
this._onConnectionStatusChange();
},
setWallpaper: function(dataurl) {
document.getElementById("meta").style.backgroundImage = "url(" + dataurl + ")";
},
_onConnectionStatusChange: function() {
if (this.connection.status != Connection.Status.CONNECTED) {
this.hide();
this.listTabsResponse = null;
} else {
this.show();
this.connection.client.listTabs(
response => {
this.listTabsResponse = response;
let front = getDeviceFront(this.connection.client, this.listTabsResponse);
front.getWallpaper().then(longstr => {
longstr.string().then(dataURL => {
longstr.release().then(null, Cu.reportError);
this.setWallpaper(dataURL);
});
});
}
);
}
},
setTab: function(name) {
var tab = document.querySelector(".tab.selected");
var panel = document.querySelector(".tabpanel.selected");
if (tab) tab.classList.remove("selected");
if (panel) panel.classList.remove("selected");
var tab = document.querySelector(".tab." + name);
var panel = document.querySelector(".tabpanel." + name);
if (tab) tab.classList.add("selected");
if (panel) panel.classList.add("selected");
},
screenshot: function() {
if (!this.listTabsResponse)
return;
let front = getDeviceFront(this.connection.client, this.listTabsResponse);
front.screenshotToBlob().then(blob => {
let topWindow = Services.wm.getMostRecentWindow("navigator:browser");
let gBrowser = topWindow.gBrowser;
let url = topWindow.URL.createObjectURL(blob);
let tab = gBrowser.selectedTab = gBrowser.addTab(url);
tab.addEventListener("TabClose", function onTabClose() {
tab.removeEventListener("TabClose", onTabClose, false);
topWindow.URL.revokeObjectURL(url);
}, false);
}).then(null, console.error);
},
_getTargetForApp: function(manifest) { // FIXME <- will be implemented in bug 912476
if (!this.listTabsResponse)
return null;
let actor = this.listTabsResponse.webappsActor;
let deferred = promise.defer();
let request = {
to: actor,
type: "getAppActor",
manifestURL: manifest,
}
this.connection.client.request(request, (res) => {
if (res.error) {
deferred.reject(res.error);
} else {
let options = {
form: res.actor,
client: this.connection.client,
chrome: false
};
devtools.TargetFactory.forRemoteTab(options).then((target) => {
deferred.resolve(target)
}, (error) => {
deferred.reject(error);
});
}
});
return deferred.promise;
},
openToolbox: function(manifest) {
this._getTargetForApp(manifest).then((target) => {
gDevTools.showToolbox(target, "webconsole", devtools.Toolbox.HostType.WINDOW);
}, console.error);
},
startApp: function(manifest) {
let deferred = promise.defer();
if (!this.listTabsResponse) {
deferred.reject();
} else {
let actor = this.listTabsResponse.webappsActor;
let request = {
to: actor,
type: "launch",
manifestURL: manifest,
}
this.connection.client.request(request, (res) => {
deferred.resolve()
});
}
return deferred.promise;
},
stopApp: function(manifest) {
let deferred = promise.defer();
if (!this.listTabsResponse) {
deferred.reject();
} else {
let actor = this.listTabsResponse.webappsActor;
let request = {
to: actor,
type: "close",
manifestURL: manifest,
}
this.connection.client.request(request, (res) => {
deferred.resolve()
});
}
return deferred.promise;
},
}

View File

@ -0,0 +1,98 @@
<?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 % appMgrDTD SYSTEM "chrome://browser/locale/devtools/app-manager.dtd" >
%appMgrDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf8"/>
<base href="chrome://browser/content/devtools/app-manager/"></base>
<title>&device.title;</title>
<link rel="stylesheet" href="chrome://browser/skin/devtools/app-manager/device.css" type="text/css"/>
</head>
<body onload="UI.init()">
<div id="notConnectedMessage"><span>&device.notConnected;</span></div>
<section id="content">
<aside id="sidebar">
<div id="meta">
<header>
<h1>
<span template='{"type":"textContent","path":"device.description.name"}'></span>
<span template='{"type":"textContent","path":"device.description.version"}'></span>
<span template='{"type":"textContent","path":"device.description.channel"}'></span>
</h1>
<h3>
<span>Gecko </span>
<span template='{"type":"textContent","path":"device.description.geckoversion"}'></span>
</h3>
<p template='{"type":"localizedContent","property":"device.deviceSize", "paths":["device.description.width","device.description.height","device.description.dpi"]}'></p>
</header>
<button onclick="UI.screenshot()">&device.screenshot;</button>
<div id="tabs-headers">
<div onclick="UI.setTab('apps')" class="tab sidebar-item apps">&device.installedApps;</div>
<div onclick="UI.setTab('permissions')" class="tab sidebar-item permissions">&device.permissions;</div>
</div>
</div>
</aside>
<section id="detail">
<div id="tabs">
<div class="tabpanel apps">
<div class="app-list" template-loop='{"arrayPath":"apps.all","childSelector":"#app-template"}'></div>
</div>
<div class="tabpanel permissions permission-table">
<div class="permission-table-header">
<div>&device.name;</div>
<div>&device.app;</div>
<div>&device.privileged;</div>
<div>&device.certified;</div>
</div>
<div class="permission-table-body" >
<section template-loop='{"arrayPath":"device.permissions","childSelector":"#permission-template"}'></section>
</div>
<div class="permission-table-footer">
<div class="allow-label">&device.allow;</div>
<div class="prompt-label">&device.prompt;</div>
<div class="deny-label">&device.deny;</div>
</div>
</div>
</div>
</section>
</section>
<iframe id="connection-footer" hidden="true"></iframe>
</body>
<template id="permission-template">
<div class="permission">
<div template='{"type":"textContent","path":"name"}'></div>
<div template='{"type":"attribute", "name":"permission", "path":"app"}'></div>
<div template='{"type":"attribute", "name":"permission", "path":"privileged"}'></div>
<div template='{"type":"attribute", "name":"permission", "path":"certified"}'></div>
</div>
</template>
<template id="app-template">
<div class="app" template='{"type":"attribute","path":"running","name":"running"}'>
<img class="app-icon" template='{"type":"attribute","path":"iconURL","name":"src"}'></img>
<span class="app-name" template='{"type":"textContent","path":"name"}'></span>
<div class="app-buttons">
<button class="button-debug" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.openToolbox(this.dataset.manifest)">&device.debugApp;</button>
<button class="button-start" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.startApp(this.dataset.manifest)">&device.startApp;</button>
<button class="button-stop" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.stopApp(this.dataset.manifest)">&device.stopApp;</button>
</div>
</div>
</template>
<script type="application/javascript;version=1.8" src="utils.js"></script>
<script type="application/javascript;version=1.8" src="template.js"></script>
<script type="application/javascript;version=1.8" src="device.js"></script>
</html>

View File

@ -0,0 +1,54 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Cu = Components.utils;
Cu.import("resource:///modules/devtools/gDevTools.jsm");
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {require} = devtools;
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
let connection;
window.addEventListener("message", function(event) {
try {
let json = JSON.parse(event.data);
if (json.name == "connection") {
let cid = +json.cid;
for (let c of ConnectionManager.connections) {
if (c.uid == cid) {
connection = c;
onNewConnection();
break;
}
}
}
} catch(e) { Cu.reportError(e); }
// Forward message
let panels = document.querySelectorAll(".panel");
for (let frame of panels) {
frame.contentWindow.postMessage(event.data, "*");
}
}, false);
function onNewConnection() {
connection.on(Connection.Status.CONNECTED, () => {
document.querySelector("#content").classList.add("connected");
});
connection.on(Connection.Status.DISCONNECTED, () => {
document.querySelector("#content").classList.remove("connected");
});
}
function selectTab(id) {
for (let type of ["button", "panel"]) {
let oldSelection = document.querySelector("." + type + "[selected]");
let newSelection = document.querySelector("." + id + "-" + type);
if (!newSelection) continue;
if (oldSelection) oldSelection.removeAttribute("selected");
newSelection.setAttribute("selected", "true");
}
}
selectTab("projects");

View File

@ -0,0 +1,38 @@
<?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 window [
<!ENTITY % appMgrDTD SYSTEM "chrome://browser/locale/devtools/app-manager.dtd" >
%appMgrDTD;
]>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/app-manager/index.css"?>
<window id="app-manager-window"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&index.title;"
windowtype="devtools:app-manager"
macanimationtype="document"
fullscreenbutton="true"
screenX="4" screenY="4"
width="800" height="600"
persist="screenX screenY width height sizemode">
<vbox flex="1">
<hbox id="content" flex="1">
<vbox id="tabs">
<button class="button projects-button" onclick="selectTab('projects')">&index.projects;</button>
<button class="button device-button" onclick="selectTab('device')">&index.device;</button>
</vbox>
<hbox id="tab-panels" flex="1">
<iframe flex="1" class="panel projects-panel" src="chrome://browser/content/devtools/app-manager/projects.xhtml"/>
<iframe flex="1" class="panel device-panel" src="chrome://browser/content/devtools/app-manager/device.xhtml"/>
</hbox>
</hbox>
<iframe id="connection-footer" src="chrome://browser/content/devtools/app-manager/connection-footer.xhtml"></iframe>
</vbox>
<script type="application/javascript;version=1.8" src="chrome://browser/content/devtools/app-manager/index.js"></script>
</window>

View File

@ -0,0 +1,269 @@
/* 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 = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource:///modules/devtools/gDevTools.jsm");
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {require} = devtools;
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
const {AppProjects} = require("devtools/app-manager/app-projects");
const {AppValidator} = require("devtools/app-manager/app-validator");
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
const promise = require("sdk/core/promise");
window.addEventListener("message", function(event) {
try {
let json = JSON.parse(event.data);
if (json.name == "connection") {
let cid = parseInt(json.cid);
for (let c of ConnectionManager.connections) {
if (c.uid == cid) {
UI.connection = c;
UI.onNewConnection();
break;
}
}
}
} catch(e) {}
}, false);
let UI = {
onload: function() {
this.template = new Template(document.body, AppProjects.store, Utils.l10n);
this.template.start();
AppProjects.store.on("set", (event,path,value) => {
if (path == "projects") {
AppProjects.store.object.projects.forEach(UI.validate);
}
});
},
onNewConnection: function() {
this.connection.on(Connection.Events.STATUS_CHANGED, () => this._onConnectionStatusChange());
this._onConnectionStatusChange();
},
_onConnectionStatusChange: function() {
if (this.connection.status != Connection.Status.CONNECTED) {
document.body.classList.remove("connected");
this.listTabsResponse = null;
} else {
document.body.classList.add("connected");
this.connection.client.listTabs(
response => {this.listTabsResponse = response}
);
}
},
_selectFolder: function() {
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
fp.init(window, Utils.l10n("project.filePickerTitle"), Ci.nsIFilePicker.modeGetFolder);
let res = fp.show();
if (res != Ci.nsIFilePicker.returnCancel)
return fp.file;
return null;
},
addPackaged: function() {
let folder = this._selectFolder();
if (!folder)
return;
AppProjects.addPackaged(folder)
.then(function (project) {
UI.validate(project);
UI.selectProject(project.location);
});
},
addHosted: function() {
let urlInput = document.querySelector("#url-input");
let manifestURL = urlInput.value;
AppProjects.addHosted(manifestURL)
.then(function (project) {
UI.validate(project);
UI.selectProject(project.location);
});
},
_getLocalIconURL: function(project, manifest) {
let icon;
if (manifest.icons) {
let size = Object.keys(manifest.icons).sort(function(a, b) b - a)[0];
if (size) {
icon = manifest.icons[size];
}
}
if (!icon)
return null;
if (project.type == "hosted") {
let manifestURL = Services.io.newURI(project.location, null, null);
let origin = Services.io.newURI(manifestURL.prePath, null, null);
return Services.io.newURI(icon, null, origin).spec;
} else if (project.type == "packaged") {
let projectFolder = FileUtils.File(project.location);
let folderURI = Services.io.newFileURI(projectFolder).spec;
return folderURI + icon.replace(/^\/|\\/, "");
}
},
validate: function(project) {
let validation = new AppValidator(project);
validation.validate()
.then(function () {
if (validation.manifest) {
project.name = validation.manifest.name;
project.icon = UI._getLocalIconURL(project, validation.manifest);
project.manifest = validation.manifest;
}
project.validationStatus = "valid";
if (validation.warnings.length > 0) {
project.warningsCount = validation.warnings.length;
project.warnings = validation.warnings.join(",\n ");
project.validationStatus = "warning";
} else {
project.warnings = "";
project.warningsCount = 0;
}
if (validation.errors.length > 0) {
project.errorsCount = validation.errors.length;
project.errors = validation.errors.join(",\n ");
project.validationStatus = "error";
} else {
project.errors = "";
project.errorsCount = 0;
}
});
},
update: function(location) {
let project = AppProjects.get(location);
this.validate(project);
},
remove: function(location) {
AppProjects.remove(location);
},
_getProjectManifestURL: function (project) {
if (project.type == "packaged") {
return "app://" + project.packagedAppOrigin + "/manifest.webapp";
} else if (project.type == "hosted") {
return project.location;
}
},
start: function(location) {
let project = AppProjects.get(location);
let request = {
to: this.listTabsResponse.webappsActor,
type: "launch",
manifestURL: this._getProjectManifestURL(project)
};
this.connection.client.request(request, (res) => {
});
},
stop: function(location) {
let project = AppProjects.get(location);
let request = {
to: this.listTabsResponse.webappsActor,
type: "close",
manifestURL: this._getProjectManifestURL(project)
};
this.connection.client.request(request, (res) => {
});
},
_getTargetForApp: function(manifest) { // FIXME <- will be implemented in bug 912476
if (!this.listTabsResponse)
return null;
let actor = this.listTabsResponse.webappsActor;
let deferred = promise.defer();
let request = {
to: actor,
type: "getAppActor",
manifestURL: manifest,
}
this.connection.client.request(request, (res) => {
if (res.error) {
deferred.reject(res.error);
} else {
let options = {
form: res.actor,
client: this.connection.client,
chrome: false
};
devtools.TargetFactory.forRemoteTab(options).then((target) => {
deferred.resolve(target)
}, (error) => {
deferred.reject(error);
});
}
});
return deferred.promise;
},
openToolbox: function(location) {
let project = AppProjects.get(location);
let manifest = this._getProjectManifestURL(project);
this._getTargetForApp(manifest).then((target) => {
gDevTools.showToolbox(target,
null,
devtools.Toolbox.HostType.WINDOW,
this.connection.uid);
}, console.error);
},
reveal: function(location) {
let project = AppProjects.get(location);
if (project.type == "packaged") {
let projectFolder = FileUtils.File(project.location);
projectFolder.reveal();
} else {
// TODO: eventually open hosted apps in firefox
// when permissions are correctly supported by firefox
}
},
selectProject: function(location) {
let projects = AppProjects.store.object.projects;
let idx = 0;
for (; idx < projects.length; idx++) {
if (projects[idx].location == location) {
break;
}
}
if (idx == projects.length) {
// Not found
return;
}
let oldButton = document.querySelector(".project-item.selected");
if (oldButton) {
oldButton.classList.remove("selected");
}
let button = document.getElementById(location);
button.classList.add("selected");
let template = '{"path":"projects.' + idx + '","childSelector":"#lense-template"}';
let lense = document.querySelector("#lense");
lense.setAttribute("template-for", template);
this.template._processFor(lense);
},
}

View File

@ -0,0 +1,93 @@
<?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 % appMgrDTD SYSTEM "chrome://browser/locale/devtools/app-manager.dtd" >
%appMgrDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf8"/>
<base href="chrome://browser/content/devtools/app-manager/"></base>
<title>&projects.title;</title>
<link rel="stylesheet" href="chrome://browser/skin/devtools/app-manager/projects.css" type="text/css"/>
</head>
<body onload="UI.onload()">
<aside id="sidebar">
<div id="project-list" template='{"type":"attribute","path":"projects.length","name":"projects-count"}'>
<div template-loop='{"arrayPath":"projects","childSelector":"#project-item-template"}'></div>
<div id="no-project">&projects.noProject;</div>
</div>
<div id="new-packaged-project" onclick="UI.addPackaged()">&projects.addPackaged;</div>
<div id="new-hosted-project">&projects.addHosted;
<form onsubmit="UI.addHosted(); return false;" id="new-hosted-project-wrapper">
<input value="" id="url-input" type="url" pattern="https?://.+" placeholder="&projects.hostedManifestPlaceHolder;" size="50" />
<div onclick="UI.addHosted()" id="new-hosted-project-click"></div>
<input type="submit" hidden="true"></input>
</form>
</div>
</aside>
<section id="lense"></section>
</body>
<template id="project-item-template">
<div class="project-item" template='{"type":"attribute","path":"location","name":"id"}' onclick="UI.selectProject(this.id)">
<div class="project-item-status" template='{"type":"attribute","path":"validationStatus","name":"status"}'></div>
<img class="project-item-icon" template='{"type":"attribute","path":"icon","name":"src"}' />
<div class="project-item-meta">
<div class="button-remove" onclick="UI.remove(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}' title="&projects.removeApp;"></div>
<strong template='{"type":"textContent","path":"name"}'></strong>
<span class="project-item-type" template='{"type":"textContent","path":"type"}'></span>
<p class="project-item-description" template='{"type":"textContent","path":"manifest.description"}'></p>
<div template='{"type":"attribute","path":"validationStatus","name":"status"}'>
<div class="project-item-errors"><span template='{"type":"textContent","path":"errorsCount"}'></span></div>
<div class="project-item-warnings"><span template='{"type":"textContent","path":"warningsCount"}'></span></div>
</div>
</div>
</div>
</template>
<template id="lense-template">
<div>
<div class="project-details" template='{"type":"attribute","path":"validationStatus","name":"status"}'>
<div class="project-header">
<img class="project-icon" template='{"type":"attribute","path":"icon","name":"src"}'/>
<div class="project-details">
<div class="project-title">
<h1 template='{"type":"textContent","path":"name"}'></h1>
<div class="project-status" template='{"type":"attribute","path":"validationStatus","name":"status"}'>
<p class="project-validation" template='{"type":"textContent","path":"validationStatus"}'></p>
<p class="project-type" template='{"type":"textContent","path":"type"}'></p>
</div>
</div>
<span template='{"type":"textContent","path":"manifest.developer.name"}'></span>
<p class="project-location" template='{"type":"textContent","path":"location"}' onclick="UI.reveal(this.textContent)"></p>
<p class="project-description" template='{"type":"textContent","path":"manifest.description"}'></p>
</div>
</div>
<div class="project-buttons">
<button class="project-button-refresh" onclick="UI.update(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.reloadFiles;</button>
<!-- Not available until bug 911785 is fixed
<button class="device-action project-button-install" onclick="UI.install(this, this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.installApp;</button>
-->
<button class="device-action project-button-start" onclick="UI.start(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.startApp;</button>
<button class="device-action project-button-stop" onclick="UI.stop(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.stopApp;</button>
<!-- Not available until bug 911785 is fixed
<button class="device-action project-button-debug" onclick="UI.openToolbox(this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}'>&projects.debugApp;</button>
-->
</div>
<div class="project-errors" template='{"type":"textContent","path":"errors"}'></div>
<div class="project-warnings" template='{"type":"textContent","path":"warnings"}'></div>
</div>
</div>
</template>
<script type="application/javascript;version=1.8" src="utils.js"></script>
<script type="application/javascript;version=1.8" src="projects.js"></script>
<script type="application/javascript;version=1.8" src="template.js"></script>
</html>

View File

@ -65,6 +65,7 @@ function Template(root, store, l10nResolver) {
this._nodeListeners = new Map();
this._loopListeners = new Map();
this._forListeners = new Map();
this._root = root;
this._doc = this._root.ownerDocument;
@ -134,6 +135,14 @@ Template.prototype = {
}
}
// For:
set = this._forListeners.get(path);
if (set) {
for (let elt of set) {
this._processFor(elt);
}
}
// Nodes:
set = this._nodeListeners.get(path);
if (set) {
@ -176,6 +185,14 @@ Template.prototype = {
set.add(element);
},
_registerFor: function(path, element) {
if (!this._forListeners.has(path)) {
this._forListeners.set(path, new Set());
}
let set = this._forListeners.get(path);
set.add(element);
},
_processNode: function(element, rootPath="") {
// The actual magic.
// The element has a template attribute.
@ -263,8 +280,8 @@ Template.prototype = {
// through the array, and build one child per
// item. The template for this child is pointed
// by the childSelector property.
let e = element;
try {
let e = element;
let template, count;
let str = e.getAttribute("template-loop");
let json = JSON.parse(str);
@ -304,12 +321,51 @@ Template.prototype = {
}
},
_processFor: function(element, rootPath="") {
let e = element;
try {
let template;
let str = e.getAttribute("template-for");
let json = JSON.parse(str);
if (!("path" in json) ||
!("childSelector" in json)) {
throw new Error("missing property");
}
if (rootPath) {
json.path = rootPath + "." + json.path;
}
let templateParent = this._doc.querySelector(json.childSelector);
if (!templateParent) {
throw new Error("can't find child");
}
let content = this._doc.createElement("div");
content.innerHTML = templateParent.innerHTML;
content = content.firstElementChild;
this._processTree(content, json.path);
this._unregisterNodes(e.querySelectorAll("[template]"));
this._registerFor(json.path, e);
e.innerHTML = "";
e.appendChild(content);
} catch(exception) {
console.error("Invalid template: " + e.outerHTML + " (" + exception + ")");
}
},
_processTree: function(parent, rootPath="") {
let loops = parent.querySelectorAll(":not(template) [template-loop]");
let fors = parent.querySelectorAll(":not(template) [template-for]");
let nodes = parent.querySelectorAll(":not(template) [template]");
for (let e of loops) {
this._processLoop(e, rootPath);
}
for (let e of fors) {
this._processFor(e, rootPath);
}
for (let e of nodes) {
this._processNode(e, rootPath);
}

View File

@ -0,0 +1,53 @@
/* 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/. */
/**
*
* Some helpers for common operations in the App Manager:
*
* . mergeStores: merge several store into one.
* . l10n: resolves strings from app-manager.properties.
*
*/
let Utils = (function() {
const Cu = Components.utils;
const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {require} = devtools;
const EventEmitter = require("devtools/shared/event-emitter");
function _forwardSetEvent(key, store, finalStore) {
store.on("set", function(event, path, value) {
finalStore.emit("set", [key].concat(path), value);
});
}
function mergeStores(stores) {
let finalStore = {object:{}};
EventEmitter.decorate(finalStore);
for (let key in stores) {
finalStore.object[key] = stores[key].object,
_forwardSetEvent(key, stores[key], finalStore);
}
return finalStore;
}
let strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties");
function l10n (property, args = []) {
if (args && args.length > 0) {
return strings.formatStringFromName(property, args, args.length);
} else {
return strings.GetStringFromName(property);
}
}
return {
mergeStores: mergeStores,
l10n: l10n
}
})();

View File

@ -66,3 +66,12 @@ browser.jar:
content/browser/devtools/connect.css (framework/connect/connect.css)
content/browser/devtools/connect.js (framework/connect/connect.js)
content/browser/devtools/app-manager/template.js (app-manager/content/template.js)
content/browser/devtools/app-manager/utils.js (app-manager/content/utils.js)
content/browser/devtools/app-manager/connection-footer.js (app-manager/content/connection-footer.js)
content/browser/devtools/app-manager/connection-footer.xhtml (app-manager/content/connection-footer.xhtml)
content/browser/devtools/app-manager/device.js (app-manager/content/device.js)
content/browser/devtools/app-manager/device.xhtml (app-manager/content/device.xhtml)
content/browser/devtools/app-manager/projects.js (app-manager/content/projects.js)
content/browser/devtools/app-manager/projects.xhtml (app-manager/content/projects.xhtml)
content/browser/devtools/app-manager/index.xul (app-manager/content/index.xul)
content/browser/devtools/app-manager/index.js (app-manager/content/index.js)

View File

@ -0,0 +1,49 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY index.title "App Manager">
<!ENTITY index.projects "My Apps">
<!ENTITY index.device "My Device">
<!ENTITY device.screenshot "Screenshot">
<!ENTITY device.title "Device Control Center">
<!ENTITY device.notConnected "Not connected. Please connect your device below.">
<!ENTITY device.startApp "Start">
<!ENTITY device.stopApp "Stop">
<!ENTITY device.debugApp "Debug">
<!ENTITY device.name "Name">
<!ENTITY device.app "App">
<!ENTITY device.privileged "Privileged">
<!ENTITY device.certified "Certified">
<!ENTITY device.allow "Allow">
<!ENTITY device.prompt "Prompt">
<!ENTITY device.deny "Deny">
<!ENTITY device.installedApps "Installed Apps">
<!ENTITY device.permissions "Permissions">
<!ENTITY connection.disconnect "Disconnect">
<!ENTITY connection.showDeviceCtrlCenter "Click for More Details">
<!ENTITY connection.notConnected "Not Connected">
<!ENTITY connection.changeHostAndPort "Change">
<!ENTITY connection.startSimulator "Start Simulator">
<!ENTITY connection.saveConnectionInfo "Save">
<!ENTITY connection.connecting "Connecting…">
<!ENTITY connection.disconnecting "Disconnecting…">
<!ENTITY connection.cancel "Cancel">
<!ENTITY connection.or "or">
<!ENTITY projects.localApps "Local Apps">
<!ENTITY projects.addApp "Add">
<!ENTITY projects.addPackaged "Add Packaged App">
<!ENTITY projects.addHosted "Add Hosted App">
<!ENTITY projects.title "Local Apps">
<!ENTITY projects.appDetails "App Details">
<!ENTITY projects.removeApp "Remove">
<!ENTITY projects.reloadFiles "Refresh">
<!ENTITY projects.installApp "Install">
<!ENTITY projects.startApp "Start">
<!ENTITY projects.stopApp "Stop">
<!ENTITY projects.debugApp "Debug">
<!ENTITY projects.hostedManifestPlaceHolder "http://example.com/app/webapp.manifest">
<!ENTITY projects.noProject "No project linked. Add a new packaged app below (a directory) or a hosted app (link to a manifest file).">

View File

@ -0,0 +1,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/.
device.deviceSize=Device size: %1$Sx%2$S (%3$S DPI)
connection.connectedToDevice=Connected to %1$S
connection.connectTo=Connect to %1$S:%2$S
project.filePickerTitle=Select a webapp folder

View File

@ -52,6 +52,8 @@
locale/browser/devtools/connection-screen.dtd (%chrome/browser/devtools/connection-screen.dtd)
locale/browser/devtools/connection-screen.properties (%chrome/browser/devtools/connection-screen.properties)
locale/browser/devtools/font-inspector.dtd (%chrome/browser/devtools/font-inspector.dtd)
locale/browser/devtools/app-manager.dtd (%chrome/browser/devtools/app-manager.dtd)
locale/browser/devtools/app-manager.properties (%chrome/browser/devtools/app-manager.properties)
locale/browser/newTab.dtd (%chrome/browser/newTab.dtd)
locale/browser/newTab.properties (%chrome/browser/newTab.properties)
locale/browser/openLocation.dtd (%chrome/browser/openLocation.dtd)

View File

@ -226,6 +226,17 @@ browser.jar:
skin/classic/browser/devtools/responsiveui-rotate.png (../shared/devtools/responsiveui-rotate.png)
skin/classic/browser/devtools/responsiveui-touch.png (../shared/devtools/responsiveui-touch.png)
skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
skin/classic/browser/devtools/app-manager/connection-footer.css (../shared/devtools/app-manager/connection-footer.css)
skin/classic/browser/devtools/app-manager/index.css (../shared/devtools/app-manager/index.css)
skin/classic/browser/devtools/app-manager/device.css (../shared/devtools/app-manager/device.css)
skin/classic/browser/devtools/app-manager/projects.css (../shared/devtools/app-manager/projects.css)
skin/classic/browser/devtools/app-manager/warning.svg (../shared/devtools/app-manager/images/warning.svg)
skin/classic/browser/devtools/app-manager/error.svg (../shared/devtools/app-manager/images/error.svg)
skin/classic/browser/devtools/app-manager/plus.svg (../shared/devtools/app-manager/images/plus.svg)
skin/classic/browser/devtools/app-manager/remove.svg (../shared/devtools/app-manager/images/remove.svg)
skin/classic/browser/devtools/app-manager/index-icons.svg (../shared/devtools/app-manager/images/index-icons.svg)
skin/classic/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
skin/classic/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-16-throbber.png
skin/classic/browser/sync-16.png

View File

@ -316,6 +316,17 @@ browser.jar:
skin/classic/browser/devtools/responsiveui-rotate.png (../shared/devtools/responsiveui-rotate.png)
skin/classic/browser/devtools/responsiveui-touch.png (../shared/devtools/responsiveui-touch.png)
skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
skin/classic/browser/devtools/app-manager/connection-footer.css (../shared/devtools/app-manager/connection-footer.css)
skin/classic/browser/devtools/app-manager/index.css (../shared/devtools/app-manager/index.css)
skin/classic/browser/devtools/app-manager/device.css (../shared/devtools/app-manager/device.css)
skin/classic/browser/devtools/app-manager/projects.css (../shared/devtools/app-manager/projects.css)
skin/classic/browser/devtools/app-manager/warning.svg (../shared/devtools/app-manager/images/warning.svg)
skin/classic/browser/devtools/app-manager/error.svg (../shared/devtools/app-manager/images/error.svg)
skin/classic/browser/devtools/app-manager/plus.svg (../shared/devtools/app-manager/images/plus.svg)
skin/classic/browser/devtools/app-manager/remove.svg (../shared/devtools/app-manager/images/remove.svg)
skin/classic/browser/devtools/app-manager/index-icons.svg (../shared/devtools/app-manager/images/index-icons.svg)
skin/classic/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
skin/classic/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-throbber.png
skin/classic/browser/sync-16.png

View File

@ -0,0 +1,200 @@
/* 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/. */
/************** LAYOUT **************/
#connection-footer {
display: flex;
flex-direction: column;
height: 50px;
}
#banners-and-logs {
display: flex;
}
#logs {
display: flex;
width: 40%;
padding: 0;
width: 100%;
}
.banner {
display: none;
width: 60%;
}
#connection-footer[status="connected"] #banner-connected,
#connection-footer[status="connecting"] #banner-connecting,
#connection-footer[status="disconnected"] #banner-disconnected,
#connection-footer[status="disconnecting"] #banner-disconnecting {
display: flex;
}
body.edit-connection .banner {
display: none !important;
}
body.edit-connection #banner-editing {
display: flex !important;
}
#banner-logs {
width: 40%;
display: flex;
}
#logs > pre {
overflow: auto;
white-space: pre-line;
}
#status.banner-box {
width: 100% !important;
}
.banner-box {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
}
#banner-connected > .banner-box {
align-items: flex-start;
}
#start-simulator-box {
display: inline;
}
#start-simulator-box[simulators-count="0"] {
display: none;
}
/************** PIXELS **************/
* {
margin: 0;
padding: 0;
-moz-box-sizing: border-box;
font-size: 0.9rem;
}
body {
color: #333;
background-color: white;
font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
}
button {
background: linear-gradient(to bottom, #49535C, #394148);
box-shadow: 0px 1px 1px #3C444D, inset 0 1px 0px rgba(255,255,255,0.1);
color: #9FA6AD;
text-shadow: 0px 1px 1px rgba(0,0,0,0.6);
border: 1px solid #111;
cursor: pointer;
border-radius: 3px;
padding: 3px 10px;
}
button.left {
margin-right: 0px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
button.right {
margin-left: -6px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
button.action-primary {
background: linear-gradient(to bottom, #276DA3, #1E5580);
color: #EEE;
}
button.action-cancel {
background: linear-gradient(to bottom, #B32B02, #942300);
color: #EEE;
}
#banners-and-logs {
border-top: #111 solid;
border-width: 1px 0;
background: linear-gradient(to bottom, #323A42, #29313A);
color: #A8BABF;
box-shadow: inset 0 0 1px #424A51;
}
#status {
background: linear-gradient(to bottom, #454F59, #404952);
box-shadow: inset 0 0 1px #606D78, inset 0 1px 0 #5E6973;
}
#logs > pre {
border: 1px solid #111;
box-shadow: 0px 1px 1px #49525A, inset 0 0 5px rgba(0,0,0,0.3);
font-size: 10px;
background: #22272D;
padding: 5px;
height: 100%;
padding-left: 20px;
position: relative;
}
#logs > pre span{
text-shadow: 0 1px 2px #000;
color: #3195FB;
position: fixed;
right: calc(30% - 15px);
bottom: -1px;
}
#logs > pre b {
font-size: 10px;
color: #70C4FF;
}
.banner-box {
box-shadow: inset 0 0 1px #667480, inset 0 1px 0 #5E6973;
border-right: 1px solid #111;
background-position: center right;
background-size: 1px 100%;
background-repeat: no-repeat;
padding: 10px 20px;
position: relative;
}
.connected-status {
color: #B3BFC9;
text-shadow: 0px 1px 2px rgba(0,0,0,0.9);
padding-bottom: 10px;
}
.connected-status {
font-size: 150%;
top: 10%;
padding-right: 3px;
position: relative;
}
.connected-indicator {
box-shadow: inset 0 1px 0 rgba(255,255,255,0.3), inset 0 0px 1px rgba(255,255,255,0.3);
height: 100%;
flex: 0 0 10px;
}
#banner-connected .connected-indicator,
#banner-connecting .connected-indicator {
background: linear-gradient(to bottom, #69B8FF, #339FFF );
}
#banner-disconnected .connected-indicator,
#banner-editing .connected-indicator,
#banner-disconnecting .connected-indicator {
background: linear-gradient(to bottom, #375A87, #1C4375 );
}

View File

@ -0,0 +1,378 @@
/* 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/. */
/***************** GENERAL *****************/
* {
margin: 0;
padding: 0;
-moz-box-sizing: border-box;
}
html, body {
height: 100%;
}
body {
font-size: 0.9rem;
color: #333;
background-color: rgb(225, 225, 225);
font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
display: flex;
flex-direction: column;
}
template {
display: none;
}
h1 {
font-size: 20px;
}
#content {
display: flex;
flex-direction: row;
height: 100%;
overflow: hidden;
}
#detail {
background-image: url('noise.png');
display: flex;
flex-grow: 1;
z-index: 1;
overflow: hidden;
}
#meta {
background-size: 100%;
padding-top: 50px;
}
#connection-footer {
border-width: 0;
height: 50px;
min-height: 50px;
}
/***************** APP BUTTONS *****************/
.app-buttons {
display: block;
margin-left: 20px;
color: #BBB;
}
button {
margin: 0;
font-size: 11px;
border: 1px solid #CCC;
padding: 5px 15px;
cursor: pointer;
background: rgba(255,255,255,0.4);
text-transform: uppercase;
border-radius: 3px;
border-width: 1px;
}
.app-buttons > button {
display: none;
}
.app[running="false"] > .app-buttons > .button-start,
.app[running="true"] > .app-buttons > .button-stop,
.app[running="true"] > .app-buttons > .button-debug {
display: inline-block;
}
.button-debug {
color: #3498DB;
}
.button-debug:hover {
background-color: #3498DB;
color: #FFF;
}
.button-start {
color: #18BC9C
}
.button-start:hover {
background-color: #18BC9C;
color: #FFF;
}
.button-stop {
color: #E74C3C;
}
.button-stop:hover {
background-color: #E74C3C;
color: #FFF;
}
/***************** PERMISSIONS *****************/
.permission-table {
display: flex;
flex-direction: column;
height: 100%;
}
.permission-table-body {
overflow: auto;
display: flex;
flex-grow: 1;
flex-direction: column;
}
.permission-table-header,
.permission-table-footer {
display: flex;
background: #FFF;
border-top: 1px solid #CCC;
z-index: 2;
flex-shrink: 0;
}
.permission-table-header > div,
.permission-table-footer > div {
z-index: 2;
flex-grow: 1;
background: linear-gradient(to bottom, #49535C, #394148);
box-shadow: 0px 1px 3px rgba(12, 20, 30, 0.5), inset 0 1px 0px rgba(255,255,255,0.1);
color: #9FA6AD;
text-shadow: 0px 1px 1px rgba(0,0,0,0.6);
border: 0;
margin: auto 0;
padding: 5px;
text-align: center;
background: transparent;
box-shadow: none;
text-shadow: none;
}
.permission-table-header > div {
flex-basis: 20%;
}
.permission-table-header > div:first-child {
text-align: left;
padding-left: 10px;
flex-basis: 30%;
}
.permission-table-header {
border: 0;
border-bottom: 1px solid #CCC;
box-shadow: 0 1px 4px rgba(0,0,0,0.3);
}
.permission-table-footer {
box-shadow: 0 -1px 4px rgba(0,0,0,0.3);
}
.permission {
display: flex;
flex-grow: 1;
}
.permission:nth-child(odd) {
background: #E4E4E4;
}
.permission:hover {
background: #EEE;
}
.permission > div {
flex-grow: 1;
flex-basis: 20%;
text-align: center;
padding: 3px;
border-right: 1px solid #CCC;
border-bottom: 1px solid #CCC;
}
.permission > div:first-child {
text-align: left;
padding: 3px 10px;
flex-basis: 30%;
font-weight: bold;
}
.permission > div[permission="1"]:before, .allow-label:after {
color: #98CF39;
content: ' \2713';
}
.permission > div[permission="2"]:before, .deny-label:after {
color: #CC4908;
content: ' \2715';
}
.permission > div[permission="3"]:before, .prompt-label:after {
color: #009EED;
content: ' !';
}
/***************** SIDEBAR *****************/
#sidebar {
background: #EEE;
position: relative;
box-shadow: 0 1px 6px rgba(0,0,0,0.3);
display: flex;
flex-direction: column;
flex: 0 0 350px;
overflow: hidden;
z-index: 100;
}
.sidebar-item {
background-color: #F6F6F6;
box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
color: #666;
line-height: 120%;
cursor: pointer;
display: flex;
padding: 15px 10px;
display: block;
text-align: left;
flex-grow: 1;
}
.sidebar-item > * {
flex-shrink: 0;
}
.sidebar-item:hover {
background-color: #EEE;
}
.sidebar-item.selected {
background: linear-gradient(to bottom, #276DA3, #155282);
color: #FFF;
}
/***************** HEADER *****************/
header {
padding-top: 140px;
background-image: linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,0.7));
color: #FFF;
text-shadow: 0 1px 2px rgba(0,0,0,0.8);
padding: 10px;
}
/***************** APPS *****************/
.apps {
display: flex;
flex-direction: column;
overflow: auto;
}
.app {
display: flex;
align-items: center;
order: 1;
}
.app-name {
flex-grow: 1;
font-weight: bold;
}
.app {
padding: 10px 20px;
border-bottom: 1px solid #CCC;
}
.app:hover {
background-color: #EFEFEF;
}
.app-icon {
width: 32px;
height: 32px;
margin-right: 10px;
}
/***************** NOT CONNECTED *****************/
body:not(.notconnected) > #notConnectedMessage,
body.notconnected > #content {
display: none;
}
#notConnectedMessage {
flex-grow: 1;
flex-direction: column;
margin: 50px auto;
}
#notConnectedMessage > span {
padding: 20px;
border: 1px solid #CCC;
border-radius: 5px;
}
#notConnectedMessage > span:before {
content: '';
background: url('error.svg') no-repeat;
background-size: 18px;
height: 24px;
width: 24px;
position: relative;
top: 10px;
display: inline-block;
}
/***************** TABS *****************/
#tabs {
flex-grow: 1;
overflow: auto;
}
.tabpanel:not(.selected) {
display: none;
}
#tabs-headers {
flex-shrink: 0;
display: flex;
flex-direction: column;
}

View File

@ -0,0 +1,14 @@
<?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 svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="64px" height="64px" viewBox="0 0 64 64">
<path fill="#E25026" d="M32,4.894c-15.74,0-28.5,12.76-28.5,28.5s12.76,28.5,28.5,28.5s28.5-12.76,28.5-28.5S47.74,4.894,32,4.894
z M46.903,48.674c-1.817,1.817-4.691,1.76-6.449,0.002l-8.327-8.327l-8.151,8.151c-1.877,1.877-4.87,1.814-6.685,0
c-1.877-1.877-1.879-4.811-0.002-6.687l8.151-8.151l-8.327-8.327c-1.76-1.76-1.817-4.634,0-6.451c1.76-1.76,4.691-1.76,6.451,0
l8.327,8.327l8.151-8.151c1.877-1.877,4.811-1.874,6.687,0.002c1.814,1.814,1.877,4.808,0,6.685l-8.151,8.151l8.327,8.327
C48.662,43.982,48.662,46.914,46.903,48.674z"/>
</svg>

After

Width:  |  Height:  |  Size: 1021 B

View File

@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="160px" height="160px" viewBox="0 0 160 160">
<filter
id="AI_GaussianBlur_4">
<feGaussianBlur
stdDeviation="2"
id="feGaussianBlur4" />
</filter>
<g
id="g54">
<linearGradient
id="SVGID_5_"
gradientUnits="userSpaceOnUse"
x1="40"
y1="146.0156"
x2="40"
y2="93.9844">
<stop
offset="0"
style="stop-color:#58595B"
id="stop57" />
<stop
offset="1"
style="stop-color:#808285"
id="stop59" />
</linearGradient>
<path
d="M55.609,140.812c0,2.846-2.357,5.203-5.203,5.203H29.594c-2.846,0-5.203-2.357-5.203-5.203V99.188 c0-2.846,2.357-5.203,5.203-5.203h20.812c2.846,0,5.203,2.357,5.203,5.203V140.812z M51.707,105.691 c0-0.691-0.61-1.301-1.301-1.301H29.594c-0.691,0-1.301,0.609-1.301,1.301v28.617c0,0.691,0.61,1.301,1.301,1.301h20.812 c0.691,0,1.301-0.609,1.301-1.301V105.691z M43.252,99.188h-6.504c-0.366,0-0.65,0.284-0.65,0.65s0.285,0.65,0.65,0.65h6.504 c0.366,0,0.65-0.284,0.65-0.65S43.618,99.188,43.252,99.188z M40,137.561c-1.789,0-3.252,1.463-3.252,3.252 s1.463,3.252,3.252,3.252s3.252-1.463,3.252-3.252S41.789,137.561,40,137.561z"
id="path61"
fill="url(#SVGID_5_)" />
</g>
<g
id="g16">
<linearGradient
id="SVGID_1_"
gradientUnits="userSpaceOnUse"
x1="40"
y1="55.868"
x2="40"
y2="15.5872">
<stop
offset="0"
style="stop-color:#58595B"
id="stop19" />
<stop
offset="1"
style="stop-color:#808285"
id="stop21" />
</linearGradient>
<path
fill="url(#SVGID_1_)"
d="M40.001,15.587c-2.742,0.001-8.104,9.047-9.931,18.021l-6.165,6.164v16.095l4.297,0.001l5.991-5.991 c0.612,0.641,1.287,1.181,2.011,1.609h7.581c0.728-0.426,1.408-0.965,2.021-1.609l5.991,5.991l4.298-0.001V39.773l-6.165-6.164 C48.104,24.635,42.741,15.587,40.001,15.587z M40.006,25.023c0.955,0,2.636,2.682,3.683,5.891c-1.134-0.21-2.343-0.328-3.602-0.328 c-1.324,0-2.592,0.132-3.775,0.364C37.358,27.726,39.047,25.023,40.006,25.023z"
id="path23" />
<linearGradient
id="SVGID_2_"
gradientUnits="userSpaceOnUse"
x1="40"
y1="64.4128"
x2="40"
y2="53.6309">
<stop
offset="0"
style="stop-color:#58595B"
id="stop26" />
<stop
offset="1"
style="stop-color:#808285"
id="stop28" />
</linearGradient>
<path
fill="url(#SVGID_2_)"
d="M41.927,55.598c0,1.891-1.467,3.424-1.957,3.424c-0.489,0-1.957-1.534-1.957-3.424 c0-0.732,0.133-1.409,0.356-1.967h-2.266c-0.266,0.98-0.415,2.077-0.416,3.235c0.001,4.168,3.234,7.547,4.313,7.547 s4.313-3.378,4.313-7.546c0-1.159-0.15-2.254-0.416-3.236l-2.326,0.001C41.794,54.188,41.927,54.865,41.927,55.598z"
id="path30" />
</g>
<g
id="g38">
<linearGradient
id="SVGID_3_"
gradientUnits="userSpaceOnUse"
x1="120"
y1="55.868"
x2="120"
y2="15.5872">
<stop
offset="0"
style="stop-color:#1C75BC"
id="stop41" />
<stop
offset="1"
style="stop-color:#27AAE1"
id="stop43" />
</linearGradient>
<path
fill="url(#SVGID_3_)"
d="M120.001,15.587c-2.742,0.001-8.104,9.047-9.931,18.021l-6.165,6.164v16.095l4.297,0.001 l5.991-5.991c0.612,0.641,1.287,1.181,2.011,1.609h7.581c0.728-0.426,1.408-0.965,2.021-1.609l5.991,5.991l4.298-0.001V39.773 l-6.165-6.164C128.104,24.635,122.741,15.587,120.001,15.587z M120.006,25.023c0.955,0,2.636,2.682,3.683,5.891 c-1.134-0.21-2.343-0.328-3.602-0.328c-1.324,0-2.592,0.132-3.775,0.364C117.358,27.726,119.047,25.023,120.006,25.023z"
id="path45" />
<linearGradient
id="SVGID_4_"
gradientUnits="userSpaceOnUse"
x1="120"
y1="64.4128"
x2="120"
y2="53.6309">
<stop
offset="0"
style="stop-color:#1C75BC"
id="stop48" />
<stop
offset="1"
style="stop-color:#27AAE1"
id="stop50" />
</linearGradient>
<path
fill="url(#SVGID_4_)"
d="M121.927,55.598c0,1.891-1.467,3.424-1.957,3.424c-0.489,0-1.957-1.534-1.957-3.424 c0-0.732,0.133-1.409,0.356-1.967h-2.266c-0.266,0.98-0.415,2.077-0.416,3.235c0.001,4.168,3.234,7.547,4.313,7.547 s4.313-3.378,4.313-7.546c0-1.159-0.15-2.254-0.416-3.236l-2.326,0.001C121.794,54.188,121.927,54.865,121.927,55.598z"
id="path52" />
</g>
<g
id="g67">
<linearGradient
id="SVGID_6_"
gradientUnits="userSpaceOnUse"
x1="120"
y1="146.0156"
x2="120"
y2="93.9844">
<stop
offset="0"
style="stop-color:#1C75BC"
id="stop70" />
<stop
offset="1"
style="stop-color:#27AAE1"
id="stop72" />
</linearGradient>
<path
fill="url(#SVGID_6_)"
d="M135.609,140.812c0,2.846-2.357,5.203-5.203,5.203h-20.812c-2.846,0-5.203-2.357-5.203-5.203V99.188 c0-2.846,2.357-5.203,5.203-5.203h20.812c2.846,0,5.203,2.357,5.203,5.203V140.812z M131.707,105.691 c0-0.691-0.61-1.301-1.301-1.301h-20.812c-0.691,0-1.301,0.609-1.301,1.301v28.617c0,0.691,0.61,1.301,1.301,1.301h20.812 c0.691,0,1.301-0.609,1.301-1.301V105.691z M123.252,99.188h-6.504c-0.366,0-0.65,0.284-0.65,0.65s0.285,0.65,0.65,0.65h6.504 c0.366,0,0.65-0.284,0.65-0.65S123.618,99.188,123.252,99.188z M120,137.561c-1.789,0-3.252,1.463-3.252,3.252 s1.463,3.252,3.252,3.252s3.252-1.463,3.252-3.252S121.789,137.561,120,137.561z"
id="path74" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,13 @@
<?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 svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="64px" height="64px" viewBox="0 0 64 64">
<path fill="#ABABAB" d="M32.336,3.894c-15.74,0-28.5,12.76-28.5,28.5s12.76,28.5,28.5,28.5s28.5-12.76,28.5-28.5
S48.076,3.894,32.336,3.894z M44.86,36.966h-7.823v7.62c0,2.582-2.12,4.702-4.702,4.702c-2.584,0-4.704-2.12-4.704-4.702v-7.62
h-7.817c-2.52,0-4.572-2.056-4.572-4.572s2.053-4.572,4.572-4.572h7.817v-7.62c0-2.582,2.12-4.702,4.704-4.702
c2.582,0,4.702,2.12,4.702,4.702v7.62h7.823c2.514,0,4.57,2.056,4.57,4.572S47.374,36.966,44.86,36.966z"/>
</svg>

After

Width:  |  Height:  |  Size: 934 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="64px" height="64px" viewBox="0 0 64 64">
<path d="m 12.183457,12.241457 c -11.129861,11.12986 -11.129861,29.175226 0,40.305086 11.12986,11.129861 29.175226,11.129861 40.305086,0 11.129861,-11.12986 11.129861,-29.175226 0,-40.305086 -11.12986,-11.129861 -29.175226,-11.129861 -40.305086,0 z m 32.241241,14.52963 -5.531697,5.531696 5.388154,5.388154 c 1.82575,1.82575 1.82575,4.823882 0,6.649632 -1.827164,1.827164 -4.825297,1.827164 -6.651047,0.0014 l -5.388153,-5.388153 -5.527454,5.527453 c -1.781909,1.781909 -4.686704,1.779081 -6.465784,0 -1.779081,-1.77908 -1.781202,-4.684582 0,-6.465784 l 5.527453,-5.527454 -5.388153,-5.388153 c -1.82575,-1.82575 -1.82575,-4.823883 0.0014,-6.651047 1.82575,-1.82575 4.823882,-1.82575 6.649632,0 l 5.388154,5.388154 5.531696,-5.531697 c 1.777667,-1.777666 4.68529,-1.777666 6.46437,0.0014 1.779081,1.77908 1.779081,4.686703 0.0014,6.46437 z"
style="fill:#d8abab" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g opacity="0.1">
<g>
<path fill="#FFFFFF" d="M12,2.3c-1.127,0-3.333,3.721-4.084,7.411l-2.535,2.535v6.619l1.767,0l2.464-2.464
c0.252,0.264,0.529,0.486,0.827,0.662h3.118c0.299-0.175,0.579-0.397,0.831-0.662l2.464,2.464l1.767,0v-6.619l-2.535-2.535
C15.333,6.021,13.127,2.3,12,2.3z M12.003,6.181c0.393,0,1.084,1.103,1.515,2.423c-0.466-0.087-0.963-0.135-1.481-0.135
c-0.545,0-1.066,0.054-1.553,0.15C10.914,7.292,11.608,6.181,12.003,6.181z"/>
<path fill="#FFFFFF" d="M12.792,18.755c0,0.778-0.603,1.408-0.805,1.408c-0.201,0-0.805-0.631-0.805-1.408
c0-0.301,0.055-0.579,0.147-0.809h-0.932c-0.109,0.403-0.171,0.854-0.171,1.33c0,1.714,1.33,3.104,1.774,3.104
s1.774-1.389,1.774-3.103c0-0.477-0.062-0.927-0.171-1.331l-0.957,0C12.738,18.175,12.792,18.453,12.792,18.755z"/>
</g>
<g>
<g>
<path fill="#414042" d="M12,2c-1.127,0-3.333,3.721-4.084,7.411l-2.535,2.535v6.619l1.767,0l2.464-2.464
c0.252,0.264,0.529,0.486,0.827,0.662h3.118c0.299-0.175,0.579-0.397,0.831-0.662l2.464,2.464l1.767,0v-6.619l-2.535-2.535
C15.333,5.721,13.127,2,12,2z M12.003,5.881c0.393,0,1.084,1.103,1.515,2.423c-0.466-0.087-0.963-0.135-1.481-0.135
c-0.545,0-1.066,0.054-1.553,0.15C10.914,6.992,11.608,5.881,12.003,5.881z"/>
<path fill="#414042" d="M12.792,18.455c0,0.778-0.603,1.408-0.805,1.408c-0.201,0-0.805-0.631-0.805-1.408
c0-0.301,0.055-0.579,0.147-0.809h-0.932c-0.109,0.403-0.171,0.854-0.171,1.33c0,1.714,1.33,3.104,1.774,3.104
s1.774-1.389,1.774-3.103c0-0.477-0.062-0.927-0.171-1.331l-0.957,0C12.738,17.875,12.792,18.153,12.792,18.455z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64">
<path fill="#ECB51F" d="M61.689,51.121L36.437,7.384c-2.441-4.227-6.434-4.227-8.875,0L2.311,51.121
c-2.441,4.227-0.444,7.686,4.437,7.686h50.504C62.133,58.807,64.13,55.349,61.689,51.121z M35.968,47.68
c0,2.191-1.688,3.877-3.968,3.877s-3.968-1.686-3.968-3.877v-0.093c0-2.187,1.688-3.873,3.968-3.873s3.968,1.686,3.968,3.873V47.68z
M36.059,21.548l-1.961,17.146c-0.137,1.233-0.958,2.009-2.098,2.009s-1.961-0.776-2.098-2.009l-1.961-17.146
c-0.137-1.322,0.592-2.325,1.825-2.325h4.469C35.466,19.223,36.196,20.226,36.059,21.548z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,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/. */
* {
margin: 0;
padding: 0;
-moz-box-sizing: border-box;
font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
}
#tabs {
box-shadow: inset -1px 0 5px rgba(0,0,0,0.9);
background: #22272D;
}
.button {
width: 80px;
height: 80px;
-moz-appearance: none;
border: none;
border-top: 1px solid #323234;
border-bottom: 1px solid #121214;
background-color: transparent;
color: white;
cursor: pointer;
text-align: center;
-moz-box-align: end;
font-size: 10px;
text-shadow: 0 1px 2px #111;
color: #9FA6AD;
}
.button[selected] {
color: #26A6DE;
border-color: #191B1E;
box-shadow: inset -5px 0 5px #111;
}
.button::-moz-focus-inner {
border-width: 0;
}
.panel {
border-width: 0;
}
.panel:not([selected="true"]) {
display: none;
}
.projects-button {
background: url('chrome://browser/skin/devtools/app-manager/index-icons.svg') no-repeat;
background-position: left -5px;
}
.projects-button[selected] {
background-position: right -5px;
}
.device-button {
background-image: url('chrome://browser/skin/devtools/app-manager/index-icons.svg'), radial-gradient(at center left, red 40%, transparent);
background-position: left -85px, top left;
background-repeat: no-repeat, no-repeat;
background-size: 160px 160px, 2px 80px;
}
.connected .device-button {
background-image: url('chrome://browser/skin/devtools/app-manager/index-icons.svg'), radial-gradient(at center left, #B1FC0D 40%, transparent);
}
.device-button[selected] {
background-position: right -85px, top left;
}
#connection-footer {
border-width: 0;
height: 50px;
min-height: 50px;
}

View File

@ -0,0 +1,428 @@
/* 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/. */
* {
margin: 0;
padding: 0;
-moz-box-sizing: border-box;
font-size: 0.9rem;
}
html, body {
height: 100%;
}
template {
display: none;
}
body {
display: flex;
color: #333;
background-color: white;
font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
display: flex;
overflow: hidden;
}
body:not(.connected) button.device-action {
display: none;
}
strong {
color: #111;
}
/********* SIDEBAR ***********/
#sidebar {
display: flex;
flex-direction: column;
flex: 0 0 350px;
overflow: hidden;
z-index: 100;
background: #EEE;
position: relative;
box-shadow: 0 1px 6px rgba(0,0,0,0.3);
}
#project-list {
height: 100%;
overflow: auto;
}
#project-list:not([projects-count="0"]) > #no-project {
display: none;
}
#no-project {
padding: 100px 20px 0;
font-weight: bold;
color: #BBB;
font-size: 22px;
}
/********* PROJECT MENU ***********/
.project-item {
padding: 10px 0;
background-color: #F6F6F6;
box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
color: #666;
line-height: 120%;
cursor: pointer;
display: flex;
}
.project-item:hover {
background-color: #EEE;
}
.project-item > * {
flex-shrink: 0;
}
.project-item.selected {
background-color: #FFF;
}
.button-remove {
background-image: url('remove.svg');
background-size: 20px;
width: 20px;
height: 20px;
float: right;
margin-right: 10px;
visibility: hidden;
}
.project-item:hover .button-remove {
visibility: visible;
}
.project-item-status {
width: 10px;
margin: -10px 0;
border-right: 1px solid rgba(0,0,0,0.1);
box-shadow: inset 0 0 1px 1px rgba(255,255,255,0.2), inset 0 -1px 0px 0px rgba(0,0,0,0.2);
}
.project-item-status[status="warning"] {
background: linear-gradient(to bottom, #F5BD56, #E3A93D);
}
.project-item-status[status="error"] {
background: linear-gradient(to bottom, #E34F22, #B83C16);
}
.project-item-status[status="valid"] {
background: linear-gradient(to bottom, #90D11F, #77AD18);
}
.project-item-icon {
width: 32px;
height: 32px;
margin: 0 10px;
}
.project-item-meta {
flex-grow: 1;
flex-shrink: 1 !important;
}
.project-item-type {
font-size: 10px;
line-height: 20px;
float: right;
padding-right: 10px;
color: #AAA;
text-transform: uppercase;
}
.project-item-description {
color: #AAA;
}
/********* ADD PROJECT ***********/
#new-packaged-project {
box-shadow: 0 -1px 10px rgba(0,0,0,0.3);
background-position: calc(100% - 10px) 10px;
}
#new-packaged-project,
#new-hosted-project {
background-color: #EEE;
border: none;
border-top: 1px solid #DDD;
padding: 10px;
font-weight: bold;
}
#new-packaged-project:hover,
#new-hosted-project:hover {
background-color: #DDD;
}
#new-hosted-project-wrapper {
display: flex;
align-items: center;
}
#new-packaged-project,
#new-hosted-project-click {
background-image: url('plus.svg');
background-size: 20px;
background-repeat: no-repeat;
cursor: pointer;
}
#new-hosted-project-click {
background-position: top right;
width: 20px;
height: 20px;
margin-left: 5px;
}
#url-input {
flex-grow: 1;
width: 90%;
box-shadow: none;
border-radius: 3px;
border: 1px solid #DDD;
padding: 4px;
margin-top: 4px;
}
/********* LENSE ***********/
#lense {
height: 100%;
flex-grow: 1;
display: flex;
z-index: 1;
overflow: hidden;
background-color: rgb(225, 225, 225);
background-image: url('rocket.svg'), url('noise.png');
background-repeat: no-repeat, repeat;
background-size: 35%, auto;
background-position: center center, top left;
}
#lense > div {
display: flex;
flex-grow: 1;
}
/********* PROJECT ***********/
.project-details {
background-color: rgb(225, 225, 225);
padding: 10px;
flex-grow: 1;
line-height: 160%;
}
.project-status {
display: flex;
}
.project-title {
flex-direction: row;
display: flex;
align-items: flex-start;
}
.project-title > h1 {
flex-grow: 1;
font-size: 24px;
}
.project-location {
color: gray;
font-size: 10px;
cursor: pointer;
}
.project-location:hover {
text-decoration: underline;
}
.project-header {
display: flex;
border-bottom: 1px solid #CCC;
margin: 10px 20px 10px 20px;
padding-bottom: 10px;
}
.project-icon {
flex-shrink: 0;
width: 64px;
height: 64px;
}
.project-location {
font-size: 11px;
color: #999;
}
.project-description {
font-style: italic;
color: #333;
}
.project-status > p {
text-transform: uppercase;
font-size: 10px;
padding: 2px 10px;
border-radius: 2px;
margin-top: 6px;
line-height: 10px;
}
.project-validation {
color: #FFF;
}
[status="valid"] > .project-validation {
background-color: #82BD1B;
}
[status="warning"] > .project-validation {
background-color: #ECB51F;
}
[status="error"] > .project-validation {
background-color: #C24119;
}
/********* PROJECT BUTTONS ***********/
.project-buttons {
display: flex;
margin-left: 20px;
color: #BBB;
}
.project-buttons > button {
margin: 0;
font-size: 11px;
border: 1px solid #CCC;
border-left-width: 0;
padding: 5px 15px;
cursor: pointer;
background: rgba(255,255,255,0.4);
text-transform: uppercase;
}
.project-buttons > button:first-child {
border-left-width: 1px;
}
.project-button-debug {
color: #3498DB;
}
.project-button-debug:hover {
background-color: #3498DB;
color: #FFF;
}
.project-button-install,
.project-button-start {
color: #18BC9C
}
.project-button-install:hover,
.project-button-start:hover {
background-color: #18BC9C;
color: #FFF;
}
.project-button-stop {
color: #E74C3C;
}
.project-button-stop:hover {
background-color: #E74C3C;
color: #FFF;
}
.project-button-refresh {
color: #777;
}
.project-button-refresh:hover {
background-color: #777;
color: #FFF;
}
/********* ERRORS AND WARNINGS ***********/
.project-warnings,
.project-errors,
.project-item-warnings,
.project-item-errors {
display: none;
}
[status="warning"] > .project-item-warnings,
[status="error"] > .project-item-errors,
[status="warning"] > .project-warnings,
[status="error"] > .project-errors {
display: block;
}
.project-warnings {
margin: 20px 20px 0;
padding: 10px 10px;
border-left: 3px solid #ECB51E;
background-color: rgba(236, 181, 20, 0.1);
}
.project-errors {
margin: 20px;
padding: 10px 10px;
border-left: 3px solid #E34F22;
background-color: rgba(227,79,34,0.1);
}
.project-item-warnings {
background-image: url('warning.svg');
}
.project-item-errors {
background-image: url('error.svg');
color: rgb(227, 79, 34);
}
.project-item-warnings,
.project-item-errors {
background-repeat: no-repeat;
background-size: 12px;
background-position: left center;
margin-top: 6px;
}
.project-item-warnings > span,
.project-item-errors > span {
font-size: 11px;
padding-left: 16px;
font-weight: bold;
}

View File

@ -253,6 +253,17 @@ browser.jar:
skin/classic/browser/devtools/responsiveui-rotate.png (../shared/devtools/responsiveui-rotate.png)
skin/classic/browser/devtools/responsiveui-touch.png (../shared/devtools/responsiveui-touch.png)
skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
skin/classic/browser/devtools/app-manager/connection-footer.css (../shared/devtools/app-manager/connection-footer.css)
skin/classic/browser/devtools/app-manager/index.css (../shared/devtools/app-manager/index.css)
skin/classic/browser/devtools/app-manager/device.css (../shared/devtools/app-manager/device.css)
skin/classic/browser/devtools/app-manager/projects.css (../shared/devtools/app-manager/projects.css)
skin/classic/browser/devtools/app-manager/warning.svg (../shared/devtools/app-manager/images/warning.svg)
skin/classic/browser/devtools/app-manager/error.svg (../shared/devtools/app-manager/images/error.svg)
skin/classic/browser/devtools/app-manager/plus.svg (../shared/devtools/app-manager/images/plus.svg)
skin/classic/browser/devtools/app-manager/remove.svg (../shared/devtools/app-manager/images/remove.svg)
skin/classic/browser/devtools/app-manager/index-icons.svg (../shared/devtools/app-manager/images/index-icons.svg)
skin/classic/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
skin/classic/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-throbber.png
skin/classic/browser/sync-16.png
@ -516,6 +527,17 @@ browser.jar:
skin/classic/aero/browser/devtools/responsiveui-rotate.png (../shared/devtools/responsiveui-rotate.png)
skin/classic/aero/browser/devtools/responsiveui-touch.png (../shared/devtools/responsiveui-touch.png)
skin/classic/aero/browser/devtools/responsiveui-screenshot.png (../shared/devtools/responsiveui-screenshot.png)
skin/classic/aero/browser/devtools/app-manager/connection-footer.css (../shared/devtools/app-manager/connection-footer.css)
skin/classic/aero/browser/devtools/app-manager/index.css (../shared/devtools/app-manager/index.css)
skin/classic/aero/browser/devtools/app-manager/device.css (../shared/devtools/app-manager/device.css)
skin/classic/aero/browser/devtools/app-manager/projects.css (../shared/devtools/app-manager/projects.css)
skin/classic/aero/browser/devtools/app-manager/warning.svg (../shared/devtools/app-manager/images/warning.svg)
skin/classic/aero/browser/devtools/app-manager/error.svg (../shared/devtools/app-manager/images/error.svg)
skin/classic/aero/browser/devtools/app-manager/plus.svg (../shared/devtools/app-manager/images/plus.svg)
skin/classic/aero/browser/devtools/app-manager/remove.svg (../shared/devtools/app-manager/images/remove.svg)
skin/classic/aero/browser/devtools/app-manager/index-icons.svg (../shared/devtools/app-manager/images/index-icons.svg)
skin/classic/aero/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
skin/classic/aero/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/aero/browser/sync-throbber.png
skin/classic/aero/browser/sync-16.png

View File

@ -138,6 +138,12 @@ Connection.Events = {
Connection.prototype = {
logs: "",
log: function(str) {
let d = new Date();
let hours = ("0" + d.getHours()).slice(-2);
let minutes = ("0" + d.getMinutes()).slice(-2);
let seconds = ("0" + d.getSeconds()).slice(-2);
let timestamp = [hours, minutes, seconds].join(":") + ": ";
str = timestamp + str;
this.logs += "\n" + str;
this.emit(Connection.Events.NEW_LOG, str);
},
@ -212,10 +218,10 @@ Connection.prototype = {
_clientConnect: function () {
let transport;
if (!this._host) {
if (!this.host) {
transport = DebuggerServer.connectPipe();
} else {
transport = debuggerSocketConnect(this._host, this._port);
transport = debuggerSocketConnect(this.host, this.port);
}
this._client = new DebuggerClient(transport);
this._client.addOneTimeListener("closed", this._onDisconnected);
@ -243,12 +249,13 @@ Connection.prototype = {
}
clearTimeout(this._timeoutID);
switch (this.status) {
case Connection.Status.CONNECTED:
this.log("disconnected (unexpected)");
break;
case Connection.Status.CONNECTING:
this.log("Connection error");
this.log("connection error. Possible causes: USB port not connected, port not forwarded (adb forward), wrong host or port, remote debugging not enabled on the device.");
break;
default:
this.log("disconnected");
@ -263,7 +270,7 @@ Connection.prototype = {
},
_onTimeout: function() {
this.log("connection timeout");
this.log("connection timeout. Possible causes: didn't click on 'accept' (prompt).");
this.emit(Connection.Events.TIMEOUT);
this.disconnect();
},

View File

@ -145,6 +145,25 @@ let DeviceActor = protocol.ActorClass({
}, {request: {},response: { value: RetVal("json")}}),
getWallpaper: method(function() {
let deferred = promise.defer();
this._getSetting("wallpaper.image").then((blob) => {
let CC = Components.Constructor;
let FileReader = CC("@mozilla.org/files/filereader;1");
let reader = new FileReader();
let conn = this.conn;
reader.addEventListener("load", function() {
let str = new LongStringActor(conn, reader.result);
deferred.resolve(str);
});
reader.addEventListener("error", function() {
deferred.reject(reader.error);
});
reader.readAsDataURL(blob);
});
return deferred.promise;
}, {request: {},response: { value: RetVal("longstring")}}),
screenshotToDataURL: method(function() {
let window = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
let canvas = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");