Bug 980965. Stop using DOM constructors as functions in chrome code. r=bholley
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 10 Mar 2014 17:38:14 -0400
changeset 191079 dabf18234b5c539da24445a5054f3d30c583bb71
parent 191078 bb77e0285652bcb3471f426b828279deac8c34ca
child 191080 2045471633ac12ae219ae644b2b219b6af03b745
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs980965
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 980965. Stop using DOM constructors as functions in chrome code. r=bholley
addon-sdk/source/lib/sdk/io/buffer.js
addon-sdk/source/test/fixtures/chrome-worker/xhr.js
browser/base/content/test/newtab/head.js
content/base/test/chrome/test_bug780199.xul
content/media/test/crashtests/868504.html
content/media/test/crashtests/876024-2.html
content/media/test/crashtests/876834.html
content/media/test/crashtests/894104.html
dom/tests/mochitest/chrome/test_sandbox_bindings.xul
dom/tests/mochitest/chrome/test_xray_event_constructor.xul
dom/xbl/test/file_bug821850.xhtml
testing/specialpowers/content/specialpowersAPI.js
toolkit/content/aboutTelemetry.js
toolkit/devtools/server/actors/highlighter.js
toolkit/devtools/server/actors/inspector.js
webapprt/Startup.jsm
--- a/addon-sdk/source/lib/sdk/io/buffer.js
+++ b/addon-sdk/source/lib/sdk/io/buffer.js
@@ -53,17 +53,17 @@ function Buffer(subject, encoding /*, bu
         else
           throw new Error('Could not instantiate buffer');
       }
       break;
     case 'string':
       // If string encode it and use buffer for the returned Uint8Array
       // to create a local patched version that acts like node buffer.
       encoding = encoding || 'utf8';
-      return Uint8Array(TextEncoder(encoding).encode(subject).buffer);
+      return Uint8Array(new TextEncoder(encoding).encode(subject).buffer);
     case 'object':
       // This form of the constructor uses the form of
       // Uint8Array(buffer, offset, length);
       // So we can instantiate a typed array within the constructor
       // to inherit the appropriate properties, where both the
       // `subject` and newly instantiated buffer share the same underlying
       // data structure.
       if (arguments.length === 3)
@@ -79,28 +79,28 @@ exports.Buffer = Buffer;
 
 // Tests if `value` is a Buffer.
 Buffer.isBuffer = value => value instanceof Buffer
 
 // Returns true if the encoding is a valid encoding argument & false otherwise
 Buffer.isEncoding = function (encoding) {
   if (!encoding) return false;
   try {
-    TextDecoder(encoding);
+    new TextDecoder(encoding);
   } catch(e) {
     return false;
   }
   return true;
 }
 
 // Gives the actual byte length of a string. encoding defaults to 'utf8'.
 // This is not the same as String.prototype.length since that returns the
 // number of characters in a string.
 Buffer.byteLength = (value, encoding = 'utf8') =>
-  TextEncoder(encoding).encode(value).byteLength
+  new TextEncoder(encoding).encode(value).byteLength
 
 // Direct copy of the nodejs's buffer implementation:
 // https://github.com/joyent/node/blob/b255f4c10a80343f9ce1cee56d0288361429e214/lib/buffer.js#L146-L177
 Buffer.concat = function(list, length) {
   if (!Array.isArray(list))
     throw new TypeError('Usage: Buffer.concat(list[, length])');
 
   if (typeof length === 'undefined') {
@@ -151,17 +151,17 @@ Object.defineProperties(Buffer.prototype
       return view;
     }
   },
   toString: {
     value: function(encoding, start, end) {
       encoding = !!encoding ? (encoding + '').toLowerCase() : 'utf8';
       start = Math.max(0, ~~start);
       end = Math.min(this.length, end === void(0) ? this.length : ~~end);
-      return TextDecoder(encoding).decode(this.subarray(start, end));
+      return new TextDecoder(encoding).decode(this.subarray(start, end));
     }
   },
   toJSON: {
     value: function() {
       return { type: 'Buffer', data: Array.slice(this, 0) };
     }
   },
   get: {
@@ -257,17 +257,17 @@ Object.defineProperties(Buffer.prototype
 
       offset = ~~offset;
 
       // Clamp length if it would overflow buffer, or if its
       // undefined
       if (length == null || length + offset > this.length)
         length = this.length - offset;
 
-      let buffer = TextEncoder(encoding).encode(string);
+      let buffer = new TextEncoder(encoding).encode(string);
       let result = Math.min(buffer.length, length);
       if (buffer.length !== length)
         buffer = buffer.subarray(0, length);
 
       Uint8Array.set(this, buffer, offset);
       return result;
     }
   },
--- a/addon-sdk/source/test/fixtures/chrome-worker/xhr.js
+++ b/addon-sdk/source/test/fixtures/chrome-worker/xhr.js
@@ -1,11 +1,11 @@
 /* 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';
 
-let xhr = XMLHttpRequest();
+let xhr = new XMLHttpRequest();
 xhr.open("GET", "data:text/plain,ok", true);
 xhr.onload = function () {
   postMessage(xhr.responseText);
 };
 xhr.send(null);
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -487,17 +487,17 @@ function sendDragEvent(aEventType, aTarg
 
 /**
  * Creates a custom drag event.
  * @param aEventType The drag event's type.
  * @param aData The event's drag data (optional).
  * @return The drag event.
  */
 function createDragEvent(aEventType, aData) {
-  let dataTransfer = new getContentWindow().DataTransfer("dragstart", false);
+  let dataTransfer = new (getContentWindow()).DataTransfer("dragstart", false);
   dataTransfer.mozSetDataAt("text/x-moz-url", aData, 0);
   let event = getContentDocument().createEvent("DragEvents");
   event.initDragEvent(aEventType, true, true, getContentWindow(), 0, 0, 0, 0, 0,
                       false, false, false, false, 0, null, dataTransfer);
 
   return event;
 }
 
--- a/content/base/test/chrome/test_bug780199.xul
+++ b/content/base/test/chrome/test_bug780199.xul
@@ -34,17 +34,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   function continueTest() {
     // Check that a new page wasn't loaded.
     is(b.contentDocument.documentElement.textContent, "testvalue");
     SimpleTest.finish();
   }
 
   function test() {
     b = document.getElementById("b");
-    var m = MutationObserver(callback);
+    var m = new MutationObserver(callback);
     m.observe(b, { attributes: true, attributeOldValue: true });
     b.contentDocument.documentElement.textContent = "testvalue";
     b.setAttribute("src", b.getAttribute("src"));
   }
 
   ]]>
   </script>
   <browser id="b" src="data:text/plain,initial"/>
