michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const {Cc, Ci, Cu, CC} = require("chrome"); michael@0: const Services = require("Services"); michael@0: const protocol = require("devtools/server/protocol"); michael@0: const {method, RetVal} = protocol; michael@0: const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); michael@0: const {LongStringActor} = require("devtools/server/actors/string"); michael@0: const {DebuggerServer} = require("devtools/server/main"); michael@0: michael@0: Cu.import("resource://gre/modules/PermissionsTable.jsm") michael@0: michael@0: const APP_MAP = { michael@0: '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'firefox', michael@0: '{3550f703-e582-4d05-9a08-453d09bdfdc6}': 'thunderbird', michael@0: '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}': 'seamonkey', michael@0: '{718e30fb-e89b-41dd-9da7-e25a45638b28}': 'sunbird', michael@0: '{3c2e2abc-06d4-11e1-ac3b-374f68613e61}': 'b2g', michael@0: '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'mobile/android', michael@0: '{a23983c0-fd0e-11dc-95ff-0800200c9a66}': 'mobile/xul' michael@0: } michael@0: michael@0: exports.register = function(handle) { michael@0: handle.addGlobalActor(DeviceActor, "deviceActor"); michael@0: }; michael@0: michael@0: exports.unregister = function(handle) { michael@0: }; michael@0: michael@0: let DeviceActor = protocol.ActorClass({ michael@0: typeName: "device", michael@0: michael@0: _desc: null, michael@0: michael@0: _getAppIniString : function(section, key) { michael@0: let inifile = Services.dirsvc.get("GreD", Ci.nsIFile); michael@0: inifile.append("application.ini"); michael@0: michael@0: if (!inifile.exists()) { michael@0: inifile = Services.dirsvc.get("CurProcD", Ci.nsIFile); michael@0: inifile.append("application.ini"); michael@0: } michael@0: michael@0: if (!inifile.exists()) { michael@0: return undefined; michael@0: } michael@0: michael@0: let iniParser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].getService(Ci.nsIINIParserFactory).createINIParser(inifile); michael@0: try { michael@0: return iniParser.getString(section, key); michael@0: } catch (e) { michael@0: return undefined; michael@0: } michael@0: }, michael@0: michael@0: _getSetting: function(name) { michael@0: let deferred = promise.defer(); michael@0: michael@0: if ("@mozilla.org/settingsService;1" in Cc) { michael@0: let settingsService = Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService); michael@0: let req = settingsService.createLock().get(name, { michael@0: handle: (name, value) => deferred.resolve(value), michael@0: handleError: (error) => deferred.reject(error), michael@0: }); michael@0: } else { michael@0: deferred.reject(new Error("No settings service")); michael@0: } michael@0: return deferred.promise; michael@0: }, michael@0: michael@0: getDescription: method(function() { michael@0: // Most of this code is inspired from Nightly Tester Tools: michael@0: // https://wiki.mozilla.org/Auto-tools/Projects/NightlyTesterTools michael@0: michael@0: let appInfo = Services.appinfo; michael@0: let win = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType); michael@0: let utils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); michael@0: michael@0: desc = { michael@0: appid: appInfo.ID, michael@0: apptype: APP_MAP[appInfo.ID], michael@0: vendor: appInfo.vendor, michael@0: name: appInfo.name, michael@0: version: appInfo.version, michael@0: appbuildid: appInfo.appBuildID, michael@0: platformbuildid: appInfo.platformBuildID, michael@0: platformversion: appInfo.platformVersion, michael@0: geckobuildid: appInfo.platformBuildID, michael@0: geckoversion: appInfo.platformVersion, michael@0: changeset: this._getAppIniString("App", "SourceStamp"), michael@0: useragent: win.navigator.userAgent, michael@0: locale: Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).getSelectedLocale("global"), michael@0: os: null, michael@0: hardware: "unknown", michael@0: processor: appInfo.XPCOMABI.split("-")[0], michael@0: compiler: appInfo.XPCOMABI.split("-")[1], michael@0: dpi: utils.displayDPI, michael@0: brandName: null, michael@0: channel: null, michael@0: profile: null, michael@0: width: win.screen.width, michael@0: height: win.screen.height michael@0: }; michael@0: michael@0: // Profile michael@0: let profd = Services.dirsvc.get("ProfD", Ci.nsILocalFile); michael@0: let profservice = Cc["@mozilla.org/toolkit/profile-service;1"].getService(Ci.nsIToolkitProfileService); michael@0: var profiles = profservice.profiles; michael@0: while (profiles.hasMoreElements()) { michael@0: let profile = profiles.getNext().QueryInterface(Ci.nsIToolkitProfile); michael@0: if (profile.rootDir.path == profd.path) { michael@0: desc.profile = profile.name; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!desc.profile) { michael@0: desc.profile = profd.leafName; michael@0: } michael@0: michael@0: // Channel michael@0: try { michael@0: desc.channel = Services.prefs.getCharPref('app.update.channel'); michael@0: } catch(e) {} michael@0: michael@0: if (desc.apptype == "b2g") { michael@0: // B2G specific michael@0: desc.os = "B2G"; michael@0: michael@0: return this._getSetting('deviceinfo.hardware') michael@0: .then(value => desc.hardware = value) michael@0: .then(() => this._getSetting('deviceinfo.os')) michael@0: .then(value => desc.version = value) michael@0: .then(() => desc); michael@0: } michael@0: michael@0: // Not B2G michael@0: desc.os = appInfo.OS; michael@0: let bundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); michael@0: if (bundle) { michael@0: desc.brandName = bundle.GetStringFromName("brandFullName"); michael@0: } michael@0: michael@0: return desc; michael@0: michael@0: }, {request: {},response: { value: RetVal("json")}}), michael@0: michael@0: getWallpaper: method(function() { michael@0: let deferred = promise.defer(); michael@0: this._getSetting("wallpaper.image").then((blob) => { michael@0: let FileReader = CC("@mozilla.org/files/filereader;1"); michael@0: let reader = new FileReader(); michael@0: let conn = this.conn; michael@0: reader.addEventListener("load", function() { michael@0: let str = new LongStringActor(conn, reader.result); michael@0: deferred.resolve(str); michael@0: }); michael@0: reader.addEventListener("error", function() { michael@0: deferred.reject(reader.error); michael@0: }); michael@0: reader.readAsDataURL(blob); michael@0: }); michael@0: return deferred.promise; michael@0: }, {request: {},response: { value: RetVal("longstring")}}), michael@0: michael@0: screenshotToDataURL: method(function() { michael@0: let window = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType); michael@0: let canvas = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); michael@0: let width = window.innerWidth; michael@0: let height = window.innerHeight; michael@0: canvas.setAttribute('width', width); michael@0: canvas.setAttribute('height', height); michael@0: let context = canvas.getContext('2d'); michael@0: let flags = michael@0: context.DRAWWINDOW_DRAW_CARET | michael@0: context.DRAWWINDOW_DRAW_VIEW | michael@0: context.DRAWWINDOW_USE_WIDGET_LAYERS; michael@0: context.drawWindow(window, 0, 0, width, height, 'rgb(255,255,255)', flags); michael@0: let dataURL = canvas.toDataURL('image/png') michael@0: return new LongStringActor(this.conn, dataURL); michael@0: }, {request: {},response: { value: RetVal("longstring")}}), michael@0: michael@0: getRawPermissionsTable: method(function() { michael@0: return { michael@0: rawPermissionsTable: PermissionsTable, michael@0: UNKNOWN_ACTION: Ci.nsIPermissionManager.UNKNOWN_ACTION, michael@0: ALLOW_ACTION: Ci.nsIPermissionManager.ALLOW_ACTION, michael@0: DENY_ACTION: Ci.nsIPermissionManager.DENY_ACTION, michael@0: PROMPT_ACTION: Ci.nsIPermissionManager.PROMPT_ACTION michael@0: }; michael@0: }, {request: {},response: { value: RetVal("json")}}) michael@0: }); michael@0: michael@0: let DeviceFront = protocol.FrontClass(DeviceActor, { michael@0: initialize: function(client, form) { michael@0: protocol.Front.prototype.initialize.call(this, client); michael@0: this.actorID = form.deviceActor; michael@0: client.addActorPool(this); michael@0: this.manage(this); michael@0: }, michael@0: michael@0: screenshotToBlob: function() { michael@0: return this.screenshotToDataURL().then(longstr => { michael@0: return longstr.string().then(dataURL => { michael@0: let deferred = promise.defer(); michael@0: longstr.release().then(null, Cu.reportError); michael@0: let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); michael@0: req.open("GET", dataURL, true); michael@0: req.responseType = "blob"; michael@0: req.onload = () => { michael@0: deferred.resolve(req.response); michael@0: }; michael@0: req.onerror = () => { michael@0: deferred.reject(req.status); michael@0: } michael@0: req.send(); michael@0: return deferred.promise; michael@0: }); michael@0: }); michael@0: }, michael@0: }); michael@0: michael@0: const _knownDeviceFronts = new WeakMap(); michael@0: michael@0: exports.getDeviceFront = function(client, form) { michael@0: if (_knownDeviceFronts.has(client)) michael@0: return _knownDeviceFronts.get(client); michael@0: michael@0: let front = new DeviceFront(client, form); michael@0: _knownDeviceFronts.set(client, front); michael@0: return front; michael@0: }