Skip to content

Commit

Permalink
Bug 999417 - Land the new App Manager UI. r=jryans r=mshal
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Rouget committed May 12, 2014
1 parent a5f6ffe commit a33bf3b
Show file tree
Hide file tree
Showing 30 changed files with 2,560 additions and 9 deletions.
7 changes: 7 additions & 0 deletions browser/app/profile/firefox.js
Original file line number Diff line number Diff line change
Expand Up @@ -1239,6 +1239,13 @@ pref("devtools.appmanager.enabled", true);
pref("devtools.appmanager.lastTab", "help");
pref("devtools.appmanager.manifestEditor.enabled", true);

// Enable devtools webide
#ifdef MOZ_DEVTOOLS_WEBIDE
pref("devtools.webide.enabled", true);
#else
pref("devtools.webide.enabled", false);
#endif

// Toolbox preferences
pref("devtools.toolbox.footer.height", 250);
pref("devtools.toolbox.sidebar.width", 500);
Expand Down
56 changes: 48 additions & 8 deletions browser/devtools/app-manager/app-projects.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const {Cc,Ci,Cu} = require("chrome");
const {Cc,Ci,Cu,Cr} = require("chrome");
const ObservableObject = require("devtools/shared/observable-object");
const promise = require("devtools/toolkit/deprecated-sync-thenables");

const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
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
Expand Down Expand Up @@ -37,20 +38,45 @@ const IDB = {
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.
projects.push(cursor.value);


// 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 {
deferred.resolve(projects);
let removePromises = [];
for (let location of toRemove) {
removePromises.push(IDB.remove(location));
}
promise.all(removePromises).then(() => {
deferred.resolve(projects);
});
}
};
};
Expand Down Expand Up @@ -83,6 +109,12 @@ const IDB = {
update: function(project) {
let deferred = promise.defer();

// Clone object to make it storable by IndexedDB.
// Projects are proxified objects (for the template
// mechanismn in the first version of the App Manager).
// This will change in the future.
project = JSON.parse(JSON.stringify(project));

var transaction = IDB._db.transaction(["projects"], "readwrite");
var objectStore = transaction.objectStore("projects");
var request = objectStore.put(project);
Expand Down Expand Up @@ -131,6 +163,14 @@ const AppProjects = {
},

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,
Expand All @@ -151,6 +191,10 @@ const AppProjects = {
},

addHosted: function(manifestURL) {
let existingProject = this.get(manifestURL);
if (existingProject) {
return promise.reject("Already added");
}
let project = {
type: "hosted",
location: manifestURL
Expand All @@ -163,11 +207,7 @@ const AppProjects = {
},

update: function (project) {
return IDB.update({
type: project.type,
location: project.location,
packagedAppOrigin: project.packagedAppOrigin
}).then(() => project);
return IDB.update(project);
},

remove: function(location) {
Expand Down
2 changes: 2 additions & 0 deletions browser/devtools/app-manager/app-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ AppValidator.prototype._fetchManifest = function (manifestURL) {
this.manifestURL = manifestURL;

let req = new XMLHttpRequest();
req.overrideMimeType('text/plain');
try {
req.open("GET", manifestURL, true);
} catch(e) {
Expand Down Expand Up @@ -155,6 +156,7 @@ AppValidator.prototype.validateLaunchPath = function (manifest) {
}

let req = new XMLHttpRequest();
req.overrideMimeType('text/plain');
try {
req.open("HEAD", indexURL, true);
} catch(e) {
Expand Down
12 changes: 11 additions & 1 deletion browser/devtools/framework/gDevTools.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,17 @@ let gDevToolsBrowser = {
* Open the App Manager
*/
openAppManager: function(gBrowser) {
gBrowser.selectedTab = gBrowser.addTab("about:app-manager");
if (Services.prefs.getBoolPref("devtools.webide.enabled")) {
let win = Services.wm.getMostRecentWindow("devtools:webide");
if (win) {
win.focus();
} else {
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
}
} else {
gBrowser.selectedTab = gBrowser.addTab("about:app-manager");
}
},

/**
Expand Down
3 changes: 3 additions & 0 deletions browser/devtools/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ DIRS += [
'webconsole',
]

if CONFIG['MOZ_DEVTOOLS_WEBIDE']:
DIRS += ['webide']

EXTRA_COMPONENTS += [
'devtools-clhandler.js',
'devtools-clhandler.manifest',
Expand Down
10 changes: 10 additions & 0 deletions browser/devtools/webide/Makefile.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# 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/.

PREF_JS_EXPORTS = $(srcdir)/webide-prefs.js

include $(topsrcdir)/config/rules.mk

libs::
$(NSINSTALL) $(srcdir)/modules/*.js $(FINAL_TARGET)/modules/devtools
122 changes: 122 additions & 0 deletions browser/devtools/webide/content/details.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* 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 {Services} = Cu.import("resource://gre/modules/Services.jsm");
const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
const {AppProjects} = require("devtools/app-manager/app-projects");
const {AppValidator} = require("devtools/app-manager/app-validator");
const {AppManager} = require("devtools/app-manager");

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("#errorslist").innerHTML = "";
document.querySelector("#warningslist").innerHTML = "";

}

function updateUI() {
resetUI();

let project = AppManager.selectedProject;
if (!project) {
return;
}

if (project.type != "runtimeApp") {
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") {
document.querySelector("#type").textContent = manifest.type || "web";
} else {
document.querySelector("#type").textContent = project.type + " " + (manifest.type || "web");
}

if (project.type == "packaged") {
let manifest = AppManager.getProjectManifestURL(project);
if (manifest) {
document.querySelector("#manifestURLHeader").classList.remove("hidden");
document.querySelector("#manifestURL").textContent = manifest;
}
}
}

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);
}
}
}

function removeProject() {
AppManager.removeSelectedProject();
}
52 changes: 52 additions & 0 deletions browser/devtools/webide/content/details.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?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://webide/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>
</main>

<ul class="validation_messages" id="errorslist"></ul>
<ul class="validation_messages" id="warningslist"></ul>

</body>
</html>
12 changes: 12 additions & 0 deletions browser/devtools/webide/content/jar.mn
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# 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)
7 changes: 7 additions & 0 deletions browser/devtools/webide/content/moz.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- Mode: python; c-basic-offset: 4; 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']
Loading

0 comments on commit a33bf3b

Please sign in to comment.