--- a/content/media/test/crashtests/868504.html
+++ b/content/media/test/crashtests/868504.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
 <script>
 
 function boom()
 {
-    AudioContext().createBufferSource().playbackRate.linearRampToValueAtTime(0, -1);
+    new AudioContext().createBufferSource().playbackRate.linearRampToValueAtTime(0, -1);
 }
 
 </script>
 </head>
 <body onload="boom();"></body>
 </html>
--- a/content/media/test/crashtests/876024-2.html
+++ b/content/media/test/crashtests/876024-2.html
@@ -1,17 +1,17 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="UTF-8">
 <script>
 
 function boom()
 {
-  var bufferSource = AudioContext().createScriptProcessor().context.createBufferSource();
+  var bufferSource = new AudioContext().createScriptProcessor().context.createBufferSource();
   bufferSource.start(0, 0, 0);
   bufferSource.stop(562949953421313);
 }
 
 </script></head>
 
 <body onload="boom();"></body>
 </html>
--- a/content/media/test/crashtests/876834.html
+++ b/content/media/test/crashtests/876834.html
@@ -1,4 +1,4 @@
 <!DOCTYPE html>
 <script>
-OfflineAudioContext(0, 0, 3229622);
+new OfflineAudioContext(0, 0, 3229622);
 </script>
