merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 14 Sep 2015 13:58:37 +0200
changeset 296568 fba4b0cd3823975949765acc0b16b964d1712b75
parent 296554 d885df6c27c10f5fba28b9523f00b5c416b476b2 (current diff)
parent 296567 a92e195a3362fa097b6fb8842b44de941e8f333c (diff)
child 296577 93ab4d92079dc19bf60f00b752c968936ab7a888
child 296629 339d71ae5098204393ce1c581f6874a6e72633a6
child 296749 7964e9224244d276a39cc76f33f26d70d289f55c
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.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
merge mozilla-inbound to mozilla-central a=merge
--- a/browser/components/customizableui/test/browser_880164_customization_context_menus.js
+++ b/browser/components/customizableui/test/browser_880164_customization_context_menus.js
@@ -271,16 +271,18 @@ add_task(function() {
   yield endCustomizing();
 });
 
 // Test the toolbarbutton panel context menu in customization mode
 // without opening the panel before customization mode
 add_task(function() {
   this.otherWin = yield openAndLoadWindow(null, true);
 
+  yield new Promise(resolve => waitForFocus(resolve, this.otherWin));
+
   yield startCustomizing(this.otherWin);
 
   let contextMenu = this.otherWin.document.getElementById("customizationPanelItemContextMenu");
   let shownPromise = popupShown(contextMenu);
   let newWindowButton = this.otherWin.document.getElementById("wrapper-new-window-button");
   EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2}, this.otherWin);
   yield shownPromise;
 
