|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 "use strict"; |
|
5 |
|
6 Cu.import("resource://gre/modules/osfile.jsm"); |
|
7 const {VariablesView} = |
|
8 Cu.import("resource:///modules/devtools/VariablesView.jsm", {}); |
|
9 |
|
10 const VARIABLES_VIEW_URL = |
|
11 "chrome://browser/content/devtools/widgets/VariablesView.xul"; |
|
12 |
|
13 function ManifestEditor(project) { |
|
14 this.project = project; |
|
15 this._onContainerReady = this._onContainerReady.bind(this); |
|
16 this._onEval = this._onEval.bind(this); |
|
17 this._onSwitch = this._onSwitch.bind(this); |
|
18 this._onDelete = this._onDelete.bind(this); |
|
19 this._onNew = this._onNew.bind(this); |
|
20 } |
|
21 |
|
22 ManifestEditor.prototype = { |
|
23 get manifest() { return this.project.manifest; }, |
|
24 |
|
25 get editable() { return this.project.type == "packaged"; }, |
|
26 |
|
27 show: function(containerElement) { |
|
28 let deferred = promise.defer(); |
|
29 let iframe = this._iframe = document.createElement("iframe"); |
|
30 |
|
31 iframe.addEventListener("load", function onIframeLoad() { |
|
32 iframe.removeEventListener("load", onIframeLoad, true); |
|
33 deferred.resolve(iframe.contentWindow); |
|
34 }, true); |
|
35 |
|
36 iframe.setAttribute("src", VARIABLES_VIEW_URL); |
|
37 iframe.classList.add("variables-view"); |
|
38 containerElement.appendChild(iframe); |
|
39 |
|
40 return deferred.promise.then(this._onContainerReady); |
|
41 }, |
|
42 |
|
43 _onContainerReady: function(varWindow) { |
|
44 let variablesContainer = varWindow.document.querySelector("#variables"); |
|
45 |
|
46 variablesContainer.classList.add("manifest-editor"); |
|
47 |
|
48 let editor = this.editor = new VariablesView(variablesContainer); |
|
49 |
|
50 editor.onlyEnumVisible = true; |
|
51 editor.alignedValues = true; |
|
52 editor.actionsFirst = true; |
|
53 |
|
54 if (this.editable) { |
|
55 editor.eval = this._onEval; |
|
56 editor.switch = this._onSwitch; |
|
57 editor.delete = this._onDelete; |
|
58 editor.new = this._onNew; |
|
59 } |
|
60 |
|
61 return this.update(); |
|
62 }, |
|
63 |
|
64 _onEval: function(variable, value) { |
|
65 let parent = this._descend(variable.ownerView.symbolicPath); |
|
66 try { |
|
67 parent[variable.name] = JSON.parse(value); |
|
68 } catch(e) { |
|
69 Cu.reportError(e); |
|
70 } |
|
71 |
|
72 this.update(); |
|
73 }, |
|
74 |
|
75 _onSwitch: function(variable, newName) { |
|
76 if (variable.name == newName) { |
|
77 return; |
|
78 } |
|
79 |
|
80 let parent = this._descend(variable.ownerView.symbolicPath); |
|
81 parent[newName] = parent[variable.name]; |
|
82 delete parent[variable.name]; |
|
83 |
|
84 this.update(); |
|
85 }, |
|
86 |
|
87 _onDelete: function(variable) { |
|
88 let parent = this._descend(variable.ownerView.symbolicPath); |
|
89 delete parent[variable.name]; |
|
90 }, |
|
91 |
|
92 _onNew: function(variable, newName, newValue) { |
|
93 let parent = this._descend(variable.symbolicPath); |
|
94 try { |
|
95 parent[newName] = JSON.parse(newValue); |
|
96 } catch(e) { |
|
97 Cu.reportError(e); |
|
98 } |
|
99 |
|
100 this.update(); |
|
101 }, |
|
102 |
|
103 /** |
|
104 * Returns the value located at a given path in the manifest. |
|
105 * @param path array |
|
106 * A string for each path component: ["developer", "name"] |
|
107 */ |
|
108 _descend: function(path) { |
|
109 let parent = this.manifest; |
|
110 while (path.length) { |
|
111 parent = parent[path.shift()]; |
|
112 } |
|
113 return parent; |
|
114 }, |
|
115 |
|
116 update: function() { |
|
117 this.editor.rawObject = this.manifest; |
|
118 this.editor.commitHierarchy(); |
|
119 |
|
120 // Wait until the animation from commitHierarchy has completed |
|
121 let deferred = promise.defer(); |
|
122 setTimeout(deferred.resolve, this.editor.lazyEmptyDelay + 1); |
|
123 return deferred.promise; |
|
124 }, |
|
125 |
|
126 save: function() { |
|
127 if (this.editable) { |
|
128 let validator = new AppValidator(this.project); |
|
129 let manifestFile = validator._getPackagedManifestFile(); |
|
130 let path = manifestFile.path; |
|
131 |
|
132 let encoder = new TextEncoder(); |
|
133 let data = encoder.encode(JSON.stringify(this.manifest, null, 2)); |
|
134 |
|
135 return OS.File.writeAtomic(path, data, { tmpPath: path + ".tmp" }); |
|
136 } |
|
137 |
|
138 return promise.resolve(); |
|
139 }, |
|
140 |
|
141 destroy: function() { |
|
142 if (this._iframe) { |
|
143 this._iframe.remove(); |
|
144 } |
|
145 } |
|
146 }; |