--- a/content/media/test/crashtests/894104.html
+++ b/content/media/test/crashtests/894104.html
@@ -5,16 +5,16 @@
 <script>
 
 function boom()
 {
   var frame = document.getElementById("f");
   var frameWin = frame.contentWindow;
   frameWin.VTTCue;
   document.body.removeChild(frame);
-  frameWin.VTTCue(0, 1, "Bug 894104").getCueAsHTML();
+  new frameWin.VTTCue(0, 1, "Bug 894104").getCueAsHTML();
 }
 
 </script>
 </head>
 
 <body onload="boom();"><iframe id="f" src="data:text/html,1"></iframe></body>
 </html>
--- a/dom/tests/mochitest/chrome/test_sandbox_bindings.xul
+++ b/dom/tests/mochitest/chrome/test_sandbox_bindings.xul
@@ -103,23 +103,23 @@ https://bugzilla.mozilla.org/show_bug.cg
               "We should claim to have a DONE constant");
         isnot(Object.getOwnPropertyDescriptor(xhr, "prototype"), undefined,
               "We should claim to have 'prototype' property");
       } catch (e) {
         ok(false, "'XMLHttpRequest' shouldn't throw in a sandbox");
       }
       try {
         var xhr = Components.utils.evalInSandbox("new XMLHttpRequest()", sandbox);
-        is("" + xhr, "[object XrayWrapper " + XMLHttpRequest() + "]", "'XMLHttpRequest()' in a sandbox should create an XMLHttpRequest object");
+        is("" + xhr, "[object XrayWrapper " + new XMLHttpRequest() + "]", "'XMLHttpRequest()' in a sandbox should create an XMLHttpRequest object");
       } catch (e) {
         ok(false, "'new XMLHttpRequest()' shouldn't throw in a sandbox (1)");
       }
       try {
         var xhr = Components.utils.evalInSandbox("XMLHttpRequest.prototype.toString = function () { return 'Failed'; }; new XMLHttpRequest();", sandbox);
-        is(xhr.toString(), "[object XrayWrapper " + XMLHttpRequest() + "]", "XMLHttpRequest.prototype.toString in the sandbox should not override the native toString behaviour");
+        is(xhr.toString(), "[object XrayWrapper " + new XMLHttpRequest() + "]", "XMLHttpRequest.prototype.toString in the sandbox should not override the native toString behaviour");
       } catch (e) {
         ok(false, "'new XMLHttpRequest()' shouldn't throw in a sandbox (2)");
       }
 
       try {
         // have to run this test before document.defaultView.XMLHttpRequest
         // gets munged in the sandbox.
         var proto = Components.utils.evalInSandbox("XMLHttpRequest.prototype", sandbox);
@@ -134,18 +134,18 @@ https://bugzilla.mozilla.org/show_bug.cg
            "'dispatchEvent' is not an own property on XMLHttpRequest.prototype; it's on EventTarget.prototype")
       } catch (e) {
         ok(false, "XMLHttpRequest.prototype manipulation via an Xray shouldn't throw" + e);
       }
 
       try {
         Components.utils.evalInSandbox("document.defaultView.XMLHttpRequest = function() {};", sandbox);
         var win = Components.utils.evalInSandbox("document.defaultView", sandbox);
-        var xhr = win.XMLHttpRequest();
-        is("" + xhr, "[object XrayWrapper " + XMLHttpRequest() + "]", "'XMLHttpRequest()' in a sandbox should create an XMLHttpRequest object");
+        var xhr = new win.XMLHttpRequest();
+        is("" + xhr, "[object XrayWrapper " + new XMLHttpRequest() + "]", "'XMLHttpRequest()' in a sandbox should create an XMLHttpRequest object");
       } catch (e) {
         ok(false, "'XMLHttpRequest()' shouldn't throw in a sandbox");
       }
       try {
         var canvas = Components.utils.evalInSandbox("document.createElement('canvas').getContext('2d')", sandbox);
         is(canvas.DRAWWINDOW_DRAW_CARET, CanvasRenderingContext2D.DRAWWINDOW_DRAW_CARET, "Constants should be defined on DOM objects in a sandbox");
       } catch (e) {
         ok(false, "'document.createElement('canvas').getContext('2D')' shouldn't throw in a sandbox");
--- a/dom/tests/mochitest/chrome/test_xray_event_constructor.xul
+++ b/dom/tests/mochitest/chrome/test_xray_event_constructor.xul
@@ -20,16 +20,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript">
   <![CDATA[
   /** Test for Bug 861493 **/
    SimpleTest.waitForExplicitFinish();
 
    addLoadEvent(function() {
      ok(Components.utils.isXrayWrapper($("t").contentWindow),
         "Should have xray");
-     var e = new $("t").contentWindow.Event("test", { bubbles: true });
+     var e = new ($("t").contentWindow).Event("test", { bubbles: true });
      is(e.bubbles, true, "Dictionary should have worked");
      SimpleTest.finish();
    })
 
   ]]>
   </script>
 </window>
--- a/dom/xbl/test/file_bug821850.xhtml
+++ b/dom/xbl/test/file_bug821850.xhtml
@@ -219,20 +219,22 @@ https://bugzilla.mozilla.org/show_bug.cg
                                    .QueryInterface(Ci.nsIDocShell)
                                    .chromeEventHandler.ownerDocument.defaultView;
 
       // Untrusted events should not trigger event handlers without
       // exposeToUntrustedContent=true.
       window.triggeredTrustedHandler = false;
       var untrustedEvent = new CustomEvent('testtrusted');
       ok(!untrustedEvent.isTrusted, "Created an untrusted event");
+      is(untrustedEvent.type, 'testtrusted', "Constructor should see type");
       bound.dispatchEvent(untrustedEvent);
       ok(!window.triggeredTrustedHandler, "untrusted events should not trigger trusted handler");
       var trustedEvent = new chromeWin.CustomEvent('testtrusted');
       ok(trustedEvent.isTrusted, "Created a trusted event");
+      is(trustedEvent.type, 'testtrusted', "Wrapped constructor should see type");
       SpecialPowers.wrap(bound).dispatchEvent(trustedEvent);
       ok(window.triggeredTrustedHandler, "trusted events should trigger trusted handler");
 
       //
       // We check key events as well, since they're implemented differently.
       //
       // NB: We don't check isTrusted on the events we create here, because
       // according to smaug, old-style event initialization doesn't mark the
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -121,34 +121,23 @@ function wrapPrivileged(obj) {
         // Wrap exceptions and re-throw them.
         throw wrapIfUnwrapped(e);
       }
     };
     var constructTrap = function() {
       // The arguments may or may not be wrappers. Unwrap them if necessary.
       var unwrappedArgs = Array.prototype.slice.call(arguments).map(unwrapIfWrapped);
 
-      // Constructors are tricky, because we can't easily call apply on them.
-      // As a workaround, we create a wrapper constructor with the same
-      // |prototype| property. ES semantics dictate that the return value from
-      // |new| is the return value of the |new|-ed function i.f.f. the returned
-      // value is an object. We can thus mimic the behavior of |new|-ing the
-      // underlying constructor just be passing along its return value in our
-      // constructor.
-      var FakeConstructor = function() {
-        try {
-          return doApply(obj, this, unwrappedArgs);
-        } catch (e) {
-          // Wrap exceptions and re-throw them.
-          throw wrapIfUnwrapped(e);
-        }
-      };
-      FakeConstructor.prototype = obj.prototype;
-
-      return wrapPrivileged(new FakeConstructor());
+      // We want to invoke "obj" as a constructor, but using unwrappedArgs as
+      // the arguments.  Make sure to wrap and re-throw exceptions!
+      try {
+        return wrapPrivileged(new obj(...unwrappedArgs));
+      } catch (e) {
+        throw wrapIfUnwrapped(e);
+      }
     };
 
     return Proxy.createFunction(handler, callTrap, constructTrap);
   }
 
   // Otherwise, just make a regular object proxy.
   return Proxy.create(handler);
 };