@@ -293,16 +295,18 @@ add_task(function() {
   checkContextMenu(contextMenu, expectedEntries, this.otherWin);
 
   let hiddenContextPromise = popupHidden(contextMenu);
   contextMenu.hidePopup();
   yield hiddenContextPromise;
   yield endCustomizing(this.otherWin);
   yield promiseWindowClosed(this.otherWin);
   this.otherWin = null;
+
+  yield new Promise(resolve => waitForFocus(resolve, window));
 });
 
 // Bug 945191 - Combined buttons show wrong context menu options
 // when they are in the toolbar.
 add_task(function() {
   yield startCustomizing();
   let contextMenu = document.getElementById("customizationPanelItemContextMenu");
   let shownPromise = popupShown(contextMenu);
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_change_policy_redirect.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script>
+    parent.postMessage('childLoadComplete', 'http://mochi.test:8888');
+  </script>
+</head>
+<body>
+</body>
+</html>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -249,16 +249,18 @@ support-files =
   referrer_helper.js
   referrer_testserver.sjs
   script_postmessages_fileList.js
   iframe_postMessages.html
   test_performance_observer.js
   performance_observer.html
   test_anonymousContent_style_csp.html^headers^
   file_explicit_user_agent.sjs
+  referrer_change_server.sjs
+  file_change_policy_redirect.html
 
 [test_anonymousContent_api.html]
 [test_anonymousContent_append_after_reflow.html]
 [test_anonymousContent_insert.html]
 [test_anonymousContent_manipulate_content.html]
 [test_anonymousContent_style_csp.html]
 [test_applet_alternate_content.html]
 [test_appname_override.html]
@@ -840,8 +842,10 @@ skip-if = buildapp == 'mulet' || buildap
 [test_window_element_enumeration.html]
 [test_referrer_redirect.html]
 [test_postMessages.html]
 support-files = worker_postMessages.js
 [test_window_proto.html]
 [test_frameLoader_switchProcess.html]
 skip-if = e10s || os != 'linux' || buildapp != 'browser'
 [test_explicit_user_agent.html]
+[test_change_policy.html]
+skip-if = buildapp == 'b2g' #no ssl support
new file mode 100644
--- /dev/null
+++ b/dom/base/test/referrer_change_server.sjs
@@ -0,0 +1,139 @@
+var BASE_URL = 'example.com/tests/dom/base/test/referrer_change_server.sjs';
+
+function createTestUrl(aPolicy, aAction, aName) {
+  return 'http://' + BASE_URL + '?' +
+         'action=' + aAction + '&' +
+         'policy=' + aPolicy + '&' +
+         'name=' + aName + '&' +
+         'type=link';
+}
+
+function createTest(aMetaPolicy, aReferrerPolicy, aName) {
+  return '<!DOCTYPE HTML>\n\
+         <html>'+
+           '<meta name="referrer" content="' + aMetaPolicy + '">' +
+           '<body>' +
+             '<a href="' + createTestUrl(aReferrerPolicy, 'test', aName + aReferrerPolicy) + '" id="link">' + aReferrerPolicy + '</a>' +
+             '<script>' +
+
+               // LOAD EVENT (of the test)
+               // fires when the page is loaded, then click link
+               // first change meta referrer, then click link
+               'window.addEventListener("load", function() {\n\
+                 document.getElementsByName("referrer")[0].content = "'+aReferrerPolicy+'";\n\
+                 document.getElementById("link").click();\n\
+               }.bind(window), false);' +
+
+             '</script>\n\
+           </body>\n\
+         </html>';
+}
+
+function createTest2(aMetaPolicy, aReferrerPolicy, aName) {
+  return '<!DOCTYPE HTML>\n\
+         <html>'+
+           '<meta name="referrer" content="' + aMetaPolicy + '">' +
+           '<body>' +
+             '<a href="' + createTestUrl(aReferrerPolicy, 'test', aName + aReferrerPolicy) + '" id="link">' + aReferrerPolicy + '</a>' +
+             '<script>' +
+
+               // LOAD EVENT (of the test)
+               // fires when the page is loaded, then click link
+               // first change meta referrer, then click link
+               'window.addEventListener("load", function() {\n\
+                 document.getElementsByName("referrer")[0].setAttribute("content", "'+aReferrerPolicy+'");\n\
+                 document.getElementById("link").click();\n\
+               }.bind(window), false);' +
+
+             '</script>\n\
+           </body>\n\
+         </html>';
+}
+
+function handleRequest(request, response) {
+  var sharedKey = 'referrer_change_server.sjs';
+  var params = request.queryString.split('&');
+  var action = params[0].split('=')[1];
+
+  if (action === 'resetState') {
+    var state = getSharedState(sharedKey);
+    state = {};
+    setSharedState(sharedKey, JSON.stringify(state));
+    response.write("");
+    return;
+  } else if (action === 'test') {
+    // ?action=test&policy=origin&name=name
+    var policy = params[1].split('=')[1];
+    var name = params[2].split('=')[1];
+    var type = params[3].split('=')[1];
+    var result = getSharedState(sharedKey);
+
+    if (result === '') {
+      result = {};
+    } else {
+      result = JSON.parse(result);
+    }
+
+    if (!result["tests"]) {
+      result["tests"] = {};
+    }
+
+    var referrerLevel = "none";
+    var test = {}
+    if (request.hasHeader('Referer')) {
+        let referrer = request.getHeader('Referer');
+        if (referrer.indexOf("referrer_change_server") > 0) {
+          referrerLevel = "full";
+        } else if (referrer == "http://mochi.test:8888") {
+          referrerLevel = "origin";
+        }
+      test.referrer = request.getHeader('Referer');
+    } else {
+      test.referrer = '';
+    }
+    test.policy = referrerLevel;
+    test.expected = policy;
+
+    result["tests"][name] = test;
+
+    setSharedState(sharedKey, JSON.stringify(result));
+
+    // forward link click to redirect URL to finish test
+    if (type === 'link') {
+      var loc = 'https://example.com/tests/dom/base/test/file_change_policy_redirect.html';
+      response.setStatusLine('1.1', 302, 'Found');
+      response.setHeader('Location', loc, false);
+    }
+
+    return;
+  } else if (action === 'get-test-results') {
+    // ?action=get-result
+    response.setHeader('Cache-Control', 'no-cache', false);
+    response.setHeader('Content-Type', 'text/plain', false);
+    response.write(getSharedState(sharedKey));
+    return;
+  } else if (action === 'generate-policy-test') {
+    // ?action=generate-policy-test&referrerPolicy=b64-encoded-string&name=name&newPolicy=b64-encoded-string
+    response.setHeader('Cache-Control', 'no-cache', false);
+    response.setHeader('Content-Type', 'text/html; charset=utf-8', false);
+    var referrerPolicy = unescape(params[1].split('=')[1]);
+    var name = unescape(params[2].split('=')[1]);
+    var newPolicy = params[3].split('=')[1];
+
+    response.write(createTest(referrerPolicy, newPolicy, name));
+    return;
+  } else if (action === 'generate-policy-test2') {
+    // ?action=generate-policy-test2&referrerPolicy=b64-encoded-string&name=name&newPolicy=b64-encoded-string
+    response.setHeader('Cache-Control', 'no-cache', false);
+    response.setHeader('Content-Type', 'text/html; charset=utf-8', false);
+    var referrerPolicy = unescape(params[1].split('=')[1]);
+    var name = unescape(params[2].split('=')[1]);
+    var newPolicy = params[3].split('=')[1];
+
+    response.write(createTest2(referrerPolicy, newPolicy, name));
+    return;
+  } else {
+    response.write("I don't know action "+action);
+    return;
+  }
+}
--- a/dom/base/test/test_bug682592.html
+++ b/dom/base/test/test_bug682592.html
@@ -34,16 +34,17 @@ https://bugzilla.mozilla.org/show_bug.cg
    So, instead of many diferent reftests, this mochitest implements a
    reftest-like. It creates reference text fragments in reference iframe, test
    text fragments in test iframe, and compare the documents. Then, it reloads
    test iframe. Reference iframe does not need to be reloaded between tests.
    It's ok (and maybe, desired) to keep bidi always enabled in that document. 
 */
 
 SimpleTest.waitForExplicitFinish();
+SimpleTest.requestLongerTimeout(2);
 if (navigator.platform.startsWith("Linux arm")) { /* bugs 982875, 999429 */
   SimpleTest.expectAssertions(0, 4);
 }
 
 var refFrame = document.getElementById("iframe-ref")
 var testFrame = document.getElementById("iframe-test");
 
 refFrame.addEventListener("load", function() {
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_change_policy.html
@@ -0,0 +1,129 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test policies for Bug 1101288</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+<!--
+This checks if the right policies are applied from a given string when the policy is changed after the document has been loaded.
+https://bugzilla.mozilla.org/show_bug.cgi?id=1101288
+-->
+<script type="application/javascript;version=1.7">
+
+SimpleTest.waitForExplicitFinish();
+var advance = function() { tests.next(); };
+
+/**
+ * Listen for notifications from the child.
+ * These are sent in case of error, or when the loads we await have completed.
+ */
+window.addEventListener("message", function(event) {
+  if (event.data == "childLoadComplete") {
+    // all loads happen, continue the test.
+    advance();
+  }
+});
+
+/**
+ * helper to perform an XHR.
+ */
+function doXHR(aUrl, onSuccess, onFail) {
+  var xhr = new XMLHttpRequest();
+  xhr.responseType = "json";
+  xhr.onload = function () {
+    onSuccess(xhr);
+  };
+  xhr.onerror = function () {
+    onFail(xhr);
+  };
+  xhr.open('GET', aUrl, true);
+  xhr.send(null);
+}
+
+/**
+ * Grabs the results via XHR and passes to checker.
+ */
+function checkIndividualResults(aTestname, aExpectedReferrer, aName) {
+  doXHR('/tests/dom/base/test/referrer_change_server.sjs?action=get-test-results',
+        function(xhr) {
+          var results = xhr.response;
+          info(JSON.stringify(xhr.response));
+
+          for (i in aName) {
+            ok(aName[i] in results.tests, aName[i] + " tests have to be performed.");
+            is(results.tests[aName[i]].policy, aExpectedReferrer[i], aTestname + ' --- ' + results.tests[aName[i]].policy + ' (' + results.tests[aName[i]].referrer + ')');
+          }
+          advance();
+        },
+        function(xhr) {
+          ok(false, "Can't get results from the counter server.");
+          SimpleTest.finish();
+        });
+}
+
+function resetState() {
+  doXHR('/tests/dom/base/test/referrer_change_server.sjs?action=resetState',
+    advance,
+    function(xhr) {
+      ok(false, "error in reset state");
+      SimpleTest.finish();
+    });
+}
+
+
+/**
+ * This is the main test routine -- serialized by use of a generator.
+ * It resets the counter, then performs two tests in sequence using
+ * the same iframe.
+ */
+var tests = (function() {
+  var iframe = document.getElementById("testframe");
+  var sjs = "/tests/dom/base/test/referrer_change_server.sjs?action=generate-policy-test";
+
+  yield resetState();
+  var name = "no-referrer-unsafe-url";
+  yield iframe.src = sjs + "&policy=" + escape('no-referrer') + "&name=" + name + "&newPolicy=" + escape('unsafe-url');
+  yield checkIndividualResults("unsafe-url (changed) with no-referrer first", ["full"], [name+'unsafe-url']);
+
+  yield resetState();
+  var name = "origin-no-referrer";
+  yield iframe.src = sjs + "&policy=" + escape('origin') + "&name=" + name + "&newPolicy=" + escape('no-referrer');
+  yield checkIndividualResults("no-referrer (changed) with origin first", ["none"], [name+'no-referrer']);
+
+  yield resetState();
+  var name = "unsafe-url-no-referrer";
+  yield iframe.src = sjs + "&policy=" + escape('unsafe-url') + "&name=" + name + "&newPolicy=" + escape('no-referrer');
+  yield checkIndividualResults("no-referrer (changed) with unsafe-url first", ["none"], [name+'no-referrer']);
+
+  sjs = "/tests/dom/base/test/referrer_change_server.sjs?action=generate-policy-test2";
+
+  yield resetState();
+  var name = "no-referrer-unsafe-url";
+  yield iframe.src = sjs + "&policy=" + escape('no-referrer') + "&name=" + name + "&newPolicy=" + escape('unsafe-url');
+  yield checkIndividualResults("unsafe-url (changed) with no-referrer first", ["full"], [name+'unsafe-url']);
+
+  yield resetState();
+  var name = "origin-no-referrer";
+  yield iframe.src = sjs + "&policy=" + escape('origin') + "&name=" + name + "&newPolicy=" + escape('no-referrer');
+  yield checkIndividualResults("no-referrer (changed) with origin first", ["none"], [name+'no-referrer']);
+
+  yield resetState();
+  var name = "unsafe-url-no-referrer";
+  yield iframe.src = sjs + "&policy=" + escape('unsafe-url') + "&name=" + name + "&newPolicy=" + escape('no-referrer');
+  yield checkIndividualResults("no-referrer (changed) with unsafe-url first", ["none"], [name+'no-referrer']);
+
+  // complete.  Be sure to yield so we don't call this twice.
+  yield SimpleTest.finish();
+})();
+
+</script>
+</head>
+
+<body onload="tests.next();">
+  <iframe id="testframe"></iframe>
+
+</body>
+</html>
+
--- a/dom/filesystem/moz.build
+++ b/dom/filesystem/moz.build
@@ -36,11 +36,8 @@ IPDL_SOURCES += [
     'PFileSystemRequest.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
-
-if CONFIG['CLANG_CXX']:
-    ALLOW_COMPILER_WARNINGS = True
--- a/dom/html/HTMLMetaElement.cpp
+++ b/dom/html/HTMLMetaElement.cpp
@@ -43,33 +43,57 @@ HTMLMetaElement::GetItemValueText(DOMStr
 }
 
 void
 HTMLMetaElement::SetItemValueText(const nsAString& aValue)
 {
   SetContent(aValue);
 }
 
+nsresult
+HTMLMetaElement::SetMetaReferrer(nsIDocument* aDocument)
+{
+  if (!aDocument ||
+      !AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, nsGkAtoms::referrer, eIgnoreCase)) {
+    return NS_OK;
+  }
+  nsAutoString content;
+  nsresult rv = GetContent(content);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  Element* headElt = aDocument->GetHeadElement();
+  if (headElt && nsContentUtils::ContentIsDescendantOf(this, headElt)) {
+      content = nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(content);
+      aDocument->SetHeaderData(nsGkAtoms::referrer, content);
+  }
+  return NS_OK;
+}
 
 nsresult
 HTMLMetaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                               const nsAttrValue* aValue, bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
+    nsIDocument *document = GetUncomposedDoc();
     if (aName == nsGkAtoms::content) {
-      nsIDocument *document = GetUncomposedDoc();
       if (document && AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
                                   nsGkAtoms::viewport, eIgnoreCase)) {
         nsAutoString content;
         nsresult rv = GetContent(content);
         NS_ENSURE_SUCCESS(rv, rv);
         nsContentUtils::ProcessViewportInfo(document, content);
       }
       CreateAndDispatchEvent(document, NS_LITERAL_STRING("DOMMetaChanged"));
     }
+    // Update referrer policy when it got changed from JS
+    nsresult rv = SetMetaReferrer(document);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
                                             aNotify);
 }
 
 nsresult
 HTMLMetaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
@@ -82,29 +106,21 @@ HTMLMetaElement::BindToTree(nsIDocument*
   NS_ENSURE_SUCCESS(rv, rv);
   if (aDocument &&
       AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, nsGkAtoms::viewport, eIgnoreCase)) {
     nsAutoString content;
     rv = GetContent(content);
     NS_ENSURE_SUCCESS(rv, rv);
     nsContentUtils::ProcessViewportInfo(aDocument, content);
   }
-  if (aDocument &&
-      AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, nsGkAtoms::referrer, eIgnoreCase)) {
-    nsAutoString content;
-    rv = GetContent(content);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Referrer Policy spec requires a <meta name="referrer" tag to be in the
-    // <head> element.
-    Element* headElt = aDocument->GetHeadElement();
-    if (headElt && nsContentUtils::ContentIsDescendantOf(this, headElt)) {
-      content = nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(content);
-      aDocument->SetHeaderData(nsGkAtoms::referrer, content);
-    }
+  // Referrer Policy spec requires a <meta name="referrer" tag to be in the
+  // <head> element.
+  rv = SetMetaReferrer(aDocument);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
   CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMMetaAdded"));
   return rv;
 }
 
 void
 HTMLMetaElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
