|
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 |
|
5 const {Cc, Ci, Cu, CC} = require("chrome"); |
|
6 const Services = require("Services"); |
|
7 const protocol = require("devtools/server/protocol"); |
|
8 const {method, RetVal} = protocol; |
|
9 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); |
|
10 const {LongStringActor} = require("devtools/server/actors/string"); |
|
11 const {DebuggerServer} = require("devtools/server/main"); |
|
12 |
|
13 Cu.import("resource://gre/modules/PermissionsTable.jsm") |
|
14 |
|
15 const APP_MAP = { |
|
16 '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'firefox', |
|
17 '{3550f703-e582-4d05-9a08-453d09bdfdc6}': 'thunderbird', |
|
18 '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}': 'seamonkey', |
|
19 '{718e30fb-e89b-41dd-9da7-e25a45638b28}': 'sunbird', |
|
20 '{3c2e2abc-06d4-11e1-ac3b-374f68613e61}': 'b2g', |
|
21 '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'mobile/android', |
|
22 '{a23983c0-fd0e-11dc-95ff-0800200c9a66}': 'mobile/xul' |
|
23 } |
|
24 |
|
25 exports.register = function(handle) { |
|
26 handle.addGlobalActor(DeviceActor, "deviceActor"); |
|
27 }; |
|
28 |
|
29 exports.unregister = function(handle) { |
|
30 }; |
|
31 |
|
32 let DeviceActor = protocol.ActorClass({ |
|
33 typeName: "device", |
|
34 |
|
35 _desc: null, |
|
36 |
|
37 _getAppIniString : function(section, key) { |
|
38 let inifile = Services.dirsvc.get("GreD", Ci.nsIFile); |
|
39 inifile.append("application.ini"); |
|
40 |
|
41 if (!inifile.exists()) { |
|
42 inifile = Services.dirsvc.get("CurProcD", Ci.nsIFile); |
|
43 inifile.append("application.ini"); |
|
44 } |
|
45 |
|
46 if (!inifile.exists()) { |
|
47 return undefined; |
|
48 } |
|
49 |
|
50 let iniParser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].getService(Ci.nsIINIParserFactory).createINIParser(inifile); |
|
51 try { |
|
52 return iniParser.getString(section, key); |
|
53 } catch (e) { |
|
54 return undefined; |
|
55 } |
|
56 }, |
|
57 |
|
58 _getSetting: function(name) { |
|
59 let deferred = promise.defer(); |
|
60 |
|
61 if ("@mozilla.org/settingsService;1" in Cc) { |
|
62 let settingsService = Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService); |
|
63 let req = settingsService.createLock().get(name, { |
|
64 handle: (name, value) => deferred.resolve(value), |
|
65 handleError: (error) => deferred.reject(error), |
|
66 }); |
|
67 } else { |
|
68 deferred.reject(new Error("No settings service")); |
|
69 } |
|
70 return deferred.promise; |
|
71 }, |
|
72 |
|
73 getDescription: method(function() { |
|
74 // Most of this code is inspired from Nightly Tester Tools: |
|
75 // https://wiki.mozilla.org/Auto-tools/Projects/NightlyTesterTools |
|
76 |
|
77 let appInfo = Services.appinfo; |
|
78 let win = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType); |
|
79 let utils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); |
|
80 |
|
81 desc = { |
|
82 appid: appInfo.ID, |
|
83 apptype: APP_MAP[appInfo.ID], |
|
84 vendor: appInfo.vendor, |
|
85 name: appInfo.name, |
|
86 version: appInfo.version, |
|
87 appbuildid: appInfo.appBuildID, |
|
88 platformbuildid: appInfo.platformBuildID, |
|
89 platformversion: appInfo.platformVersion, |
|
90 geckobuildid: appInfo.platformBuildID, |
|
91 geckoversion: appInfo.platformVersion, |
|
92 changeset: this._getAppIniString("App", "SourceStamp"), |
|
93 useragent: win.navigator.userAgent, |
|
94 locale: Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).getSelectedLocale("global"), |
|
95 os: null, |
|
96 hardware: "unknown", |
|
97 processor: appInfo.XPCOMABI.split("-")[0], |
|
98 compiler: appInfo.XPCOMABI.split("-")[1], |
|
99 dpi: utils.displayDPI, |
|
100 brandName: null, |
|
101 channel: null, |
|
102 profile: null, |
|
103 width: win.screen.width, |
|
104 height: win.screen.height |
|
105 }; |
|
106 |
|
107 // Profile |
|
108 let profd = Services.dirsvc.get("ProfD", Ci.nsILocalFile); |
|
109 let profservice = Cc["@mozilla.org/toolkit/profile-service;1"].getService(Ci.nsIToolkitProfileService); |
|
110 var profiles = profservice.profiles; |
|
111 while (profiles.hasMoreElements()) { |
|
112 let profile = profiles.getNext().QueryInterface(Ci.nsIToolkitProfile); |
|
113 if (profile.rootDir.path == profd.path) { |
|
114 desc.profile = profile.name; |
|
115 break; |
|
116 } |
|
117 } |
|
118 |
|
119 if (!desc.profile) { |
|
120 desc.profile = profd.leafName; |
|
121 } |
|
122 |
|
123 // Channel |
|
124 try { |
|
125 desc.channel = Services.prefs.getCharPref('app.update.channel'); |
|
126 } catch(e) {} |
|
127 |
|
128 if (desc.apptype == "b2g") { |
|
129 // B2G specific |
|
130 desc.os = "B2G"; |
|
131 |
|
132 return this._getSetting('deviceinfo.hardware') |
|
133 .then(value => desc.hardware = value) |
|
134 .then(() => this._getSetting('deviceinfo.os')) |
|
135 .then(value => desc.version = value) |
|
136 .then(() => desc); |
|
137 } |
|
138 |
|
139 // Not B2G |
|
140 desc.os = appInfo.OS; |
|
141 let bundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); |
|
142 if (bundle) { |
|
143 desc.brandName = bundle.GetStringFromName("brandFullName"); |
|
144 } |
|
145 |
|
146 return desc; |
|
147 |
|
148 }, {request: {},response: { value: RetVal("json")}}), |
|
149 |
|
150 getWallpaper: method(function() { |
|
151 let deferred = promise.defer(); |
|
152 this._getSetting("wallpaper.image").then((blob) => { |
|
153 let FileReader = CC("@mozilla.org/files/filereader;1"); |
|
154 let reader = new FileReader(); |
|
155 let conn = this.conn; |
|
156 reader.addEventListener("load", function() { |
|
157 let str = new LongStringActor(conn, reader.result); |
|
158 deferred.resolve(str); |
|
159 }); |
|
160 reader.addEventListener("error", function() { |
|
161 deferred.reject(reader.error); |
|
162 }); |
|
163 reader.readAsDataURL(blob); |
|
164 }); |
|
165 return deferred.promise; |
|
166 }, {request: {},response: { value: RetVal("longstring")}}), |
|
167 |
|
168 screenshotToDataURL: method(function() { |
|
169 let window = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType); |
|
170 let canvas = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); |
|
171 let width = window.innerWidth; |
|
172 let height = window.innerHeight; |
|
173 canvas.setAttribute('width', width); |
|
174 canvas.setAttribute('height', height); |
|
175 let context = canvas.getContext('2d'); |
|
176 let flags = |
|
177 context.DRAWWINDOW_DRAW_CARET | |
|
178 context.DRAWWINDOW_DRAW_VIEW | |
|
179 context.DRAWWINDOW_USE_WIDGET_LAYERS; |
|
180 context.drawWindow(window, 0, 0, width, height, 'rgb(255,255,255)', flags); |
|
181 let dataURL = canvas.toDataURL('image/png') |
|
182 return new LongStringActor(this.conn, dataURL); |
|
183 }, {request: {},response: { value: RetVal("longstring")}}), |
|
184 |
|
185 getRawPermissionsTable: method(function() { |
|
186 return { |
|
187 rawPermissionsTable: PermissionsTable, |
|
188 UNKNOWN_ACTION: Ci.nsIPermissionManager.UNKNOWN_ACTION, |
|
189 ALLOW_ACTION: Ci.nsIPermissionManager.ALLOW_ACTION, |
|
190 DENY_ACTION: Ci.nsIPermissionManager.DENY_ACTION, |
|
191 PROMPT_ACTION: Ci.nsIPermissionManager.PROMPT_ACTION |
|
192 }; |
|
193 }, {request: {},response: { value: RetVal("json")}}) |
|
194 }); |
|
195 |
|
196 let DeviceFront = protocol.FrontClass(DeviceActor, { |
|
197 initialize: function(client, form) { |
|
198 protocol.Front.prototype.initialize.call(this, client); |
|
199 this.actorID = form.deviceActor; |
|
200 client.addActorPool(this); |
|
201 this.manage(this); |
|
202 }, |
|
203 |
|
204 screenshotToBlob: function() { |
|
205 return this.screenshotToDataURL().then(longstr => { |
|
206 return longstr.string().then(dataURL => { |
|
207 let deferred = promise.defer(); |
|
208 longstr.release().then(null, Cu.reportError); |
|
209 let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); |
|
210 req.open("GET", dataURL, true); |
|
211 req.responseType = "blob"; |
|
212 req.onload = () => { |
|
213 deferred.resolve(req.response); |
|
214 }; |
|
215 req.onerror = () => { |
|
216 deferred.reject(req.status); |
|
217 } |
|
218 req.send(); |
|
219 return deferred.promise; |
|
220 }); |
|
221 }); |
|
222 }, |
|
223 }); |
|
224 |
|
225 const _knownDeviceFronts = new WeakMap(); |
|
226 |
|
227 exports.getDeviceFront = function(client, form) { |
|
228 if (_knownDeviceFronts.has(client)) |
|
229 return _knownDeviceFronts.get(client); |
|
230 |
|
231 let front = new DeviceFront(client, form); |
|
232 _knownDeviceFronts.set(client, front); |
|
233 return front; |
|
234 } |