From 5d1a5fa1fa3bdfa4cfd7961080cb1f0097749305 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Wed, 26 Mar 2014 11:09:13 +0100 Subject: [PATCH 1/3] Backed out changeset 7e0f13fbecd3 (bug 983610) --- b2g/chrome/content/devtools.js | 62 ++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/b2g/chrome/content/devtools.js b/b2g/chrome/content/devtools.js index 6af09e1c767..136118af1b5 100644 --- a/b2g/chrome/content/devtools.js +++ b/b2g/chrome/content/devtools.js @@ -38,7 +38,7 @@ let developerHUD = { _targets: new Map(), _frames: new Map(), _client: null, - _conn: null, + _webappsActor: null, _watchers: [], _logging: true, @@ -61,33 +61,32 @@ let developerHUD = { RemoteDebugger.start(); } - // We instantiate a local debugger connection so that watchers can use our - // DebuggerClient to send requests to tab actors (e.g. the consoleActor). - // Note the special usage of the private _serverConnection, which we need - // to call connectToChild and set up child process actors on a frame we - // intend to track. These actors will use the connection to communicate with - // our DebuggerServer in the parent process. - let transport = DebuggerServer.connectPipe(); - this._conn = transport._serverConnection; - this._client = new DebuggerClient(transport); + this._client = new DebuggerClient(DebuggerServer.connectPipe()); + this._client.connect((type, traits) => { - for (let w of this._watchers) { - if (w.init) { - w.init(this._client); - } - } + // FIXME(Bug 962577) see below. + this._client.listTabs((res) => { + this._webappsActor = res.webappsActor; - Services.obs.addObserver(this, 'remote-browser-shown', false); - Services.obs.addObserver(this, 'inprocess-browser-shown', false); - Services.obs.addObserver(this, 'message-manager-disconnect', false); + for (let w of this._watchers) { + if (w.init) { + w.init(this._client); + } + } - let systemapp = document.querySelector('#systemapp'); - this.trackFrame(systemapp); + Services.obs.addObserver(this, 'remote-browser-shown', false); + Services.obs.addObserver(this, 'inprocess-browser-shown', false); + Services.obs.addObserver(this, 'message-manager-disconnect', false); - let frames = systemapp.contentWindow.document.querySelectorAll('iframe[mozapp]'); - for (let frame of frames) { - this.trackFrame(frame); - } + let systemapp = document.querySelector('#systemapp'); + this.trackFrame(systemapp); + + let frames = systemapp.contentWindow.document.querySelectorAll('iframe[mozapp]'); + for (let frame of frames) { + this.trackFrame(frame); + } + }); + }); SettingsListener.observe('hud.logging', this._logging, enabled => { this._logging = enabled; @@ -118,12 +117,17 @@ let developerHUD = { if (this._targets.has(frame)) return; - let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner) - .frameLoader - .messageManager; + // FIXME(Bug 962577) Factor getAppActor out of webappsActor. + this._client.request({ + to: this._webappsActor, + type: 'getAppActor', + manifestURL: frame.appManifestURL + }, (res) => { + if (res.error) { + return; + } - DebuggerServer.connectToChild(this._conn, mm).then(actor => { - let target = new Target(frame, actor); + let target = new Target(frame, res.actor); this._targets.set(frame, target); for (let w of this._watchers) { From 8ebd01832e60df209a7a43311cd576b65336e181 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Wed, 26 Mar 2014 11:09:16 +0100 Subject: [PATCH 2/3] Backed out changeset 246f001547ac (bug 981085) --- dom/apps/src/AppsUtils.jsm | 86 +++++++------------- dom/apps/src/OperatorApps.jsm | 130 +++++++++++++++---------------- dom/apps/src/Webapps.jsm | 142 +++++++++++++++++++++------------- 3 files changed, 177 insertions(+), 181 deletions(-) diff --git a/dom/apps/src/AppsUtils.jsm b/dom/apps/src/AppsUtils.jsm index 8581553b957..55305467acf 100644 --- a/dom/apps/src/AppsUtils.jsm +++ b/dom/apps/src/AppsUtils.jsm @@ -15,14 +15,11 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/WebappOSUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); -XPCOMUtils.defineLazyServiceGetter(this, "NetworkUtil", - "@mozilla.org/network/util;1", - "nsINetUtil"); - -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); +XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { + return Cc["@mozilla.org/network/util;1"] + .getService(Ci.nsINetUtil); +}); // Shared code for AppsServiceChild.jsm, Webapps.jsm and Webapps.js @@ -342,7 +339,7 @@ this.AppsUtils = { checkManifestContentType(aInstallOrigin, aWebappOrigin, aContentType) { let hadCharset = { }; let charset = { }; - let contentType = NetworkUtil.parseContentType(aContentType, charset, hadCharset); + let contentType = NetUtil.parseContentType(aContentType, charset, hadCharset); if (aInstallOrigin != aWebappOrigin && contentType != "application/x-web-app-manifest+json") { return false; @@ -499,57 +496,30 @@ this.AppsUtils = { return true; }, - // Asynchronously loads a JSON file. aPath is a string representing the path + // Loads a JSON file using OS.file. aFile is a string representing the path // of the file to be read. - loadJSONAsync: function(aPath) { - let deferred = Promise.defer(); - - try { - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - file.initWithPath(aPath); - - let channel = NetUtil.newChannel(file); - channel.contentType = "application/json"; - - NetUtil.asyncFetch(channel, function(aStream, aResult) { - if (!Components.isSuccessCode(aResult)) { - deferred.resolve(null); - - if (aResult == Cr.NS_ERROR_FILE_NOT_FOUND) { - // We expect this under certain circumstances, like for webapps.json - // on firstrun, so we return early without reporting an error. - return; - } - - Cu.reportError("AppsUtils: Could not read from json file " + aPath); - return; - } - - try { - // Obtain a converter to read from a UTF-8 encoded input stream. - let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Ci.nsIScriptableUnicodeConverter); - converter.charset = "UTF-8"; - - // Read json file into a string - let data = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(aStream, - aStream.available()) || "")); - aStream.close(); - - deferred.resolve(data); - } catch (ex) { - Cu.reportError("AppsUtils: Could not parse JSON: " + - aPath + " " + ex + "\n" + ex.stack); - deferred.resolve(null); - } - }); - } catch (ex) { - Cu.reportError("AppsUtils: Could not read from " + - aPath + " : " + ex + "\n" + ex.stack); - deferred.resolve(null); - } - - return deferred.promise; + // Returns a Promise resolved with the json payload or rejected with + // OS.File.Error + loadJSONAsync: function(aFile) { + debug("_loadJSONAsync: " + aFile); + return Task.spawn(function() { + let file = yield OS.File.open(aFile, { read: true }); + let rawData = yield file.read(); + // Read json file into a string + let data; + try { + // Obtain a converter to read from a UTF-8 encoded input stream. + let converter = new TextDecoder(); + data = JSON.parse(converter.decode(rawData)); + file.close(); + } catch (ex) { + debug("Error parsing JSON: " + aFile + ". Error: " + ex); + Cu.reportError("OperatorApps: Could not parse JSON: " + + aFile + " " + ex + "\n" + ex.stack); + throw ex; + } + throw new Task.Result(data); + }); }, // Returns the MD5 hash of a string. diff --git a/dom/apps/src/OperatorApps.jsm b/dom/apps/src/OperatorApps.jsm index 7bcf55d9f6a..205f0f7698a 100644 --- a/dom/apps/src/OperatorApps.jsm +++ b/dom/apps/src/OperatorApps.jsm @@ -18,8 +18,6 @@ Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/AppsUtils.jsm"); Cu.import("resource://gre/modules/Task.jsm"); -let Path = OS.Path; - #ifdef MOZ_B2G_RIL XPCOMUtils.defineLazyServiceGetter(this, "iccProvider", "@mozilla.org/ril/content-helper;1", @@ -60,6 +58,9 @@ function isFirstRunWithSIM() { } #ifdef MOZ_B2G_RIL +let File = OS.File; +let Path = OS.Path; + let iccListener = { notifyStkCommand: function() {}, @@ -139,37 +140,38 @@ this.OperatorAppsRegistry = { debug("copying " + aOrg + " to " + aDst); return aDst && Task.spawn(function() { try { - let orgDir = Cc["@mozilla.org/file/local;1"] - .createInstance(Ci.nsIFile); - orgDir.initWithPath(aOrg); - if (!orgDir.isDirectory()) { + let orgInfo = yield File.stat(aOrg); + if (!orgInfo.isDir) { return; } - let dstDir = Cc["@mozilla.org/file/local;1"] - .createInstance(Ci.nsIFile); - dstDir.initWithPath(aDst); - if (!dstDir.exists()) { - dstDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + let dirDstExists = yield File.exists(aDst); + if (!dirDstExists) { + yield File.makeDir(aDst); } - - let entries = orgDir.directoryEntries; - while (entries.hasMoreElements()) { - let entry = entries.getNext().QueryInterface(Ci.nsIFile); - - if (!entry.isDirectory()) { - // Remove the file, because copyTo doesn't overwrite files. - let dstFile = dstDir.clone(); - dstFile.append(entry.leafName); - if(dstFile.exists()) { - dstFile.remove(false); + let iterator = new File.DirectoryIterator(aOrg); + if (!iterator) { + debug("No iterator over: " + aOrg); + return; + } + try { + while (true) { + let entry; + try { + entry = yield iterator.next(); + } catch (ex if ex == StopIteration) { + break; } - entry.copyTo(dstDir, entry.leafName); - } else { - yield this._copyDirectory(entry.path, - Path.join(aDst, entry.name)); + if (!entry.isDir) { + yield File.copy(entry.path, Path.join(aDst, entry.name)); + } else { + yield this._copyDirectory(entry.path, + Path.join(aDst, entry.name)); + } } + } finally { + iterator.close(); } } catch (e) { debug("Error copying " + aOrg + " to " + aDst + ". " + e); @@ -193,27 +195,23 @@ this.OperatorAppsRegistry = { // DIRECTORY_NAME + SINGLE_VARIANT_SOURCE_DIR and move all apps (and // configuration file) to PREF_SINGLE_VARIANT_DIR and return // PREF_SINGLE_VARIANT_DIR as sourceDir. - let svFinalDir = Cc["@mozilla.org/file/local;1"] - .createInstance(Ci.nsIFile); - svFinalDir.initWithPath(svFinalDirName); - if (!svFinalDir.exists()) { - svFinalDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + let existsDir = yield File.exists(svFinalDirName); + if (!existsDir) { + yield File.makeDir(svFinalDirName, {ignoreExisting: true}); } - let svIndex = svFinalDir.clone(); - svIndex.append(SINGLE_VARIANT_CONF_FILE); - if (!svIndex.exists()) { - let svSourceDir = FileUtils.getFile(DIRECTORY_NAME, - [SINGLE_VARIANT_SOURCE_DIR]); - - yield this._copyDirectory(svSourceDir.path, svFinalDirName); - - debug("removing directory:" + svSourceDir.path); - try { - svSourceDir.remove(true); - } catch(ex) { } + let existsSvIndex = yield File.exists(Path.join(svFinalDirName, + SINGLE_VARIANT_CONF_FILE)); + if (!existsSvIndex) { + let svSourceDirName = FileUtils.getFile(DIRECTORY_NAME, + [SINGLE_VARIANT_SOURCE_DIR]).path; + yield this._copyDirectory(svSourceDirName, svFinalDirName); + debug("removing directory:" + svSourceDirName); + File.removeDir(svSourceDirName, { + ignoreAbsent: true, + ignorePermissions: true + }); } - this.appsDir = svFinalDirName; }.bind(this)); }, @@ -285,18 +283,18 @@ this.OperatorAppsRegistry = { if (isPackage) { debug("aId:" + aId + ". Installing as packaged app."); - let installPack = this.appsDir.clone(); - installPack.append(aId); - installPack.append(APPLICATION_ZIP); - - if (!installPack.exists()) { - debug("SV " + installPack.path + " file do not exists for app " + aId); - return; - } - - appData.app.localInstallPath = installPack.path; - appData.app.updateManifest = aManifest; - DOMApplicationRegistry.confirmInstall(appData); + let installPack = OS.Path.join(this.appsDir.path, aId, APPLICATION_ZIP); + OS.File.exists(installPack).then( + function(aExists) { + if (!aExists) { + debug("SV " + installPack.path + " file do not exists for app " + + aId); + return; + } + appData.app.localInstallPath = installPack; + appData.app.updateManifest = aManifest; + DOMApplicationRegistry.confirmInstall(appData); + }); } else { debug("aId:" + aId + ". Installing as hosted app."); appData.app.manifest = aManifest; @@ -317,24 +315,18 @@ this.OperatorAppsRegistry = { for (let i = 0; i < aIdsApp.length; i++) { let aId = aIdsApp[i]; let aMetadata = yield AppsUtils.loadJSONAsync( - Path.join(this.appsDir.path, aId, METADATA)); - if (!aMetadata) { - debug("Error reading metadata file"); - return; - } - + OS.Path.join(this.appsDir.path, aId, METADATA)); debug("metadata:" + JSON.stringify(aMetadata)); let isPackage = true; let manifest; let manifests = [UPDATEMANIFEST, MANIFEST]; for (let j = 0; j < manifests.length; j++) { - manifest = yield AppsUtils.loadJSONAsync( - Path.join(this.appsDir.path, aId, manifests[j])); - - if (!manifest) { - isPackage = false; - } else { + try { + manifest = yield AppsUtils.loadJSONAsync( + OS.Path.join(this.appsDir.path, aId, manifests[j])); break; + } catch (e) { + isPackage = false; } } if (manifest) { @@ -364,7 +356,7 @@ this.OperatorAppsRegistry = { return Task.spawn(function () { let key = normalizeCode(aMcc) + "-" + normalizeCode(aMnc); - let file = Path.join(this.appsDir.path, SINGLE_VARIANT_CONF_FILE); + let file = OS.Path.join(this.appsDir.path, SINGLE_VARIANT_CONF_FILE); let aData = yield AppsUtils.loadJSONAsync(file); if (!aData || !(key in aData)) { return; diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index 96ec8671c9f..5733dfa5eb0 100755 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -178,7 +178,7 @@ this.DOMApplicationRegistry = { // loads the current registry, that could be empty on first run. loadCurrentRegistry: function() { - return AppsUtils.loadJSONAsync(this.appsFile).then((aData) => { + return this._loadJSONAsync(this.appsFile).then((aData) => { if (!aData) { return; } @@ -513,7 +513,7 @@ this.DOMApplicationRegistry = { } // a - let data = yield AppsUtils.loadJSONAsync(file.path); + let data = yield this._loadJSONAsync(file.path); if (!data) { return; } @@ -944,6 +944,56 @@ this.DOMApplicationRegistry = { } }, + _loadJSONAsync: function(aPath) { + let deferred = Promise.defer(); + + try { + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + file.initWithPath(aPath); + let channel = NetUtil.newChannel(file); + channel.contentType = "application/json"; + NetUtil.asyncFetch(channel, function(aStream, aResult) { + if (!Components.isSuccessCode(aResult)) { + deferred.resolve(null); + + if (aResult == Cr.NS_ERROR_FILE_NOT_FOUND) { + // We expect this under certain circumstances, like for webapps.json + // on firstrun, so we return early without reporting an error. + return; + } + + Cu.reportError("DOMApplicationRegistry: Could not read from json file " + + aPath); + return; + } + + try { + // Obtain a converter to read from a UTF-8 encoded input stream. + let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + + // Read json file into a string + let data = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(aStream, + aStream.available()) || "")); + aStream.close(); + + deferred.resolve(data); + } catch (ex) { + Cu.reportError("DOMApplicationRegistry: Could not parse JSON: " + + aPath + " " + ex + "\n" + ex.stack); + deferred.resolve(null); + } + }); + } catch (ex) { + Cu.reportError("DOMApplicationRegistry: Could not read from " + + aPath + " : " + ex + "\n" + ex.stack); + deferred.resolve(null); + } + + return deferred.promise; + }, + addMessageListener: function(aMsgNames, aApp, aMm) { aMsgNames.forEach(function (aMsgName) { let man = aApp && aApp.manifestURL; @@ -1179,30 +1229,10 @@ this.DOMApplicationRegistry = { _writeFile: function(aPath, aData) { debug("Saving " + aPath); - let deferred = Promise.defer(); - - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - file.initWithPath(aPath); - - // Initialize the file output stream - let ostream = FileUtils.openSafeFileOutputStream(file); - - // Obtain a converter to convert our data to a UTF-8 encoded input stream. - let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Ci.nsIScriptableUnicodeConverter); - converter.charset = "UTF-8"; - - // Asynchronously copy the data to the file. - let istream = converter.convertToInputStream(aData); - NetUtil.asyncCopy(istream, ostream, function(aResult) { - if (!Components.isSuccessCode(aResult)) { - deferred.reject() - } else { - deferred.resolve(); - } - }); - - return deferred.promise; + return OS.File.writeAtomic(aPath, + new TextEncoder().encode(aData), + { tmpPath: aPath + ".tmp" }) + .then(null, Cu.reportError); }, doLaunch: function (aData, aMm) { @@ -1371,7 +1401,7 @@ this.DOMApplicationRegistry = { return; } - AppsUtils.loadJSONAsync(file.path).then((aJSON) => { + this._loadJSONAsync(file.path).then((aJSON) => { if (!aJSON) { debug("startDownload: No update manifest found at " + file.path + " " + aManifestURL); @@ -1484,7 +1514,7 @@ this.DOMApplicationRegistry = { // Update the handlers and permissions for this app. this.updateAppHandlers(aOldManifest, aData, app); - AppsUtils.loadJSONAsync(staged.path).then((aUpdateManifest) => { + this._loadJSONAsync(staged.path).then((aUpdateManifest) => { let appObject = AppsUtils.cloneAppObject(app); appObject.updateManifest = aUpdateManifest; this.notifyUpdateHandlers(appObject, aData, appFile.path); @@ -2575,7 +2605,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, let fileNames = ["manifest.webapp", "update.webapp", "manifest.json"]; for (let fileName of fileNames) { - this._manifestCache[id] = yield AppsUtils.loadJSONAsync(OS.Path.join(dir.path, fileName)); + this._manifestCache[id] = yield this._loadJSONAsync(OS.Path.join(dir.path, fileName)); if (this._manifestCache[id]) { break; } @@ -2866,41 +2896,48 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, * @returns {String} the MD5 hash of the file */ _computeFileHash: function(aFilePath) { - let deferred = Promise.defer(); - - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - file.initWithPath(aFilePath); - - NetUtil.asyncFetch(file, function(inputStream, status) { - if (!Components.isSuccessCode(status)) { - debug("Error reading " + aFilePath + ": " + e); - deferred.reject(); - return; - } - + return Task.spawn(function*() { let hasher = Cc["@mozilla.org/security/hash;1"] .createInstance(Ci.nsICryptoHash); // We want to use the MD5 algorithm. hasher.init(hasher.MD5); - const PR_UINT32_MAX = 0xffffffff; - hasher.updateFromStream(inputStream, PR_UINT32_MAX); + const CHUNK_SIZE = 16384; // Return the two-digit hexadecimal code for a byte. function toHexString(charCode) { return ("0" + charCode.toString(16)).slice(-2); } + let file; + try { + file = yield OS.File.open(aFilePath, { read: true }); + } catch(e) { + debug("Error opening " + aFilePath + ": " + e); + return null; + } + + try { + let array; + do { + array = yield file.read(CHUNK_SIZE); + hasher.update(array, array.length); + } while (array.length == CHUNK_SIZE); + } catch(e) { + debug("Error reading " + aFilePath + ": " + e); + return null; + } + + yield file.close(); + // We're passing false to get the binary hash and not base64. let data = hasher.finish(false); // Convert the binary hash data to a hex string. let hash = [toHexString(data.charCodeAt(i)) for (i in data)].join(""); debug("File hash computed: " + hash); - deferred.resolve(hash); + return hash; }); - - return deferred.promise; }, /** @@ -2928,15 +2965,12 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, aOldApp.manifestHash = aOldApp.staged.manifestHash; aOldApp.etag = aOldApp.staged.etag || aOldApp.etag; aOldApp.staged = {}; - // Move the staged update manifest to a non staged one. - try { - let staged = this._getAppDir(aId); - staged.append("staged-update.webapp"); - staged.moveTo(staged.parent, "update.webapp"); - } catch (ex) { - // We don't really mind much if this fails. - } + let dirPath = this._getAppDir(aId).path; + + // We don't really mind much if this fails. + OS.File.move(OS.Path.join(dirPath, "staged-update.webapp"), + OS.Path.join(dirPath, "update.webapp")); } // Save the updated registry, and cleanup the tmp directory. From 87351c1f332599e868bdd373153a8f32b215fa77 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Wed, 26 Mar 2014 11:09:34 +0100 Subject: [PATCH 3/3] Backed out changeset 81f230994acd (bug 980502) for hopefully fixing gi tests --- browser/app/profile/firefox.js | 3 - browser/devtools/moz.build | 3 +- browser/devtools/webaudioeditor/moz.build | 12 - .../devtools/webaudioeditor/test/browser.ini | 12 - .../browser_audionode-actor-get-set-param.js | 51 --- .../test/browser_audionode-actor-get-type.js | 29 -- .../test/browser_audionode-actor-is-source.js | 28 -- .../test/browser_webaudio-actor-simple.js | 35 -- .../test/doc_complex-context.html | 44 -- .../test/doc_simple-context.html | 26 -- .../test/doc_simple-node-creation.html | 28 -- browser/devtools/webaudioeditor/test/head.js | 160 ------- .../devtools/webaudioeditor/test/moz.build | 6 - toolkit/devtools/server/actors/audionode.js | 173 ------- toolkit/devtools/server/actors/webaudio.js | 427 ------------------ toolkit/devtools/server/main.js | 1 - 16 files changed, 1 insertion(+), 1037 deletions(-) delete mode 100644 browser/devtools/webaudioeditor/moz.build delete mode 100644 browser/devtools/webaudioeditor/test/browser.ini delete mode 100644 browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js delete mode 100644 browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js delete mode 100644 browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js delete mode 100644 browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js delete mode 100644 browser/devtools/webaudioeditor/test/doc_complex-context.html delete mode 100644 browser/devtools/webaudioeditor/test/doc_simple-context.html delete mode 100644 browser/devtools/webaudioeditor/test/doc_simple-node-creation.html delete mode 100644 browser/devtools/webaudioeditor/test/head.js delete mode 100644 browser/devtools/webaudioeditor/test/moz.build delete mode 100644 toolkit/devtools/server/actors/audionode.js delete mode 100644 toolkit/devtools/server/actors/webaudio.js diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 890eb0cddef..07f122da9cf 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1205,9 +1205,6 @@ pref("devtools.styleeditor.autocompletion-enabled", true); // Enable the Shader Editor. pref("devtools.shadereditor.enabled", false); -// Enable the Web Audio Editor -pref("devtools.webaudioeditor.enabled", false); - // Enable tools for Chrome development. pref("devtools.chrome.enabled", false); diff --git a/browser/devtools/moz.build b/browser/devtools/moz.build index cee54ed9a78..3f19e867c90 100644 --- a/browser/devtools/moz.build +++ b/browser/devtools/moz.build @@ -23,7 +23,6 @@ DIRS += [ 'styleeditor', 'styleinspector', 'tilt', - 'webaudioeditor', 'webconsole', ] @@ -32,4 +31,4 @@ EXTRA_COMPONENTS += [ 'devtools-clhandler.manifest', ] -JAR_MANIFESTS += ['jar.mn'] +JAR_MANIFESTS += ['jar.mn'] \ No newline at end of file diff --git a/browser/devtools/webaudioeditor/moz.build b/browser/devtools/webaudioeditor/moz.build deleted file mode 100644 index 91a9dd1e7a7..00000000000 --- a/browser/devtools/webaudioeditor/moz.build +++ /dev/null @@ -1,12 +0,0 @@ -# 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/. - -TEST_DIRS += ['test'] - -JS_MODULES_PATH = 'modules/devtools/webaudioeditor' - -EXTRA_JS_MODULES += [ -] - diff --git a/browser/devtools/webaudioeditor/test/browser.ini b/browser/devtools/webaudioeditor/test/browser.ini deleted file mode 100644 index db3344e244d..00000000000 --- a/browser/devtools/webaudioeditor/test/browser.ini +++ /dev/null @@ -1,12 +0,0 @@ -[DEFAULT] - -support-files = - doc_simple-context.html - doc_complex-context.html - doc_simple-node-creation.html - head.js - -[browser_webaudio-actor-simple.js] -[browser_audionode-actor-get-set-param.js] -[browser_audionode-actor-is-source.js] -[browser_audionode-actor-get-type.js] diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js deleted file mode 100644 index b2135ea4049..00000000000 --- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js +++ /dev/null @@ -1,51 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Test AudioNode#getParam() / AudioNode#setParam() - */ - -function spawnTest () { - let [target, debuggee, front] = yield initBackend(SIMPLE_CONTEXT_URL); - let [_, [destNode, oscNode, gainNode]] = yield Promise.all([ - front.setup({ reload: true }), - get3(front, "create-node") - ]); - - let freq = yield oscNode.getParam("frequency"); - info(typeof freq); - ise(freq, 440, "AudioNode:getParam correctly fetches AudioParam"); - - let type = yield oscNode.getParam("type"); - ise(type, "sine", "AudioNode:getParam correctly fetches non-AudioParam"); - - let type = yield oscNode.getParam("not-a-valid-param"); - is(type, undefined, "AudioNode:getParam correctly returns false for invalid param"); - - let resSuccess = yield oscNode.setParam("frequency", "220", "number"); - let freq = yield oscNode.getParam("frequency"); - ise(freq, 220, "AudioNode:setParam correctly sets a `number` AudioParam"); - is(resSuccess, undefined, "AudioNode:setParam returns undefined for correctly set AudioParam"); - - resSuccess = yield oscNode.setParam("type", "square", "string"); - let type = yield oscNode.getParam("type"); - ise(type, "square", "AudioNode:setParam correctly sets a `string` non-AudioParam"); - is(resSuccess, undefined, "AudioNode:setParam returns undefined for correctly set AudioParam"); - - resSuccess = yield oscNode.setParam("type", "\"triangle\"", "string"); - type = yield oscNode.getParam("type"); - ise(type, "triangle", "AudioNode:setParam correctly removes quotes in `string` non-AudioParam"); - - try { - yield oscNode.setParam("frequency", "hello", "string"); - ok(false, "setParam with invalid types should throw"); - } catch (e) { - ok(/is not a finite floating-point/.test(e.message), "AudioNode:setParam returns error with correct message when attempting an invalid assignment"); - is(e.type, "TypeError", "AudioNode:setParam returns error with correct type when attempting an invalid assignment"); - freq = yield oscNode.getParam("frequency"); - ise(freq, 220, "AudioNode:setParam does not modify value when an error occurs"); - } - - yield removeTab(target.tab); - finish(); -} diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js deleted file mode 100644 index c4ac400d95b..00000000000 --- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js +++ /dev/null @@ -1,29 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Test AudioNode#getType() - */ - -function spawnTest () { - let [target, debuggee, front] = yield initBackend(SIMPLE_NODES_URL); - let [_, nodes] = yield Promise.all([ - front.setup({ reload: true }), - getN(front, "create-node", 14) - ]); - - let actualTypes = yield Promise.all(nodes.map(node => node.getType())); - let expectedTypes = [ - "AudioDestinationNode", - "AudioBufferSourceNode", "ScriptProcessorNode", "AnalyserNode", "GainNode", - "DelayNode", "BiquadFilterNode", "WaveShaperNode", "PannerNode", "ConvolverNode", - "ChannelSplitterNode", "ChannelMergerNode", "DynamicsCompressorNode", "OscillatorNode" - ]; - - expectedTypes.forEach((type, i) => { - is(actualTypes[i], type, type + " successfully created with correct type"); - }); - - yield removeTab(target.tab); - finish(); -} diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js deleted file mode 100644 index 942edde7f4d..00000000000 --- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js +++ /dev/null @@ -1,28 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Test AudioNode#isSource() - */ - -function spawnTest () { - let [target, debuggee, front] = yield initBackend(SIMPLE_NODES_URL); - let [_, nodes] = yield Promise.all([ - front.setup({ reload: true }), - getN(front, "create-node", 14) - ]); - - let actualTypes = yield Promise.all(nodes.map(node => node.getType())); - let isSourceResult = yield Promise.all(nodes.map(node => node.isSource())); - - actualTypes.forEach((type, i) => { - let shouldBeSource = type === "AudioBufferSourceNode" || type === "OscillatorNode"; - if (shouldBeSource) - is(isSourceResult[i], true, type + "'s isSource() yields into `true`"); - else - is(isSourceResult[i], false, type + "'s isSource() yields into `false`"); - }); - - yield removeTab(target.tab); - finish(); -} diff --git a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js deleted file mode 100644 index 86dead3bc10..00000000000 --- a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js +++ /dev/null @@ -1,35 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Test basic communication of Web Audio actor - */ - -function spawnTest () { - let [target, debuggee, front] = yield initBackend(SIMPLE_CONTEXT_URL); - let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = yield Promise.all([ - front.setup({ reload: true }), - once(front, "start-context"), - get3(front, "create-node"), - get2(front, "connect-node") - ]); - - let destType = yield destNode.getType(); - let oscType = yield oscNode.getType(); - let gainType = yield gainNode.getType(); - - is(destType, "AudioDestinationNode", "WebAudioActor:create-node returns AudioNodeActor for AudioDestination"); - is(oscType, "OscillatorNode", "WebAudioActor:create-node returns AudioNodeActor"); - is(gainType, "GainNode", "WebAudioActor:create-node returns AudioNodeActor"); - - let { source, dest } = connect1; - is(source.actorID, oscNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on source (osc->gain)"); - is(dest.actorID, gainNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on dest (osc->gain)"); - - let { source, dest } = connect2; - is(source.actorID, gainNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on source (gain->dest)"); - is(dest.actorID, destNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on dest (gain->dest)"); - - yield removeTab(target.tab); - finish(); -} diff --git a/browser/devtools/webaudioeditor/test/doc_complex-context.html b/browser/devtools/webaudioeditor/test/doc_complex-context.html deleted file mode 100644 index 396bbce3f29..00000000000 --- a/browser/devtools/webaudioeditor/test/doc_complex-context.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - Web Audio Editor test page - - - - - - - - diff --git a/browser/devtools/webaudioeditor/test/doc_simple-context.html b/browser/devtools/webaudioeditor/test/doc_simple-context.html deleted file mode 100644 index d6d8e70d2fc..00000000000 --- a/browser/devtools/webaudioeditor/test/doc_simple-context.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - Web Audio Editor test page - - - - - - - - diff --git a/browser/devtools/webaudioeditor/test/doc_simple-node-creation.html b/browser/devtools/webaudioeditor/test/doc_simple-node-creation.html deleted file mode 100644 index 95991660de8..00000000000 --- a/browser/devtools/webaudioeditor/test/doc_simple-node-creation.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - Web Audio Editor test page - - - - - - - - diff --git a/browser/devtools/webaudioeditor/test/head.js b/browser/devtools/webaudioeditor/test/head.js deleted file mode 100644 index 7ed921426d0..00000000000 --- a/browser/devtools/webaudioeditor/test/head.js +++ /dev/null @@ -1,160 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ -"use strict"; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -let { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); - -// Enable logging for all the tests. Both the debugger server and frontend will -// be affected by this pref. -let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log"); -Services.prefs.setBoolPref("devtools.debugger.log", true); - -let { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); -let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {}); -let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); -let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); -let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {}); -let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {}); - -let { WebAudioFront } = devtools.require("devtools/server/actors/webaudio"); -let TargetFactory = devtools.TargetFactory; -let Toolbox = devtools.Toolbox; - -const EXAMPLE_URL = "http://example.com/browser/browser/devtools/webaudioeditor/test/"; -const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html"; -const COMPLEX_CONTEXT_URL = EXAMPLE_URL + "doc_complex-context.html"; -const SIMPLE_NODES_URL = EXAMPLE_URL + "doc_simple-node-creation.html"; - -// All tests are asynchronous. -waitForExplicitFinish(); - -let gToolEnabled = Services.prefs.getBoolPref("devtools.webaudioeditor.enabled"); - -registerCleanupFunction(() => { - info("finish() was called, cleaning up..."); - Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging); - Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", gToolEnabled); - Cu.forceGC(); -}); - -function addTab(aUrl, aWindow) { - info("Adding tab: " + aUrl); - - let deferred = Promise.defer(); - let targetWindow = aWindow || window; - let targetBrowser = targetWindow.gBrowser; - - targetWindow.focus(); - let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl); - let linkedBrowser = tab.linkedBrowser; - - linkedBrowser.addEventListener("load", function onLoad() { - linkedBrowser.removeEventListener("load", onLoad, true); - info("Tab added and finished loading: " + aUrl); - deferred.resolve(tab); - }, true); - - return deferred.promise; -} - -function removeTab(aTab, aWindow) { - info("Removing tab."); - - let deferred = Promise.defer(); - let targetWindow = aWindow || window; - let targetBrowser = targetWindow.gBrowser; - let tabContainer = targetBrowser.tabContainer; - - tabContainer.addEventListener("TabClose", function onClose(aEvent) { - tabContainer.removeEventListener("TabClose", onClose, false); - info("Tab removed and finished closing."); - deferred.resolve(); - }, false); - - targetBrowser.removeTab(aTab); - return deferred.promise; -} - -function handleError(aError) { - ok(false, "Got an error: " + aError.message + "\n" + aError.stack); - finish(); -} - -function once(aTarget, aEventName, aUseCapture = false) { - info("Waiting for event: '" + aEventName + "' on " + aTarget + "."); - - let deferred = Promise.defer(); - - for (let [add, remove] of [ - ["on", "off"], // Use event emitter before DOM events for consistency - ["addEventListener", "removeEventListener"], - ["addListener", "removeListener"] - ]) { - if ((add in aTarget) && (remove in aTarget)) { - aTarget[add](aEventName, function onEvent(...aArgs) { - aTarget[remove](aEventName, onEvent, aUseCapture); - deferred.resolve(...aArgs); - }, aUseCapture); - break; - } - } - - return deferred.promise; -} - -function test () { - Task.spawn(spawnTest).then(finish, handleError); -} - -function initBackend(aUrl) { - info("Initializing a web audio editor front."); - - if (!DebuggerServer.initialized) { - DebuggerServer.init(() => true); - DebuggerServer.addBrowserActors(); - } - - return Task.spawn(function*() { - let tab = yield addTab(aUrl); - let target = TargetFactory.forTab(tab); - let debuggee = target.window.wrappedJSObject; - - yield target.makeRemote(); - - let front = new WebAudioFront(target.client, target.form); - return [target, debuggee, front]; - }); -} - -// Due to web audio will fire most events synchronously back-to-back, -// and we can't yield them in a chain without missing actors, this allows -// us to listen for `n` events and return a promise resolving to them. -// -// Takes a `front` object that is an event emitter, the number of -// programs that should be listened to and waited on, and an optional -// `onAdd` function that calls with the entire actors array on program link -function getN (front, eventName, count, spread) { - let actors = []; - let deferred = Promise.defer(); - front.on(eventName, function onEvent (...args) { - let actor = args[0]; - if (actors.length !== count) { - actors.push(spread ? args : actor); - } - if (actors.length === count) { - front.off(eventName, onEvent); - deferred.resolve(actors); - } - }); - return deferred.promise; -} - -function get (front, eventName) { return getN(front, eventName, 1); } -function get2 (front, eventName) { return getN(front, eventName, 2); } -function get3 (front, eventName) { return getN(front, eventName, 3); } -function getSpread (front, eventName) { return getN(front, eventName, 1, true); } -function get2Spread (front, eventName) { return getN(front, eventName, 2, true); } -function get3Spread (front, eventName) { return getN(front, eventName, 3, true); } -function getNSpread (front, eventName, count) { return getN(front, eventName, count, true); } diff --git a/browser/devtools/webaudioeditor/test/moz.build b/browser/devtools/webaudioeditor/test/moz.build deleted file mode 100644 index a21913edfc3..00000000000 --- a/browser/devtools/webaudioeditor/test/moz.build +++ /dev/null @@ -1,6 +0,0 @@ -# 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/. - -BROWSER_CHROME_MANIFESTS += ['browser.ini'] diff --git a/toolkit/devtools/server/actors/audionode.js b/toolkit/devtools/server/actors/audionode.js deleted file mode 100644 index c3f5e0e14f0..00000000000 --- a/toolkit/devtools/server/actors/audionode.js +++ /dev/null @@ -1,173 +0,0 @@ -/* 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/. */ -"use strict"; - -const {Cc, Ci, Cu, Cr} = require("chrome"); -const protocol = require("devtools/server/protocol"); -const { method, Arg, Option, RetVal } = protocol; - -// Add a grip type for our `getParam` method, as the type can be -// unknown. -protocol.types.addDictType("audio-node-param-grip", { - type: "string", - value: "nullable:primitive" -}); - -/** - * An Audio Node actor allowing communication to a specific audio node in the - * Audio Context graph. - */ -let AudioNodeActor = exports.AudioNodeActor = protocol.ActorClass({ - typeName: "audionode", - - /** - * Create the Audio Node actor. - * - * @param DebuggerServerConnection conn - * The server connection. - * @param AudioNode node - * The AudioNode that was created. - */ - initialize: function (conn, node) { - protocol.Actor.prototype.initialize.call(this, conn); - this.node = XPCNativeWrapper.unwrap(node); - try { - this.type = this.node.toString().match(/\[object (.*)\]$/)[1]; - } catch (e) { - this.type = ""; - } - }, - - /** - * Returns the name of the audio type. - * Examples: "OscillatorNode", "MediaElementAudioSourceNode" - */ - getType: method(function () { - return this.type; - }, { - response: { type: RetVal("string") } - }), - - /** - * Returns a boolean indicating if the node is a source node, - * like BufferSourceNode, MediaElementAudioSourceNode, OscillatorNode, etc. - */ - isSource: method(function () { - return !!~this.type.indexOf("Source") || this.type === "OscillatorNode"; - }, { - response: { source: RetVal("boolean") } - }), - - /** - * Changes a param on the audio node. Responds with a `string` that's either - * an empty string `""` on success, or a description of the error upon - * param set failure. - * - * @param String param - * Name of the AudioParam to change. - * @param String value - * Value to change AudioParam to. Subsequently cast via `type`. - * @param String type - * Datatype that `value` should be cast to. - */ - setParam: method(function (param, value, dataType) { - // Strip quotes because sometimes UIs include that for strings - if (dataType === "string") { - value = value.replace(/[\'\"]*/g, ""); - } - try { - if (isAudioParam(this.node, param)) - this.node[param].value = cast(value, dataType); - else - this.node[param] = cast(value, dataType); - return undefined; - } catch (e) { - return constructError(e); - } - }, { - request: { - param: Arg(0, "string"), - value: Arg(1, "string"), - dataType: Arg(2, "string") - }, - response: { error: RetVal("nullable:json") } - }), - - /** - * Gets a param on the audio node. - * - * @param String param - * Name of the AudioParam to fetch. - */ - getParam: method(function (param) { - // If property does not exist, just return "undefined" - if (!this.node[param]) - return undefined; - let value = isAudioParam(this.node, param) ? this.node[param].value : this.node[param]; - let type = typeof type; - return value; - return { type: type, value: value }; - }, { - request: { - param: Arg(0, "string") - }, - response: { text: RetVal("nullable:primitive") } - }), -}); - -/** - * Casts string `value` to specified `type`. - * - * @param String value - * The string to cast. - * @param String type - * The datatype to cast `value` to. - */ -function cast (value, type) { - if (!type || type === "string") - return value; - if (type === "number") - return parseFloat(value); - if (type === "boolean") - return value === "true"; - return undefined; -} - -/** - * Determines whether or not property is an AudioParam. - * - * @param AudioNode node - * An AudioNode. - * @param String prop - * Property of `node` to evaluate to see if it's an AudioParam. - * @return Boolean - */ -function isAudioParam (node, prop) { - return /AudioParam/.test(node[prop].toString()); -} - -/** - * Takes an `Error` object and constructs a JSON-able response - * - * @param Error err - * A TypeError, RangeError, etc. - * @return Object - */ -function constructError (err) { - return { - message: err.message, - type: err.constructor.name - }; -} - -/** - * The corresponding Front object for the AudioNodeActor. - */ -let AudioNodeFront = protocol.FrontClass(AudioNodeActor, { - initialize: function (client, form) { - protocol.Front.prototype.initialize.call(this, client, form); - client.addActorPool(this); - this.manage(this); - } -}); diff --git a/toolkit/devtools/server/actors/webaudio.js b/toolkit/devtools/server/actors/webaudio.js deleted file mode 100644 index 4ac9b803b0d..00000000000 --- a/toolkit/devtools/server/actors/webaudio.js +++ /dev/null @@ -1,427 +0,0 @@ -/* 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/. */ -"use strict"; - -const {Cc, Ci, Cu, Cr} = require("chrome"); - -const Services = require("Services"); - -const events = require("sdk/event/core"); -const protocol = require("devtools/server/protocol"); - -const { on, once, off, emit } = events; -const { method, Arg, Option, RetVal } = protocol; -const { AudioNodeActor } = require("devtools/server/actors/audionode"); -const console = Cu.import("resource://gre/modules/devtools/Console.jsm").console; - -exports.register = function(handle) { - handle.addTabActor(WebAudioActor, "webaudioActor"); -}; - -exports.unregister = function(handle) { - handle.removeTabActor(WebAudioActor); -}; - -/** - * A WebGL Shader contributing to building a WebGL Program. - -/** - * The Web Audio Actor handles simple interaction with an AudioContext - * high-level methods. After instantiating this actor, you'll need to set it - * up by calling setup(). - */ -let WebAudioActor = exports.WebAudioActor = protocol.ActorClass({ - typeName: "webaudio", - initialize: function(conn, tabActor) { - protocol.Actor.prototype.initialize.call(this, conn); - this.tabActor = tabActor; - this._onGlobalCreated = this._onGlobalCreated.bind(this); - this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this); - - this._onStartContext = this._onStartContext.bind(this); - this._onConnectNode = this._onConnectNode.bind(this); - this._onConnectParam = this._onConnectParam.bind(this); - this._onDisconnectNode = this._onDisconnectNode.bind(this); - this._onParamChange = this._onParamChange.bind(this); - this._onCreateNode = this._onCreateNode.bind(this); - }, - - destroy: function(conn) { - protocol.Actor.prototype.destroy.call(this, conn); - this.finalize(); - }, - - /** - * Starts waiting for the current tab actor's document global to be - * created, in order to instrument the Canvas context and become - * aware of everything the content does with Web Audio. - * - * See ContentObserver and WebAudioInstrumenter for more details. - */ - setup: method(function({ reload }) { - if (this._initialized) { - return; - } - this._initialized = true; - - // Weak map mapping audio nodes to their corresponding actors - this._nodeActors = new Map(); - - this._contentObserver = new ContentObserver(this.tabActor); - this._webaudioObserver = new WebAudioObserver(); - - on(this._contentObserver, "global-created", this._onGlobalCreated); - on(this._contentObserver, "global-destroyed", this._onGlobalDestroyed); - - on(this._webaudioObserver, "start-context", this._onStartContext); - on(this._webaudioObserver, "connect-node", this._onConnectNode); - on(this._webaudioObserver, "connect-param", this._onConnectParam); - on(this._webaudioObserver, "disconnect-node", this._onDisconnectNode); - on(this._webaudioObserver, "param-change", this._onParamChange); - on(this._webaudioObserver, "create-node", this._onCreateNode); - - if (reload) { - this.tabActor.window.location.reload(); - } - }, { - request: { reload: Option(0, "boolean") }, - oneway: true - }), - - /** - * Stops listening for document global changes and puts this actor - * to hibernation. This method is called automatically just before the - * actor is destroyed. - */ - finalize: method(function() { - if (!this._initialized) { - return; - } - this._initialized = false; - - this._contentObserver.stopListening(); - off(this._contentObserver, "global-created", this._onGlobalCreated); - off(this._contentObserver, "global-destroyed", this._onGlobalDestroyed); - - off(this._webaudioObserver, "start-context", this._onStartContext); - off(this._webaudioObserver, "connect-node", this._onConnectNode); - off(this._webaudioObserver, "connect-param", this._onConnectParam); - off(this._webaudioObserver, "disconnect-node", this._onDisconnectNode); - off(this._webaudioObserver, "param-change", this._onParamChange); - off(this._webaudioObserver, "create-node", this._onCreateNode); - - this._contentObserver = null; - this._webaudioObserver = null; - }, { - oneway: true - }), - - /** - * Events emitted by this actor. - */ - events: { - "start-context": { - type: "startContext" - }, - "connect-node": { - type: "connectNode", - source: Option(0, "audionode"), - dest: Option(0, "audionode") - }, - "disconnect-node": { - type: "disconnectNode", - source: Arg(0, "audionode") - }, - "connect-param": { - type: "connectParam", - source: Arg(0, "audionode"), - param: Arg(1, "string") - }, - "change-param": { - type: "changeParam", - source: Option(0, "audionode"), - param: Option(0, "string"), - value: Option(0, "string") - }, - "create-node": { - type: "createNode", - source: Arg(0, "audionode") - } - }, - - /** - * Invoked whenever the current tab actor's document global is created. - */ - _onGlobalCreated: function(window) { - WebAudioInstrumenter.handle(window, this._webaudioObserver); - }, - - /** - * Invoked whenever the current tab actor's inner window is destroyed. - */ - _onGlobalDestroyed: function(id) { - }, - - /** - * Helper for constructing an AudioNodeActor, assigning to - * internal weak map, and tracking via `manage` so it is assigned - * an `actorID`. - */ - _constructAudioNode: function (node) { - let actor = new AudioNodeActor(this.conn, node); - this.manage(actor); - this._nodeActors.set(node, actor); - return actor; - }, - - /** - * Takes an AudioNode and returns the stored actor for it. - * In some cases, we won't have an actor stored (for example, - * connecting to an AudioDestinationNode, since it's implicitly - * created), so make a new actor and store that. - */ - _actorFor: function (node) { - let actor = this._nodeActors.get(node); - if (!actor) { - actor = this._constructAudioNode(node); - } - return actor; - }, - - /** - * Called on first audio node creation, signifying audio context usage - */ - _onStartContext: function () { - events.emit(this, "start-context"); - }, - - /** - * Called when one audio node is connected to another. - */ - _onConnectNode: function (source, dest) { - let sourceActor = this._actorFor(source); - let destActor = this._actorFor(dest); - events.emit(this, "connect-node", { - source: sourceActor, - dest: destActor - }); - }, - - /** - * Called when an audio node is connected to an audio param. - * Implement in bug 986705 - */ - _onConnectParam: function (source, dest) { - // TODO bug 986705 - }, - - /** - * Called when an audio node is disconnected. - */ - _onDisconnectNode: function (node) { - let actor = this._actorFor(node); - events.emit(this, "disconnect-node", actor); - }, - - /** - * Called when a parameter changes on an audio node - */ - _onParamChange: function (node, param, value) { - let actor = this._actorFor(node); - events.emit(this, "param-change", { - source: actor, - param: param, - value: value - }); - }, - - /** - * Called on node creation. - */ - _onCreateNode: function (node) { - let actor = this._constructAudioNode(node); - events.emit(this, "create-node", actor); - } -}); - -/** - * The corresponding Front object for the WebAudioActor. - */ -let WebAudioFront = exports.WebAudioFront = protocol.FrontClass(WebAudioActor, { - initialize: function(client, { webaudioActor }) { - protocol.Front.prototype.initialize.call(this, client, { actor: webaudioActor }); - client.addActorPool(this); - this.manage(this); - } -}); - -/** - * Handles adding an observer for the creation of content document globals, - * event sent immediately after a web content document window has been set up, - * but before any script code has been executed. This will allow us to - * instrument the HTMLCanvasElement with the appropriate inspection methods. - * TODO use ContentObserver from bug 917226 once landed, bug 986704 - */ -function ContentObserver(tabActor) { - this._contentWindow = tabActor.window; - this._onContentGlobalCreated = this._onContentGlobalCreated.bind(this); - this._onInnerWindowDestroyed = this._onInnerWindowDestroyed.bind(this); - this.startListening(); -} - -ContentObserver.prototype = { - /** - * Starts listening for the required observer messages. - */ - startListening: function() { - Services.obs.addObserver( - this._onContentGlobalCreated, "content-document-global-created", false); - Services.obs.addObserver( - this._onInnerWindowDestroyed, "inner-window-destroyed", false); - }, - - /** - * Stops listening for the required observer messages. - */ - stopListening: function() { - Services.obs.removeObserver( - this._onContentGlobalCreated, "content-document-global-created", false); - Services.obs.removeObserver( - this._onInnerWindowDestroyed, "inner-window-destroyed", false); - }, - - /** - * Fired immediately after a web content document window has been set up. - */ - _onContentGlobalCreated: function(subject, topic, data) { - if (subject == this._contentWindow) { - emit(this, "global-created", subject); - } - }, - - /** - * Fired when an inner window is removed from the backward/forward cache. - */ - _onInnerWindowDestroyed: function(subject, topic, data) { - let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data; - emit(this, "global-destroyed", id); - } -}; - -/** - * Instruments an AudioContext with inspector methods. - * TODO refactor with CallWatcherActor, bug 986704 - */ -let WebAudioInstrumenter = { - /** - * Overrides all AudioContext methods. - * - * @param nsIDOMWindow window - * The window to perform the instrumentation in. - * @param WebAudioObserver observer - * The observer watching function calls in the context. - */ - handle: function(window, observer) { - let self = this; - - let AudioContext = unwrap(window.AudioContext); - let AudioNode = unwrap(window.AudioNode); - let ctxProto = AudioContext.prototype; - let nodeProto = AudioNode.prototype; - - // All Web Audio nodes inherit from AudioNode's prototype, so - // hook into the `connect` and `disconnect` methods - - // audionode.connect(node|param); - let originalConnect = nodeProto.connect; - nodeProto.connect = function (...args) { - let source = unwrap(this); - let nodeOrParam = unwrap(args[0]); - originalConnect.apply(source, args); - - // Alert observer differently if connecting to an AudioNode or AudioParam - if (nodeOrParam instanceof AudioNode) - observer.connectNode(source, nodeOrParam); - else - observer.connectParam(source, nodeOrParam); - }; - - // audionode.disconnect() - let originalDisconnect = nodeProto.disconnect; - nodeProto.disconnect = function (...args) { - let source = unwrap(this); - originalDisconnect.apply(source, args); - observer.disconnectNode(source); - }; - - - // Keep track of the first node created, so we can alert - // the front end that an audio context is being used since - // we're not hooking into the constructor itself, just its - // instance's methods. - let firstNodeCreated = false; - - // Patch all of AudioContext's methods that create an audio node - // and hook into the observer - NODE_CREATION_METHODS.forEach(method => { - let originalMethod = ctxProto[method]; - ctxProto[method] = function (...args) { - let node = originalMethod.apply(this, args); - // Fire the start-up event if this is the first node created - // and trigger a `create-node` event for the context destination - if (!firstNodeCreated) { - firstNodeCreated = true; - observer.startContext(); - observer.createNode(node.context.destination); - } - observer.createNode(node); - return node; - }; - }); - } -}; - -/** - * An observer that captures an Audio Context's actions and emits - * events - */ -function WebAudioObserver () {} - -WebAudioObserver.prototype = { - startContext: function () { - emit(this, "start-context"); - }, - - connectNode: function (source, dest) { - emit(this, "connect-node", source, dest); - }, - - connectParam: function (source, param) { - emit(this, "connect-param", source, param); - }, - - disconnectNode: function (source) { - emit(this, "disconnect-node", source); - }, - - createNode: function (source) { - emit(this, "create-node", source); - }, - - paramChange: function (node, param, val) { - emit(this, "param-change", node, param, val); - } -}; - -function unwrap (obj) { - return XPCNativeWrapper.unwrap(obj); -} - -let NODE_CREATION_METHODS = [ - "createBufferSource", "createMediaElementSource", "createMediaStreamSource", - "createMediaStreamDestination", "createScriptProcessor", "createAnalyser", - "createGain", "createDelay", "createBiquadFilter", "createWaveShaper", - "createPanner", "createConvolver", "createChannelSplitter", "createChannelMerger", - "createDynamicsCompressor", "createOscillator" -]; diff --git a/toolkit/devtools/server/main.js b/toolkit/devtools/server/main.js index cf08ab3895c..699126240bf 100644 --- a/toolkit/devtools/server/main.js +++ b/toolkit/devtools/server/main.js @@ -392,7 +392,6 @@ var DebuggerServer = { this.addActors("resource://gre/modules/devtools/server/actors/webconsole.js"); this.registerModule("devtools/server/actors/inspector"); this.registerModule("devtools/server/actors/webgl"); - this.registerModule("devtools/server/actors/webaudio"); this.registerModule("devtools/server/actors/stylesheets"); this.registerModule("devtools/server/actors/styleeditor"); this.registerModule("devtools/server/actors/storage");