--- a/dom/html/HTMLMetaElement.h
+++ b/dom/html/HTMLMetaElement.h
@@ -62,14 +62,17 @@ public:
 
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
 protected:
   virtual ~HTMLMetaElement();
 
   virtual void GetItemValueText(DOMString& text) override;
   virtual void SetItemValueText(const nsAString& text) override;
+
+private:
+  nsresult SetMetaReferrer(nsIDocument* aDocument);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_HTMLMetaElement_h
--- a/dom/media/mediasink/DecodedStream.cpp
+++ b/dom/media/mediasink/DecodedStream.cpp
@@ -188,67 +188,30 @@ void
 DecodedStreamData::SetPlaying(bool aPlaying)
 {
   if (mPlaying != aPlaying) {
     mPlaying = aPlaying;
     UpdateStreamBlocking(mStream, !mPlaying);
   }
 }
 
-class OutputStreamListener : public MediaStreamListener {
-  typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
-public:
-  explicit OutputStreamListener(OutputStreamData* aOwner) : mOwner(aOwner) {}
-
-  void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
-  {
-    if (event == EVENT_FINISHED) {
-      nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
-        this, &OutputStreamListener::DoNotifyFinished);
-      aGraph->DispatchToMainThreadAfterStreamStateUpdate(r.forget());
-    }
-  }
-
-  void Forget()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    mOwner = nullptr;
-  }
-
-private:
-  void DoNotifyFinished()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    if (mOwner) {
-      // Remove the finished stream so it won't block the decoded stream.
-      mOwner->Remove();
-    }
-  }
-
-  // Main thread only
-  OutputStreamData* mOwner;
-};
-
 OutputStreamData::~OutputStreamData()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  mListener->Forget();
   // Break the connection to the input stream if necessary.
   if (mPort) {
     mPort->Destroy();
   }
 }
 
 void
 OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream)
 {
   mOwner = aOwner;
   mStream = aStream;
-  mListener = new OutputStreamListener(this);
-  aStream->AddListener(mListener);
 }
 
 void
 OutputStreamData::Connect(MediaStream* aStream)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mPort, "Already connected?");
   MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream.");