--- a/toolkit/content/aboutTelemetry.js
+++ b/toolkit/content/aboutTelemetry.js
@@ -373,17 +373,17 @@ function SymbolicationRequest_handleSymb
 SymbolicationRequest.prototype.fetchSymbols =
 function SymbolicationRequest_fetchSymbols() {
   let symbolServerURI =
     getPref(PREF_SYMBOL_SERVER_URI, DEFAULT_SYMBOL_SERVER_URI);
   let request = {"memoryMap" : this.memoryMap, "stacks" : this.stacks,
                  "version" : 3};
   let requestJSON = JSON.stringify(request);
 
-  this.symbolRequest = XMLHttpRequest();
+  this.symbolRequest = new XMLHttpRequest();
   this.symbolRequest.open("POST", symbolServerURI, true);
   this.symbolRequest.setRequestHeader("Content-type", "application/json");
   this.symbolRequest.setRequestHeader("Content-length",
                                       requestJSON.length);
   this.symbolRequest.setRequestHeader("Connection", "close");
   this.symbolRequest.onreadystatechange = this.handleSymbolResponse.bind(this);
   this.symbolRequest.send(requestJSON);
 }
--- a/toolkit/devtools/server/actors/highlighter.js
+++ b/toolkit/devtools/server/actors/highlighter.js
@@ -406,17 +406,17 @@ BoxModelHighlighter.prototype = {
         this.chromeLayoutHelper.scrollIntoViewIfNeeded(node);
       }
     }
   },
 
   _trackMutations: function() {
     if (this.currentNode) {
       let win = this.currentNode.ownerDocument.defaultView;
-      this.currentNodeObserver = win.MutationObserver(this._update);
+      this.currentNodeObserver = new win.MutationObserver(this._update);
       this.currentNodeObserver.observe(this.currentNode, {attributes: true});
     }
   },
 
   _untrackMutations: function() {
     if (this.currentNode) {
       if (this.currentNodeObserver) {
         // The following may fail with a "can't access dead object" exception
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -978,17 +978,17 @@ var WalkerActor = protocol.ActorClass({
   /**
    * Watch the given document node for mutations using the DOM observer
    * API.
    */
   _watchDocument: function(actor) {
     let node = actor.rawNode;
     // Create the observer on the node's actor.  The node will make sure
     // the observer is cleaned up when the actor is released.
-    actor.observer = actor.rawNode.defaultView.MutationObserver(this.onMutations);
+    actor.observer = new actor.rawNode.defaultView.MutationObserver(this.onMutations);
     actor.observer.observe(node, {
       attributes: true,
       characterData: true,
       childList: true,
       subtree: true
     });
   },
 
--- a/webapprt/Startup.jsm
+++ b/webapprt/Startup.jsm
@@ -42,17 +42,17 @@ function isFirstRunOrUpdate() {
     return true;
   }
 
   return false;
 }
 
 function writeFile(aPath, aData) {
   return Task.spawn(function() {
-    let data = TextEncoder().encode(aData);
+    let data = new TextEncoder().encode(aData);
     yield OS.File.writeAtomic(aPath, data, { tmpPath: aPath + ".tmp" });
   });
 }
 
 function createBrandingFiles() {
   return Task.spawn(function() {
     let manifest = WebappRT.localeManifest;
     let name = WebappRT.localeManifest.name;