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