@@ -277,23 +240,16 @@ OutputStreamData::Disconnect()
     mPort = nullptr;
   }
   // Block the stream again. It will be unlocked when connecting
   // to the input stream.
   mStream->ChangeExplicitBlockerCount(1);
   return true;
 }
 
-void
-OutputStreamData::Remove()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mOwner->Remove(mStream);
-}
-
 MediaStreamGraph*
 OutputStreamData::Graph() const
 {
   return mStream->Graph();
 }
 
 void
 OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
--- a/dom/media/mediasink/DecodedStream.h
+++ b/dom/media/mediasink/DecodedStream.h
@@ -21,55 +21,46 @@
 namespace mozilla {
 
 class DecodedStream;
 class DecodedStreamData;
 class MediaData;
 class MediaInputPort;
 class MediaStream;
 class MediaStreamGraph;
-class OutputStreamListener;
 class OutputStreamManager;
 class ProcessedMediaStream;
 class TimeStamp;
 
 template <class T> class MediaQueue;
 
-namespace layers {
-class Image;
-} // namespace layers
-
 class OutputStreamData {
 public:
   ~OutputStreamData();
   void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream);
 
   // Connect mStream to the input stream.
   void Connect(MediaStream* aStream);
   // Disconnect mStream from its input stream.
   // Return false is mStream is already destroyed, otherwise true.
   bool Disconnect();
-  // Called by OutputStreamListener to remove self from the output streams
-  // managed by OutputStreamManager.
-  void Remove();
   // Return true if aStream points to the same object as mStream.
   // Used by OutputStreamManager to remove an output stream.
   bool Equals(MediaStream* aStream)
   {
     return mStream == aStream;
   }
   // Return the graph mStream belongs to.
   MediaStreamGraph* Graph() const;
 
 private:
   OutputStreamManager* mOwner;
   nsRefPtr<ProcessedMediaStream> mStream;
   // mPort connects our mStream to an input stream.
   nsRefPtr<MediaInputPort> mPort;
-  nsRefPtr<OutputStreamListener> mListener;
 };
 
 class OutputStreamManager {
 public:
   // Add the output stream to the collection.
   void Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
   // Remove the output stream from the collection.
   void Remove(MediaStream* aStream);
--- a/js/src/jit-test/tests/ion/bug1185957.js
+++ b/js/src/jit-test/tests/ion/bug1185957.js
@@ -6,10 +6,13 @@ var test = `
 class test {
     constructor() {};
 }
 (function() {
     test()
 })();
 `;
 
+// Throw, even if we cannot run the test
 if (classesEnabled())
     eval(test);
+else
+    throw new TypeError();
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -120,17 +120,17 @@ ResolvePath(JSContext* cx, HandleString 
 static JSObject*
 FileAsTypedArray(JSContext* cx, const char* pathname)
 {
     FILE* file = fopen(pathname, "rb");
     if (!file) {
         JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
         return nullptr;
     }
-    AutoCloseInputFile autoClose(file);
+    AutoCloseFile autoClose(file);
 
     RootedObject obj(cx);
     if (fseek(file, 0, SEEK_END) != 0) {
         JS_ReportError(cx, "can't seek end of %s", pathname);
     } else {
         size_t len = ftell(file);
         if (fseek(file, 0, SEEK_SET) != 0) {
             JS_ReportError(cx, "can't seek start of %s", pathname);
@@ -206,16 +206,60 @@ osfile_readFile(JSContext* cx, unsigned 
 
 static bool
 osfile_readRelativeToScript(JSContext* cx, unsigned argc, Value* vp)
 {
     return ReadFile(cx, argc, vp, true);
 }
 
 static bool
+osfile_writeTypedArrayToFile(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() != 2 ||
+        !args[0].isString() ||
+        !args[1].isObject() ||
+        !args[1].toObject().is<TypedArrayObject>())
+    {
+        JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
+                             JSSMSG_INVALID_ARGS, "writeTypedArrayToFile");
+        return false;
+    }
+
+    RootedString givenPath(cx, args[0].toString());
+    RootedString str(cx, ResolvePath(cx, givenPath, RootRelative));
+    if (!str)
+        return false;
+
+    JSAutoByteString filename(cx, str);
+    if (!filename)
+        return false;
+
+    FILE* file = fopen(filename.ptr(), "wb");
+    if (!file) {
+        JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
+        return false;
+    }
+    AutoCloseFile autoClose(file);
+
+    TypedArrayObject* obj = &args[1].toObject().as<TypedArrayObject>();
+
+    if (fwrite(obj->viewData(), obj->bytesPerElement(), obj->length(), file) != obj->length() ||
+        !autoClose.release())
+    {
+        JS_ReportError(cx, "can't write %s", filename.ptr());
+        return false;
+    }
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
 Redirect(JSContext* cx, FILE* fp, HandleString relFilename)
 {
     RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
     if (!filename)
         return false;
     JSAutoByteString filenameABS(cx, filename);
     if (!filenameABS)
         return false;
@@ -266,16 +310,20 @@ static const JSFunctionSpecWithHelp osfi
 "readRelativeToScript(filename, [\"binary\"])",
 "  Read filename into returned string. Filename is relative to the directory\n"
 "  containing the current script."),
 
     JS_FS_HELP_END
 };
 
 static const JSFunctionSpecWithHelp osfile_unsafe_functions[] = {
+    JS_FN_HELP("writeTypedArrayToFile", osfile_writeTypedArrayToFile, 2, 0,
+"writeTypedArrayToFile(filename, data)",
+"  Write the contents of a typed array to the named file."),
+
     JS_FN_HELP("redirect", osfile_redirect, 2, 0,
 "redirect(stdoutFilename[, stderrFilename])",
 "  Redirect stdout and/or stderr to the named file. Pass undefined to avoid\n"
 "   redirecting. Filenames are relative to the current working directory."),
 
     JS_FS_HELP_END
 };
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -568,17 +568,17 @@ Process(JSContext* cx, const char* filen
     } else {
         file = fopen(filename, "r");
         if (!file) {
             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
                                  JSSMSG_CANT_OPEN, filename, strerror(errno));
             return;
         }
     }
-    AutoCloseInputFile autoClose(file);
+    AutoCloseFile autoClose(file);
 
     if (!forceTTY && !isatty(fileno(file))) {
         // It's not interactive - just execute it.
         RunFile(cx, filename, file, compileOnly);
     } else {
         // It's an interactive filehandle; drop into read-eval-print loop.
         ReadEvalPrintLoop(cx, file, gOutFile, compileOnly);
     }
@@ -659,17 +659,17 @@ CreateMappedArrayBuffer(JSContext* cx, u
     }
 
     FILE* file = fopen(filename.ptr(), "r");
     if (!file) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
                              JSSMSG_CANT_OPEN, filename.ptr(), strerror(errno));
         return false;
     }
-    AutoCloseInputFile autoClose(file);
+    AutoCloseFile autoClose(file);
 
     if (!sizeGiven) {
         struct stat st;
         if (fstat(fileno(file), &st) < 0) {
             JS_ReportError(cx, "Unable to stat file");
             return false;
         }
         if (st.st_size < off_t(offset)) {
@@ -1289,17 +1289,17 @@ js::shell::FileAsString(JSContext* cx, c
     size_t len, cc;
     char* buf;
 
     file = fopen(pathname, "rb");
     if (!file) {
         JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
         return nullptr;
     }
-    AutoCloseInputFile autoClose(file);
+    AutoCloseFile autoClose(file);
 
     if (fseek(file, 0, SEEK_END) != 0) {
         JS_ReportError(cx, "can't seek end of %s", pathname);
     } else {
         len = ftell(file);
         if (fseek(file, 0, SEEK_SET) != 0) {
             JS_ReportError(cx, "can't seek start of %s", pathname);
         } else {
--- a/js/src/shell/jsshell.h
+++ b/js/src/shell/jsshell.h
@@ -1,8 +1,14 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
 #ifndef jsshell_js_h
 #define jsshell_js_h
 
 #include "jsapi.h"
 
 namespace js {
 namespace shell {
 
@@ -18,24 +24,30 @@ const JSErrorFormatString*
 my_GetErrorMessage(void* userRef, const unsigned errorNumber);
 
 static void
 my_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report);
 
 JSString*
 FileAsString(JSContext* cx, const char* pathname);
 
-class AutoCloseInputFile
+class AutoCloseFile
 {
   private:
     FILE* f_;
   public:
-    explicit AutoCloseInputFile(FILE* f) : f_(f) {}
-    ~AutoCloseInputFile() {
-        if (f_ && f_ != stdin)
-            fclose(f_);
+    explicit AutoCloseFile(FILE* f) : f_(f) {}
+    ~AutoCloseFile() {
+        (void) release();
+    }
+    bool release() {
+        bool success = true;
+        if (f_ && f_ != stdin && f_ != stdout && f_ != stderr)
+            success = !fclose(f_);
+        f_ = nullptr;
+        return success;
     }
 };
 
 } /* namespace shell */
 } /* namespace js */
 
 #endif
--- a/mozglue/misc/TimeStamp.h
+++ b/mozglue/misc/TimeStamp.h
@@ -499,23 +499,37 @@ public:
   {
     TimeStamp result = *this;
     result -= aOther;
     return result;
   }
   TimeStamp& operator+=(const TimeDuration& aOther)
   {
     MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
-    mValue += aOther.mValue;
+    TimeStampValue value = mValue + aOther.mValue;
+    // Check for underflow.
+    // (We don't check for overflow because it's not obvious what the error
+    //  behavior should be in that case.)
+    if (aOther.mValue < 0 && value > mValue) {
+      value = 0;
+    }
+    mValue = value;
     return *this;
   }
   TimeStamp& operator-=(const TimeDuration& aOther)
   {
     MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
-    mValue -= aOther.mValue;
+    TimeStampValue value = mValue - aOther.mValue;
+    // Check for underflow.
+    // (We don't check for overflow because it's not obvious what the error
+    //  behavior should be in that case.)
+    if (aOther.mValue > 0 && value > mValue) {
+      value = 0;
+    }
+    mValue = value;
     return *this;
   }
 
   bool operator<(const TimeStamp& aOther) const
   {
     MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
     MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
     return mValue < aOther.mValue;
--- a/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_editing.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_editing.js
@@ -27,18 +27,19 @@ function synthesizeDblClickOnCell(aTree,
   let rect = tbo.getCoordsForCellItem(row, aTree.columns[column], "text");
   let x = rect.x + rect.width / 2;
   let y = rect.y + rect.height / 2;
   // Simulate the double click.
   EventUtils.synthesizeMouse(aTree.body, x, y, { clickCount: 2 },
                              aTree.ownerDocument.defaultView);
 }
 
-function togglePasswords() {
+function* togglePasswords() {
   pwmgrdlg.document.querySelector("#togglePasswords").doCommand();
+  yield new Promise(resolve => waitForFocus(resolve, pwmgrdlg));
 }
 
 function* editUsernamePromises(site, oldUsername, newUsername) {
   is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login found");
   let login = Services.logins.findLogins({}, site, "", "")[0];
   is(login.username, oldUsername, "Correct username saved");
   is(getUsername(0), oldUsername, "Correct username shown");
   synthesizeDblClickOnCell(signonsTree, 1, 0);
@@ -108,19 +109,19 @@ add_task(function* test_setup() {
     }, pwmgrdlg);
   });
 });
 
 add_task(function* test_edit_multiple_logins() {
   function* testLoginChange(site, oldUsername, oldPassword, newUsername, newPassword) {
     addLogin(site, oldUsername, oldPassword);
     yield* editUsernamePromises(site, oldUsername, newUsername);
-    togglePasswords();
+    yield* togglePasswords();
     yield* editPasswordPromises(site, oldPassword, newPassword);
-    togglePasswords();
+    yield* togglePasswords();
   }
 
   yield* testLoginChange("http://c.tn/", "userC", "passC", "usernameC", "passwordC");
   yield* testLoginChange("http://b.tn/", "userB", "passB", "usernameB", "passwordB");
   yield* testLoginChange("http://a.tn/", "userA", "passA", "usernameA", "passwordA");
 
   pwmgrdlg.close();
 });