Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 09 Feb 2015 16:29:48 -0500
changeset 228190 2cb22c058add3ad58b68adae3ab06596fd4f723f
parent 228118 1af029247ca5bbceaf30edf2e8da4cdc1866e5fc (current diff)
parent 228189 9bdf571458b070127fb2aa6c6f23c44dd74165b1 (diff)
child 228193 c1884d2ddd4b5c113f218ca8a9bfe3fc3a5a36e4
child 228302 c121fcf2c4f11c661544425414bea7415c3feaeb
child 228368 caf8d804fc2cccc9b62a05467b8e58143ccadb86
push id28258
push userryanvm@gmail.com
push dateMon, 09 Feb 2015 21:30:07 +0000
treeherderautoland@2cb22c058add [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
first release with
nightly linux32
2cb22c058add / 38.0a1 / 20150210030231 / files
nightly linux64
2cb22c058add / 38.0a1 / 20150210030231 / files
nightly mac
2cb22c058add / 38.0a1 / 20150210030231 / files
nightly win32
2cb22c058add / 38.0a1 / 20150210030231 / files
nightly win64
2cb22c058add / 38.0a1 / 20150210030231 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
dom/base/test/TestCSPParser.cpp
dom/base/test/csp/chrome.ini
dom/base/test/csp/file_CSP.css
dom/base/test/csp/file_CSP.sjs
dom/base/test/csp/file_CSP_bug663567.xsl
dom/base/test/csp/file_CSP_bug663567_allows.xml
dom/base/test/csp/file_CSP_bug663567_allows.xml^headers^
dom/base/test/csp/file_CSP_bug663567_blocks.xml
dom/base/test/csp/file_CSP_bug663567_blocks.xml^headers^
dom/base/test/csp/file_CSP_bug802872.html
dom/base/test/csp/file_CSP_bug802872.html^headers^
dom/base/test/csp/file_CSP_bug802872.js
dom/base/test/csp/file_CSP_bug802872.sjs
dom/base/test/csp/file_CSP_bug885433_allows.html
dom/base/test/csp/file_CSP_bug885433_allows.html^headers^
dom/base/test/csp/file_CSP_bug885433_blocks.html
dom/base/test/csp/file_CSP_bug885433_blocks.html^headers^
dom/base/test/csp/file_CSP_bug888172.html
dom/base/test/csp/file_CSP_bug888172.sjs
dom/base/test/csp/file_CSP_bug909029_none.html
dom/base/test/csp/file_CSP_bug909029_none.html^headers^
dom/base/test/csp/file_CSP_bug909029_star.html
dom/base/test/csp/file_CSP_bug909029_star.html^headers^
dom/base/test/csp/file_CSP_bug910139.sjs
dom/base/test/csp/file_CSP_bug910139.xml
dom/base/test/csp/file_CSP_bug910139.xsl
dom/base/test/csp/file_CSP_bug941404.html
dom/base/test/csp/file_CSP_bug941404_xhr.html
dom/base/test/csp/file_CSP_bug941404_xhr.html^headers^
dom/base/test/csp/file_CSP_evalscript_main.html
dom/base/test/csp/file_CSP_evalscript_main.html^headers^
dom/base/test/csp/file_CSP_evalscript_main.js
dom/base/test/csp/file_CSP_evalscript_main_allowed.html
dom/base/test/csp/file_CSP_evalscript_main_allowed.html^headers^
dom/base/test/csp/file_CSP_evalscript_main_allowed.js
dom/base/test/csp/file_CSP_frameancestors.sjs
dom/base/test/csp/file_CSP_frameancestors_main.html
dom/base/test/csp/file_CSP_frameancestors_main.js
dom/base/test/csp/file_CSP_inlinescript_main.html
dom/base/test/csp/file_CSP_inlinescript_main.html^headers^
dom/base/test/csp/file_CSP_inlinescript_main_allowed.html
dom/base/test/csp/file_CSP_inlinescript_main_allowed.html^headers^
dom/base/test/csp/file_CSP_inlinestyle_main.html
dom/base/test/csp/file_CSP_inlinestyle_main.html^headers^
dom/base/test/csp/file_CSP_inlinestyle_main_allowed.html
dom/base/test/csp/file_CSP_inlinestyle_main_allowed.html^headers^
dom/base/test/csp/file_CSP_main.html
dom/base/test/csp/file_CSP_main.html^headers^
dom/base/test/csp/file_CSP_main.js
dom/base/test/csp/file_base-uri.html
dom/base/test/csp/file_bug836922_npolicies.html
dom/base/test/csp/file_bug836922_npolicies.html^headers^
dom/base/test/csp/file_bug836922_npolicies_ro_violation.sjs
dom/base/test/csp/file_bug836922_npolicies_violation.sjs
dom/base/test/csp/file_bug886164.html
dom/base/test/csp/file_bug886164.html^headers^
dom/base/test/csp/file_bug886164_2.html
dom/base/test/csp/file_bug886164_2.html^headers^
dom/base/test/csp/file_bug886164_3.html
dom/base/test/csp/file_bug886164_3.html^headers^
dom/base/test/csp/file_bug886164_4.html
dom/base/test/csp/file_bug886164_4.html^headers^
dom/base/test/csp/file_bug886164_5.html
dom/base/test/csp/file_bug886164_5.html^headers^
dom/base/test/csp/file_bug886164_6.html
dom/base/test/csp/file_bug886164_6.html^headers^
dom/base/test/csp/file_connect-src.html
dom/base/test/csp/file_csp_allow_https_schemes.html
dom/base/test/csp/file_csp_bug768029.html
dom/base/test/csp/file_csp_bug768029.sjs
dom/base/test/csp/file_csp_bug773891.html
dom/base/test/csp/file_csp_bug773891.sjs
dom/base/test/csp/file_csp_invalid_source_expression.html
dom/base/test/csp/file_csp_path_matching.html
dom/base/test/csp/file_csp_path_matching.js
dom/base/test/csp/file_csp_path_matching_redirect.html
dom/base/test/csp/file_csp_path_matching_redirect_server.sjs
dom/base/test/csp/file_csp_redirects_main.html
dom/base/test/csp/file_csp_redirects_page.sjs
dom/base/test/csp/file_csp_redirects_resource.sjs
dom/base/test/csp/file_csp_referrerdirective.html
dom/base/test/csp/file_csp_report.html
dom/base/test/csp/file_csp_testserver.sjs
dom/base/test/csp/file_form-action.html
dom/base/test/csp/file_hash_source.html
dom/base/test/csp/file_hash_source.html^headers^
dom/base/test/csp/file_leading_wildcard.html
dom/base/test/csp/file_multi_policy_injection_bypass.html
dom/base/test/csp/file_multi_policy_injection_bypass.html^headers^
dom/base/test/csp/file_multi_policy_injection_bypass_2.html
dom/base/test/csp/file_multi_policy_injection_bypass_2.html^headers^
dom/base/test/csp/file_nonce_source.html
dom/base/test/csp/file_nonce_source.html^headers^
dom/base/test/csp/file_policyuri_regression_from_multipolicy.html
dom/base/test/csp/file_policyuri_regression_from_multipolicy.html^headers^
dom/base/test/csp/file_policyuri_regression_from_multipolicy_policy
dom/base/test/csp/file_redirect_content.sjs
dom/base/test/csp/file_redirect_report.sjs
dom/base/test/csp/file_report_uri_missing_in_report_only_header.html
dom/base/test/csp/file_report_uri_missing_in_report_only_header.html^headers^
dom/base/test/csp/file_self_none_as_hostname_confusion.html
dom/base/test/csp/file_self_none_as_hostname_confusion.html^headers^
dom/base/test/csp/file_subframe_run_js_if_allowed.html
dom/base/test/csp/file_subframe_run_js_if_allowed.html^headers^
dom/base/test/csp/file_worker_redirect.html
dom/base/test/csp/file_worker_redirect.sjs
dom/base/test/csp/mochitest.ini
dom/base/test/csp/referrerdirective.sjs
dom/base/test/csp/test_301_redirect.html
dom/base/test/csp/test_302_redirect.html
dom/base/test/csp/test_303_redirect.html
dom/base/test/csp/test_307_redirect.html
dom/base/test/csp/test_CSP.html
dom/base/test/csp/test_CSP_bug663567.html
dom/base/test/csp/test_CSP_bug802872.html
dom/base/test/csp/test_CSP_bug885433.html
dom/base/test/csp/test_CSP_bug888172.html
dom/base/test/csp/test_CSP_bug909029.html
dom/base/test/csp/test_CSP_bug910139.html
dom/base/test/csp/test_CSP_bug941404.html
dom/base/test/csp/test_CSP_evalscript.html
dom/base/test/csp/test_CSP_frameancestors.html
dom/base/test/csp/test_CSP_inlinescript.html
dom/base/test/csp/test_CSP_inlinestyle.html
dom/base/test/csp/test_CSP_referrerdirective.html
dom/base/test/csp/test_base-uri.html
dom/base/test/csp/test_bug836922_npolicies.html
dom/base/test/csp/test_bug886164.html
dom/base/test/csp/test_bug949549.html
dom/base/test/csp/test_connect-src.html
dom/base/test/csp/test_csp_allow_https_schemes.html
dom/base/test/csp/test_csp_bug768029.html
dom/base/test/csp/test_csp_bug773891.html
dom/base/test/csp/test_csp_invalid_source_expression.html
dom/base/test/csp/test_csp_path_matching.html
dom/base/test/csp/test_csp_path_matching_redirect.html
dom/base/test/csp/test_csp_redirects.html
dom/base/test/csp/test_csp_report.html
dom/base/test/csp/test_form-action.html
dom/base/test/csp/test_hash_source.html
dom/base/test/csp/test_leading_wildcard.html
dom/base/test/csp/test_multi_policy_injection_bypass.html
dom/base/test/csp/test_nonce_source.html
dom/base/test/csp/test_policyuri_regression_from_multipolicy.html
dom/base/test/csp/test_report_uri_missing_in_report_only_header.html
dom/base/test/csp/test_self_none_as_hostname_confusion.html
dom/base/test/csp/test_subframe_run_js_if_allowed.html
dom/base/test/csp/test_worker_redirect.html
dom/base/test/file_CrossSiteXHR_cache_server.sjs
dom/base/test/file_CrossSiteXHR_inner.html
dom/base/test/file_CrossSiteXHR_inner.jar
dom/base/test/file_CrossSiteXHR_inner_data.sjs
dom/base/test/file_CrossSiteXHR_server.sjs
dom/base/test/mixedcontentblocker/bug803225_test_mailto.html
dom/base/test/mixedcontentblocker/file_mixed_content_frameNavigation.html
dom/base/test/mixedcontentblocker/file_mixed_content_frameNavigation_blankTarget.html
dom/base/test/mixedcontentblocker/file_mixed_content_frameNavigation_grandchild.html
dom/base/test/mixedcontentblocker/file_mixed_content_frameNavigation_innermost.html
dom/base/test/mixedcontentblocker/file_mixed_content_frameNavigation_secure.html
dom/base/test/mixedcontentblocker/file_mixed_content_frameNavigation_secure_grandchild.html
dom/base/test/mixedcontentblocker/file_mixed_content_main.html
dom/base/test/mixedcontentblocker/file_mixed_content_main_bug803225.html
dom/base/test/mixedcontentblocker/file_mixed_content_main_bug803225_websocket_wsh.py
dom/base/test/mixedcontentblocker/file_mixed_content_server.sjs
dom/base/test/mixedcontentblocker/mochitest.ini
dom/base/test/mixedcontentblocker/test_mixed_content_blocker.html
dom/base/test/mixedcontentblocker/test_mixed_content_blocker_bug803225.html
dom/base/test/mixedcontentblocker/test_mixed_content_blocker_frameNavigation.html
dom/base/test/test_CrossSiteXHR.html
dom/base/test/test_CrossSiteXHR_cache.html
dom/base/test/test_CrossSiteXHR_origin.html
dom/base/test/unit/test_cspreports.js
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/tests/ecma_2/RegExp/function-001.js
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/combination_history_001.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/combination_history_002.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/combination_history_003.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/combination_history_004.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/combination_history_005.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/combination_history_006.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/combination_history_007.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_back.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_forward.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_go_minus.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_go_plus.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_pushstate.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_pushstate_err.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_pushstate_nooptionalparam.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_replacestate.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_replacestate_err.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_replacestate_nooptionalparam.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/history_state.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/location_assign.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/location_hash.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/location_host.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/location_hostname.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/location_href.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/location_pathname.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/location_port.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/location_protocol.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/location_replace.html
testing/web-platform/tests/old-tests/submission/Infraware/Session_History/contents/Session_History/location_search.html
--- a/b2g/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,21 +1,21 @@
 [
 {
-"clang_version": "r170890"
+"clang_version": "r183744"
 },
 {
 "size": 88,
 "digest": "0d2ae9bcd7cea34ec0b768270725e98410dbb3bc150c7381e0dcf3eb5dbb3e69ac76dbb0f46b056151d6a6fa8681cab06da68173ae8598f3397b8f7628e67381",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 56126352,
-"digest": "e156e2a39abd5bf272ee30748a6825f22ddd27565b097c66662a2a6f2e9892bc5b4bf30a3552dffbe867dbfc39e7ee086e0b2cd7935f6ea216c0cf936178a88f",
+"size": 59602619,
+"digest": "86662ebc0ef650490559005948c4f0cb015dad72c7cac43732c2bf2995247081e30c139cf8008d19670a0009fc302c4eee2676981ee3f9ff4a15c01af22b783b",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 },
 {
 "size": 166506,
 "digest": "f3c214fd571f21d64937584645212f8d7c2d12c9016be613bd2bc9cecd80b3d52a741423cc1ca69bd85fb924c3d0572c85a1734d12db1616df37abcc397e9252",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -441,16 +441,19 @@ PluginContent.prototype = {
       Services.telemetry.getHistogramById('FLASH_PLUGIN_WIDTH')
                        .add(pluginRect.width);
       Services.telemetry.getHistogramById('FLASH_PLUGIN_HEIGHT')
                        .add(pluginRect.height);
       Services.telemetry.getHistogramById('FLASH_PLUGIN_AREA')
                        .add(pluginRect.width * pluginRect.height);
 
       let state = this._getPluginInfo(plugin).fallbackType;
+      if (state === null) {
+        state = Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED;
+      }
       Services.telemetry.getHistogramById('FLASH_PLUGIN_STATES')
                        .add(state);
     }
   },
 
   _finishRecordingFlashPluginTelemetry: function () {
     if (this.flashPluginStats) {
       Services.telemetry.getHistogramById('FLASH_PLUGIN_INSTANCES_ON_PAGE')
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -71,16 +71,18 @@
 #include "nsInProcessTabChildGlobal.h"
 
 #include "Layers.h"
 #include "ClientLayerManager.h"
 
 #include "AppProcessChecker.h"
 #include "ContentParent.h"
 #include "TabParent.h"
+#include "mozilla/plugins/PPluginWidgetParent.h"
+#include "../plugins/ipc/PluginWidgetParent.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "nsIAppsService.h"
@@ -965,16 +967,18 @@ nsFrameLoader::Hide()
   baseWin->SetParentWidget(nullptr);
 }
 
 nsresult
 nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
                                          nsRefPtr<nsFrameLoader>& aFirstToSwap,
                                          nsRefPtr<nsFrameLoader>& aSecondToSwap)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   Element* ourContent = mOwnerContent;
   Element* otherContent = aOther->mOwnerContent;
 
   if (!ourContent || !otherContent) {
     // Can't handle this
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
@@ -1019,16 +1023,27 @@ nsFrameLoader::SwapWithOtherRemoteLoader
   }
 
   rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
   if (NS_FAILED(rv)) {
     mInSwap = aOther->mInSwap = false;
     return rv;
   }
 
+  // Native plugin windows used by this remote content need to be reparented.
+  const nsTArray<mozilla::plugins::PPluginWidgetParent*>& plugins =
+    aOther->mRemoteBrowser->ManagedPPluginWidgetParent();
+  nsPIDOMWindow* newWin = ourDoc->GetWindow();
+  if (newWin) {
+    nsRefPtr<nsIWidget> newParent = ((nsGlobalWindow*)newWin)->GetMainWidget();
+    for (uint32_t idx = 0; idx < plugins.Length(); ++idx) {
+      static_cast<mozilla::plugins::PluginWidgetParent*>(plugins[idx])->SetParent(newParent);
+    }
+  }
+
   SetOwnerContent(otherContent);
   aOther->SetOwnerContent(ourContent);
 
   mRemoteBrowser->SetOwnerElement(otherContent);
   aOther->mRemoteBrowser->SetOwnerElement(ourContent);
 
   nsRefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
   nsRefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
new file mode 100644
--- /dev/null
+++ b/dom/base/nsFrameLoader.cpp.rej
@@ -0,0 +1,21 @@
+--- nsFrameLoader.cpp
++++ nsFrameLoader.cpp
+@@ -48,17 +48,17 @@
+ #include "nsIDOMHTMLDocument.h"
+ #include "nsIXULWindow.h"
+ #include "nsIEditor.h"
+ #include "nsIMozBrowserFrame.h"
+ #include "nsIPermissionManager.h"
+ #include "nsISHistory.h"
+ #include "nsNullPrincipal.h"
+ #include "nsIScriptError.h"
+-
++#include "nsGlobalWindow.h"
+ #include "nsLayoutUtils.h"
+ #include "nsView.h"
+ 
+ #include "nsIURI.h"
+ #include "nsIURL.h"
+ #include "nsNetUtil.h"
+ 
+ #include "nsGkAtoms.h"
--- a/dom/base/test/test_user_select.html
+++ b/dom/base/test/test_user_select.html
@@ -9,16 +9,18 @@
 <style type="text/css">
 @font-face {
   font-family: Ahem;
   src: url("Ahem.ttf");
 }
 body { font-family: Ahem; font-size: 20px; }
 s { -moz-user-select: none; }
 n { display: none; }
+a { position:absolute; bottom: 0; right:0; }
+.text { -moz-user-select: text; }
 </style>
 
 </head>
 <body>
 
 <div id="test1">aaaaaaa<s>bbbbbbbb</s>ccccccc</div>
 <div id="test2"><s>aaaaaaa</s>bbbbbbbbccccccc</div>
 <div id="test3">aaaaaaabbbbbbbb<s>ccccccc</s></div>
@@ -26,29 +28,38 @@ n { display: none; }
 <div id="test5"><x><s>aaaaaaa</s></x>bbbbbbbbccccccc</div>
 <div id="test6">aaaaaaabbbbbbbb<x><s>ccccccc</s></x></div>
 <div id="test7">aaaaaaa<x><s><n>bbbb</n>bbbb</s></x>ccccccc</div>
 <div id="test8"><x><s>aa<n>aaa</n>aa</s></x>bbbbbbbbccccccc</div>
 <div id="test9">aaaaaaabbbbbbbb<x><s>cc<n>ccccc</n></s></x></div>
 <div id="testA">aaaaaaa<n>bbb<s>bbbbb</s></n>ccccccc</div>
 <div id="testB"><n><s>aaaa</s>aaa</n>bbbbbbbbccccccc</div>
 <div id="testC">aaaaaaabbbbbbbb<n>cc<s>c</s>cccc</n></div>
+<div id="testE">aaa<s id="testEc1">aaaa<a class="text">bbbb</a>dd<a>cccc</a>ddddddd</s>eeee</div>
 
 <iframe id="testD" src="data:text/html,<body>aaaa<span style='-moz-user-select:none'>bbbb</span>cccc"></iframe>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 function test()
 {
   function clear(w)
   {
     var sel = (w ? w : window).getSelection();
     sel.removeAllRanges();
   }
+  function doneTest(e)
+  {
+    // We hide the elements we're done with so that later tests
+    // are inside the rather narrow iframe mochitest gives us.
+    // It matters for synthesizeMouse event tests.
+    e.style.display = 'none';
+    e.offsetHeight;
+  }
 
   function dragSelect(e, x1, x2, x3)
   {
     dir = x2 > x1 ? 1 : -1;
     synthesizeMouse(e, x1, 5, { type: "mousedown" });
     synthesizeMouse(e, x1 + dir, 5, { type: "mousemove" });
     if (x3)
       synthesizeMouse(e, x3, 5, { type: "mousemove" });
@@ -70,158 +81,187 @@ function test()
       var data = arr[i];
       var r = new Range()
       r.setStart(node(e, data[0]), data[1]);
       r.setEnd(node(e, data[2]), data[3]);
       sel.addRange(r);
     }
   }
 
+  function NL(s) { return s.replace(/(\r\n|\n\r|\r)/g, '\n'); }
+
   function checkText(text, e)
   {
     var sel = window.getSelection();
-    is(sel.toString(), text, e.id + ": selected text")
+    is(NL(sel.toString()), text, e.id + ": selected text")
   }
 
   function checkRangeText(text, index)
   {
     var r = window.getSelection().getRangeAt(index);
-    is(r.toString(), text, e.id + ": range["+index+"].toString()")
+    is(NL(r.toString()), text, e.id + ": range["+index+"].toString()")
   }
 
   function node(e, index)
   {
     return index == -1 ? e : e.childNodes[index];
   }
 
+  function checkRangeCount(n, e)
+  {
+    var sel = window.getSelection();
+    is(sel.rangeCount, n, e.id + ": Selection range count");
+  }
+
+  function checkRange(i, expected, e) {
+    var sel = window.getSelection();
+    var r = sel.getRangeAt(i);
+    is(r.startContainer, node(e, expected[0]), e.id + ": range["+i+"].startContainer");
+    is(r.startOffset, expected[1], e.id + ": range["+i+"].startOffset");
+    is(r.endContainer, node(e, expected[2]), e.id + ": range["+i+"].endContainer");
+    is(r.endOffset, expected[3], e.id + ": range["+i+"].endOffset");
+  }
+
   function checkRanges(arr, e)
   {
-    var sel = window.getSelection();
-    is(sel.rangeCount, arr.length, e.id + ": Selection range count");
+    checkRangeCount(arr.length, e);
     for (i = 0; i < arr.length; ++i) {
       var expected = arr[i];
-      var r = sel.getRangeAt(i);
-      is(r.startContainer, node(e, expected[0]), e.id + ": range["+i+"].startContainer");
-      is(r.startOffset, expected[1], e.id + ": range["+i+"].startOffset");
-      is(r.endContainer, node(e, expected[2]), e.id + ": range["+i+"].endContainer");
-      is(r.endOffset, expected[3], e.id + ": range["+i+"].endOffset");
+      checkRange(i, expected, e);
     }
   }
 
   // ======================================================
   // ================== dragSelect tests ==================
   // ======================================================
 
   var e = document.getElementById('test1');
   dragSelect(e, 20, 340);
   checkText('aaaaaacc', e);
   checkRanges([[0,1,-1,1], [2,0,2,2]], e);
 
   clear();
   dragSelect(e, 20, 260, 120);
   checkText('aaaaa', e);
   checkRanges([[0,1,0,6]], e);
+  doneTest(e);
 
   clear();
   e = document.getElementById('test2');
   dragSelect(e, 20, 340);
   checkText('', e);
   checkRanges([], e);
 
   clear();
   dragSelect(e, 340, 20, 140);
   checkText('bbbbbbbbcc', e);
   checkRanges([[1,0,1,10]], e);
+  // #test2 is used again below
 
   clear();
   e = document.getElementById('test3');
   dragSelect(e, 20, 340, 295);
   checkText('aaaaaabbbbbbbb', e);
   checkRanges([[0,1,0,15]], e);
+  // #test3 is used again below
 
   clear();
   e = document.getElementById('test4');
   dragSelect(e, 20, 340);
   checkText('aaaaaacc', e);
   checkRanges([[0,1,1,0], [2,0,2,2]], e);
+  doneTest(e);
 
   clear();
   e = document.getElementById('test5');
   dragSelect(e, 340, 20, 140);
   checkText('bbbbbbbbcc', e);
   checkRanges([[1,0,1,10]], e);
+  doneTest(e);
 
   clear();
   e = document.getElementById('test6');
   dragSelect(e, 20, 340, 295);
   checkText('aaaaaabbbbbbbb', e);
   checkRanges([[0,1,0,15]], e);
+  doneTest(e);
 
   clear();
   e = document.getElementById('test7');
   dragSelect(e, 20, 340);
   checkText('aaaaaacccccc', e);
   checkRanges([[0,1,1,0], [2,0,2,6]], e);
+  doneTest(e);
 
   clear();
   e = document.getElementById('test8');
   dragSelect(e, 340, 20, 140);
   checkText('bbbbbccccc', e);
   checkRanges([[1,3,1,13]], e);
+  doneTest(e);
 
   clear();
   e = document.getElementById('test9');
   dragSelect(e, 20, 340, 295);
   checkText('aaaaaabbbbbbbb', e);
   checkRanges([[0,1,0,15]], e);
+  doneTest(e);
 
   clear();
   e = document.getElementById('testA');
   dragSelect(e, 20, 340);
   checkText('aaaaaaccccccc', e);
   checkRanges([[0,1,2,7]], e);
   checkRangeText('aaaaaabbbbbbbbccccccc', 0);
+  doneTest(e);
 
   clear();
   e = document.getElementById('testB');
   dragSelect(e, 340, 20, 140);
   checkText('bbbbbbbccccccc', e);
   checkRanges([[1,1,1,15]], e);
+  doneTest(e);
 
   clear();
-  e = document.getElementById('test9');
-  dragSelect(e, 20, 340, 295);
-  checkText('aaaaaabbbbbbbb', e);
-  checkRanges([[0,1,0,15]], e);
+  e = document.getElementById('testE');
+  dragSelect(e, 20, 360, 295);
+  checkText('aa\nbbbb\nee', e);
+  checkRangeCount(3, e);
+  checkRange(0, [0,1,-1,1], e);
+  checkRange(1, [1,0,-1,2], e.children[0]);
+  checkRange(2, [2,0,2,2], e);
+  doneTest(e);
 
   // ======================================================
   // ================== shift+click tests =================
   // ======================================================
 
   // test extending a selection that starts in a -moz-user-select:none node
   clear();
   e = document.getElementById('test2');
   init([[0,0,0,1]], e);
   checkRangeText('aaaaaaa', 0);
   checkText('', e);
   shiftClick(e, 340);
   checkRangeText('bbbbbbbbcc', 0);
   checkText('bbbbbbbbcc', e);
   checkRanges([[-1,1,1,10]], e);
+  doneTest(e);
 
   // test extending a selection that end in a -moz-user-select:none node
   clear();
   e = document.getElementById('test3');
   init([[1,0,1,1]], e);
   checkRangeText('ccccccc', 0);
   checkText('', e);
   shiftClick(e, 20);
   checkRangeText('aaaaaabbbbbbbb', 0);
   checkText('aaaaaabbbbbbbb', e);
   checkRanges([[0,1,-1,1]], e);
+  doneTest(e);
 
   // ======================================================
   // ==================== Script tests ====================
   // ======================================================
 
   clear();
   e = document.getElementById('testD');
   clear(e.contentWindow);
@@ -242,16 +282,17 @@ function test()
   e.contentWindow.focus();
   synthesizeKey("a", { accelKey:true }, e.contentWindow);
   sel = e.contentWindow.getSelection();
   is(window.getSelection().rangeCount, 0, "testD: no selection in outer window");
   is(sel.toString(), 'aaaacccc', "testD: kbd selection");
   is(sel.rangeCount, 2, "testD: kbd selection is filtered");
   is(sel.getRangeAt(0).toString(), 'aaaa', "testD: kbd selection is filtered");
   is(sel.getRangeAt(1).toString(), 'cccc', "testD: kbd selection is filtered");
+  doneTest(e);
 
   clear();
   SimpleTest.finish();
 }
 window.onload = function() { setTimeout(test, 0); };
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -4048,17 +4048,19 @@ def getJSToNativeConversionInfo(type, de
         setToNullCode = "${declName} = nullptr;\n"
         template = wrapObjectTemplate(templateBody, type, setToNullCode,
                                       failureCode)
         return JSToNativeConversionInfo(template, declType=declType,
                                         dealWithOptional=isOptional,
                                         declArgs=declArgs)
 
     def incrementNestingLevel():
-        return 1 if nestingLevel is "" else ++nestingLevel
+        if nestingLevel is "":
+            return 1
+        return nestingLevel + 1
 
     assert not (isEnforceRange and isClamp)  # These are mutually exclusive
 
     if type.isArray():
         raise TypeError("Can't handle array arguments yet")
 
     if type.isSequence():
         assert not isEnforceRange and not isClamp
@@ -6827,22 +6829,24 @@ class CGMethodCall(CGThing):
                                       nativeMethodName, static, descriptor,
                                       method,
                                       argConversionStartsAt=argConversionStartsAt,
                                       isConstructor=isConstructor)
 
         def filteredSignatures(signatures, descriptor):
             def typeExposedInWorkers(type):
                 return (not type.isGeckoInterface() or
-                        type.inner.isExternal() or
                         type.inner.isExposedInAnyWorker())
             if descriptor.workers:
                 # Filter out the signatures that should not be exposed in a
                 # worker.  The IDL parser enforces the return value being
                 # exposed correctly, but we have to check the argument types.
+                #
+                # If this code changes, adjust the self._deps
+                # computation in CGDDescriptor.__init__ as needed.
                 assert all(typeExposedInWorkers(sig[0]) for sig in signatures)
                 signatures = filter(
                     lambda sig: all(typeExposedInWorkers(arg.type)
                                     for arg in sig[1]),
                     signatures)
                 if len(signatures) == 0:
                     raise TypeError("%s.%s has a worker binding with no "
                                     "signatures that take arguments exposed in "
@@ -11003,16 +11007,31 @@ def memberProperties(m, descriptor):
 
 class CGDescriptor(CGThing):
     def __init__(self, descriptor):
         CGThing.__init__(self)
 
         assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
 
         self._deps = descriptor.interface.getDeps()
+        # If we have a worker descriptor, add dependencies on interface types we
+        # have as method arguments to overloaded methods.  See the
+        # filteredSignatures() bit in CGMethodCall.  Note that we have to add
+        # both interfaces that _are_ exposed in workers and ones that aren't;
+        # the idea is that we have to notice when the exposure set changes.
+        if descriptor.workers:
+            methods = (m for m in descriptor.interface.members if
+                       m.isMethod() and isMaybeExposedIn(m, descriptor) and
+                       len(m.signatures()) != 1)
+            for m in methods:
+                for sig in m.signatures():
+                    for arg in sig[1]:
+                        if (arg.type.isGeckoInterface() and
+                            not arg.type.inner.isExternal()):
+                            self._deps.add(arg.type.inner.filename())
 
         cgThings = []
         cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" %
                                   descriptor.nativeType))
         parent = descriptor.interface.parent
         if parent:
             cgThings.append(CGGeneric("static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
                                       "              \"Can't inherit from an interface with a different ownership model.\");\n" %
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -201,24 +201,21 @@ class Configuration:
                 getter = lambda x: x.interface.isCallback()
             elif key == 'isExternal':
                 getter = lambda x: x.interface.isExternal()
             elif key == 'isJSImplemented':
                 getter = lambda x: x.interface.isJSImplemented()
             elif key == 'isNavigatorProperty':
                 getter = lambda x: x.interface.getNavigatorProperty() != None
             elif key == 'isExposedInAnyWorker':
-                getter = lambda x: (not x.interface.isExternal() and
-                                    x.interface.isExposedInAnyWorker())
+                getter = lambda x: x.interface.isExposedInAnyWorker()
             elif key == 'isExposedInSystemGlobals':
-                getter = lambda x: (not x.interface.isExternal() and
-                                    x.interface.isExposedInSystemGlobals())
+                getter = lambda x: x.interface.isExposedInSystemGlobals()
             elif key == 'isExposedInWindow':
-                getter = lambda x: (not x.interface.isExternal() and
-                                    x.interface.isExposedInWindow())
+                getter = lambda x: x.interface.isExposedInWindow()
             else:
                 # Have to watch out: just closing over "key" is not enough,
                 # since we're about to mutate its value
                 getter = (lambda attrName: lambda x: getattr(x, attrName))(key)
             tofilter.append((getter, val))
         for f in tofilter:
             curr = filter(lambda x: f[0](x) == f[1], curr)
         return curr
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -443,25 +443,75 @@ class IDLIdentifierPlaceholder(IDLObject
             scope._lookupIdentifier(self.identifier)
         except:
             raise WebIDLError("Unresolved type '%s'." % self.identifier,
                               [self.location])
 
         obj = self.identifier.resolve(scope, None)
         return scope.lookupIdentifier(obj)
 
-class IDLExternalInterface(IDLObjectWithIdentifier):
+class IDLExposureMixins():
+    def __init__(self, location):
+        # _exposureGlobalNames are the global names listed in our [Exposed]
+        # extended attribute.  exposureSet is the exposure set as defined in the
+        # Web IDL spec: it contains interface names.
+        self._exposureGlobalNames = set()
+        self.exposureSet = set()
+        self._location = location
+
+    def finish(self, scope):
+        # Verify that our [Exposed] value, if any, makes sense.
+        for globalName in self._exposureGlobalNames:
+            if globalName not in scope.globalNames:
+                raise WebIDLError("Unknown [Exposed] value %s" % globalName,
+                                  [self._location])
+
+        if len(self._exposureGlobalNames) == 0:
+            self._exposureGlobalNames.add(scope.primaryGlobalName)
+
+        globalNameSetToExposureSet(scope, self._exposureGlobalNames,
+                                   self.exposureSet)
+
+    def isExposedInWindow(self):
+        return 'Window' in self.exposureSet
+
+    def isExposedInAnyWorker(self):
+        return len(self.getWorkerExposureSet()) > 0
+
+    def isExposedInSystemGlobals(self):
+        return 'BackstagePass' in self.exposureSet
+
+    def isExposedInSomeButNotAllWorkers(self):
+        """
+        Returns true if the Exposed extended attribute for this interface
+        exposes it in some worker globals but not others.  The return value does
+        not depend on whether the interface is exposed in Window or System
+        globals.
+        """
+        if not self.isExposedInAnyWorker():
+            return False
+        workerScopes = self.parentScope.globalNameMapping["Worker"]
+        return len(workerScopes.difference(self.exposureSet)) > 0
+
+    def getWorkerExposureSet(self):
+        # Subclasses that might be exposed in workers should override as needed
+        return set()
+
+
+class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
     def __init__(self, location, parentScope, identifier):
         assert isinstance(identifier, IDLUnresolvedIdentifier)
         assert isinstance(parentScope, IDLScope)
         self.parent = None
         IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
+        IDLExposureMixins.__init__(self, location)
         IDLObjectWithIdentifier.resolve(self, parentScope)
 
     def finish(self, scope):
+        IDLExposureMixins.finish(self, scope)
         pass
 
     def validate(self):
         pass
 
     def isExternal(self):
         return True
 
@@ -542,17 +592,17 @@ def convertExposedAttrToGlobalNameSet(ex
     else:
         assert exposedAttr.hasArgs()
         targetSet.update(exposedAttr.args())
 
 def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
     for name in nameSet:
         exposureSet.update(globalScope.globalNameMapping[name])
 
-class IDLInterface(IDLObjectWithScope):
+class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
     def __init__(self, location, parentScope, name, parent, members,
                  isKnownNonPartial):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
         assert isKnownNonPartial or not parent
         assert isKnownNonPartial or len(members) == 0
 
         self.parent = None
@@ -577,23 +627,19 @@ class IDLInterface(IDLObjectWithScope):
         self.interfacesImplementingSelf = set()
         self._hasChildInterfaces = False
         self._isOnGlobalProtoChain = False
         # Tracking of the number of reserved slots we need for our
         # members and those of ancestor interfaces.
         self.totalMembersInSlots = 0
         # Tracking of the number of own own members we have in slots
         self._ownMembersInSlots = 0
-        # _exposureGlobalNames are the global names listed in our [Exposed]
-        # extended attribute.  exposureSet is the exposure set as defined in the
-        # Web IDL spec: it contains interface names.
-        self._exposureGlobalNames = set()
-        self.exposureSet = set()
 
         IDLObjectWithScope.__init__(self, location, parentScope, name)
+        IDLExposureMixins.__init__(self, location)
 
         if isKnownNonPartial:
             self.setNonPartial(location, parent, members)
 
     def __str__(self):
         return "Interface '%s'" % self.identifier.name
 
     def ctor(self):
@@ -623,27 +669,17 @@ class IDLInterface(IDLObjectWithScope):
 
         self._finished = True
 
         if not self._isKnownNonPartial:
             raise WebIDLError("Interface %s does not have a non-partial "
                               "declaration" % self.identifier.name,
                               [self.location])
 
-        # Verify that our [Exposed] value, if any, makes sense.
-        for globalName in self._exposureGlobalNames:
-            if globalName not in scope.globalNames:
-                raise WebIDLError("Unknown [Exposed] value %s" % globalName,
-                                  [self.location])
-
-        if len(self._exposureGlobalNames) == 0:
-            self._exposureGlobalNames.add(scope.primaryGlobalName)
-
-        globalNameSetToExposureSet(scope, self._exposureGlobalNames,
-                                   self.exposureSet)
+        IDLExposureMixins.finish(self, scope)
 
         # Now go ahead and merge in our partial interfaces.
         for partial in self._partialInterfaces:
             partial.finish(scope)
             self.addExtendedAttributes(partial.propagatedExtendedAttrs)
             self.members.extend(partial.members)
 
         # Now that we've merged in our partial interfaces, set the
@@ -1058,37 +1094,16 @@ class IDLInterface(IDLObjectWithScope):
             len(self.getConsequentialInterfaces()) == 0 and
             # No attributes of any kinds
             not any(m.isAttr() for m in self.members) and
             # There is at least one regular operation, and all regular
             # operations have the same identifier
             len(set(m.identifier.name for m in self.members if
                     m.isMethod() and not m.isStatic())) == 1)
 
-    def isExposedInWindow(self):
-        return 'Window' in self.exposureSet
-
-    def isExposedInAnyWorker(self):
-        return len(self.getWorkerExposureSet()) > 0
-
-    def isExposedInSystemGlobals(self):
-        return 'BackstagePass' in self.exposureSet
-
-    def isExposedInSomeButNotAllWorkers(self):
-        """
-        Returns true if the Exposed extended attribute for this interface
-        exposes it in some worker globals but not others.  The return value does
-        not depend on whether the interface is exposed in Window or System
-        globals.
-        """
-        if not self.isExposedInAnyWorker():
-            return False
-        workerScopes = self.parentScope.globalNameMapping["Worker"]
-        return len(workerScopes.difference(self.exposureSet)) > 0
-
     def getWorkerExposureSet(self):
         workerScopes = self.parentScope.globalNameMapping["Worker"]
         return workerScopes.intersection(self.exposureSet)
 
     def inheritanceDepth(self):
         depth = 0
         parent = self.parent
         while parent:
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -414,17 +414,19 @@ public:
   void ReceiveAnySequence(JSContext*, nsTArray<JS::Value>&);
   void ReceiveNullableAnySequence(JSContext*, Nullable<nsTArray<JS::Value> >&);
   void ReceiveAnySequenceSequence(JSContext*, nsTArray<nsTArray<JS::Value> >&);
 
   void ReceiveObjectSequence(JSContext*, nsTArray<JSObject*>&);
   void ReceiveNullableObjectSequence(JSContext*, nsTArray<JSObject*>&);
 
   void PassSequenceOfSequences(const Sequence< Sequence<int32_t> >&);
+  void PassSequenceOfSequencesOfSequences(const Sequence<Sequence<Sequence<int32_t>>>&);
   void ReceiveSequenceOfSequences(nsTArray< nsTArray<int32_t> >&);
+  void ReceiveSequenceOfSequencesOfSequences(nsTArray<nsTArray<nsTArray<int32_t>>>&);
 
   // MozMap types
   void PassMozMap(const MozMap<int32_t> &);
   void PassNullableMozMap(const Nullable< MozMap<int32_t> >&);
   void PassMozMapOfNullableInts(const MozMap<Nullable<int32_t> >&);
   void PassOptionalMozMapOfNullableInts(const Optional<MozMap<Nullable<int32_t> > > &);
   void PassOptionalNullableMozMapOfNullableInts(const Optional<Nullable<MozMap<Nullable<int32_t> > > > &);
   void PassCastableObjectMozMap(const MozMap< OwningNonNull<TestInterface> >&);
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -401,17 +401,19 @@ interface TestInterface {
   sequence<any> receiveAnySequence();
   sequence<any>? receiveNullableAnySequence();
   sequence<sequence<any>> receiveAnySequenceSequence();
 
   sequence<object> receiveObjectSequence();
   sequence<object?> receiveNullableObjectSequence();
 
   void passSequenceOfSequences(sequence<sequence<long>> arg);
+  void passSequenceOfSequencesOfSequences(sequence<sequence<sequence<long>>> arg);
   sequence<sequence<long>> receiveSequenceOfSequences();
+  sequence<sequence<sequence<long>>> receiveSequenceOfSequencesOfSequences();
 
   // MozMap types
   void passMozMap(MozMap<long> arg);
   void passNullableMozMap(MozMap<long>? arg);
   void passMozMapOfNullableInts(MozMap<long?> arg);
   void passOptionalMozMapOfNullableInts(optional MozMap<long?> arg);
   void passOptionalNullableMozMapOfNullableInts(optional MozMap<long?>? arg);
   void passCastableObjectMozMap(MozMap<TestInterface> arg);
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -266,16 +266,17 @@ interface TestExampleInterface {
   sequence<any>? receiveNullableAnySequence();
   //XXXbz No support for sequence of sequence return values yet.
   //sequence<sequence<any>> receiveAnySequenceSequence();
 
   sequence<object> receiveObjectSequence();
   sequence<object?> receiveNullableObjectSequence();
 
   void passSequenceOfSequences(sequence<sequence<long>> arg);
+  void passSequenceOfSequencesOfSequences(sequence<sequence<sequence<long>>> arg);
   //XXXbz No support for sequence of sequence return values yet.
   //sequence<sequence<long>> receiveSequenceOfSequences();
 
   // MozMap types
   void passMozMap(MozMap<long> arg);
   void passNullableMozMap(MozMap<long>? arg);
   void passMozMapOfNullableInts(MozMap<long?> arg);
   void passOptionalMozMapOfNullableInts(optional MozMap<long?> arg);
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -278,16 +278,17 @@ interface TestJSImplInterface {
   sequence<any>? receiveNullableAnySequence();
   //XXXbz No support for sequence of sequence return values yet.
   //sequence<sequence<any>> receiveAnySequenceSequence();
 
   sequence<object> receiveObjectSequence();
   sequence<object?> receiveNullableObjectSequence();
 
   void passSequenceOfSequences(sequence<sequence<long>> arg);
+  void passSequenceOfSequencesOfSequences(sequence<sequence<sequence<long>>> arg);
   //XXXbz No support for sequence of sequence return values yet.
   //sequence<sequence<long>> receiveSequenceOfSequences();
 
   // MozMap types
   void passMozMap(MozMap<long> arg);
   void passNullableMozMap(MozMap<long>? arg);
   void passMozMapOfNullableInts(MozMap<long?> arg);
   void passOptionalMozMapOfNullableInts(optional MozMap<long?> arg);
--- a/dom/broadcastchannel/tests/broadcastchannel_sharedWorker.js
+++ b/dom/broadcastchannel/tests/broadcastchannel_sharedWorker.js
@@ -1,12 +1,12 @@
 onconnect = function(evt) {
   evt.ports[0].onmessage = function(evt) {
     var bc = new BroadcastChannel('foobar');
     bc.addEventListener('message', function(event) {
-      evt.target.postMessage(event.data == "hello world from the window" ? "OK" : "KO");
-      bc.postMessage("hello world from the worker");
+      bc.postMessage(event.data == "hello world from the window" ?
+                       "hello world from the worker" : "KO");
       bc.close();
     }, false);
 
     evt.target.postMessage("READY");
   }
 }
--- a/dom/broadcastchannel/tests/test_broadcastchannel_sharedWorker.html
+++ b/dom/broadcastchannel/tests/test_broadcastchannel_sharedWorker.html
@@ -24,18 +24,16 @@ function runTests() {
   var worker = new SharedWorker("broadcastchannel_sharedWorker.js");
 
   var bc = new BroadcastChannel('foobar');
 
   worker.port.onmessage = function(event) {
     if (event.data == "READY") {
       ok(true, "SharedWorker is ready!");
       bc.postMessage('hello world from the window');
-    } else if(event.data == "OK") {
-      ok(true, "SharedWorker has received the message");
     } else {
       ok(false, "Something wrong happened");
     }
   };
 
   bc.onmessage = function(event) {
     is("hello world from the worker", event.data, "The message matches!");
     bc.close();
--- a/dom/html/TimeRanges.cpp
+++ b/dom/html/TimeRanges.cpp
@@ -167,10 +167,19 @@ TimeRanges::Find(double aTime, double aT
 }
 
 bool
 TimeRanges::WrapObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector)
 {
   return TimeRangesBinding::Wrap(aCx, this, aReflector);
 }
 
+void
+TimeRanges::Shift(double aOffset)
+{
+  for (index_type i = 0; i < mRanges.Length(); ++i) {
+    mRanges[i].mStart += aOffset;
+    mRanges[i].mEnd += aOffset;
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/TimeRanges.h
+++ b/dom/html/TimeRanges.h
@@ -56,16 +56,19 @@ public:
   {
     return mRanges.Length();
   }
 
   virtual double Start(uint32_t aIndex, ErrorResult& aRv);
 
   virtual double End(uint32_t aIndex, ErrorResult& aRv);
 
+  // Shift all values by aOffset seconds.
+  void Shift(double aOffset);
+
 private:
   ~TimeRanges();
 
   // Comparator which orders TimeRanges by start time. Used by Normalize().
   struct TimeRange
   {
     TimeRange(double aStart, double aEnd)
       : mStart(aStart),
--- a/dom/ipc/PPluginWidget.ipdl
+++ b/dom/ipc/PPluginWidget.ipdl
@@ -43,21 +43,22 @@ parent:
    * window for plugins. On Linux, this returns an XID for a socket widget
    * embedded in the chrome side native window. On Windows this returns the
    * native HWND of the plugin widget.
    */
   sync GetNativePluginPort() returns (uintptr_t value);
 
 child:
   /**
-   * Sent from chrome when the chrome side widget is getting torn down.
-   * @param aParentInitiated - indicates if the shutdown is associated
-   * with a tab closure vs. content reload.
+   * Sent from content when a plugin is unloaded via layout. We shut down
+   * some things in response to this so that we don't end up firing async
+   * msgs after the child is destroyed. We know that after this call
+   * the child actor may not be on the other side.
    */
-  async ParentShutdown(bool aParentInitiated);
+  async ParentShutdown();
 
   /**
    * Requests a full update of the plugin window.
    */
   async UpdateWindow(uintptr_t aChildId);
 };
 
 }
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -60,19 +60,16 @@ public:
   // Called by the decode thread to keep track of the number of bytes read
   // from the resource.
   virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) = 0;
 
   // Increments the parsed and decoded frame counters by the passed in counts.
   // Can be called on any thread.
   virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) = 0;
 
-  // For decoders with a notion of timestamp offset, returns the value in microseconds.
-  virtual int64_t GetTimestampOffset() const { return 0; }
-
   // Return the duration of the media in microseconds.
   virtual int64_t GetMediaDuration() = 0;
 
   // Set the duration of the media in microseconds.
   virtual void SetMediaDuration(int64_t aDuration) = 0;
 
   // Sets the duration of the media in microseconds. The MediaDecoder
   // fires a durationchange event to its owner (e.g., an HTML audio
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -607,16 +607,18 @@ AudioCallbackDriver::Init()
     mAudioStream.own(stream);
   } else {
     NS_WARNING("Could not create a cubeb stream for MediaStreamGraph, falling back to a SystemClockDriver");
     // Fall back to a driver using a normal thread.
     mNextDriver = new SystemClockDriver(GraphImpl());
     mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd,
                                mStateComputedTime, mNextStateComputedTime);
     mGraphImpl->SetCurrentDriver(mNextDriver);
+    DebugOnly<bool> found = mGraphImpl->RemoveMixerCallback(this);
+    NS_WARN_IF_FALSE(!found, "Mixer callback not added when switching?");
     mNextDriver->Start();
     return;
   }
 
   cubeb_stream_register_device_changed_callback(mAudioStream,
                                                 AudioCallbackDriver::DeviceChangedCallback_s);
 
   StartStream();
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -43,16 +43,37 @@ AudioData::SizeOfIncludingThis(MallocSiz
 {
   size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData);
   if (mAudioBuffer) {
     size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
   }
   return size;
 }
 
+/* static */
+already_AddRefed<AudioData>
+AudioData::TransferAndUpdateTimestampAndDuration(AudioData* aOther,
+                                                  int64_t aTimestamp,
+                                                  int64_t aDuration)
+{
+  NS_ENSURE_TRUE(aOther, nullptr);
+  nsRefPtr<AudioData> v = new AudioData(aOther->mOffset,
+                                        aTimestamp,
+                                        aDuration,
+                                        aOther->mFrames,
+                                        aOther->mAudioData,
+                                        aOther->mChannels,
+                                        aOther->mRate);
+  v->mDiscontinuity = aOther->mDiscontinuity;
+  // Remove aOther's AudioData as it can't be shared across two targets.
+  aOther->mAudioData.forget();
+
+  return v.forget();
+}
+
 static bool
 ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
 {
   return aPlane.mWidth <= PlanarYCbCrImage::MAX_DIMENSION &&
          aPlane.mHeight <= PlanarYCbCrImage::MAX_DIMENSION &&
          aPlane.mWidth * aPlane.mHeight < MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
          aPlane.mStride > 0;
 }
@@ -132,32 +153,34 @@ VideoData::ShallowCopyUpdateDuration(Vid
                                      int64_t aDuration)
 {
   nsRefPtr<VideoData> v = new VideoData(aOther->mOffset,
                                         aOther->mTime,
                                         aDuration,
                                         aOther->mKeyframe,
                                         aOther->mTimecode,
                                         aOther->mDisplay);
+  v->mDiscontinuity = aOther->mDiscontinuity;
   v->mImage = aOther->mImage;
   return v.forget();
 }
 
 /* static */
 already_AddRefed<VideoData>
 VideoData::ShallowCopyUpdateTimestamp(VideoData* aOther,
                                       int64_t aTimestamp)
 {
   NS_ENSURE_TRUE(aOther, nullptr);
   nsRefPtr<VideoData> v = new VideoData(aOther->mOffset,
                                         aTimestamp,
                                         aOther->GetEndTime() - aTimestamp,
                                         aOther->mKeyframe,
                                         aOther->mTimecode,
                                         aOther->mDisplay);
+  v->mDiscontinuity = aOther->mDiscontinuity;
   v->mImage = aOther->mImage;
   return v.forget();
 }
 
 /* static */
 already_AddRefed<VideoData>
 VideoData::ShallowCopyUpdateTimestampAndDuration(VideoData* aOther,
                                                  int64_t aTimestamp,
@@ -165,16 +188,17 @@ VideoData::ShallowCopyUpdateTimestampAnd
 {
   NS_ENSURE_TRUE(aOther, nullptr);
   nsRefPtr<VideoData> v = new VideoData(aOther->mOffset,
                                         aTimestamp,
                                         aDuration,
                                         aOther->mKeyframe,
                                         aOther->mTimecode,
                                         aOther->mDisplay);
+  v->mDiscontinuity = aOther->mDiscontinuity;
   v->mImage = aOther->mImage;
   return v.forget();
 }
 
 /* static */
 void VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage,
                                     VideoInfo& aInfo,
                                     const YCbCrBuffer &aBuffer,
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -79,16 +79,25 @@ public:
             uint32_t aChannels,
             uint32_t aRate)
     : MediaData(AUDIO_DATA, aOffset, aTime, aDuration)
     , mFrames(aFrames)
     , mChannels(aChannels)
     , mRate(aRate)
     , mAudioData(aData) {}
 
+  // Creates a new VideoData identical to aOther, but with a different
+  // specified timestamp and duration. All data from aOther is copied
+  // into the new AudioData but the audio data which is transferred.
+  // After such call, the original aOther is unusable.
+  static already_AddRefed<AudioData>
+  TransferAndUpdateTimestampAndDuration(AudioData* aOther,
+                                        int64_t aTimestamp,
+                                        int64_t aDuration);
+
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
   // If mAudioBuffer is null, creates it from mAudioData.
   void EnsureAudioBuffer();
 
   const uint32_t mFrames;
   const uint32_t mChannels;
   const uint32_t mRate;
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -172,17 +172,17 @@ MediaDecoderReader::ComputeStartTime(con
 {
   int64_t startTime = std::min<int64_t>(aAudio ? aAudio->mTime : INT64_MAX,
                                         aVideo ? aVideo->mTime : INT64_MAX);
   if (startTime == INT64_MAX) {
     startTime = 0;
   }
   DECODER_LOG("ComputeStartTime first video frame start %lld", aVideo ? aVideo->mTime : -1);
   DECODER_LOG("ComputeStartTime first audio frame start %lld", aAudio ? aAudio->mTime : -1);
-  MOZ_ASSERT(startTime >= 0);
+  NS_ASSERTION(startTime >= 0, "Start time is negative");
   return startTime;
 }
 
 class RequestVideoWithSkipTask : public nsRunnable {
 public:
   RequestVideoWithSkipTask(MediaDecoderReader* aReader,
                            int64_t aTimeThreshold)
     : mReader(aReader)
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -425,16 +425,21 @@ public:
   /**
    * Not safe to call off the MediaStreamGraph thread unless monitor is held!
    */
   GraphDriver* CurrentDriver() {
     AssertOnGraphThreadOrNotRunning();
     return mDriver;
   }
 
+  bool RemoveMixerCallback(MixerCallbackReceiver* aReceiver)
+  {
+    return mMixer.RemoveCallback(aReceiver);
+  }
+
   /**
    * Effectively set the new driver, while we are switching.
    * It is only safe to call this at the very end of an iteration, when there
    * has been a SwitchAtNextIteration call during the iteration. The driver
    * should return and pass the control to the new driver shortly after.
    * We can also switch from Revive() (on MainThread), in which case the
    * monitor is held
    */
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -201,17 +201,16 @@ static bool sIsEMEEnabled = false;
 static bool sDemuxSkipToNextKeyframe = true;
 
 nsresult
 MP4Reader::Init(MediaDecoderReader* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   PlatformDecoderModule::Init();
   mStream = new MP4Stream(mDecoder->GetResource());
-  mTimestampOffset = GetDecoder()->GetTimestampOffset();
 
   InitLayersBackendType();
 
   mAudio.mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   NS_ENSURE_TRUE(mAudio.mTaskQueue, NS_ERROR_FAILURE);
 
   mVideo.mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   NS_ENSURE_TRUE(mVideo.mTaskQueue, NS_ERROR_FAILURE);
@@ -337,17 +336,17 @@ MP4Reader::PreReadMetadata()
   if (mPlatform) {
     RequestCodecResource();
   }
 }
 
 bool
 MP4Reader::InitDemuxer()
 {
-  mDemuxer = new MP4Demuxer(mStream, mTimestampOffset, &mDemuxerMonitor);
+  mDemuxer = new MP4Demuxer(mStream, &mDemuxerMonitor);
   return mDemuxer->Init();
 }
 
 nsresult
 MP4Reader::ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags)
 {
   if (!mDemuxerInitialized) {
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -122,17 +122,16 @@ private:
   virtual bool IsWaitingOnCDMResource() MOZ_OVERRIDE;
 
   Microseconds GetNextKeyframeTime();
   bool ShouldSkip(bool aSkipToNextKeyframe, int64_t aTimeThreshold);
 
   size_t SizeOfQueue(TrackType aTrack);
 
   nsRefPtr<MP4Stream> mStream;
-  int64_t mTimestampOffset;
   nsAutoPtr<mp4_demuxer::MP4Demuxer> mDemuxer;
   nsRefPtr<PlatformDecoderModule> mPlatform;
 
   class DecoderCallback : public MediaDataDecoderCallback {
   public:
     DecoderCallback(MP4Reader* aReader,
                     mp4_demuxer::TrackType aType)
       : mReader(aReader)
--- a/dom/media/gtest/TestMP4Demuxer.cpp
+++ b/dom/media/gtest/TestMP4Demuxer.cpp
@@ -19,17 +19,17 @@ public:
 
   nsRefPtr<MockMediaResource> resource;
   Monitor mMonitor;
   nsAutoPtr<MP4Demuxer> demuxer;
 
   explicit MP4DemuxerBinding(const char* aFileName = "dash_dashinit.mp4")
     : resource(new MockMediaResource(aFileName))
     , mMonitor("TestMP4Demuxer monitor")
-    , demuxer(new MP4Demuxer(new MP4Stream(resource), 0, &mMonitor))
+    , demuxer(new MP4Demuxer(new MP4Stream(resource), &mMonitor))
   {
     EXPECT_EQ(NS_OK, resource->Open(nullptr));
   }
 
 private:
   virtual ~MP4DemuxerBinding()
   {
   }
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -280,17 +280,17 @@ public:
     bool initSegment = IsInitSegmentPresent(aData);
     if (initSegment) {
       mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/mp4"));
       mStream = new MP4Stream(mResource);
       // We use a timestampOffset of 0 for ContainerParser, and require
       // consumers of ParseStartAndEndTimestamps to add their timestamp offset
       // manually. This allows the ContainerParser to be shared across different
       // timestampOffsets.
-      mParser = new mp4_demuxer::MoofParser(mStream, 0, 0, &mMonitor);
+      mParser = new mp4_demuxer::MoofParser(mStream, 0, &mMonitor);
       mInitData = new LargeDataBuffer();
     } else if (!mStream || !mParser) {
       return false;
     }
 
     mResource->AppendData(aData);
     nsTArray<MediaByteRange> byteRanges;
     MediaByteRange mbr =
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -49,16 +49,18 @@ MediaSourceReader::MediaSourceReader(Med
   : MediaDecoderReader(aDecoder)
   , mLastAudioTime(0)
   , mLastVideoTime(0)
   , mPendingSeekTime(-1)
   , mWaitingForSeekData(false)
   , mTimeThreshold(0)
   , mDropAudioBeforeThreshold(false)
   , mDropVideoBeforeThreshold(false)
+  , mAudioDiscontinuity(false)
+  , mVideoDiscontinuity(false)
   , mEnded(false)
   , mMediaSourceDuration(0)
   , mHasEssentialTrackBuffers(false)
 #ifdef MOZ_FMP4
   , mSharedDecoderManager(new SharedDecoderManager())
 #endif
 {
 }
@@ -86,123 +88,137 @@ MediaSourceReader::IsWaitingMediaResourc
   }
 
   return !mHasEssentialTrackBuffers;
 }
 
 size_t
 MediaSourceReader::SizeOfVideoQueueInFrames()
 {
-  if (!mVideoReader) {
+  if (!GetVideoReader()) {
     MSE_DEBUG("MediaSourceReader(%p)::SizeOfVideoQueue called with no video reader", this);
     return 0;
   }
-  return mVideoReader->SizeOfVideoQueueInFrames();
+  return GetVideoReader()->SizeOfVideoQueueInFrames();
 }
 
 size_t
 MediaSourceReader::SizeOfAudioQueueInFrames()
 {
-  if (!mAudioReader) {
+  if (!GetAudioReader()) {
     MSE_DEBUG("MediaSourceReader(%p)::SizeOfAudioQueue called with no audio reader", this);
     return 0;
   }
-  return mAudioReader->SizeOfAudioQueueInFrames();
+  return GetAudioReader()->SizeOfAudioQueueInFrames();
 }
 
 nsRefPtr<MediaDecoderReader::AudioDataPromise>
 MediaSourceReader::RequestAudioData()
 {
   nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
   MSE_DEBUGV("MediaSourceReader(%p)::RequestAudioData", this);
-  if (!mAudioReader) {
+  if (!GetAudioReader()) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this);
     mAudioPromise.Reject(DECODE_ERROR, __func__);
     return p;
   }
   if (IsSeeking()) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called mid-seek. Rejecting.", this);
     mAudioPromise.Reject(CANCELED, __func__);
     return p;
   }
   MOZ_DIAGNOSTIC_ASSERT(!mAudioSeekRequest.Exists());
 
-  SwitchReaderResult ret = SwitchAudioReader(mLastAudioTime);
+  SwitchSourceResult ret = SwitchAudioSource(&mLastAudioTime);
   switch (ret) {
-    case READER_NEW:
-      mAudioSeekRequest.Begin(mAudioReader->Seek(mLastAudioTime, 0)
+    case SOURCE_NEW:
+      mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mLastAudioTime), 0)
                               ->RefableThen(GetTaskQueue(), __func__, this,
                                             &MediaSourceReader::CompleteAudioSeekAndDoRequest,
                                             &MediaSourceReader::CompleteAudioSeekAndRejectPromise));
       break;
-    case READER_ERROR:
+    case SOURCE_ERROR:
       if (mLastAudioTime) {
         CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
         break;
       }
       // Fallback to using current reader
     default:
       DoAudioRequest();
       break;
   }
   return p;
 }
 
 void MediaSourceReader::DoAudioRequest()
 {
-  mAudioRequest.Begin(mAudioReader->RequestAudioData()
+  mAudioRequest.Begin(GetAudioReader()->RequestAudioData()
                       ->RefableThen(GetTaskQueue(), __func__, this,
                                     &MediaSourceReader::OnAudioDecoded,
                                     &MediaSourceReader::OnAudioNotDecoded));
 }
 
 void
 MediaSourceReader::OnAudioDecoded(AudioData* aSample)
 {
   MOZ_DIAGNOSTIC_ASSERT(!IsSeeking());
   mAudioRequest.Complete();
 
+  int64_t ourTime = aSample->mTime + mAudioSourceDecoder->GetTimestampOffset();
+  if (aSample->mDiscontinuity) {
+    mAudioDiscontinuity = true;
+  }
+
   MSE_DEBUGV("MediaSourceReader(%p)::OnAudioDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]",
-             this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity);
+             this, ourTime, aSample->mDuration, aSample->mDiscontinuity);
   if (mDropAudioBeforeThreshold) {
-    if (aSample->mTime < mTimeThreshold) {
+    if (ourTime < mTimeThreshold) {
       MSE_DEBUG("MediaSourceReader(%p)::OnAudioDecoded mTime=%lld < mTimeThreshold=%lld",
-                this, aSample->mTime, mTimeThreshold);
-      mAudioRequest.Begin(mAudioReader->RequestAudioData()
+                this, ourTime, mTimeThreshold);
+      mAudioRequest.Begin(GetAudioReader()->RequestAudioData()
                           ->RefableThen(GetTaskQueue(), __func__, this,
                                         &MediaSourceReader::OnAudioDecoded,
                                         &MediaSourceReader::OnAudioNotDecoded));
       return;
     }
     mDropAudioBeforeThreshold = false;
   }
 
-  mLastAudioTime = aSample->mTime + aSample->mDuration;
+  // Adjust the sample time into our reference.
+  nsRefPtr<AudioData> newSample =
+    AudioData::TransferAndUpdateTimestampAndDuration(aSample,
+                                                     ourTime,
+                                                     aSample->mDuration);
+  mLastAudioTime = newSample->GetEndTime();
+  if (mAudioDiscontinuity) {
+    newSample->mDiscontinuity = true;
+    mAudioDiscontinuity = false;
+  }
 
-  mAudioPromise.Resolve(aSample, __func__);
+  mAudioPromise.Resolve(newSample, __func__);
 }
 
 // Find the closest approximation to the end time for this stream.
 // mLast{Audio,Video}Time differs from the actual end time because of
 // Bug 1065207 - the duration of a WebM fragment is an estimate not the
 // actual duration. In the case of audio time an example of where they
 // differ would be the actual sample duration being small but the
 // previous sample being large. The buffered end time uses that last
 // sample duration as an estimate of the end time duration giving an end
 // time that is greater than mLastAudioTime, which is the actual sample
 // end time.
 // Reader switching is based on the buffered end time though so they can be
 // quite different. By using the EOS_FUZZ_US and the buffered end time we
 // attempt to account for this difference.
 static void
-AdjustEndTime(int64_t* aEndTime, MediaDecoderReader* aReader)
+AdjustEndTime(int64_t* aEndTime, SourceBufferDecoder* aDecoder)
 {
-  if (aReader) {
+  if (aDecoder) {
     nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
-    aReader->GetBuffered(ranges);
+    aDecoder->GetBuffered(ranges);
     if (ranges->Length() > 0) {
       // End time is a double so we convert to nearest by adding 0.5.
       int64_t end = ranges->GetEndTime() * USECS_PER_S + 0.5;
       *aEndTime = std::max(*aEndTime, end);
     }
   }
 }
 
@@ -216,109 +232,124 @@ MediaSourceReader::OnAudioNotDecoded(Not
   if (aReason == DECODE_ERROR || aReason == CANCELED) {
     mAudioPromise.Reject(aReason, __func__);
     return;
   }
 
   // End of stream. Force switching past this stream to another reader by
   // switching to the end of the buffered range.
   MOZ_ASSERT(aReason == END_OF_STREAM);
-  if (mAudioReader) {
-    AdjustEndTime(&mLastAudioTime, mAudioReader);
+  if (mAudioSourceDecoder) {
+    AdjustEndTime(&mLastAudioTime, mAudioSourceDecoder);
   }
 
-  // See if we can find a different reader that can pick up where we left off.
-  if (SwitchAudioReader(mLastAudioTime) == READER_NEW) {
-    mAudioSeekRequest.Begin(mAudioReader->Seek(mLastAudioTime, 0)
+  // See if we can find a different source that can pick up where we left off.
+  if (SwitchAudioSource(&mLastAudioTime) == SOURCE_NEW) {
+    mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mLastAudioTime), 0)
                             ->RefableThen(GetTaskQueue(), __func__, this,
                                           &MediaSourceReader::CompleteAudioSeekAndDoRequest,
                                           &MediaSourceReader::CompleteAudioSeekAndRejectPromise));
     return;
   }
 
   CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
 }
 
-
 nsRefPtr<MediaDecoderReader::VideoDataPromise>
 MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
 {
   nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
   MSE_DEBUGV("MediaSourceReader(%p)::RequestVideoData(%d, %lld)",
              this, aSkipToNextKeyframe, aTimeThreshold);
-  if (!mVideoReader) {
+  if (!GetVideoReader()) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called with no video reader", this);
     mVideoPromise.Reject(DECODE_ERROR, __func__);
     return p;
   }
   if (aSkipToNextKeyframe) {
     mTimeThreshold = aTimeThreshold;
     mDropAudioBeforeThreshold = true;
     mDropVideoBeforeThreshold = true;
   }
   if (IsSeeking()) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called mid-seek. Rejecting.", this);
     mVideoPromise.Reject(CANCELED, __func__);
     return p;
   }
   MOZ_DIAGNOSTIC_ASSERT(!mVideoSeekRequest.Exists());
 
-  SwitchReaderResult ret = SwitchVideoReader(mLastVideoTime);
+  SwitchSourceResult ret = SwitchVideoSource(&mLastVideoTime);
   switch (ret) {
-    case READER_NEW:
-      mVideoSeekRequest.Begin(mVideoReader->Seek(mLastVideoTime, 0)
+    case SOURCE_NEW:
+      mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mLastVideoTime), 0)
                              ->RefableThen(GetTaskQueue(), __func__, this,
                                            &MediaSourceReader::CompleteVideoSeekAndDoRequest,
                                            &MediaSourceReader::CompleteVideoSeekAndRejectPromise));
       break;
-    case READER_ERROR:
+    case SOURCE_ERROR:
       if (mLastVideoTime) {
         CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
         break;
       }
       // Fallback to using current reader.
     default:
       DoVideoRequest();
       break;
   }
 
   return p;
 }
 
 void
 MediaSourceReader::DoVideoRequest()
 {
-  mVideoRequest.Begin(mVideoReader->RequestVideoData(mDropVideoBeforeThreshold, mTimeThreshold)
+  mVideoRequest.Begin(GetVideoReader()->RequestVideoData(mDropVideoBeforeThreshold, GetReaderVideoTime(mTimeThreshold))
                       ->RefableThen(GetTaskQueue(), __func__, this,
                                     &MediaSourceReader::OnVideoDecoded,
                                     &MediaSourceReader::OnVideoNotDecoded));
 }
 
 void
 MediaSourceReader::OnVideoDecoded(VideoData* aSample)
 {
   MOZ_DIAGNOSTIC_ASSERT(!IsSeeking());
   mVideoRequest.Complete();
 
+  // Adjust the sample time into our reference.
+  int64_t ourTime = aSample->mTime + mVideoSourceDecoder->GetTimestampOffset();
+  if (aSample->mDiscontinuity) {
+    mVideoDiscontinuity = true;
+  }
+
   MSE_DEBUGV("MediaSourceReader(%p)::OnVideoDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]",
-             this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity);
+             this, ourTime, aSample->mDuration, aSample->mDiscontinuity);
   if (mDropVideoBeforeThreshold) {
-    if (aSample->mTime < mTimeThreshold) {
+    if (ourTime < mTimeThreshold) {
       MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld",
-                this, aSample->mTime, mTimeThreshold);
+                this, ourTime, mTimeThreshold);
       DoVideoRequest();
       return;
     }
     mDropVideoBeforeThreshold = false;
     mTimeThreshold = 0;
   }
 
-  mLastVideoTime = aSample->mTime + aSample->mDuration;
+  // Adjust the sample time into our reference.
+  nsRefPtr<VideoData> newSample =
+    VideoData::ShallowCopyUpdateTimestampAndDuration(aSample,
+                                                     ourTime,
+                                                     aSample->mDuration);
 
-  mVideoPromise.Resolve(aSample, __func__);
+  mLastVideoTime = newSample->GetEndTime();
+  if (mVideoDiscontinuity) {
+    newSample->mDiscontinuity = true;
+    mVideoDiscontinuity = false;
+  }
+
+  mVideoPromise.Resolve(newSample, __func__);
 }
 
 void
 MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason)
 {
   MOZ_DIAGNOSTIC_ASSERT(!IsSeeking());
   mVideoRequest.Complete();
 
@@ -326,23 +357,23 @@ MediaSourceReader::OnVideoNotDecoded(Not
   if (aReason == DECODE_ERROR || aReason == CANCELED) {
     mVideoPromise.Reject(aReason, __func__);
     return;
   }
 
   // End of stream. Force switching past this stream to another reader by
   // switching to the end of the buffered range.
   MOZ_ASSERT(aReason == END_OF_STREAM);
-  if (mVideoReader) {
-    AdjustEndTime(&mLastVideoTime, mVideoReader);
+  if (mVideoSourceDecoder) {
+    AdjustEndTime(&mLastVideoTime, mVideoSourceDecoder);
   }
 
   // See if we can find a different reader that can pick up where we left off.
-  if (SwitchVideoReader(mLastVideoTime) == READER_NEW) {
-    mVideoSeekRequest.Begin(mVideoReader->Seek(mLastVideoTime, 0)
+  if (SwitchVideoSource(&mLastVideoTime) == SOURCE_NEW) {
+    mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mLastVideoTime), 0)
                            ->RefableThen(GetTaskQueue(), __func__, this,
                                          &MediaSourceReader::CompleteVideoSeekAndDoRequest,
                                          &MediaSourceReader::CompleteVideoSeekAndRejectPromise));
     return;
   }
 
   CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
 }
@@ -390,19 +421,19 @@ MediaSourceReader::ContinueShutdown()
                                        &MediaSourceReader::ContinueShutdown,
                                        &MediaSourceReader::ContinueShutdown);
     mShutdownTrackBuffers.AppendElement(mTrackBuffers[0]);
     mTrackBuffers.RemoveElementAt(0);
     return;
   }
 
   mAudioTrack = nullptr;
-  mAudioReader = nullptr;
+  mAudioSourceDecoder = nullptr;
   mVideoTrack = nullptr;
-  mVideoReader = nullptr;
+  mVideoSourceDecoder = nullptr;
 
 #ifdef MOZ_FMP4
   if (mSharedDecoderManager) {
     mSharedDecoderManager->Shutdown();
     mSharedDecoderManager = nullptr;
   }
 #endif
 
@@ -417,130 +448,160 @@ MediaSourceReader::ContinueShutdown()
 
 void
 MediaSourceReader::BreakCycles()
 {
   MediaDecoderReader::BreakCycles();
 
   // These were cleared in Shutdown().
   MOZ_ASSERT(!mAudioTrack);
-  MOZ_ASSERT(!mAudioReader);
+  MOZ_ASSERT(!mAudioSourceDecoder);
   MOZ_ASSERT(!mVideoTrack);
-  MOZ_ASSERT(!mVideoReader);
+  MOZ_ASSERT(!mVideoSourceDecoder);
   MOZ_ASSERT(!mTrackBuffers.Length());
 
   for (uint32_t i = 0; i < mShutdownTrackBuffers.Length(); ++i) {
     mShutdownTrackBuffers[i]->BreakCycles();
   }
   mShutdownTrackBuffers.Clear();
 }
 
-already_AddRefed<MediaDecoderReader>
-MediaSourceReader::SelectReader(int64_t aTarget,
-                                int64_t aTolerance,
-                                const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders)
+already_AddRefed<SourceBufferDecoder>
+MediaSourceReader::SelectDecoder(int64_t aTarget,
+                                 int64_t aTolerance,
+                                 const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders)
 {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
   // Consider decoders in order of newest to oldest, as a newer decoder
   // providing a given buffered range is expected to replace an older one.
   for (int32_t i = aTrackDecoders.Length() - 1; i >= 0; --i) {
-    nsRefPtr<MediaDecoderReader> newReader = aTrackDecoders[i]->GetReader();
+    nsRefPtr<SourceBufferDecoder> newDecoder = aTrackDecoders[i];
 
     nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
-    aTrackDecoders[i]->GetBuffered(ranges);
+    newDecoder->GetBuffered(ranges);
     if (ranges->Find(double(aTarget) / USECS_PER_S,
                      double(aTolerance) / USECS_PER_S) == dom::TimeRanges::NoIndex) {
-      MSE_DEBUGV("MediaSourceReader(%p)::SelectReader(%lld) newReader=%p target not in ranges=%s",
-                 this, aTarget, newReader.get(), DumpTimeRanges(ranges).get());
+      MSE_DEBUGV("MediaSourceReader(%p)::SelectDecoder(%lld) newDecoder=%p target not in ranges=%s",
+                 this, aTarget, newDecoder.get(), DumpTimeRanges(ranges).get());
       continue;
     }
 
-    return newReader.forget();
+    return newDecoder.forget();
   }
 
   return nullptr;
 }
 
 bool
 MediaSourceReader::HaveData(int64_t aTarget, MediaData::Type aType)
 {
   TrackBuffer* trackBuffer = aType == MediaData::AUDIO_DATA ? mAudioTrack : mVideoTrack;
   MOZ_ASSERT(trackBuffer);
-  nsRefPtr<MediaDecoderReader> reader = SelectReader(aTarget, EOS_FUZZ_US, trackBuffer->Decoders());
-  return !!reader;
+  nsRefPtr<SourceBufferDecoder> decoder = SelectDecoder(aTarget, EOS_FUZZ_US, trackBuffer->Decoders());
+  return !!decoder;
 }
 
-MediaSourceReader::SwitchReaderResult
-MediaSourceReader::SwitchAudioReader(int64_t aTarget)
+MediaSourceReader::SwitchSourceResult
+MediaSourceReader::SwitchAudioSource(int64_t* aTarget)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   // XXX: Can't handle adding an audio track after ReadMetadata.
   if (!mAudioTrack) {
-    return READER_ERROR;
+    return SOURCE_ERROR;
   }
 
   // We first search without the tolerance and then search with it, so that, in
   // the case of perfectly-aligned data, we don't prematurely jump to a new
   // reader and skip the last few samples of the current one.
-  nsRefPtr<MediaDecoderReader> newReader = SelectReader(aTarget, /* aTolerance = */ 0, mAudioTrack->Decoders());
-  if (!newReader) {
-    newReader = SelectReader(aTarget, EOS_FUZZ_US, mAudioTrack->Decoders());
+  bool usedFuzz = false;
+  nsRefPtr<SourceBufferDecoder> newDecoder =
+    SelectDecoder(*aTarget, /* aTolerance = */ 0, mAudioTrack->Decoders());
+  if (!newDecoder) {
+    newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mAudioTrack->Decoders());
+    usedFuzz = true;
   }
-  if (newReader && newReader != mAudioReader) {
-    mAudioReader->SetIdle();
-    mAudioReader = newReader;
-    MSE_DEBUGV("MediaSourceReader(%p)::SwitchAudioReader switched reader to %p", this, mAudioReader.get());
-    return READER_NEW;
+  if (newDecoder && newDecoder != mAudioSourceDecoder) {
+    GetAudioReader()->SetIdle();
+    mAudioSourceDecoder = newDecoder;
+    if (usedFuzz) {
+      // A decoder buffered range is continuous. We would have failed the exact
+      // search but succeeded the fuzzy one if our target was shortly before
+      // start time.
+      nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
+      newDecoder->GetBuffered(ranges);
+      int64_t startTime = ranges->GetStartTime() * USECS_PER_S;
+      if (*aTarget < startTime) {
+        *aTarget = startTime;
+      }
+    }
+    MSE_DEBUGV("MediaSourceReader(%p)::SwitchAudioSource switched decoder to %p (fuzz:%d)",
+               this, mAudioSourceDecoder.get(), usedFuzz);
+    return SOURCE_NEW;
   }
-  return newReader ? READER_EXISTING : READER_ERROR;
+  return newDecoder ? SOURCE_EXISTING : SOURCE_ERROR;
 }
 
-MediaSourceReader::SwitchReaderResult
-MediaSourceReader::SwitchVideoReader(int64_t aTarget)
+MediaSourceReader::SwitchSourceResult
+MediaSourceReader::SwitchVideoSource(int64_t* aTarget)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   // XXX: Can't handle adding a video track after ReadMetadata.
   if (!mVideoTrack) {
-    return READER_ERROR;
+    return SOURCE_ERROR;
   }
 
   // We first search without the tolerance and then search with it, so that, in
   // the case of perfectly-aligned data, we don't prematurely jump to a new
   // reader and skip the last few samples of the current one.
-  nsRefPtr<MediaDecoderReader> newReader = SelectReader(aTarget, /* aTolerance = */ 0, mVideoTrack->Decoders());
-  if (!newReader) {
-    newReader = SelectReader(aTarget, EOS_FUZZ_US, mVideoTrack->Decoders());
+  bool usedFuzz = false;
+  nsRefPtr<SourceBufferDecoder> newDecoder =
+    SelectDecoder(*aTarget, /* aTolerance = */ 0, mVideoTrack->Decoders());
+  if (!newDecoder) {
+    newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mVideoTrack->Decoders());
+    usedFuzz = true;
   }
-  if (newReader && newReader != mVideoReader) {
-    mVideoReader->SetIdle();
-    mVideoReader = newReader;
-    MSE_DEBUGV("MediaSourceReader(%p)::SwitchVideoReader switched reader to %p", this, mVideoReader.get());
-    return READER_NEW;
+  if (newDecoder && newDecoder != mVideoSourceDecoder) {
+    GetVideoReader()->SetIdle();
+    mVideoSourceDecoder = newDecoder;
+    if (usedFuzz) {
+      // A decoder buffered range is continuous. We would have failed the exact
+      // search but succeeded the fuzzy one if our target was shortly before
+      // start time.
+      nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
+      newDecoder->GetBuffered(ranges);
+      int64_t startTime = ranges->GetStartTime() * USECS_PER_S;
+      if (*aTarget < startTime) {
+        *aTarget = startTime;
+      }
+    }
+    MSE_DEBUGV("MediaSourceReader(%p)::SwitchVideoSource switched decoder to %p (fuzz:%d)",
+               this, mVideoSourceDecoder.get(), usedFuzz);
+    return SOURCE_NEW;
   }
-  return newReader ? READER_EXISTING : READER_ERROR;
+  return newDecoder ? SOURCE_EXISTING : SOURCE_ERROR;
 }
 
 bool
 MediaSourceReader::IsDormantNeeded()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  if (mVideoReader) {
-    return mVideoReader->IsDormantNeeded();
+  if (GetVideoReader()) {
+    return GetVideoReader()->IsDormantNeeded();
   }
 
   return false;
 }
 
 void
 MediaSourceReader::ReleaseMediaResources()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  if (mVideoReader) {
-    mVideoReader->ReleaseMediaResources();
+  if (GetVideoReader()) {
+    GetVideoReader()->ReleaseMediaResources();
   }
 }
 
 MediaDecoderReader*
 CreateReaderForType(const nsACString& aType, AbstractMediaDecoder* aDecoder)
 {
 #ifdef MOZ_FMP4
   // The MP4Reader that supports fragmented MP4 and uses
@@ -699,79 +760,87 @@ MediaSourceReader::Seek(int64_t aTime, i
 
   // Store pending seek target in case the track buffers don't contain
   // the desired time and we delay doing the seek.
   mPendingSeekTime = aTime;
 
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mWaitingForSeekData = true;
+    mDropAudioBeforeThreshold = false;
+    mDropVideoBeforeThreshold = false;
+    mTimeThreshold = 0;
   }
 
   AttemptSeek();
   return p;
 }
 
 void
 MediaSourceReader::CancelSeek()
 {
   MOZ_ASSERT(OnDecodeThread());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mWaitingForSeekData = false;
   mPendingSeekTime = -1;
-  if (mAudioReader) {
+  if (GetAudioReader()) {
     mAudioSeekRequest.DisconnectIfExists();
-    mAudioReader->CancelSeek();
+    GetAudioReader()->CancelSeek();
   }
-  if (mVideoReader) {
+  if (GetVideoReader()) {
     mVideoSeekRequest.DisconnectIfExists();
-    mVideoReader->CancelSeek();
+    GetVideoReader()->CancelSeek();
   }
   mSeekPromise.RejectIfExists(NS_OK, __func__);
 }
 
 void
 MediaSourceReader::OnVideoSeekCompleted(int64_t aTime)
 {
   mVideoSeekRequest.Complete();
 
+  // The aTime we receive is in the sub-reader's reference.
+  int64_t ourTime = aTime + mVideoSourceDecoder->GetTimestampOffset();
+
   if (mAudioTrack) {
-    mPendingSeekTime = aTime;
+    mPendingSeekTime = ourTime;
     DoAudioSeek();
   } else {
     mPendingSeekTime = -1;
-    mSeekPromise.Resolve(aTime, __func__);
+    mSeekPromise.Resolve(ourTime, __func__);
   }
 }
 
 void
 MediaSourceReader::OnVideoSeekFailed(nsresult aResult)
 {
   mVideoSeekRequest.Complete();
   mPendingSeekTime = -1;
   mSeekPromise.Reject(aResult, __func__);
 }
 
 void
 MediaSourceReader::DoAudioSeek()
 {
-    SwitchAudioReader(mPendingSeekTime);
-    mAudioSeekRequest.Begin(mAudioReader->Seek(mPendingSeekTime, 0)
-                           ->RefableThen(GetTaskQueue(), __func__, this,
-                                         &MediaSourceReader::OnAudioSeekCompleted,
-                                         &MediaSourceReader::OnAudioSeekFailed));
-    MSE_DEBUG("MediaSourceReader(%p)::DoAudioSeek reader=%p", this, mAudioReader.get());
+  SwitchAudioSource(&mPendingSeekTime);
+  mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mPendingSeekTime), 0)
+                         ->RefableThen(GetTaskQueue(), __func__, this,
+                                       &MediaSourceReader::OnAudioSeekCompleted,
+                                       &MediaSourceReader::OnAudioSeekFailed));
+  MSE_DEBUG("MediaSourceReader(%p)::DoAudioSeek reader=%p", this, GetAudioReader());
 }
 
 void
 MediaSourceReader::OnAudioSeekCompleted(int64_t aTime)
 {
   mAudioSeekRequest.Complete();
   mPendingSeekTime = -1;
-  mSeekPromise.Resolve(aTime, __func__);
+  // The aTime we receive is in the sub-reader's reference.
+  mSeekPromise.Resolve(aTime + mAudioSourceDecoder->GetTimestampOffset(),
+                       __func__);
 }
 
 void
 MediaSourceReader::OnAudioSeekFailed(nsresult aResult)
 {
   mAudioSeekRequest.Complete();
   mPendingSeekTime = -1;
   mSeekPromise.Reject(aResult, __func__);
@@ -806,22 +875,22 @@ MediaSourceReader::AttemptSeek()
   } else {
     MOZ_CRASH();
   }
 }
 
 void
 MediaSourceReader::DoVideoSeek()
 {
-  SwitchVideoReader(mPendingSeekTime);
-  mVideoSeekRequest.Begin(mVideoReader->Seek(mPendingSeekTime, 0)
+  SwitchVideoSource(&mPendingSeekTime);
+  mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mPendingSeekTime), 0)
                           ->RefableThen(GetTaskQueue(), __func__, this,
                                         &MediaSourceReader::OnVideoSeekCompleted,
                                         &MediaSourceReader::OnVideoSeekFailed));
-  MSE_DEBUG("MediaSourceReader(%p)::DoVideoSeek reader=%p", this, mVideoReader.get());
+  MSE_DEBUG("MediaSourceReader(%p)::DoVideoSeek reader=%p", this, GetVideoReader());
 }
 
 nsresult
 MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(aBuffered->Length() == 0);
   if (mTrackBuffers.IsEmpty()) {
@@ -898,63 +967,63 @@ MediaSourceReader::ReadMetadata(MediaInf
   if (!mAudioTrack && !mVideoTrack) {
     MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata missing track: mAudioTrack=%p mVideoTrack=%p",
               this, mAudioTrack.get(), mVideoTrack.get());
     return NS_ERROR_FAILURE;
   }
 
   if (mAudioTrack) {
     MOZ_ASSERT(mAudioTrack->IsReady());
-    mAudioReader = mAudioTrack->Decoders()[0]->GetReader();
+    mAudioSourceDecoder = mAudioTrack->Decoders()[0];
 
-    const MediaInfo& info = mAudioReader->GetMediaInfo();
+    const MediaInfo& info = GetAudioReader()->GetMediaInfo();
     MOZ_ASSERT(info.HasAudio());
     mInfo.mAudio = info.mAudio;
     mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
     MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p duration=%lld",
-              this, mAudioReader.get(),
-              mAudioReader->GetDecoder()->GetMediaDuration());
+              this, mAudioSourceDecoder.get(),
+              mAudioSourceDecoder->GetReader()->GetDecoder()->GetMediaDuration());
   }
 
   if (mVideoTrack) {
     MOZ_ASSERT(mVideoTrack->IsReady());
-    mVideoReader = mVideoTrack->Decoders()[0]->GetReader();
+    mVideoSourceDecoder = mVideoTrack->Decoders()[0];
 
-    const MediaInfo& info = mVideoReader->GetMediaInfo();
+    const MediaInfo& info = GetVideoReader()->GetMediaInfo();
     MOZ_ASSERT(info.HasVideo());
     mInfo.mVideo = info.mVideo;
     mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
     MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p duration=%lld",
-              this, mVideoReader.get(),
-              mVideoReader->GetDecoder()->GetMediaDuration());
+              this, GetVideoReader(),
+              GetVideoReader()->GetDecoder()->GetMediaDuration());
   }
 
   *aInfo = mInfo;
   *aTags = nullptr; // TODO: Handle metadata.
 
   return NS_OK;
 }
 
 void
 MediaSourceReader::ReadUpdatedMetadata(MediaInfo* aInfo)
 {
   if (mAudioTrack) {
     MOZ_ASSERT(mAudioTrack->IsReady());
-    mAudioReader = mAudioTrack->Decoders()[0]->GetReader();
+    mAudioSourceDecoder = mAudioTrack->Decoders()[0];
 
-    const MediaInfo& info = mAudioReader->GetMediaInfo();
+    const MediaInfo& info = GetAudioReader()->GetMediaInfo();
     MOZ_ASSERT(info.HasAudio());
     mInfo.mAudio = info.mAudio;
   }
 
   if (mVideoTrack) {
     MOZ_ASSERT(mVideoTrack->IsReady());
-    mVideoReader = mVideoTrack->Decoders()[0]->GetReader();
+    mVideoSourceDecoder = mVideoTrack->Decoders()[0];
 
-    const MediaInfo& info = mVideoReader->GetMediaInfo();
+    const MediaInfo& info = GetVideoReader()->GetMediaInfo();
     MOZ_ASSERT(info.HasVideo());
     mInfo.mVideo = info.mVideo;
   }
   *aInfo = mInfo;
 }
 
 void
 MediaSourceReader::Ended()
@@ -994,31 +1063,31 @@ MediaSourceReader::GetMozDebugReaderData
     result += nsPrintfCString("\tDumping Audio Track Decoders: - mLastAudioTime: %f\n", double(mLastAudioTime) / USECS_PER_S);
     for (int32_t i = mAudioTrack->Decoders().Length() - 1; i >= 0; --i) {
       nsRefPtr<MediaDecoderReader> newReader = mAudioTrack->Decoders()[i]->GetReader();
 
       nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
       mAudioTrack->Decoders()[i]->GetBuffered(ranges);
       result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n",
                                 i, newReader.get(), DumpTimeRanges(ranges).get(),
-                                newReader.get() == mAudioReader.get() ? "true" : "false",
+                                newReader.get() == GetAudioReader() ? "true" : "false",
                                 mAudioTrack->Decoders()[i]->GetResource()->GetSize());
     }
   }
 
   if (mVideoTrack) {
     result += nsPrintfCString("\tDumping Video Track Decoders - mLastVideoTime: %f\n", double(mLastVideoTime) / USECS_PER_S);
     for (int32_t i = mVideoTrack->Decoders().Length() - 1; i >= 0; --i) {
       nsRefPtr<MediaDecoderReader> newReader = mVideoTrack->Decoders()[i]->GetReader();
 
       nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
       mVideoTrack->Decoders()[i]->GetBuffered(ranges);
       result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n",
                                 i, newReader.get(), DumpTimeRanges(ranges).get(),
-                                newReader.get() == mVideoReader.get() ? "true" : "false",
+                                newReader.get() == GetVideoReader() ? "true" : "false",
                                 mVideoTrack->Decoders()[i]->GetResource()->GetSize());
     }
   }
   aString += NS_ConvertUTF8toUTF16(result);
 }
 
 #ifdef MOZ_EME
 nsresult
@@ -1035,12 +1104,36 @@ MediaSourceReader::SetCDMProxy(CDMProxy*
   return NS_OK;
 }
 #endif
 
 bool
 MediaSourceReader::IsActiveReader(MediaDecoderReader* aReader)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  return aReader == mVideoReader.get() || aReader == mAudioReader.get();
+  return aReader == GetVideoReader() || aReader == GetAudioReader();
+}
+
+MediaDecoderReader*
+MediaSourceReader::GetAudioReader() const
+{
+  return mAudioSourceDecoder ? mAudioSourceDecoder->GetReader() : nullptr;
+}
+
+MediaDecoderReader*
+MediaSourceReader::GetVideoReader() const
+{
+  return mVideoSourceDecoder ? mVideoSourceDecoder->GetReader() : nullptr;
+}
+
+int64_t
+MediaSourceReader::GetReaderAudioTime(int64_t aTime) const
+{
+  return aTime - mAudioSourceDecoder->GetTimestampOffset();
+}
+
+int64_t
+MediaSourceReader::GetReaderVideoTime(int64_t aTime) const
+{
+  return aTime - mVideoSourceDecoder->GetTimestampOffset();
 }
 
 } // namespace mozilla
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -137,39 +137,41 @@ public:
   // Set the duration of the attached mediasource element.
   void SetMediaSourceDuration(double aDuration /* seconds */);
 
 #ifdef MOZ_EME
   nsresult SetCDMProxy(CDMProxy* aProxy);
 #endif
 
   virtual bool IsAsync() const MOZ_OVERRIDE {
-    return (!mAudioReader || mAudioReader->IsAsync()) &&
-           (!mVideoReader || mVideoReader->IsAsync());
+    return (!GetAudioReader() || GetAudioReader()->IsAsync()) &&
+           (!GetVideoReader() || GetVideoReader()->IsAsync());
   }
 
   // Returns true if aReader is a currently active audio or video
   bool IsActiveReader(MediaDecoderReader* aReader);
 
   // Returns a string describing the state of the MediaSource internal
   // buffered data. Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
 private:
-  // Switch the current audio/video reader to the reader that
+  // Switch the current audio/video source to the source that
   // contains aTarget (or up to aTolerance after target). Both
   // aTarget and aTolerance are in microseconds.
-  enum SwitchReaderResult {
-    READER_ERROR = -1,
-    READER_EXISTING = 0,
-    READER_NEW = 1,
+  // Search can be made using a fuzz factor. Should an approximated value be
+  // found instead, aTarget will be updated to the actual target found.
+  enum SwitchSourceResult {
+    SOURCE_ERROR = -1,
+    SOURCE_EXISTING = 0,
+    SOURCE_NEW = 1,
   };
 
-  SwitchReaderResult SwitchAudioReader(int64_t aTarget);
-  SwitchReaderResult SwitchVideoReader(int64_t aTarget);
+  SwitchSourceResult SwitchAudioSource(int64_t* aTarget);
+  SwitchSourceResult SwitchVideoSource(int64_t* aTarget);
 
   void DoAudioRequest();
   void DoVideoRequest();
 
   void CompleteAudioSeekAndDoRequest()
   {
     mAudioSeekRequest.Complete();
     DoAudioRequest();
@@ -188,32 +190,37 @@ private:
   }
 
   void CompleteVideoSeekAndRejectPromise()
   {
     mVideoSeekRequest.Complete();
     mVideoPromise.Reject(DECODE_ERROR, __func__);
   }
 
+  MediaDecoderReader* GetAudioReader() const;
+  MediaDecoderReader* GetVideoReader() const;
+  int64_t GetReaderAudioTime(int64_t aTime) const;
+  int64_t GetReaderVideoTime(int64_t aTime) const;
+
   // Will reject the MediaPromise with END_OF_STREAM if mediasource has ended
   // or with WAIT_FOR_DATA otherwise.
   void CheckForWaitOrEndOfStream(MediaData::Type aType, int64_t aTime /* microseconds */);
 
-  // Return a reader from the set available in aTrackDecoders that has data
+  // Return a decoder from the set available in aTrackDecoders that has data
   // available in the range requested by aTarget.
-  already_AddRefed<MediaDecoderReader> SelectReader(int64_t aTarget,
-                                                    int64_t aTolerance,
-                                                    const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
+  already_AddRefed<SourceBufferDecoder> SelectDecoder(int64_t aTarget,
+                                                      int64_t aTolerance,
+                                                      const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
   bool HaveData(int64_t aTarget, MediaData::Type aType);
 
   void AttemptSeek();
   bool IsSeeking() { return mPendingSeekTime != -1; }
 
-  nsRefPtr<MediaDecoderReader> mAudioReader;
-  nsRefPtr<MediaDecoderReader> mVideoReader;
+  nsRefPtr<SourceBufferDecoder> mAudioSourceDecoder;
+  nsRefPtr<SourceBufferDecoder> mVideoSourceDecoder;
 
   nsTArray<nsRefPtr<TrackBuffer>> mTrackBuffers;
   nsTArray<nsRefPtr<TrackBuffer>> mShutdownTrackBuffers;
   nsTArray<nsRefPtr<TrackBuffer>> mEssentialTrackBuffers;
   nsRefPtr<TrackBuffer> mAudioTrack;
   nsRefPtr<TrackBuffer> mVideoTrack;
 
   MediaPromiseConsumerHolder<AudioDataPromise> mAudioRequest;
@@ -245,16 +252,19 @@ private:
   // to be added to the track buffer.
   int64_t mPendingSeekTime;
   bool mWaitingForSeekData;
 
   int64_t mTimeThreshold;
   bool mDropAudioBeforeThreshold;
   bool mDropVideoBeforeThreshold;
 
+  bool mAudioDiscontinuity;
+  bool mVideoDiscontinuity;
+
   bool mEnded;
   double mMediaSourceDuration;
 
   bool mHasEssentialTrackBuffers;
 
   void ContinueShutdown();
   MediaPromiseHolder<ShutdownPromise> mMediaSourceShutdownPromise;
 #ifdef MOZ_FMP4
--- a/dom/media/mediasource/SourceBufferDecoder.cpp
+++ b/dom/media/mediasource/SourceBufferDecoder.cpp
@@ -243,39 +243,46 @@ SourceBufferDecoder::NotifyDataArrived(c
 
 nsresult
 SourceBufferDecoder::GetBuffered(dom::TimeRanges* aBuffered)
 {
   nsresult rv = mReader->GetBuffered(aBuffered);
   if (NS_FAILED(rv)) {
     return rv;
   }
+
+  // Adjust buffered range according to timestamp offset.
+  aBuffered->Shift((double)mTimestampOffset / USECS_PER_S);
+
   if (!WasTrimmed()) {
     return NS_OK;
   }
   nsRefPtr<dom::TimeRanges> tr = new dom::TimeRanges();
   tr->Add(0, mTrimmedOffset);
   aBuffered->Intersection(tr);
   return NS_OK;
 }
 
 int64_t
 SourceBufferDecoder::ConvertToByteOffset(double aTime)
 {
-  int64_t readerOffset = mReader->GetEvictionOffset(aTime);
+  int64_t readerOffset =
+    mReader->GetEvictionOffset(aTime - double(mTimestampOffset) / USECS_PER_S);
   if (readerOffset >= 0) {
     return readerOffset;
   }
 
   // Uses a conversion based on (aTime/duration) * length.  For the
   // purposes of eviction this should be adequate since we have the
   // byte threshold as well to ensure data actually gets evicted and
   // we ensure we don't evict before the current playable point.
   if (mRealMediaDuration <= 0) {
     return -1;
   }
   int64_t length = GetResource()->GetLength();
   MOZ_ASSERT(length > 0);
-  int64_t offset = (aTime / (double(mRealMediaDuration) / USECS_PER_S)) * length;
+  int64_t offset =
+    ((aTime - double(mTimestampOffset) / USECS_PER_S) /
+      (double(mRealMediaDuration) / USECS_PER_S)) * length;
   return offset;
 }
 
 } // namespace mozilla
--- a/dom/media/mediasource/SourceBufferDecoder.h
+++ b/dom/media/mediasource/SourceBufferDecoder.h
@@ -37,17 +37,16 @@ public:
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   virtual bool IsMediaSeekable() MOZ_FINAL MOZ_OVERRIDE;
   virtual bool IsShutdown() const MOZ_FINAL MOZ_OVERRIDE;
   virtual bool IsTransportSeekable() MOZ_FINAL MOZ_OVERRIDE;
   virtual bool OnDecodeThread() const MOZ_FINAL MOZ_OVERRIDE;
   virtual bool OnStateMachineThread() const MOZ_FINAL MOZ_OVERRIDE;
-  virtual int64_t GetTimestampOffset() const MOZ_FINAL MOZ_OVERRIDE { return mTimestampOffset; }
   virtual int64_t GetMediaDuration() MOZ_FINAL MOZ_OVERRIDE;
   virtual layers::ImageContainer* GetImageContainer() MOZ_FINAL MOZ_OVERRIDE;
   virtual MediaDecoderOwner* GetOwner() MOZ_FINAL MOZ_OVERRIDE;
   virtual SourceBufferResource* GetResource() const MOZ_FINAL MOZ_OVERRIDE;
   virtual ReentrantMonitor& GetReentrantMonitor() MOZ_FINAL MOZ_OVERRIDE;
   virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE;
   virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDormant) MOZ_FINAL MOZ_OVERRIDE;
   virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDormant) MOZ_FINAL MOZ_OVERRIDE;
@@ -61,28 +60,30 @@ public:
   virtual void SetMediaDuration(int64_t aDuration) MOZ_FINAL MOZ_OVERRIDE;
   virtual void SetMediaEndTime(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
   virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_FINAL MOZ_OVERRIDE;
   virtual void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_FINAL MOZ_OVERRIDE;
   virtual void UpdatePlaybackPosition(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
   virtual bool HasInitializationData() MOZ_FINAL MOZ_OVERRIDE;
 
   // SourceBufferResource specific interface below.
+  int64_t GetTimestampOffset() const { return mTimestampOffset; }
+  void SetTimestampOffset(int64_t aOffset)  { mTimestampOffset = aOffset; }
 
   // Warning: this mirrors GetBuffered in MediaDecoder, but this class's base is
   // AbstractMediaDecoder, which does not supply this interface.
   nsresult GetBuffered(dom::TimeRanges* aBuffered);
 
   void SetReader(MediaDecoderReader* aReader)
   {
     MOZ_ASSERT(!mReader);
     mReader = aReader;
   }
 
-  MediaDecoderReader* GetReader()
+  MediaDecoderReader* GetReader() const
   {
     return mReader;
   }
 
   void SetTaskQueue(MediaTaskQueue* aTaskQueue)
   {
     MOZ_ASSERT((!mTaskQueue && aTaskQueue) || (mTaskQueue && !aTaskQueue));
     mTaskQueue = aTaskQueue;
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -34,23 +34,27 @@ extern PRLogModuleInfo* GetMediaSourceAP
 #define MSE_API(...)
 #endif
 
 // Time in seconds to substract from the current time when deciding the
 // time point to evict data before in a decoder. This is used to help
 // prevent evicting the current playback point.
 #define MSE_EVICT_THRESHOLD_TIME 2.0
 
+// Time in microsecond under which a timestamp will be considered to be 0.
+#define FUZZ_TIMESTAMP_OFFSET 100000
+
 namespace mozilla {
 
 TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType)
   : mParentDecoder(aParentDecoder)
   , mType(aType)
   , mLastStartTimestamp(0)
   , mLastTimestampOffset(0)
+  , mAdjustedTimestamp(0)
   , mShutdown(false)
 {
   MOZ_COUNT_CTOR(TrackBuffer);
   mParser = ContainerParser::CreateForMIMEType(aType);
   mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   aParentDecoder->AddTrackBuffer(this);
   mDecoderPerSegment = Preferences::GetBool("media.mediasource.decoder-per-segment", false);
   MSE_DEBUG("TrackBuffer(%p) created for parent decoder %p", this, aParentDecoder);
@@ -180,67 +184,73 @@ TrackBuffer::AppendData(LargeDataBuffer*
 
   int64_t start = 0, end = 0;
   bool gotMedia = mParser->ParseStartAndEndTimestamps(aData, start, end);
   bool gotInit = mParser->HasCompleteInitData();
 
   if (newInitData) {
     if (!gotInit) {
       // We need a new decoder, but we can't initialize it yet.
-      nsRefPtr<SourceBufferDecoder> decoder = NewDecoder(aTimestampOffset);
+      nsRefPtr<SourceBufferDecoder> decoder =
+        NewDecoder(aTimestampOffset - mAdjustedTimestamp);
       // The new decoder is stored in mDecoders/mCurrentDecoder, so we
       // don't need to do anything with 'decoder'. It's only a placeholder.
       if (!decoder) {
         mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
         return p;
       }
     } else {
-      if (!decoders.NewDecoder(aTimestampOffset)) {
+      if (!decoders.NewDecoder(aTimestampOffset - mAdjustedTimestamp)) {
         mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
         return p;
       }
     }
   } else if (!hadCompleteInitData && gotInit) {
     MOZ_ASSERT(mCurrentDecoder);
     // Queue pending decoder for initialization now that we have a full
     // init segment.
     decoders.AppendElement(mCurrentDecoder);
   }
 
   if (gotMedia) {
-    start += aTimestampOffset;
-    end += aTimestampOffset;
     if (mLastEndTimestamp &&
         (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
          mLastTimestampOffset != aTimestampOffset ||
          mDecoderPerSegment ||
          (mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
 
       if (!newInitData) {
         // This data is earlier in the timeline than data we have already
         // processed or not continuous, so we must create a new decoder
         // to handle the decoding.
-        if (!hadCompleteInitData || !decoders.NewDecoder(aTimestampOffset)) {
+        if (!hadCompleteInitData ||
+            !decoders.NewDecoder(aTimestampOffset - mAdjustedTimestamp)) {
           mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
           return p;
         }
         MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
         AppendDataToCurrentResource(oldInit, 0);
       }
       mLastStartTimestamp = start;
     } else {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp ? mLastEndTimestamp.value() : 0, start, end);
     }
     mLastEndTimestamp.reset();
     mLastEndTimestamp.emplace(end);
   }
 
+  if (gotMedia && start > 0 &&
+      (start < FUZZ_TIMESTAMP_OFFSET || start < mAdjustedTimestamp)) {
+    AdjustDecodersTimestampOffset(mAdjustedTimestamp - start);
+    mAdjustedTimestamp = start;
+  }
+
   if (!AppendDataToCurrentResource(aData, end - start)) {
     mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
     return p;
   }
 
   if (decoders.Length()) {
     // We're going to have to wait for the decoder to initialize, the promise
     // will be resolved once initialization completes.
@@ -910,9 +920,18 @@ TrackBuffer::RangeRemoval(int64_t aStart
       }
       MSE_DEBUG("TrackBuffer(%p):RangeRemoval remove empty decoders=%d", this, i);
       RemoveDecoder(decoders[i]);
     }
   }
   return true;
 }
 
+void
+TrackBuffer::AdjustDecodersTimestampOffset(int32_t aOffset)
+{
+  ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
+  for (uint32_t i = 0; i < mDecoders.Length(); i++) {
+    mDecoders[i]->SetTimestampOffset(mDecoders[i]->GetTimestampOffset() + aOffset);
+  }
+}
+
 } // namespace mozilla
--- a/dom/media/mediasource/TrackBuffer.h
+++ b/dom/media/mediasource/TrackBuffer.h
@@ -185,19 +185,21 @@ private:
 
   nsRefPtr<MediaSourceDecoder> mParentDecoder;
   const nsCString mType;
 
   // The last start and end timestamps added to the TrackBuffer via
   // AppendData.  Accessed on the main thread only.
   int64_t mLastStartTimestamp;
   Maybe<int64_t> mLastEndTimestamp;
+  void AdjustDecodersTimestampOffset(int32_t aOffset);
 
   // The timestamp offset used by our current decoder, in microseconds.
   int64_t mLastTimestampOffset;
+  int64_t mAdjustedTimestamp;
 
   // Set when the first decoder used by this TrackBuffer is initialized.
   // Protected by mParentDecoder's monitor.
   MediaInfo mInfo;
 
   void ContinueShutdown();
   MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
   bool mDecoderPerSegment;
--- a/dom/media/test/chrome.ini
+++ b/dom/media/test/chrome.ini
@@ -1,10 +1,11 @@
 #Media chrome tests
 
 [DEFAULT]
 support-files =
   basic.vtt
   seek.webm
 
 [test_texttrackcue_chrome.html]
+skip-if = os=='mac' && debug # bug 1130751
 [test_texttrack_chrome.html]
 [test_texttracklist_chrome.html]
--- a/dom/plugins/ipc/PluginWidgetChild.cpp
+++ b/dom/plugins/ipc/PluginWidgetChild.cpp
@@ -7,75 +7,59 @@
 #include "mozilla/DebugOnly.h"
 #include "nsDebug.h"
 
 #if defined(XP_WIN)
 #include "mozilla/plugins/PluginInstanceParent.h"
 using mozilla::plugins::PluginInstanceParent;
 #endif
 
-#define PWLOG(...)
-//#define PWLOG(...) printf_stderr(__VA_ARGS__)
-
 namespace mozilla {
 namespace plugins {
 
 PluginWidgetChild::PluginWidgetChild() :
   mWidget(nullptr)
 {
   MOZ_COUNT_CTOR(PluginWidgetChild);
 }
 
 PluginWidgetChild::~PluginWidgetChild()
 {
-  PWLOG("PluginWidgetChild::~PluginWidgetChild()\n");
   MOZ_COUNT_DTOR(PluginWidgetChild);
 }
 
 /*
  * Tear down scenarios
  * layout (plugin content unloading):
- *  - PluginWidgetProxy::Destroy(), calls PluginWidgetChild->SendDestroy(), (proxy disabled)
- *  - PluginWidgetParent::RecvDestroy(), sends async ParentShutdown()
- *  - PluginWidgetChild::RecvParentShutdown(), calls Send__delete__()
+ *  - PluginWidgetProxy nsIWidget Destroy()
+ *  - PluginWidgetProxy->PluginWidgetChild->SendDestroy()
+ *  - PluginWidgetParent::RecvDestroy(), sends async Destroyed() to PluginWidgetChild
+ *  - PluginWidgetChild::RecvDestroyed() calls Send__delete__()
  *  - PluginWidgetParent::ActorDestroy() called in response to __delete__.
  * PBrowser teardown (tab closing):
- *  - TabParent::Destroy()
- *  - PluginWidgetParent::ParentDestroy(), sends async ParentShutdown()
- *  - PluginWidgetChild::RecvParentShutdown(), (proxy disabled)
+ *  - PluginWidgetParent::ParentDestroy() called by TabParent::Destroy()
  *  - PluginWidgetParent::ActorDestroy()
  *  - PluginWidgetParent::~PluginWidgetParent() in response to PBrowserParent::DeallocSubtree()
  *  - PluginWidgetChild::ActorDestroy() from PPluginWidgetChild::DestroySubtree
  *  - ~PluginWidgetChild() in response to PBrowserChild::DeallocSubtree()
  **/
 
 void
-PluginWidgetChild::ShutdownProxy()
+PluginWidgetChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (mWidget) {
     mWidget->ChannelDestroyed();
   }
   mWidget = nullptr;
 }
 
-void
-PluginWidgetChild::ActorDestroy(ActorDestroyReason aWhy)
+bool
+PluginWidgetChild::RecvParentShutdown()
 {
-  PWLOG("PluginWidgetChild::ActorDestroy(%d)\n", aWhy);
-  ShutdownProxy(); // backup
-}
-
-bool
-PluginWidgetChild::RecvParentShutdown(const bool& aParentInitiated)
-{
-  PWLOG("PluginWidgetChild::RecvParentShutdown(%d)\n", aParentInitiated);
-  ShutdownProxy();
-  if (!aParentInitiated) {
-    Send__delete__(this);
-  }
+  Send__delete__(this);
   return true;
 }
 
 bool
 PluginWidgetChild::RecvUpdateWindow(const uintptr_t& aChildId)
 {
 #if defined(XP_WIN)
   NS_ASSERTION(aChildId, "Expected child hwnd value for remote plugin instance.");
--- a/dom/plugins/ipc/PluginWidgetChild.h
+++ b/dom/plugins/ipc/PluginWidgetChild.h
@@ -16,19 +16,17 @@ namespace plugins {
 class PluginWidgetChild : public PPluginWidgetChild
 {
 public:
   PluginWidgetChild();
   virtual ~PluginWidgetChild();
 
   virtual bool RecvUpdateWindow(const uintptr_t& aChildId) MOZ_OVERRIDE;
   virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
-  virtual bool RecvParentShutdown(const bool& aParentInitiated) MOZ_OVERRIDE;
-
-  void ShutdownProxy();
+  virtual bool RecvParentShutdown() MOZ_OVERRIDE;
 
   mozilla::widget::PluginWidgetProxy* mWidget;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // mozilla_plugins_PluginWidgetChild_h
--- a/dom/plugins/ipc/PluginWidgetParent.cpp
+++ b/dom/plugins/ipc/PluginWidgetParent.cpp
@@ -70,16 +70,24 @@ PluginWidgetParent::~PluginWidgetParent(
 }
 
 mozilla::dom::TabParent*
 PluginWidgetParent::GetTabParent()
 {
   return static_cast<mozilla::dom::TabParent*>(Manager());
 }
 
+void
+PluginWidgetParent::SetParent(nsIWidget* aParent)
+{
+  if (mWidget && aParent) {
+    mWidget->SetParent(aParent);
+  }
+}
+
 #if defined(XP_WIN)
 // static
 void
 PluginWidgetParent::SendAsyncUpdate(nsIWidget* aWidget)
 {
   if (!aWidget || aWidget->Destroyed()) {
     return;
   }
@@ -170,53 +178,49 @@ PluginWidgetParent::RecvCreate(nsresult*
   mWidget->RegisterPluginWindowForRemoteUpdates();
 
   return true;
 }
 
 void
 PluginWidgetParent::ActorDestroy(ActorDestroyReason aWhy)
 {
+  mActorDestroyed = true;
   PWLOG("PluginWidgetParent::ActorDestroy()\n");
 }
 
-void
-PluginWidgetParent::ShutdownCommon(bool aParentInitiated)
-{
-  if (mActorDestroyed || !mWidget) {
-    return;
-  }
-
-  PWLOG("PluginWidgetParent::ShutdownCommon()\n");
-  mWidget->UnregisterPluginWindowForRemoteUpdates();
-  DebugOnly<nsresult> rv = mWidget->Destroy();
-  NS_ASSERTION(NS_SUCCEEDED(rv), "widget destroy failure");
-  mWidget = nullptr;
-  mActorDestroyed = true;
-  unused << SendParentShutdown(aParentInitiated);
-}
-
 // Called by TabParent's Destroy() in response to an early tear down (Early
 // in that this is happening before layout in the child has had a chance
 // to destroy the child widget.) when the tab is closing. We will not receive
 // RecvDestroy here.
 void
 PluginWidgetParent::ParentDestroy()
 {
+  if (mActorDestroyed || !mWidget) {
+    return;
+  }
   PWLOG("PluginWidgetParent::ParentDestroy()\n");
-  ShutdownCommon(true);
+  mWidget->UnregisterPluginWindowForRemoteUpdates();
+  DebugOnly<nsresult> rv = mWidget->Destroy();
+  NS_ASSERTION(NS_SUCCEEDED(rv), "widget destroy failure");
+  mWidget = nullptr;
+  mActorDestroyed = true;
+  return;
 }
 
 // Called by the child when a plugin is torn down within a tab
 // normally.
 bool
 PluginWidgetParent::RecvDestroy()
 {
-  PWLOG("PluginWidgetParent::RecvDestroy()\n");
-  ShutdownCommon(false);
+  bool destroyed = mActorDestroyed;
+  ParentDestroy();
+  if (!destroyed) {
+    unused << SendParentShutdown();
+  }
   return true;
 }
 
 bool
 PluginWidgetParent::RecvSetFocus(const bool& aRaise)
 {
   ENSURE_CHANNEL;
   PWLOG("PluginWidgetParent::RecvSetFocus(%d)\n", aRaise);
--- a/dom/plugins/ipc/PluginWidgetParent.h
+++ b/dom/plugins/ipc/PluginWidgetParent.h
@@ -43,19 +43,20 @@ public:
   virtual bool RecvGetNativePluginPort(uintptr_t* value) MOZ_OVERRIDE;
 
   // Helper for compositor checks on the channel
   bool ActorDestroyed() { return mActorDestroyed; }
 
   // Called by PBrowser when it receives a Destroy() call from the child.
   void ParentDestroy();
 
+  // Sets mWidget's parent
+  void SetParent(nsIWidget* aParent);
+
 private:
-  void ShutdownCommon(bool aParentInitiated);
-
   // The tab our connection is associated with.
   mozilla::dom::TabParent* GetTabParent();
   // The chrome side native widget.
   nsCOMPtr<nsIWidget> mWidget;
 #if defined(MOZ_WIDGET_GTK)
   nsAutoPtr<nsPluginNativeWindowGtk> mWrapper;
 #endif
   bool mActorDestroyed;
--- a/dom/webidl/XMLHttpRequest.webidl
+++ b/dom/webidl/XMLHttpRequest.webidl
@@ -137,14 +137,14 @@ interface XMLHttpRequest : XMLHttpReques
   [ChromeOnly, SetterThrows=Workers]
   attribute boolean mozBackgroundRequest;
 
   [ChromeOnly, Exposed=Window]
   readonly attribute MozChannel? channel;
 
   [Throws]
   void sendAsBinary(DOMString body);
-  [Throws, ChromeOnly]
+  [Throws, ChromeOnly, Exposed=Window]
   any getInterface(IID iid);
 
   readonly attribute boolean mozAnon;
   readonly attribute boolean mozSystem;
 };
--- a/editor/libeditor/tests/chrome.ini
+++ b/editor/libeditor/tests/chrome.ini
@@ -23,11 +23,9 @@ skip-if = buildapp == 'mulet'
 [test_bug1101392.html]
 [test_composition_event_created_in_chrome.html]
 [test_contenteditable_text_input_handling.html]
 [test_dragdrop.html]
 skip-if = buildapp == 'mulet'
 [test_htmleditor_keyevent_handling.html]
 [test_selection_move_commands.xul]
 [test_texteditor_keyevent_handling.html]
-# disables the key handling test on gtk because gtk overrides some key events
-# on our editor, and the combinations depend on the system.
-skip-if = toolkit == "gtk2" || toolkit == "gtk3"
+skip-if = (debug && os=='win') || (os == 'linux') # Bug 1116205, leaks on windows debug, fails delete key on linux
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -578,17 +578,17 @@ CopyFrontToBack(TextureClient* aFront,
                 TextureClient* aBack,
                 const gfx::IntRect& aRectToCopy)
 {
   if (!aFront->Lock(OpenMode::OPEN_READ)) {
     NS_WARNING("Failed to lock the tile's front buffer");
     return false;
   }
 
-  if (!aBack->Lock(OpenMode::OPEN_WRITE)) {
+  if (!aBack->Lock(OpenMode::OPEN_READ_WRITE)) {
     NS_WARNING("Failed to lock the tile's back buffer");
     return false;
   }
 
   gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
   aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
   return true;
 }
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1115,16 +1115,25 @@ CompositorD3D11::BeginFrame(const nsIntR
 
 void
 CompositorD3D11::EndFrame()
 {
   mContext->Flush();
 
   nsIntSize oldSize = mSize;
   EnsureSize();
+  UINT presentInterval = 0;
+
+  if (gfxWindowsPlatform::GetPlatform()->IsWARP()) {
+    // When we're using WARP we cannot present immediately as it causes us
+    // to tear when rendering. When not using WARP it appears the DWM takes
+    // care of tearing for us.
+    presentInterval = 1;
+  }
+
   if (oldSize == mSize) {
     RefPtr<IDXGISwapChain1> chain;
     HRESULT hr = mSwapChain->QueryInterface((IDXGISwapChain1**)byRef(chain));
     if (SUCCEEDED(hr) && chain) {
       DXGI_PRESENT_PARAMETERS params;
       PodZero(&params);
       params.DirtyRectsCount = mInvalidRegion.GetNumRects();
       std::vector<RECT> rects;
@@ -1139,19 +1148,19 @@ CompositorD3D11::EndFrame()
         rect.top = r->y;
         rect.bottom = r->YMost();
         rect.right = r->XMost();
 
         rects.push_back(rect);
       }
 
       params.pDirtyRects = &rects.front();
-      chain->Present1(0, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, &params);
+      chain->Present1(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, &params);
     } else {
-      mSwapChain->Present(0, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
+      mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
     }
     mDisableSequenceForNextFrame = false;
     if (mTarget) {
       PaintToTarget();
     }
   }
 
   mCurrentRT = nullptr;
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -130,20 +130,16 @@ static qcms_profile *gCMSsRGBProfile = n
 
 static qcms_transform *gCMSRGBTransform = nullptr;
 static qcms_transform *gCMSInverseRGBTransform = nullptr;
 static qcms_transform *gCMSRGBATransform = nullptr;
 
 static bool gCMSInitialized = false;
 static eCMSMode gCMSMode = eCMSMode_Off;
 
-static bool gCMSIntentInitialized = false;
-static int gCMSIntent = QCMS_INTENT_DEFAULT;
-
-
 static void ShutdownCMS();
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/SourceSurfaceCairo.h"
 using namespace mozilla::gfx;
 
 /* Class to listen for pref changes so that chrome code can dynamically
    force sRGB as an output profile. See Bug #452125. */
@@ -1747,54 +1743,47 @@ bool
 gfxPlatform::OffMainThreadCompositingEnabled()
 {
   return UsesOffMainThreadCompositing();
 }
 
 eCMSMode
 gfxPlatform::GetCMSMode()
 {
-    if (gCMSInitialized == false) {
-        gCMSInitialized = true;
-
+    if (!gCMSInitialized) {
         int32_t mode = gfxPrefs::CMSMode();
         if (mode >= 0 && mode < eCMSMode_AllCount) {
             gCMSMode = static_cast<eCMSMode>(mode);
         }
 
         bool enableV4 = gfxPrefs::CMSEnableV4();
         if (enableV4) {
             qcms_enable_iccv4();
         }
+        gCMSInitialized = true;
     }
     return gCMSMode;
 }
 
 int
 gfxPlatform::GetRenderingIntent()
 {
-    if (!gCMSIntentInitialized) {
-        gCMSIntentInitialized = true;
-
-        // gfxPrefs.h is using 0 as the default for the rendering
-        // intent preference, based on that being the value for
-        // QCMS_INTENT_DEFAULT.  Assert here to catch if that ever
-        // changes and we can then figure out what to do about it.
-        MOZ_ASSERT(QCMS_INTENT_DEFAULT == 0);
+  // gfxPrefs.h is using 0 as the default for the rendering
+  // intent preference, based on that being the value for
+  // QCMS_INTENT_DEFAULT.  Assert here to catch if that ever
+  // changes and we can then figure out what to do about it.
+  MOZ_ASSERT(QCMS_INTENT_DEFAULT == 0);
 
-        /* Try to query the pref system for a rendering intent. */
-        int32_t pIntent = gfxPrefs::CMSRenderingIntent();
-        if ((pIntent >= QCMS_INTENT_MIN) && (pIntent <= QCMS_INTENT_MAX)) {
-            gCMSIntent = pIntent;
-        } else {
-            /* If the pref is out of range, use embedded profile. */
-            gCMSIntent = -1;
-        }
-    }
-    return gCMSIntent;
+  /* Try to query the pref system for a rendering intent. */
+  int32_t pIntent = gfxPrefs::CMSRenderingIntent();
+  if ((pIntent < QCMS_INTENT_MIN) || (pIntent > QCMS_INTENT_MAX)) {
+    /* If the pref is out of range, use embedded profile. */
+    pIntent = -1;
+  }
+  return pIntent;
 }
 
 void
 gfxPlatform::TransformPixel(const Color& in, Color& out, qcms_transform *transform)
 {
 
     if (transform) {
         /* we want the bytes in RGB order */
@@ -1982,17 +1971,16 @@ static void ShutdownCMS()
         gCMSOutputProfile = nullptr;
     }
     if (gCMSsRGBProfile) {
         qcms_profile_release(gCMSsRGBProfile);
         gCMSsRGBProfile = nullptr;
     }
 
     // Reset the state variables
-    gCMSIntent = -2;
     gCMSMode = eCMSMode_Off;
     gCMSInitialized = false;
 }
 
 // default SetupClusterBoundaries, based on Unicode properties;
 // platform subclasses may override if they wish
 void
 gfxPlatform::SetupClusterBoundaries(gfxTextRun *aTextRun, const char16_t *aString)
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -40,29 +40,30 @@ typedef enum JSGCInvocationKind {
     GC_SHRINK = 1
 } JSGCInvocationKind;
 
 namespace JS {
 
 #define GCREASONS(D)                            \
     /* Reasons internal to the JS engine */     \
     D(API)                                      \
-    D(MAYBEGC)                                  \
+    D(EAGER_ALLOC_TRIGGER)                      \
     D(DESTROY_RUNTIME)                          \
     D(DESTROY_CONTEXT)                          \
     D(LAST_DITCH)                               \
     D(TOO_MUCH_MALLOC)                          \
     D(ALLOC_TRIGGER)                            \
     D(DEBUG_GC)                                 \
     D(COMPARTMENT_REVIVED)                      \
     D(RESET)                                    \
     D(OUT_OF_NURSERY)                           \
     D(EVICT_NURSERY)                            \
     D(FULL_STORE_BUFFER)                        \
     D(SHARED_MEMORY_LIMIT)                      \
+    D(PERIODIC_FULL_GC)                         \
                                                 \
     /* These are reserved for future use. */    \
     D(RESERVED0)                                \
     D(RESERVED1)                                \
     D(RESERVED2)                                \
     D(RESERVED3)                                \
     D(RESERVED4)                                \
     D(RESERVED5)                                \
@@ -73,17 +74,16 @@ namespace JS {
     D(RESERVED10)                               \
     D(RESERVED11)                               \
     D(RESERVED12)                               \
     D(RESERVED13)                               \
     D(RESERVED14)                               \
     D(RESERVED15)                               \
     D(RESERVED16)                               \
     D(RESERVED17)                               \
-    D(RESERVED18)                               \
                                                 \
     /* Reasons from Firefox */                  \
     D(DOM_WINDOW_UTILS)                         \
     D(COMPONENT_UTILS)                          \
     D(MEM_PRESSURE)                             \
     D(CC_WAITING)                               \
     D(CC_FORCED)                                \
     D(LOAD_END)                                 \
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -39,17 +39,16 @@
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 #include "frontend/Parser-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 using namespace js::jit;
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -134,64 +134,16 @@ js::ExecuteRegExpLegacy(JSContext *cx, R
         /* Forbid an array, as an optimization. */
         rval.setBoolean(true);
         return true;
     }
 
     return CreateRegExpMatchResult(cx, input, matches, rval);
 }
 
-/* Note: returns the original if no escaping need be performed. */
-template <typename CharT>
-static bool
-EscapeNakedForwardSlashes(StringBuffer &sb, const CharT *oldChars, size_t oldLen)
-{
-    for (const CharT *it = oldChars; it < oldChars + oldLen; ++it) {
-        if (*it == '/' && (it == oldChars || it[-1] != '\\')) {
-            /* There's a forward slash that needs escaping. */
-            if (sb.empty()) {
-                /* This is the first one we've seen, copy everything up to this point. */
-                if (mozilla::IsSame<CharT, char16_t>::value && !sb.ensureTwoByteChars())
-                    return false;
-
-                if (!sb.reserve(oldLen + 1))
-                    return false;
-
-                sb.infallibleAppend(oldChars, size_t(it - oldChars));
-            }
-            if (!sb.append('\\'))
-                return false;
-        }
-
-        if (!sb.empty() && !sb.append(*it))
-            return false;
-    }
-
-    return true;
-}
-
-static JSAtom *
-EscapeNakedForwardSlashes(JSContext *cx, JSAtom *unescaped)
-{
-    /* We may never need to use |sb|. Start using it lazily. */
-    StringBuffer sb(cx);
-
-    if (unescaped->hasLatin1Chars()) {
-        JS::AutoCheckCannotGC nogc;
-        if (!EscapeNakedForwardSlashes(sb, unescaped->latin1Chars(nogc), unescaped->length()))
-            return nullptr;
-    } else {
-        JS::AutoCheckCannotGC nogc;
-        if (!EscapeNakedForwardSlashes(sb, unescaped->twoByteChars(nogc), unescaped->length()))
-            return nullptr;
-    }
-
-    return sb.empty() ? unescaped : sb.finishAtom();
-}
-
 /*
  * Compile a new |RegExpShared| for the |RegExpObject|.
  *
  * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
  * arguments:
  *
  *  RegExp, undefined => flags := pattern.flags
  *  RegExp, _ => throw TypeError
@@ -228,39 +180,30 @@ CompileRegExpObject(JSContext *cx, RegEx
         RootedObject sourceObj(cx, &sourceValue.toObject());
 
         if (args.hasDefined(1)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NEWREGEXP_FLAGGED);
             return false;
         }
 
         /*
-         * Only extract the 'flags' out of sourceObj; do not reuse the
-         * RegExpShared since it may be from a different compartment.
+         * Extract the 'source' and the 'flags' out of sourceObj; do not reuse
+         * the RegExpShared since it may be from a different compartment.
          */
+        RootedAtom sourceAtom(cx);
         RegExpFlag flags;
         {
             RegExpGuard g(cx);
             if (!RegExpToShared(cx, sourceObj, &g))
                 return false;
 
+            sourceAtom = g->getSource();
             flags = g->getFlags();
         }
 
-        /*
-         * 'toSource' is a permanent read-only property, so this is equivalent
-         * to executing RegExpObject::getSource on the unwrapped object.
-         */
-        RootedValue v(cx);
-        if (!GetProperty(cx, sourceObj, sourceObj, cx->names().source, &v))
-            return false;
-
-        // For proxies like CPOWs, we can't assume the result of a property get
-        // for 'source' is atomized.
-        Rooted<JSAtom*> sourceAtom(cx, AtomizeString(cx, v.toString()));
         RegExpObject *reobj = builder.build(sourceAtom, flags);
         if (!reobj)
             return false;
 
         args.rval().setObject(*reobj);
         return true;
     }
 
@@ -279,30 +222,26 @@ CompileRegExpObject(JSContext *cx, RegEx
         RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
         if (!flagStr)
             return false;
         args[1].setString(flagStr);
         if (!ParseRegExpFlags(cx, flagStr, &flags))
             return false;
     }
 
-    RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source));
-    if (!escapedSourceStr)
-        return false;
-
     CompileOptions options(cx);
     frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
-    if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), escapedSourceStr))
+    if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), source))
         return false;
 
     RegExpStatics *res = cx->global()->getRegExpStatics(cx);
     if (!res)
         return false;
-    RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
+    RegExpObject *reobj = builder.build(source, RegExpFlag(flags | res->getFlags()));
     if (!reobj)
         return false;
 
     args.rval().setObject(*reobj);
     return true;
 }
 
 MOZ_ALWAYS_INLINE bool
@@ -426,18 +365,153 @@ regexp_flags(JSContext *cx, unsigned arg
     if (ToBoolean(sticky) && !sb.append('y'))
         return false;
 
     /* Step 19. */
     args.rval().setString(sb.finishString());
     return true;
 }
 
+/* ES6 draft rev32 21.2.5.4. */
+MOZ_ALWAYS_INLINE bool
+regexp_global_impl(JSContext *cx, CallArgs args)
+{
+    MOZ_ASSERT(IsRegExp(args.thisv()));
+    Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
+
+    /* Steps 4-6. */
+    args.rval().setBoolean(reObj->global());
+    return true;
+}
+
+static bool
+regexp_global(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+    /* Steps 1-3. */
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsRegExp, regexp_global_impl>(cx, args);
+}
+
+/* ES6 draft rev32 21.2.5.5. */
+MOZ_ALWAYS_INLINE bool
+regexp_ignoreCase_impl(JSContext *cx, CallArgs args)
+{
+    MOZ_ASSERT(IsRegExp(args.thisv()));
+    Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
+
+    /* Steps 4-6. */
+    args.rval().setBoolean(reObj->ignoreCase());
+    return true;
+}
+
+static bool
+regexp_ignoreCase(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+    /* Steps 1-3. */
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsRegExp, regexp_ignoreCase_impl>(cx, args);
+}
+
+/* ES6 draft rev32 21.2.5.7. */
+MOZ_ALWAYS_INLINE bool
+regexp_multiline_impl(JSContext *cx, CallArgs args)
+{
+    MOZ_ASSERT(IsRegExp(args.thisv()));
+    Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
+
+    /* Steps 4-6. */
+    args.rval().setBoolean(reObj->multiline());
+    return true;
+}
+
+static bool
+regexp_multiline(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+    /* Steps 1-3. */
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsRegExp, regexp_multiline_impl>(cx, args);
+}
+
+/* ES6 draft rev32 21.2.5.10. */
+MOZ_ALWAYS_INLINE bool
+regexp_source_impl(JSContext *cx, CallArgs args)
+{
+    MOZ_ASSERT(IsRegExp(args.thisv()));
+    Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
+
+    /* Step 5. */
+    RootedAtom src(cx, reObj->getSource());
+    if (!src)
+        return false;
+
+    /* Step 7. */
+    RootedString str(cx, EscapeRegExpPattern(cx, src));
+    if (!str)
+        return false;
+
+    args.rval().setString(str);
+    return true;
+}
+
+static bool
+regexp_source(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+    /* Steps 1-4. */
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsRegExp, regexp_source_impl>(cx, args);
+}
+
+/* ES6 draft rev32 21.2.5.12. */
+MOZ_ALWAYS_INLINE bool
+regexp_sticky_impl(JSContext *cx, CallArgs args)
+{
+    MOZ_ASSERT(IsRegExp(args.thisv()));
+    Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
+
+    /* Steps 4-6. */
+    args.rval().setBoolean(reObj->sticky());
+    return true;
+}
+
+static bool
+regexp_sticky(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+    /* Steps 1-3. */
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsRegExp, regexp_sticky_impl>(cx, args);
+}
+
+/* ES6 draft rev32 21.2.5.15. */
+MOZ_ALWAYS_INLINE bool
+regexp_unicode_impl(JSContext *cx, CallArgs args)
+{
+    MOZ_ASSERT(IsRegExp(args.thisv()));
+
+    /* Steps 4-6. */
+    /* FIXME: When the /u flags is supported, return correct value. */
+    args.rval().setBoolean(false);
+    return true;
+}
+
+static bool
+regexp_unicode(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+    /* Steps 1-3. */
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsRegExp, regexp_unicode_impl>(cx, args);
+}
+
 static const JSPropertySpec regexp_properties[] = {
     JS_PSG("flags", regexp_flags, 0),
+    JS_PSG("global", regexp_global, 0),
+    JS_PSG("ignoreCase", regexp_ignoreCase, 0),
+    JS_PSG("multiline", regexp_multiline, 0),
+    JS_PSG("source", regexp_source, 0),
+    JS_PSG("sticky", regexp_sticky, 0),
+    JS_PSG("unicode", regexp_unicode, 0),
     JS_PS_END
 };
 
 static const JSFunctionSpec regexp_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,  regexp_toString,    0,0),
 #endif
     JS_FN(js_toString_str,  regexp_toString,    0,0),
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -535,18 +535,18 @@ EmitLoopEntry(ExclusiveContext *cx, Byte
     LoopStmtInfo *loop = LoopStmtInfo::fromStmtInfo(bce->topStmt);
     MOZ_ASSERT(loop->loopDepth > 0);
 
     uint8_t loopDepthAndFlags = PackLoopEntryDepthHintAndFlags(loop->loopDepth, loop->canIonOsr);
     return Emit2(cx, bce, JSOP_LOOPENTRY, loopDepthAndFlags) >= 0;
 }
 
 /*
- * If op is JOF_TYPESET (see the type barriers comment in jsinfer.h), reserve
- * a type set to store its result.
+ * If op is JOF_TYPESET (see the type barriers comment in TypeInference.h),
+ * reserve a type set to store its result.
  */
 static inline void
 CheckTypeSet(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
 {
     if (js_CodeSpec[op].format & JOF_TYPESET) {
         if (bce->typesetCount < UINT16_MAX)
             bce->typesetCount++;
     }
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -10,17 +10,16 @@
 
 #include "jslibmath.h"
 
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "js/Conversions.h"
 
 #include "jscntxtinlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::NegativeInfinity;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -411,37 +411,45 @@ class GCSchedulingTunables
  *   minimizing size[allocated], per the equation above.
  *
  *   The result of the above is GC triggers that focus on size[allocated] to
  *   the exclusion of other important factors and default heuristics that are
  *   not optimal for a fully incremental collector. On the other hand, this is
  *   not all bad: minimizing size[allocated] also minimizes the chance of OOM
  *   and sweeping remains one of the hardest areas to further incrementalize.
  *
- *      MAYBEGC
- *      -------
+ *      EAGER_ALLOC_TRIGGER
+ *      -------------------
  *      Occurs when we return to the event loop and find our heap is getting
  *      largish, but before t[marking] OR t[sweeping] is too large for a
  *      responsive non-incremental GC. This is intended to be the common case
  *      in normal web applications: e.g. we just finished an event handler and
  *      the few objects we allocated when computing the new whatzitz have
  *      pushed us slightly over the limit. After this GC we rescale the new
- *      MAYBEGC trigger to 150% of size[retained] so that our non-incremental
- *      GC times will always be proportional to this size rather than being
- *      dominated by sweeping.
+ *      EAGER_ALLOC_TRIGGER trigger to 150% of size[retained] so that our
+ *      non-incremental GC times will always be proportional to this size
+ *      rather than being dominated by sweeping.
  *
  *      As a concession to mutators that allocate heavily during their startup
  *      phase, we have a highFrequencyGCMode that ups the growth rate to 300%
  *      of the current size[retained] so that we'll do fewer longer GCs at the
  *      end of the mutator startup rather than more, smaller GCs.
  *
  *          Assumptions:
  *            -> Responsiveness is proportional to t[marking] + t[sweeping].
  *            -> size[retained] is proportional only to GC allocations.
  *
+ *      PERIODIC_FULL_GC
+ *      ----------------
+ *      When we return to the event loop and it has been 20 seconds since we've
+ *      done a GC, we start an incremenal, all-zones, shrinking GC.
+ *
+ *          Assumptions:
+ *            -> Our triggers are incomplete.
+ *
  *      ALLOC_TRIGGER (non-incremental)
  *      -------------------------------
  *      If we do not return to the event loop before getting all the way to our
  *      gc trigger bytes then MAYBEGC will never fire. To avoid OOMing, we
  *      succeed the current allocation and set the script interrupt so that we
  *      will (hopefully) do a GC before we overflow our max and have to raise
  *      an OOM exception for the script.
  *
@@ -597,17 +605,17 @@ class GCRuntime
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MINOR_GC);
         minorGCImpl(reason, nullptr);
     }
     void minorGC(JSContext *cx, JS::gcreason::Reason reason);
     void evictNursery(JS::gcreason::Reason reason = JS::gcreason::EVICT_NURSERY) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_EVICT_NURSERY);
         minorGCImpl(reason, nullptr);
     }
-    bool gcIfNeeded(JSContext *cx = nullptr);
+    bool gcIfRequested(JSContext *cx = nullptr);
     void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
     void startGC(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
     void gcSlice(JS::gcreason::Reason reason, int64_t millis = 0);
     void finishGC(JS::gcreason::Reason reason);
     void startDebugGC(JSGCInvocationKind gckind, SliceBudget &budget);
     void debugGCSlice(SliceBudget &budget);
 
     void runDebugGC();
@@ -727,17 +735,17 @@ class GCRuntime
     bool isCompactingGCEnabled();
 
     void setGrayRootsTracer(JSTraceDataOp traceOp, void *data);
     bool addBlackRootsTracer(JSTraceDataOp traceOp, void *data);
     void removeBlackRootsTracer(JSTraceDataOp traceOp, void *data);
 
     void setMaxMallocBytes(size_t value);
     void resetMallocBytes();
-    bool isTooMuchMalloc() const { return mallocBytes <= 0; }
+    bool isTooMuchMalloc() const { return mallocBytesUntilGC <= 0; }
     void updateMallocCounter(JS::Zone *zone, size_t nbytes);
     void onTooMuchMalloc();
 
     void setGCCallback(JSGCCallback callback, void *data);
     bool addFinalizeCallback(JSFinalizeCallback callback, void *data);
     void removeFinalizeCallback(JSFinalizeCallback func);
     bool addWeakPointerCallback(JSWeakPointerCallback callback, void *data);
     void removeWeakPointerCallback(JSWeakPointerCallback func);
@@ -1173,21 +1181,21 @@ class GCRuntime
     Callback<JSGCCallback> gcCallback;
     CallbackVector<JSFinalizeCallback> finalizeCallbacks;
     CallbackVector<JSWeakPointerCallback> updateWeakPointerCallbacks;
 
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs
      * from maxMallocBytes down to zero.
      */
-    mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> mallocBytes;
+    mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> mallocBytesUntilGC;
 
     /*
-     * Whether a GC has been triggered as a result of mallocBytes falling
-     * below zero.
+     * Whether a GC has been triggered as a result of mallocBytesUntilGC
+     * falling below zero.
      */
     mozilla::Atomic<bool, mozilla::ReleaseAcquire> mallocGCTriggered;
 
     /*
      * The trace operations to trace embedding-specific GC roots. One is for
      * tracing through black roots and the other is for tracing through gray
      * roots. The black/gray distinction is only relevant to the cycle
      * collector.
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -17,17 +17,16 @@
 #include "vm/ArrayObject.h"
 #include "vm/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/Symbol.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "jscompartmentinlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "gc/Nursery-inl.h"
 #include "vm/String-inl.h"
 #include "vm/Symbol-inl.h"
 
 using namespace js;
 using namespace js::gc;
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -6,29 +6,29 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gc/Nursery-inl.h"
 
 #include "mozilla/IntegerPrintfMacros.h"
 
 #include "jscompartment.h"
 #include "jsgc.h"
-#include "jsinfer.h"
 #include "jsutil.h"
 #include "prmjtime.h"
 
 #include "gc/GCInternals.h"
 #include "gc/Memory.h"
 #include "jit/JitFrames.h"
 #include "vm/ArrayObject.h"
 #include "vm/Debugger.h"
 #if defined(DEBUG)
 #include "vm/ScopeObject.h"
 #endif
 #include "vm/TypedArrayObject.h"
+#include "vm/TypeInference.h"
 
 #include "jsgcinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace gc;
 
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -7,21 +7,21 @@
 #ifndef gc_Zone_h
 #define gc_Zone_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jscntxt.h"
-#include "jsinfer.h"
 
 #include "gc/FindSCCs.h"
 #include "gc/GCRuntime.h"
 #include "js/TracingAPI.h"
+#include "vm/TypeInference.h"
 
 namespace js {
 
 namespace jit {
 class JitZone;
 }
 
 namespace gc {
--- a/js/src/jit-test/tests/basic/bug513898-regexp.js
+++ b/js/src/jit-test/tests/basic/bug513898-regexp.js
@@ -1,20 +1,20 @@
 /* 
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  * Contributor: haytjes <hv1989@gmail.com>
  */
 
 /* Check the undefined pattern is equivalent to empty string. */
 
-assertEq(RegExp(undefined).source, '');
+assertEq(RegExp(undefined).source, '(?:)');
 assertEq(RegExp(undefined).global, false);
 assertEq("test".replace(RegExp(undefined), "*"), '*test');
-assertEq(new RegExp(undefined).source, '');
+assertEq(new RegExp(undefined).source, '(?:)');
 assertEq(new RegExp(undefined).global, false);
 assertEq('test'.replace(new RegExp(undefined), "*"), '*test');
 
 /* Global flags. */
 
 assertEq(new RegExp(undefined, "g").global, true);
 assertEq("test".replace(new RegExp(undefined, "g"), "*"), "*t*e*s*t*");
 assertEq(RegExp(undefined, "g").global, true);
--- a/js/src/jit-test/tests/basic/bug750307.js
+++ b/js/src/jit-test/tests/basic/bug750307.js
@@ -1,6 +1,5 @@
 load(libdir + "asserts.js");
 var g = newGlobal();
 var a = g.RegExp("x");
-assertThrowsInstanceOf(function () { Object.defineProperty(a, "ignoreCase", {value: undefined}); },
-                       g.TypeError);
+Object.defineProperty(a, "ignoreCase", {value: undefined});
 a.toString();
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -39,17 +39,16 @@
 #include "jit/Sink.h"
 #include "jit/StupidAllocator.h"
 #include "jit/ValueNumbering.h"
 #include "vm/HelperThreads.h"
 #include "vm/TraceLogging.h"
 
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::ThreadLocal;
 
 // Assert that JitCode is gc::Cell aligned.
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -11,17 +11,16 @@
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonBuilder.h"
 #include "jit/IonOptimizationLevels.h"
 #include "jit/LIR.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
 
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -18,18 +18,16 @@
 #include "jit/JitSpewer.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Opcodes.h"
 #include "vm/RegExpStatics.h"
 #include "vm/TraceLogging.h"
 
-#include "jsinferinlines.h"
-#include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/CompileInfo-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -6,25 +6,25 @@
 
 #ifndef jit_IonCode_h
 #define jit_IonCode_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 
-#include "jsinfer.h"
 #include "jstypes.h"
 
 #include "gc/Heap.h"
 #include "jit/ExecutableAllocator.h"
 #include "jit/IonOptimizationLevels.h"
 #include "jit/IonTypes.h"
 #include "js/UbiNode.h"
 #include "vm/TraceLogging.h"
+#include "vm/TypeInference.h"
 
 namespace js {
 
 class AsmJSModule;
 
 namespace jit {
 
 class MacroAssembler;
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -2,17 +2,16 @@
  * 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/. */
 
 #include "jit/JitFrames-inl.h"
 
 #include "jsfun.h"
-#include "jsinfer.h"
 #include "jsobj.h"
 #include "jsscript.h"
 
 #include "gc/Marking.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
@@ -26,23 +25,24 @@
 #include "jit/Safepoints.h"
 #include "jit/Snapshots.h"
 #include "jit/VMFunctions.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Debugger.h"
 #include "vm/Interpreter.h"
 #include "vm/SPSProfiler.h"
 #include "vm/TraceLogging.h"
-
-#include "jsinferinlines.h"
+#include "vm/TypeInference.h"
+
 #include "jsscriptinlines.h"
 #include "gc/Nursery-inl.h"
 #include "jit/JitFrameIterator-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/Probes-inl.h"
+#include "vm/TypeInference-inl.h"
 
 namespace js {
 namespace jit {
 
 // Given a slot index, returns the offset, in bytes, of that slot from an
 // JitFrameLayout. Slot distances are uniform across architectures, however,
 // the distance does depend on the size of the frame header.
 static inline int32_t
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -2,16 +2,18 @@
  * 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 jit_JitOptions_h
 #define jit_JitOptions_h
 
+#include "mozilla/Maybe.h"
+
 #include "jit/IonTypes.h"
 #include "js/TypeDecls.h"
 
 namespace js {
 namespace jit {
 
 // Longer scripts can only be compiled off thread, as these compilations
 // can be expensive and stall the main thread for too long.
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -8,17 +8,16 @@
 
 #include "mozilla/DebugOnly.h"
 
 #include "jit/JitSpewer.h"
 #include "jit/LIR.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
 
 #include "jit/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace jit;
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -17,17 +17,16 @@
 #include "jit/BaselineInspector.h"
 #include "jit/IonBuilder.h"
 #include "jit/JitSpewer.h"
 #include "jit/MIRGraph.h"
 #include "jit/RangeAnalysis.h"
 #include "js/Conversions.h"
 
 #include "jsatominlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 using JS::ToInt32;
 
 using mozilla::NumbersAreIdentical;
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1,33 +1,31 @@
 /* -*- 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/. */
 
 #include "jit/MacroAssembler.h"
 
-#include "jsinfer.h"
 #include "jsprf.h"
 
 #include "builtin/TypedObject.h"
 #include "gc/GCTrace.h"
 #include "jit/AtomicOp.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "js/Conversions.h"
 #include "vm/TraceLogging.h"
 
 #include "jsgcinlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using JS::GenericNaN;
 using JS::ToInt32;
@@ -764,20 +762,27 @@ MacroAssembler::storeUnboxedProperty(T a
                 convertInt32ToDouble(value.reg().typedReg().gpr(), ScratchDoubleReg);
                 storeDouble(ScratchDoubleReg, address);
             } else if (value.reg().type() == MIRType_Double) {
                 storeDouble(value.reg().typedReg().fpu(), address);
             } else {
                 jump(failure);
             }
         } else {
+            ValueOperand reg = value.reg().valueReg();
+            Label notInt32, end;
+            branchTestInt32(Assembler::NotEqual, reg, &notInt32);
+            int32ValueToDouble(reg, ScratchDoubleReg);
+            storeDouble(ScratchDoubleReg, address);
+            jump(&end);
+            bind(&notInt32);
             if (failure)
-                branchTestNumber(Assembler::NotEqual, value.reg().valueReg(), failure);
-            unboxValue(value.reg().valueReg(), AnyRegister(ScratchDoubleReg));
-            storeDouble(ScratchDoubleReg, address);
+                branchTestDouble(Assembler::NotEqual, reg, failure);
+            storeValue(reg, address);
+            bind(&end);
         }
         break;
 
       case JSVAL_TYPE_OBJECT:
         if (value.constant()) {
             if (value.value().isObjectOrNull())
                 storePtr(ImmGCPtr(value.value().toObjectOrNull()), address);
             else
--- a/js/src/jit/OptimizationTracking.h
+++ b/js/src/jit/OptimizationTracking.h
@@ -4,21 +4,21 @@
  * 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 jit_OptimizationTracking_h
 #define jit_OptimizationTracking_h
 
 #include "mozilla/Maybe.h"
 
-#include "jsinfer.h"
 #include "jit/CompactBuffer.h"
 #include "jit/CompileInfo.h"
 #include "jit/JitAllocPolicy.h"
 #include "js/TrackedOptimizationInfo.h"
+#include "vm/TypeInference.h"
 
 namespace js {
 
 namespace jit {
 
 struct NativeToTrackedOptimizations;
 
 class OptimizationAttempt
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -13,24 +13,23 @@
 #include "jit/JitCompartment.h"
 #include "jit/JitFrames.h"
 #include "jit/mips/Simulator-mips.h"
 #include "vm/ArrayObject.h"
 #include "vm/Debugger.h"
 #include "vm/Interpreter.h"
 #include "vm/TraceLogging.h"
 
-#include "jsinferinlines.h"
-
 #include "jit/BaselineFrame-inl.h"
 #include "jit/JitFrames-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/StringObject-inl.h"
+#include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 namespace js {
 namespace jit {
 
 // Don't explicitly initialize, it's not guaranteed that this initializer will
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -83,17 +83,16 @@
 #include "vm/Symbol.h"
 #include "vm/TypedArrayCommon.h"
 #include "vm/WeakMapObject.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
-#include "jsinferinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::gc;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -462,20 +462,16 @@ struct JSContext : public js::ExclusiveC
     inline js::Nursery &nursery() {
         return runtime_->gc.nursery;
     }
 
     void minorGC(JS::gcreason::Reason reason) {
         runtime_->gc.minorGC(this, reason);
     }
 
-    void gcIfNeeded() {
-        runtime_->gc.gcIfNeeded(this);
-    }
-
   public:
     bool isExceptionPending() {
         return throwing;
     }
 
     MOZ_WARN_UNUSED_RESULT
     bool getPendingException(JS::MutableHandleValue rval);
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -23,17 +23,16 @@
 #include "proxy/DeadObjectProxy.h"
 #include "vm/Debugger.h"
 #include "vm/StopIterationObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsgcinlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1122,17 +1122,17 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     zealMode(0),
     zealFrequency(0),
     nextScheduled(0),
     deterministicOnly(false),
     incrementalLimit(0),
 #endif
     validate(true),
     fullCompartmentChecks(false),
-    mallocBytes(0),
+    mallocBytesUntilGC(0),
     mallocGCTriggered(false),
     alwaysPreserveCode(false),
 #ifdef DEBUG
     inUnsafeRegion(0),
     noGCOrAllocationCheck(0),
     relocatedArenasToRelease(nullptr),
 #endif
     lock(nullptr),
@@ -1650,24 +1650,24 @@ GCRuntime::setMaxMallocBytes(size_t valu
     resetMallocBytes();
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
         zone->setGCMaxMallocBytes(value);
 }
 
 void
 GCRuntime::resetMallocBytes()
 {
-    mallocBytes = ptrdiff_t(maxMallocBytes);
+    mallocBytesUntilGC = ptrdiff_t(maxMallocBytes);
     mallocGCTriggered = false;
 }
 
 void
 GCRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes)
 {
-    mallocBytes -= ptrdiff_t(nbytes);
+    mallocBytesUntilGC -= ptrdiff_t(nbytes);
     if (MOZ_UNLIKELY(isTooMuchMalloc()))
         onTooMuchMalloc();
     else if (zone)
         zone->updateMallocCounter(nbytes);
 }
 
 void
 GCRuntime::onTooMuchMalloc()
@@ -3203,31 +3203,31 @@ GCRuntime::triggerZoneGC(Zone *zone, JS:
 bool
 GCRuntime::maybeGC(Zone *zone)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
 #ifdef JS_GC_ZEAL
     if (zealMode == ZealAllocValue || zealMode == ZealPokeValue) {
         JS::PrepareForFullGC(rt);
-        gc(GC_NORMAL, JS::gcreason::MAYBEGC);
+        gc(GC_NORMAL, JS::gcreason::DEBUG_GC);
         return true;
     }
 #endif
 
-    if (gcIfNeeded())
+    if (gcIfRequested())
         return true;
 
     if (zone->usage.gcBytes() > 1024 * 1024 &&
         zone->threshold.isCloseToAllocTrigger(zone->usage, schedulingState.inHighFrequencyGCMode()) &&
         !isIncrementalGCInProgress() &&
         !isBackgroundSweeping())
     {
         PrepareZoneForGC(zone);
-        startGC(GC_NORMAL, JS::gcreason::MAYBEGC);
+        startGC(GC_NORMAL, JS::gcreason::EAGER_ALLOC_TRIGGER);
         return true;
     }
 
     return false;
 }
 
 void
 GCRuntime::maybePeriodicFullGC()
@@ -3243,17 +3243,17 @@ GCRuntime::maybePeriodicFullGC()
      */
 #ifndef JS_MORE_DETERMINISTIC
     int64_t now = PRMJ_Now();
     if (nextFullGCTime && nextFullGCTime <= now && !isIncrementalGCInProgress()) {
         if (chunkAllocationSinceLastGC ||
             numArenasFreeCommitted > decommitThreshold)
         {
             JS::PrepareForFullGC(rt);
-            startGC(GC_SHRINK, JS::gcreason::MAYBEGC);
+            startGC(GC_SHRINK, JS::gcreason::PERIODIC_FULL_GC);
         } else {
             nextFullGCTime = now + GC_IDLE_FULL_SPAN;
         }
     }
 #endif
 }
 
 // Do all possible decommit immediately from the current thread without
@@ -6086,17 +6086,17 @@ static bool
 IsDeterministicGCReason(JS::gcreason::Reason reason)
 {
     if (reason > JS::gcreason::DEBUG_GC &&
         reason != JS::gcreason::CC_FORCED && reason != JS::gcreason::SHUTDOWN_CC)
     {
         return false;
     }
 
-    if (reason == JS::gcreason::MAYBEGC)
+    if (reason == JS::gcreason::EAGER_ALLOC_TRIGGER)
         return false;
 
     return true;
 }
 #endif
 
 gcstats::ZoneGCStats
 GCRuntime::scanZonesBeforeGC()
@@ -6413,17 +6413,17 @@ GCRuntime::enableGenerationalGC()
     --generationalDisabled;
     if (generationalDisabled == 0) {
         nursery.enable();
         storeBuffer.enable();
     }
 }
 
 bool
-GCRuntime::gcIfNeeded(JSContext *cx /* = nullptr */)
+GCRuntime::gcIfRequested(JSContext *cx /* = nullptr */)
 {
     // This method returns whether a major GC was performed.
 
     if (minorGCRequested()) {
         if (cx)
             minorGC(cx, minorGCTriggerReason);
         else
             minorGC(minorGCTriggerReason);
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -445,17 +445,17 @@ CheckAllocatorState(ExclusiveContext *cx
 #ifdef JS_GC_ZEAL
         if (rt->gc.needZealousGC())
             rt->gc.runDebugGC();
 #endif
 
         if (rt->hasPendingInterrupt()) {
             // Invoking the interrupt callback can fail and we can't usefully
             // handle that here. Just check in case we need to collect instead.
-            ncx->gcIfNeeded();
+            rt->gc.gcIfRequested();
         }
     }
 
     return true;
 }
 
 template <typename T>
 static inline void
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -28,18 +28,16 @@
 #include "gc/Marking.h"
 #include "vm/GeneratorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/StopIterationObject.h"
 #include "vm/TypedArrayCommon.h"
 
-#include "jsinferinlines.h"
-#include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::gc;
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -17,17 +17,18 @@
 #include "vm/Probes.h"
 #include "vm/ScopeObject.h"
 #include "vm/StringObject.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsatominlines.h"
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
-#include "jsinferinlines.h"
+
+#include "vm/TypeInference-inl.h"
 
 inline void
 JSObject::finalize(js::FreeOp *fop)
 {
     js::probes::FinalizeObject(this);
 
 #ifdef DEBUG
     MOZ_ASSERT(isTenured());
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -34,17 +34,16 @@
 #include "js/CharacterEncoding.h"
 #include "vm/Opcodes.h"
 #include "vm/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/StringBuffer.h"
 
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 using JS::AutoCheckCannotGC;
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -42,17 +42,16 @@
 #include "vm/Compression.h"
 #include "vm/Debugger.h"
 #include "vm/Opcodes.h"
 #include "vm/SelfHosting.h"
 #include "vm/Shape.h"
 #include "vm/Xdr.h"
 
 #include "jsfuninlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/ScopeObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -39,21 +39,20 @@
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Opcodes.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpStatics.h"
 #include "vm/ScopeObject.h"
 #include "vm/StringBuffer.h"
 
-#include "jsinferinlines.h"
-
 #include "vm/Interpreter-inl.h"
 #include "vm/String-inl.h"
 #include "vm/StringObject-inl.h"
+#include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::unicode;
 
 using JS::Symbol;
 using JS::SymbolCode;
 using JS::ToInt32;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -204,17 +204,16 @@ UNIFIED_SOURCES += [
     'jscntxt.cpp',
     'jscompartment.cpp',
     'jsdate.cpp',
     'jsdtoa.cpp',
     'jsexn.cpp',
     'jsfriendapi.cpp',
     'jsfun.cpp',
     'jsgc.cpp',
-    'jsinfer.cpp',
     'jsiter.cpp',
     'jsnativestack.cpp',
     'jsnum.cpp',
     'jsobj.cpp',
     'json.cpp',
     'jsopcode.cpp',
     'jsprf.cpp',
     'jspropertytree.cpp',
@@ -268,16 +267,17 @@ UNIFIED_SOURCES += [
     'vm/SharedTypedArrayObject.cpp',
     'vm/SPSProfiler.cpp',
     'vm/Stack.cpp',
     'vm/String.cpp',
     'vm/StringBuffer.cpp',
     'vm/StructuredClone.cpp',
     'vm/Symbol.cpp',
     'vm/TypedArrayObject.cpp',
+    'vm/TypeInference.cpp',
     'vm/UbiNode.cpp',
     'vm/UnboxedObject.cpp',
     'vm/Unicode.cpp',
     'vm/Value.cpp',
     'vm/WeakMapPtr.cpp',
     'vm/Xdr.cpp'
 ]
 
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -15,17 +15,16 @@
 
 #include "gc/Marking.h"
 #include "proxy/DeadObjectProxy.h"
 #include "proxy/ScriptedDirectProxyHandler.h"
 #include "proxy/ScriptedIndirectProxyHandler.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 void
--- a/js/src/tests/ecma_2/RegExp/constructor-001.js
+++ b/js/src/tests/ecma_2/RegExp/constructor-001.js
@@ -35,17 +35,17 @@ var re = new RegExp();
 AddTestCase(
   "RegExp.prototype.getClassProperty = Object.prototype.toString; " +
   "(new RegExp()).getClassProperty()",
   "[object RegExp]",
   re.getClassProperty() );
 
 AddTestCase(
   "(new RegExp()).source",
-  "",
+  "(?:)",
   re.source );
 
 AddTestCase(
   "(new RegExp()).global",
   false,
   re.global );
 
 AddTestCase(
deleted file mode 100644
--- a/js/src/tests/ecma_2/RegExp/function-001.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* 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/. */
-
-
-/**
- *  File Name:          RegExp/function-001.js
- *  ECMA Section:       15.7.2.1
- *  Description:        Based on ECMA 2 Draft 7 February 1999
- *
- *  Author:             christine@netscape.com
- *  Date:               19 February 1999
- */
-var SECTION = "RegExp/function-001";
-var VERSION = "ECMA_2";
-var TITLE   = "RegExp( pattern, flags )";
-
-startTest();
-
-/*
- * for each test case, verify:
- * - verify that [[Class]] property is RegExp
- * - prototype property should be set to RegExp.prototype
- * - source is set to the empty string
- * - global property is set to false
- * - ignoreCase property is set to false
- * - multiline property is set to false
- * - lastIndex property is set to 0
- */
-
-RegExp.prototype.getClassProperty = Object.prototype.toString;
-var re = new RegExp();
-
-AddTestCase(
-  "RegExp.prototype.getClassProperty = Object.prototype.toString; " +
-  "(new RegExp()).getClassProperty()",
-  "[object RegExp]",
-  re.getClassProperty() );
-
-AddTestCase(
-  "(new RegExp()).source",
-  "",
-  re.source );
-
-AddTestCase(
-  "(new RegExp()).global",
-  false,
-  re.global );
-
-AddTestCase(
-  "(new RegExp()).ignoreCase",
-  false,
-  re.ignoreCase );
-
-AddTestCase(
-  "(new RegExp()).multiline",
-  false,
-  re.multiline );
-
-AddTestCase(
-  "(new RegExp()).lastIndex",
-  0,
-  re.lastIndex );
-
-test()
--- a/js/src/tests/ecma_2/RegExp/properties-001.js
+++ b/js/src/tests/ecma_2/RegExp/properties-001.js
@@ -14,17 +14,17 @@
  */
 var SECTION = "RegExp/properties-001.js";
 var VERSION = "ECMA_2";
 var TITLE   = "Properties of RegExp Instances";
 var BUGNUMBER ="";
 
 startTest();
 
-AddRegExpCases( new RegExp, "",   false, false, false, 0 );
+AddRegExpCases( new RegExp, "(?:)",   false, false, false, 0 );
 AddRegExpCases( /.*/,       ".*", false, false, false, 0 );
 AddRegExpCases( /[\d]{5}/g, "[\\d]{5}", true, false, false, 0 );
 AddRegExpCases( /[\S]?$/i,  "[\\S]?$", false, true, false, 0 );
 AddRegExpCases( /^([a-z]*)[^\w\s\f\n\r]+/m,  "^([a-z]*)[^\\w\\s\\f\\n\\r]+", false, false, true, 0 );
 AddRegExpCases( /[\D]{1,5}[\ -][\d]/gi,      "[\\D]{1,5}[\\ -][\\d]", true, true, false, 0 );
 AddRegExpCases( /[a-zA-Z0-9]*/gm, "[a-zA-Z0-9]*", true, false, true, 0 );
 AddRegExpCases( /x|y|z/gim, "x|y|z", true, true, true, 0 );
 
--- a/js/src/tests/ecma_5/Object/15.2.3.3-01.js
+++ b/js/src/tests/ecma_5/Object/15.2.3.3-01.js
@@ -262,60 +262,16 @@ expected =
   };
 
 expectDescriptor(pd, expected);
 
 /******************************************************************************/
 
 o = /foo/im;
 
-pd = Object.getOwnPropertyDescriptor(o, "source");
-expected =
-  {
-    value: "foo",
-    writable: false,
-    enumerable: false,
-    configurable: false
-  };
-
-expectDescriptor(pd, expected);
-
-pd = Object.getOwnPropertyDescriptor(o, "global");
-expected =
-  {
-    value: false,
-    writable: false,
-    enumerable: false,
-    configurable: false
-  };
-
-expectDescriptor(pd, expected);
-
-pd = Object.getOwnPropertyDescriptor(o, "ignoreCase");
-expected =
-  {
-    value: true,
-    writable: false,
-    enumerable: false,
-    configurable: false
-  };
-
-expectDescriptor(pd, expected);
-
-pd = Object.getOwnPropertyDescriptor(o, "multiline");
-expected =
-  {
-    value: true,
-    writable: false,
-    enumerable: false,
-    configurable: false
-  };
-
-expectDescriptor(pd, expected);
-
 pd = Object.getOwnPropertyDescriptor(o, "lastIndex");
 expected =
   {
     value: 0,
     writable: true,
     enumerable: false,
     configurable: false
   };
--- a/js/src/tests/ecma_5/Object/15.2.3.4-04.js
+++ b/js/src/tests/ecma_5/Object/15.2.3.4-04.js
@@ -11,17 +11,17 @@ var summary = 'Object.getOwnPropertyName
 
 print(BUGNUMBER + ": " + summary);
 
 /**************
  * BEGIN TEST *
  **************/
 
 var actual = Object.getOwnPropertyNames(/a/);
-var expected = ["lastIndex", "source", "global", "ignoreCase", "multiline"];
+var expected = ["lastIndex"];
 
 for (var i = 0; i < expected.length; i++)
 {
   reportCompare(actual.indexOf(expected[i]) >= 0, true,
                 expected[i] + " should be a property name on a RegExp");
 }
 
 /******************************************************************************/
--- a/js/src/tests/ecma_5/RegExp/instance-property-storage-introspection.js
+++ b/js/src/tests/ecma_5/RegExp/instance-property-storage-introspection.js
@@ -42,111 +42,89 @@ function checkDataProperty(obj, p, expec
 
 var choices = [{ msg: "RegExp.prototype",
                  get: function() { return RegExp.prototype; } },
                { msg: "new RegExp()",
                  get: function() { return new RegExp(); } },
                { msg: "/(?:)/",
                  get: Function("return /(?:)/;") }];
 
-function checkRegExp(r, msg, lastIndex, global, ignoreCase, multiline)
+function checkRegExp(r, msg, lastIndex)
 {
   var expect;
 
   expect = { value: lastIndex, enumerable: false, configurable: false, writable: true };
   checkDataProperty(r, "lastIndex", expect, msg);
-
-  // check source specially: its value is under-defined in the spec
-  var d = Object.getOwnPropertyDescriptor(r, "source");
-  assertEq(d.writable, false, "bad writable: " + msg);
-  assertEq(d.enumerable, false, "bad enumerable: " + msg);
-  assertEq(d.configurable, false, "bad configurable: " + msg);
-
-  expect = { value: global, enumerable: false, configurable: false, writable: false };
-  checkDataProperty(r, "global", expect, msg);
-
-  expect = { value: ignoreCase, enumerable: false, configurable: false, writable: false };
-  checkDataProperty(r, "ignoreCase", expect, msg);
-
-  expect = { value: multiline, enumerable: false, configurable: false, writable: false };
-  checkDataProperty(r, "multiline", expect, msg);
 }
 
-checkRegExp(RegExp.prototype, "RegExp.prototype", 0, false, false, false);
-checkRegExp(new RegExp(), "new RegExp()", 0, false, false, false);
-checkRegExp(/(?:)/, "/(?:)/", 0, false, false, false);
-checkRegExp(Function("return /(?:)/;")(), 'Function("return /(?:)/;")()', 0, false, false, false);
+checkRegExp(RegExp.prototype, "RegExp.prototype", 0);
+checkRegExp(new RegExp(), "new RegExp()", 0);
+checkRegExp(/(?:)/, "/(?:)/", 0);
+checkRegExp(Function("return /(?:)/;")(), 'Function("return /(?:)/;")()', 0);
 
 for (var i = 0; i < choices.length; i++)
 {
   var choice = choices[i];
   var msg = choice.msg;
   var r = choice.get();
 
-  checkRegExp(r, msg, 0, false, false, false);
+  checkRegExp(r, msg, 0);
 }
 
 // Now test less generic regular expressions
 
-checkRegExp(/a/gim, "/a/gim", 0, true, true, true);
+checkRegExp(/a/gim, "/a/gim", 0);
 
 var r;
 
 do
 {
   r = /abcd/mg;
-  checkRegExp(r, "/abcd/mg initially", 0, true, false, true);
+  checkRegExp(r, "/abcd/mg initially", 0);
   r.exec("abcdefg");
-  checkRegExp(r, "/abcd/mg step 1", 4, true, false, true);
+  checkRegExp(r, "/abcd/mg step 1", 4);
   r.exec("abcdabcd");
-  checkRegExp(r, "/abcd/mg step 2", 8, true, false, true);
+  checkRegExp(r, "/abcd/mg step 2", 8);
   r.exec("abcdabcd");
-  checkRegExp(r, "/abcd/mg end", 0, true, false, true);
+  checkRegExp(r, "/abcd/mg end", 0);
 
   r = /cde/ig;
-  checkRegExp(r, "/cde/ig initially", 0, true, true, false);
+  checkRegExp(r, "/cde/ig initially", 0);
   var obj = r.lastIndex = { valueOf: function() { return 2; } };
-  checkRegExp(r, "/cde/ig after lastIndex", obj, true, true, false);
+  checkRegExp(r, "/cde/ig after lastIndex", obj);
   r.exec("aaacdef");
-  checkRegExp(r, "/cde/ig after exec", 6, true, true, false);
+  checkRegExp(r, "/cde/ig after exec", 6);
   Object.defineProperty(r, "lastIndex", { value: 3 });
-  checkRegExp(r, "/cde/ig after define 3", 3, true, true, false);
+  checkRegExp(r, "/cde/ig after define 3", 3);
   Object.defineProperty(r, "lastIndex", { value: obj });
-  checkRegExp(r, "/cde/ig after lastIndex", obj, true, true, false);
+  checkRegExp(r, "/cde/ig after lastIndex", obj);
 
 
   // Tricky bits of testing: make sure that redefining lastIndex doesn't change
   // the slot where the lastIndex property is initially stored, even if
   // the redefinition also changes writability.
   r = /a/g;
-  checkRegExp(r, "/a/g initially", 0, true, false, false);
+  checkRegExp(r, "/a/g initially", 0);
   Object.defineProperty(r, "lastIndex", { value: 2 });
   r.exec("aabbbba");
-  checkRegExp(r, "/a/g after first exec", 7, true, false, false);
+  checkRegExp(r, "/a/g after first exec", 7);
   assertEq(r.lastIndex, 7);
   r.lastIndex = 2;
-  checkRegExp(r, "/a/g after assign", 2, true, false, false);
+  checkRegExp(r, "/a/g after assign", 2);
   r.exec("aabbbba");
   assertEq(r.lastIndex, 7); // check in reverse order
-  checkRegExp(r, "/a/g after second exec", 7, true, false, false);
+  checkRegExp(r, "/a/g after second exec", 7);
 
   r = /c/g;
   r.lastIndex = 2;
-  checkRegExp(r, "/c/g initially", 2, true, false, false);
+  checkRegExp(r, "/c/g initially", 2);
   Object.defineProperty(r, "lastIndex", { writable: false });
   assertEq(Object.getOwnPropertyDescriptor(r, "lastIndex").writable, false);
   try { r.exec("aabbbba"); } catch (e) { /* swallow error if thrown */ }
   assertEq(Object.getOwnPropertyDescriptor(r, "lastIndex").writable, false);
-  assertEq(Object.getOwnPropertyDescriptor(r, "source").writable, false);
-  assertEq(Object.getOwnPropertyDescriptor(r, "global").value, true);
-  assertEq(Object.getOwnPropertyDescriptor(r, "global").writable, false);
-  assertEq(Object.getOwnPropertyDescriptor(r, "ignoreCase").value, false);
-  assertEq(Object.getOwnPropertyDescriptor(r, "ignoreCase").writable, false);
-  assertEq(Object.getOwnPropertyDescriptor(r, "multiline").value, false);
-  assertEq(Object.getOwnPropertyDescriptor(r, "multiline").writable, false);
 }
 while (Math.random() > 17); // fake loop to discourage RegExp object caching
 
 /******************************************************************************/
 
 if (typeof reportCompare === "function")
   reportCompare(true, true);
 
--- a/js/src/tests/ecma_5/strict/15.10.7.js
+++ b/js/src/tests/ecma_5/strict/15.10.7.js
@@ -1,43 +1,15 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
-assertEq(testLenientAndStrict('var r = /foo/; r.source = "bar"; r.source',
-                              returns("foo"), raisesException(TypeError)),
-         true);
-assertEq(testLenientAndStrict('var r = /foo/; delete r.source',
-                              returns(false), raisesException(TypeError)),
-         true);
-
-assertEq(testLenientAndStrict('var r = /foo/; r.global = true; r.global',
-                              returns(false), raisesException(TypeError)),
-         true);
-assertEq(testLenientAndStrict('var r = /foo/; delete r.global',
-                              returns(false), raisesException(TypeError)),
-         true);
-
-assertEq(testLenientAndStrict('var r = /foo/; r.ignoreCase = true; r.ignoreCase',
-                              returns(false), raisesException(TypeError)),
-         true);
-assertEq(testLenientAndStrict('var r = /foo/; delete r.ignoreCase',
-                              returns(false), raisesException(TypeError)),
-         true);
-
-assertEq(testLenientAndStrict('var r = /foo/; r.multiline = true; r.multiline',
-                              returns(false), raisesException(TypeError)),
-         true);
-assertEq(testLenientAndStrict('var r = /foo/; delete r.multiline',
-                              returns(false), raisesException(TypeError)),
-         true);
-
 assertEq(testLenientAndStrict('var r = /foo/; r.lastIndex = 42; r.lastIndex',
                               returns(42), returns(42)),
          true);
 assertEq(testLenientAndStrict('var r = /foo/; delete r.lastIndex',
                               returns(false), raisesException(TypeError)),
          true);
 
 reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/constructor-regexp.js
@@ -0,0 +1,61 @@
+var BUGNUMBER = 1130860;
+var summary = "RegExp constructor shouldn't invoke source/flags getters on argument RegExp instance.";
+
+print(BUGNUMBER + ": " + summary);
+
+// same-compartment
+var a = /foo/;
+var flagsCalled = false;
+var sourceCalled = false;
+Object.defineProperty(a, "source", { get: () => {
+  sourceCalled = true;
+  return "bar";
+}});
+Object.defineProperty(a, "flags", { get: () => {
+  flagsCalled = true;
+  return "i";
+}});
+
+assertEq(a.source, "bar");
+assertEq(a.flags, "i");
+assertEq(sourceCalled, true);
+assertEq(flagsCalled, true);
+
+sourceCalled = false;
+flagsCalled = false;
+assertEq(new RegExp(a).source, "foo");
+assertEq(sourceCalled, false);
+assertEq(flagsCalled, false);
+
+// cross-compartment
+var g = newGlobal();
+var b = g.eval(`
+var b = /foo2/;
+var flagsCalled = false;
+var sourceCalled = false;
+Object.defineProperty(b, "source", { get: () => {
+  sourceCalled = true;
+  return "bar2";
+}});
+Object.defineProperty(b, "flags", { get: () => {
+  flagsCalled = true;
+  return "i";
+}});
+b;
+`);
+
+assertEq(b.source, "bar2");
+assertEq(b.flags, "i");
+assertEq(g.eval("sourceCalled;"), true);
+assertEq(g.eval("flagsCalled;"), true);
+
+g.eval(`
+sourceCalled = false;
+flagsCalled = false;
+`);
+assertEq(new RegExp(b).source, "foo2");
+assertEq(g.eval("sourceCalled;"), false);
+assertEq(g.eval("flagsCalled;"), false);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/descriptor.js
@@ -0,0 +1,25 @@
+var BUGNUMBER = 1120169;
+var summary = "Implement RegExp.prototype.{global, ignoreCase, multiline, sticky, unicode} - property descriptor";
+
+print(BUGNUMBER + ": " + summary);
+
+var getters = [
+  "flags",
+  "global",
+  "ignoreCase",
+  "multiline",
+  "source",
+  "sticky",
+  "unicode",
+];
+
+for (var name of getters) {
+  var desc = Object.getOwnPropertyDescriptor(RegExp.prototype, name);
+  assertEq(desc.configurable, true);
+  assertEq(desc.enumerable, false);
+  assertEq("writable" in desc, false);
+  assertEq("get" in desc, true);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/escape.js
@@ -0,0 +1,70 @@
+var BUGNUMBER = 1130860;
+var summary = 'Slash and LineTerminator should be escaped correctly.';
+
+print(BUGNUMBER + ": " + summary);
+
+function test(re, source) {
+  assertEq(re.source, source);
+  assertEq(eval("/" + re.source + "/").source, source);
+  assertEq(re.toString(), "/" + source + "/");
+}
+
+test(/\\n/,           "\\\\n");
+test(/\\\n/,          "\\\\\\n");
+test(/\\\\n/,         "\\\\\\\\n");
+test(RegExp("\\n"),   "\\n");
+test(RegExp("\\\n"),  "\\n");
+test(RegExp("\\\\n"), "\\\\n");
+
+test(/\\r/,           "\\\\r");
+test(/\\\r/,          "\\\\\\r");
+test(/\\\\r/,         "\\\\\\\\r");
+test(RegExp("\\r"),   "\\r");
+test(RegExp("\\\r"),  "\\r");
+test(RegExp("\\\\r"), "\\\\r");
+
+test(/\\u2028/,           "\\\\u2028");
+test(/\\\u2028/,          "\\\\\\u2028");
+test(/\\\\u2028/,         "\\\\\\\\u2028");
+test(RegExp("\\u2028"),   "\\u2028");
+test(RegExp("\\\u2028"),  "\\u2028");
+test(RegExp("\\\\u2028"), "\\\\u2028");
+
+test(/\\u2029/,           "\\\\u2029");
+test(/\\\u2029/,          "\\\\\\u2029");
+test(/\\\\u2029/,         "\\\\\\\\u2029");
+test(RegExp("\\u2029"),   "\\u2029");
+test(RegExp("\\\u2029"),  "\\u2029");
+test(RegExp("\\\\u2029"), "\\\\u2029");
+
+test(/\//,            "\\/");
+test(/\\\//,          "\\\\\\/");
+test(RegExp("/"),     "\\/");
+test(RegExp("\/"),    "\\/");
+test(RegExp("\\/"),   "\\/");
+test(RegExp("\\\/"),  "\\/");
+test(RegExp("\\\\/"), "\\\\\\/");
+
+test(/[/]/,             "[/]");
+test(/[\/]/,            "[\\/]");
+test(/[\\/]/,           "[\\\\/]");
+test(/[\\\/]/,          "[\\\\\\/]");
+test(RegExp("[/]"),     "[/]");
+test(RegExp("[\/]"),    "[/]");
+test(RegExp("[\\/]"),   "[\\/]");
+test(RegExp("[\\\/]"),  "[\\/]");
+test(RegExp("[\\\\/]"), "[\\\\/]");
+
+test(RegExp("\[/\]"),   "[/]");
+test(RegExp("\[\\/\]"), "[\\/]");
+
+test(/\[\/\]/,              "\\[\\/\\]");
+test(/\[\\\/\]/,            "\\[\\\\\\/\\]");
+test(RegExp("\\[/\\]"),     "\\[\\/\\]");
+test(RegExp("\\[\/\\]"),    "\\[\\/\\]");
+test(RegExp("\\[\\/\\]"),   "\\[\\/\\]");
+test(RegExp("\\[\\\/\\]"),  "\\[\\/\\]");
+test(RegExp("\\[\\\\/\\]"), "\\[\\\\\\/\\]");
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/flag-accessors.js
@@ -0,0 +1,52 @@
+var BUGNUMBER = 1120169;
+var summary = "Implement RegExp.prototype.{global, ignoreCase, multiline, sticky, unicode}";
+
+print(BUGNUMBER + ": " + summary);
+
+var props = [
+  "global",
+  "ignoreCase",
+  "multiline",
+  "sticky",
+  "unicode",
+];
+
+testThrows(RegExp.prototype);
+test(/foo/iymg, [true, true, true, true, false]);
+test(RegExp(""), [false, false, false, false, false]);
+test(RegExp("", "mygi"), [true, true, true, true, false]);
+// When the /u flag is supported, remove the following line and uncomment the
+// next line.
+assertThrowsInstanceOf(() => RegExp("", "mygui").flags, SyntaxError);
+// test(RegExp("", "mygiu"), [true, true, true, true, true]);
+
+testThrowsGeneric();
+testThrowsGeneric(1);
+testThrowsGeneric("");
+testThrowsGeneric({});
+testThrowsGeneric(new Proxy({}, {get(){ return true; }}));
+
+function test(obj, expects) {
+  for (var i = 0; i < props.length; i++) {
+    assertEq(obj[props[i]], expects[i]);
+  }
+}
+
+function testThrows(obj) {
+  for (var prop of props) {
+    assertThrowsInstanceOf(obj[prop], TypeError);
+  }
+}
+
+function testThrowsGeneric(obj) {
+  for (var prop of props) {
+    assertThrowsInstanceOf(() => genericGet(obj, prop), TypeError);
+  }
+}
+
+function genericGet(obj, prop) {
+    return Object.getOwnPropertyDescriptor(RegExp.prototype, prop).get.call(obj);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/source.js
@@ -0,0 +1,29 @@
+var BUGNUMBER = 1120169;
+var summary = "Implement RegExp.prototype.source";
+
+print(BUGNUMBER + ": " + summary);
+
+assertEq(RegExp.prototype.source, "(?:)");
+assertEq(/foo/.source, "foo");
+assertEq(/foo/iymg.source, "foo");
+assertEq(/\//.source, "\\/");
+assertEq(/\n\r/.source, "\\n\\r");
+assertEq(/\u2028\u2029/.source, "\\u2028\\u2029");
+assertEq(RegExp("").source, "(?:)");
+assertEq(RegExp("", "mygi").source, "(?:)");
+assertEq(RegExp("/").source, "\\/");
+assertEq(RegExp("\n\r").source, "\\n\\r");
+assertEq(RegExp("\u2028\u2029").source, "\\u2028\\u2029");
+
+assertThrowsInstanceOf(() => genericSource(), TypeError);
+assertThrowsInstanceOf(() => genericSource(1), TypeError);
+assertThrowsInstanceOf(() => genericSource(""), TypeError);
+assertThrowsInstanceOf(() => genericSource({}), TypeError);
+assertThrowsInstanceOf(() => genericSource(new Proxy(/foo/, {get(){ return true; }})), TypeError);
+
+function genericSource(obj) {
+    return Object.getOwnPropertyDescriptor(RegExp.prototype, "source").get.call(obj);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/ecma_6/TypedArray/from_constructor.js
+++ b/js/src/tests/ecma_6/TypedArray/from_constructor.js
@@ -22,17 +22,17 @@ for (var constructor of constructors) {
     assertEq(d[0], "A");
     assertEq(d[1], "B");
 
     // Or RegExp.
     var obj = constructor.from.call(RegExp, [1]);
     assertEq(obj instanceof constructor, false);
     assertEq(Object.getPrototypeOf(obj), RegExp.prototype);
     assertEq(Object.getOwnPropertyNames(obj).join(","),
-             "0,lastIndex,source,global,ignoreCase,multiline,sticky");
+             "0,lastIndex");
     assertEq(obj.length, undefined);
 
     // Or any JS function.
     function C(arg) {
         this.args = arguments;
     }
     var c = constructor.from.call(C, {length: 1, 0: "zero"});
     assertEq(c instanceof C, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/uint8clamped-constructor.js
@@ -0,0 +1,9 @@
+for (var v of [-300, 255.6, 300, 3.5, -3.9]) {
+    var a = new Uint8ClampedArray([v]);
+    var b = new Uint8ClampedArray(1);
+    b[0] = v;
+
+    assertEq(a[0], b[0]);
+}
+
+reportCompare(true, true);
--- a/js/src/tests/js1_5/Regress/regress-248444.js
+++ b/js/src/tests/js1_5/Regress/regress-248444.js
@@ -24,20 +24,8 @@ status = summary + ' ' + inSection(2);
 re = RegExp("[^\\\/]+$");
 actual = re.toString();
 reportCompare(expect, actual, status);
 
 status = summary + ' ' + inSection(3);
 re = RegExp("[^\\/]+$");
 actual = re.toString();
 reportCompare(expect, actual, status);
-
-status = summary + ' ' + inSection(4);
-re = RegExp("[^\/]+$");
-actual = re.toString();
-reportCompare(expect, actual, status);
-
-status = summary + ' ' + inSection(5);
-re = RegExp("[^/]+$");
-actual = re.toString();
-reportCompare(expect, actual, status);
-
-
--- a/js/src/tests/js1_8_5/extensions/clone-regexp.js
+++ b/js/src/tests/js1_8_5/extensions/clone-regexp.js
@@ -1,33 +1,37 @@
 // |reftest| skip-if(!xulRuntime.shell)
 // -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
-function testRegExp(b) {
+function testRegExp(b, c=b) {
     var a = deserialize(serialize(b));
     assertEq(a === b, false);
     assertEq(Object.getPrototypeOf(a), RegExp.prototype);
     assertEq(Object.prototype.toString.call(a), "[object RegExp]");
     for (p in a)
         throw new Error("cloned RegExp should have no enumerable properties");
 
-    assertEq(a.source, b.source);
-    assertEq(a.global, b.global);
-    assertEq(a.ignoreCase, b.ignoreCase);
-    assertEq(a.multiline, b.multiline);
-    assertEq(a.sticky, b.sticky);
+    assertEq(a.source, c.source);
+    assertEq(a.global, c.global);
+    assertEq(a.ignoreCase, c.ignoreCase);
+    assertEq(a.multiline, c.multiline);
+    assertEq(a.sticky, c.sticky);
     assertEq("expando" in a, false);
 }
 
 testRegExp(RegExp(""));
 testRegExp(/(?:)/);
 testRegExp(/^(.*)$/gimy);
 testRegExp(RegExp.prototype);
 
 var re = /\bx\b/gi;
 re.expando = true;
 testRegExp(re);
+// `source` and the flag accessors are defined on RegExp.prototype, so they're
+// not available after re.__proto__ has been changed. We solve that by passing
+// in an additional copy of the same RegExp to compare the
+// serialized-then-deserialized clone with."
 re.__proto__ = {};
-testRegExp(re);
+testRegExp(re, /\bx\b/gi);
 
 reportCompare(0, 0, 'ok');
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -1,18 +1,16 @@
 /* -*- 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/. */
 
 #include "vm/ArgumentsObject-inl.h"
 
-#include "jsinfer.h"
-
 #include "jit/JitFrames.h"
 #include "vm/GlobalObject.h"
 #include "vm/Stack.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/Stack-inl.h"
 
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -40,18 +40,16 @@
 #include "gc/Memory.h"
 #include "js/Conversions.h"
 #include "js/MemoryMetrics.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
-#include "jsinferinlines.h"
-#include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using JS::ToInt32;
 
 using mozilla::DebugOnly;
 using mozilla::UniquePtr;
--- a/js/src/vm/ArrayObject-inl.h
+++ b/js/src/vm/ArrayObject-inl.h
@@ -6,17 +6,17 @@
 
 #ifndef vm_ArrayObject_inl_h
 #define vm_ArrayObject_inl_h
 
 #include "vm/ArrayObject.h"
 
 #include "vm/String.h"
 
-#include "jsinferinlines.h"
+#include "vm/TypeInference-inl.h"
 
 namespace js {
 
 inline void
 ArrayObject::setLength(ExclusiveContext *cx, uint32_t length)
 {
     MOZ_ASSERT(lengthIsWritable());
 
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -59,16 +59,17 @@
     macro(deleteProperty, deleteProperty, "deleteProperty") \
     macro(displayURL, displayURL, "displayURL") \
     macro(done, done, "done") \
     macro(dotGenerator, dotGenerator, ".generator") \
     macro(dotGenRVal, dotGenRVal, ".genrval") \
     macro(each, each, "each") \
     macro(elementType, elementType, "elementType") \
     macro(empty, empty, "") \
+    macro(emptyRegExp, emptyRegExp, "(?:)") \
     macro(encodeURI, encodeURI, "encodeURI") \
     macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \
     macro(enumerable, enumerable, "enumerable") \
     macro(enumerate, enumerate, "enumerate") \
     macro(escape, escape, "escape") \
     macro(eval, eval, "eval") \
     macro(false, false_, "false") \
     macro(fieldOffsets, fieldOffsets, "fieldOffsets") \
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6,17 +6,16 @@
 
 #include "vm/Debugger-inl.h"
 
 #include "mozilla/DebugOnly.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jshashutil.h"
-#include "jsinfer.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jswrapper.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
@@ -25,17 +24,16 @@
 #include "js/Vector.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/DebuggerMemory.h"
 #include "vm/SPSProfiler.h"
 #include "vm/TraceLogging.h"
 #include "vm/WrapperObject.h"
 
 #include "jsgcinlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -5,25 +5,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_Interpreter_inl_h
 #define vm_Interpreter_inl_h
 
 #include "vm/Interpreter.h"
 
 #include "jscompartment.h"
-#include "jsinfer.h"
 #include "jsnum.h"
 #include "jsstr.h"
 
 #include "jit/Ion.h"
 #include "vm/ArgumentsObject.h"
 
 #include "jsatominlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/ScopeObject-inl.h"
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
 
 namespace js {
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -38,17 +38,16 @@
 #include "vm/GeneratorObject.h"
 #include "vm/Opcodes.h"
 #include "vm/Shape.h"
 #include "vm/TraceLogging.h"
 
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
 #include "jsfuninlines.h"
-#include "jsinferinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/JitFrames-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Probes-inl.h"
 #include "vm/ScopeObject-inl.h"
 #include "vm/Stack-inl.h"
@@ -437,37 +436,37 @@ js::RunScript(JSContext *cx, RunState &s
     if (state.isInvoke()) {
         InvokeState &invoke = *state.asInvoke();
         TypeMonitorCall(cx, invoke.args(), invoke.constructing());
     }
 
     return Interpret(cx, state);
 }
 
-struct AutoGCIfNeeded
+struct AutoGCIfRequested
 {
-    JSContext *cx_;
-    explicit AutoGCIfNeeded(JSContext *cx) : cx_(cx) {}
-    ~AutoGCIfNeeded() { cx_->gcIfNeeded(); }
+    JSRuntime *runtime;
+    explicit AutoGCIfRequested(JSRuntime *rt) : runtime(rt) {}
+    ~AutoGCIfRequested() { runtime->gc.gcIfRequested(); }
 };
 
 /*
  * Find a function reference and its 'this' value implicit first parameter
  * under argc arguments on cx's stack, and call the function.  Push missing
  * required arguments, allocate declared local variables, and pop everything
  * when done.  Then push the return value.
  */
 bool
 js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct)
 {
     MOZ_ASSERT(args.length() <= ARGS_LENGTH_MAX);
     MOZ_ASSERT(!cx->zone()->types.activeAnalysis);
 
     /* Perform GC if necessary on exit from the function. */
-    AutoGCIfNeeded gcIfNeeded(cx);
+    AutoGCIfRequested gcIfRequested(cx->runtime());
 
     /* MaybeConstruct is a subset of InitialFrameFlags */
     InitialFrameFlags initial = (InitialFrameFlags) construct;
 
     if (args.calleev().isPrimitive())
         return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, construct);
 
     const Class *clasp = args.callee().getClass();
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -8,26 +8,26 @@
 #define vm_NativeObject_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 
 #include <stdint.h>
 
 #include "jsfriendapi.h"
-#include "jsinfer.h"
 #include "jsobj.h"
 #include "NamespaceImports.h"
 
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
 #include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/Shape.h"
 #include "vm/String.h"
+#include "vm/TypeInference.h"
 
 namespace js {
 
 class Nursery;
 class Shape;
 
 /*
  * To really poison a set of values, using 'magic' or 'undefined' isn't good
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -10,17 +10,16 @@
 #include "jsobj.h"
 
 #include "gc/StoreBuffer.h"
 #include "gc/Zone.h"
 #include "vm/ArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "jsgcinlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 
 using mozilla::PodZero;
 
 /////////////////////////////////////////////////////////////////////
 // ObjectGroup
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -4,20 +4,20 @@
  * 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 vm_ObjectGroup_h
 #define vm_ObjectGroup_h
 
 #include "jsbytecode.h"
 #include "jsfriendapi.h"
-#include "jsinfer.h"
 
 #include "ds/IdValuePair.h"
 #include "gc/Barrier.h"
+#include "vm/TypeInference.h"
 
 namespace js {
 
 class TypeDescr;
 class UnboxedLayout;
 
 class TypeNewScript;
 class HeapTypeSet;
--- a/js/src/vm/ProxyObject.cpp
+++ b/js/src/vm/ProxyObject.cpp
@@ -3,17 +3,16 @@
  * 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/. */
 
 #include "vm/ProxyObject.h"
 
 #include "jscompartment.h"
 #include "jsgcinlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 
 /* static */ ProxyObject *
 ProxyObject::New(JSContext *cx, const BaseProxyHandler *handler, HandleValue priv, TaggedProto proto_,
                  JSObject *parent_, const ProxyOptions &options)
 {
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -332,91 +332,220 @@ RegExpObject::createShared(JSContext *cx
 }
 
 Shape *
 RegExpObject::assignInitialShape(ExclusiveContext *cx, Handle<RegExpObject*> self)
 {
     MOZ_ASSERT(self->empty());
 
     JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
-    JS_STATIC_ASSERT(SOURCE_SLOT == LAST_INDEX_SLOT + 1);
-    JS_STATIC_ASSERT(GLOBAL_FLAG_SLOT == SOURCE_SLOT + 1);
-    JS_STATIC_ASSERT(IGNORE_CASE_FLAG_SLOT == GLOBAL_FLAG_SLOT + 1);
-    JS_STATIC_ASSERT(MULTILINE_FLAG_SLOT == IGNORE_CASE_FLAG_SLOT + 1);
-    JS_STATIC_ASSERT(STICKY_FLAG_SLOT == MULTILINE_FLAG_SLOT + 1);
 
     /* The lastIndex property alone is writable but non-configurable. */
-    if (!self->addDataProperty(cx, cx->names().lastIndex, LAST_INDEX_SLOT, JSPROP_PERMANENT))
-        return nullptr;
-
-    /* Remaining instance properties are non-writable and non-configurable. */
-    unsigned attrs = JSPROP_PERMANENT | JSPROP_READONLY;
-    if (!self->addDataProperty(cx, cx->names().source, SOURCE_SLOT, attrs))
-        return nullptr;
-    if (!self->addDataProperty(cx, cx->names().global, GLOBAL_FLAG_SLOT, attrs))
-        return nullptr;
-    if (!self->addDataProperty(cx, cx->names().ignoreCase, IGNORE_CASE_FLAG_SLOT, attrs))
-        return nullptr;
-    if (!self->addDataProperty(cx, cx->names().multiline, MULTILINE_FLAG_SLOT, attrs))
-        return nullptr;
-    return self->addDataProperty(cx, cx->names().sticky, STICKY_FLAG_SLOT, attrs);
+    return self->addDataProperty(cx, cx->names().lastIndex, LAST_INDEX_SLOT, JSPROP_PERMANENT);
 }
 
 bool
 RegExpObject::init(ExclusiveContext *cx, HandleAtom source, RegExpFlag flags)
 {
     Rooted<RegExpObject *> self(cx, this);
 
     if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, self))
         return false;
 
     MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().lastIndex))->slot() ==
                LAST_INDEX_SLOT);
-    MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().source))->slot() ==
-               SOURCE_SLOT);
-    MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().global))->slot() ==
-               GLOBAL_FLAG_SLOT);
-    MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().ignoreCase))->slot() ==
-               IGNORE_CASE_FLAG_SLOT);
-    MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().multiline))->slot() ==
-               MULTILINE_FLAG_SLOT);
-    MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().sticky))->slot() ==
-               STICKY_FLAG_SLOT);
 
     /*
      * If this is a re-initialization with an existing RegExpShared, 'flags'
      * may not match getShared()->flags, so forget the RegExpShared.
      */
     self->NativeObject::setPrivate(nullptr);
 
     self->zeroLastIndex();
     self->setSource(source);
     self->setGlobal(flags & GlobalFlag);
     self->setIgnoreCase(flags & IgnoreCaseFlag);
     self->setMultiline(flags & MultilineFlag);
     self->setSticky(flags & StickyFlag);
     return true;
 }
 
+static MOZ_ALWAYS_INLINE bool
+IsLineTerminator(const JS::Latin1Char c)
+{
+    return c == '\n' || c == '\r';
+}
+
+static MOZ_ALWAYS_INLINE bool
+IsLineTerminator(const char16_t c)
+{
+    return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
+}
+
+static MOZ_ALWAYS_INLINE bool
+AppendEscapedLineTerminator(StringBuffer &sb, const JS::Latin1Char c)
+{
+    switch (c) {
+      case '\n':
+        if (!sb.append('n'))
+            return false;
+        break;
+      case '\r':
+        if (!sb.append('r'))
+            return false;
+        break;
+      default:
+        MOZ_CRASH("Bad LineTerminator");
+    }
+    return true;
+}
+
+static MOZ_ALWAYS_INLINE bool
+AppendEscapedLineTerminator(StringBuffer &sb, const char16_t c)
+{
+    switch (c) {
+      case '\n':
+        if (!sb.append('n'))
+            return false;
+        break;
+      case '\r':
+        if (!sb.append('r'))
+            return false;
+        break;
+      case 0x2028:
+        if (!sb.append("u2028"))
+            return false;
+        break;
+      case 0x2029:
+        if (!sb.append("u2029"))
+            return false;
+        break;
+      default:
+        MOZ_CRASH("Bad LineTerminator");
+    }
+    return true;
+}
+
+template <typename CharT>
+static MOZ_ALWAYS_INLINE bool
+SetupBuffer(StringBuffer &sb, const CharT *oldChars, size_t oldLen, const CharT *it)
+{
+    if (mozilla::IsSame<CharT, char16_t>::value && !sb.ensureTwoByteChars())
+        return false;
+
+    if (!sb.reserve(oldLen + 1))
+        return false;
+
+    sb.infallibleAppend(oldChars, size_t(it - oldChars));
+    return true;
+}
+
+// Note: returns the original if no escaping need be performed.
+template <typename CharT>
+static bool
+EscapeRegExpPattern(StringBuffer &sb, const CharT *oldChars, size_t oldLen)
+{
+    bool inBrackets = false;
+    bool previousCharacterWasBackslash = false;
+
+    for (const CharT *it = oldChars; it < oldChars + oldLen; ++it) {
+        CharT ch = *it;
+        if (!previousCharacterWasBackslash) {
+            if (inBrackets) {
+                if (ch == ']')
+                    inBrackets = false;
+            } else if (ch == '/') {
+                // There's a forward slash that needs escaping.
+                if (sb.empty()) {
+                    // This is the first char we've seen that needs escaping,
+                    // copy everything up to this point.
+                    if (!SetupBuffer(sb, oldChars, oldLen, it))
+                        return false;
+                }
+                if (!sb.append('\\'))
+                    return false;
+            } else if (ch == '[') {
+                inBrackets = true;
+            }
+        }
+
+        if (IsLineTerminator(ch)) {
+            // There's LineTerminator that needs escaping.
+            if (sb.empty()) {
+                // This is the first char we've seen that needs escaping,
+                // copy everything up to this point.
+                if (!SetupBuffer(sb, oldChars, oldLen, it))
+                    return false;
+            }
+            if (!previousCharacterWasBackslash) {
+                if (!sb.append('\\'))
+                    return false;
+            }
+            if (!AppendEscapedLineTerminator(sb, ch))
+                return false;
+        } else if (!sb.empty()) {
+            if (!sb.append(ch))
+                return false;
+        }
+
+        if (previousCharacterWasBackslash)
+            previousCharacterWasBackslash = false;
+        else if (ch == '\\')
+            previousCharacterWasBackslash = true;
+    }
+
+    return true;
+}
+
+// ES6 draft rev32 21.2.3.2.4.
+JSAtom *
+js::EscapeRegExpPattern(JSContext *cx, HandleAtom src)
+{
+    // Step 2.
+    if (src->length() == 0)
+        return cx->names().emptyRegExp;
+
+    // We may never need to use |sb|. Start using it lazily.
+    StringBuffer sb(cx);
+
+    if (src->hasLatin1Chars()) {
+        JS::AutoCheckCannotGC nogc;
+        if (!::EscapeRegExpPattern(sb, src->latin1Chars(nogc), src->length()))
+            return nullptr;
+    } else {
+        JS::AutoCheckCannotGC nogc;
+        if (!::EscapeRegExpPattern(sb, src->twoByteChars(nogc), src->length()))
+            return nullptr;
+    }
+
+    // Step 3.
+    return sb.empty() ? src : sb.finishAtom();
+}
+
+// ES6 draft rev32 21.2.5.14. Optimized for RegExpObject.
 JSFlatString *
 RegExpObject::toString(JSContext *cx) const
 {
-    JSAtom *src = getSource();
+    // Steps 3-4.
+    RootedAtom src(cx, getSource());
+    if (!src)
+        return nullptr;
+    RootedAtom escapedSrc(cx, EscapeRegExpPattern(cx, src));
+
+    // Step 7.
     StringBuffer sb(cx);
-    if (size_t len = src->length()) {
-        if (!sb.reserve(len + 2))
-            return nullptr;
-        sb.infallibleAppend('/');
-        if (!sb.append(src))
-            return nullptr;
-        sb.infallibleAppend('/');
-    } else {
-        if (!sb.append("/(?:)/"))
-            return nullptr;
-    }
+    size_t len = escapedSrc->length();
+    if (!sb.reserve(len + 2))
+        return nullptr;
+    sb.infallibleAppend('/');
+    if (!sb.append(escapedSrc))
+        return nullptr;
+    sb.infallibleAppend('/');
+
+    // Steps 5-7.
     if (global() && !sb.append('g'))
         return nullptr;
     if (ignoreCase() && !sb.append('i'))
         return nullptr;
     if (multiline() && !sb.append('m'))
         return nullptr;
     if (sticky() && !sb.append('y'))
         return nullptr;
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -495,11 +495,14 @@ RegExpToShared(JSContext *cx, HandleObje
 
 template<XDRMode mode>
 bool
 XDRScriptRegExpObject(XDRState<mode> *xdr, MutableHandle<RegExpObject*> objp);
 
 extern JSObject *
 CloneScriptRegExpObject(JSContext *cx, RegExpObject &re);
 
+JSAtom *
+EscapeRegExpPattern(JSContext *cx, HandleAtom src);
+
 } /* namespace js */
 
 #endif /* vm_RegExpObject_h */
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -520,17 +520,17 @@ JSRuntime::addSizeOfIncludingThis(mozill
     gc.storeBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc);
 }
 
 static bool
 InvokeInterruptCallback(JSContext *cx)
 {
     MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
 
-    cx->gcIfNeeded();
+    cx->runtime()->gc.gcIfRequested();
 
     // A worker thread may have requested an interrupt after finishing an Ion
     // compilation.
     jit::AttachFinishedCompilations(cx);
 
     // Important: Additional callbacks can occur inside the callback handler
     // if it re-enters the JS engine. The embedding must ensure that the
     // callback is disconnected before attempting such re-entry.
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -4,17 +4,17 @@
  * 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 vm_ScopeObject_inl_h
 #define vm_ScopeObject_inl_h
 
 #include "vm/ScopeObject.h"
 
-#include "jsinferinlines.h"
+#include "vm/TypeInference-inl.h"
 
 namespace js {
 
 inline void
 ScopeObject::setAliasedVar(JSContext *cx, ScopeCoordinate sc, PropertyName *name, const Value &v)
 {
     MOZ_ASSERT(is<CallObject>() || is<ClonedBlockObject>());
     JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == BlockObject::RESERVED_SLOTS);
--- a/js/src/vm/SharedTypedArrayObject.cpp
+++ b/js/src/vm/SharedTypedArrayObject.cpp
@@ -35,17 +35,16 @@
 #include "vm/ArrayBufferObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayCommon.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::IsNaN;
rename from js/src/jsinferinlines.h
rename to js/src/vm/TypeInference-inl.h
--- a/js/src/jsinferinlines.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -1,20 +1,20 @@
 /* -*- 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/. */
 
 /* Inline members for javascript type inference. */
 
-#ifndef jsinferinlines_h
-#define jsinferinlines_h
+#ifndef vm_TypeInference_inl_h
+#define vm_TypeInference_inl_h
 
-#include "jsinfer.h"
+#include "vm/TypeInference.h"
 
 #include "mozilla/PodOperations.h"
 
 #include "builtin/SymbolObject.h"
 #include "jit/BaselineJIT.h"
 #include "vm/ArrayObject.h"
 #include "vm/BooleanObject.h"
 #include "vm/NumberObject.h"
@@ -1137,9 +1137,9 @@ JSScript::types()
 }
 
 inline bool
 JSScript::ensureHasTypes(JSContext *cx)
 {
     return types() || makeTypes(cx);
 }
 
-#endif /* jsinferinlines_h */
+#endif /* vm_TypeInference_inl_h */
rename from js/src/jsinfer.cpp
rename to js/src/vm/TypeInference.cpp
--- a/js/src/jsinfer.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -1,15 +1,15 @@
 /* -*- 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/. */
 
-#include "jsinferinlines.h"
+#include "vm/TypeInference-inl.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsgc.h"
@@ -29,17 +29,16 @@
 #include "js/MemoryMetrics.h"
 #include "vm/HelperThreads.h"
 #include "vm/Opcodes.h"
 #include "vm/Shape.h"
 #include "vm/UnboxedObject.h"
 
 #include "jsatominlines.h"
 #include "jsgcinlines.h"
-#include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::DebugOnly;
rename from js/src/jsinfer.h
rename to js/src/vm/TypeInference.h
--- a/js/src/jsinfer.h
+++ b/js/src/vm/TypeInference.h
@@ -1,18 +1,18 @@
 /* -*- 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/. */
 
 /* Definitions related to javascript type inference. */
 
-#ifndef jsinfer_h
-#define jsinfer_h
+#ifndef vm_TypeInference_h
+#define vm_TypeInference_h
 
 #include "mozilla/MemoryReporting.h"
 
 #include "jsalloc.h"
 #include "jsfriendapi.h"
 #include "jstypes.h"
 
 #include "ds/IdValuePair.h"
@@ -416,18 +416,18 @@ class TypeSet
     void addType(Type type, LifoAlloc *alloc);
 
     /* Get a list of all types in this set. */
     typedef Vector<Type, 1, SystemAllocPolicy> TypeList;
     bool enumerateTypes(TypeList *list) const;
 
     /*
      * Iterate through the objects in this set. getObjectCount overapproximates
-     * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject
-     * may return nullptr.
+     * in the hash case (see SET_ARRAY_SIZE in TypeInference-inl.h), and
+     * getObject may return nullptr.
      */
     inline unsigned getObjectCount() const;
     inline ObjectKey *getObject(unsigned i) const;
     inline JSObject *getSingleton(unsigned i) const;
     inline ObjectGroup *getGroup(unsigned i) const;
     inline JSObject *getSingletonNoBarrier(unsigned i) const;
     inline ObjectGroup *getGroupNoBarrier(unsigned i) const;
 
@@ -1227,9 +1227,9 @@ PrintTypes(JSContext *cx, JSCompartment 
 // JS::ubi::Nodes can point to object groups; they're js::gc::Cell instances
 // with no associated compartment.
 namespace JS {
 namespace ubi {
 template<> struct Concrete<js::ObjectGroup> : TracerConcrete<js::ObjectGroup> { };
 }
 }
 
-#endif /* jsinfer_h */
+#endif /* vm_TypeInference_h */
--- a/js/src/vm/TypedArrayCommon.h
+++ b/js/src/vm/TypedArrayCommon.h
@@ -439,16 +439,18 @@ class ElementSpecific
             // inconsistency could confuse deterministic testing, so always
             // canonicalize NaN values in more-deterministic builds.
             d = JS::CanonicalizeNaN(d);
 #endif
             return T(d);
         }
         if (MOZ_UNLIKELY(mozilla::IsNaN(d)))
             return T(0);
+        if (SpecificArray::ArrayTypeID() == Scalar::Uint8Clamped)
+            return T(d);
         if (TypeIsUnsigned<T>())
             return T(JS::ToUint32(d));
         return T(JS::ToInt32(d));
     }
 };
 
 template<typename SomeTypedArray>
 class TypedArrayMethods
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -34,18 +34,16 @@
 #include "js/Conversions.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/TypedArrayCommon.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
-#include "jsinferinlines.h"
-#include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::IsNaN;
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -7,17 +7,16 @@
 #include "js/UbiNode.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/UniquePtr.h"
 
 #include "jscntxt.h"
-#include "jsinfer.h"
 #include "jsobj.h"
 #include "jsscript.h"
 #include "jsstr.h"
 
 #include "jit/IonCode.h"
 #include "js/Debug.h"
 #include "js/TracingAPI.h"
 #include "js/TypeDecls.h"
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -1,17 +1,16 @@
 /* -*- 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/. */
 
 #include "vm/UnboxedObject.h"
 
-#include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/Shape-inl.h"
 
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::PodCopy;
 using mozilla::UniquePtr;
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -3,19 +3,20 @@
  * 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 vm_UnboxedObject_h
 #define vm_UnboxedObject_h
 
 #include "jsgc.h"
-#include "jsinfer.h"
 #include "jsobj.h"
 
+#include "vm/TypeInference.h"
+
 namespace js {
 
 // Memory required for an unboxed value of a given type. Returns zero for types
 // which can't be used for unboxed objects.
 static inline size_t
 UnboxedTypeSize(JSValueType type)
 {
     switch (type) {
--- a/layout/forms/nsHTMLButtonControlFrame.cpp
+++ b/layout/forms/nsHTMLButtonControlFrame.cpp
@@ -258,17 +258,16 @@ CloneReflowStateWithReducedContentBox(
 
 void
 nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
                                                nsHTMLReflowMetrics& aButtonDesiredSize,
                                                const nsHTMLReflowState& aButtonReflowState,
                                                nsIFrame* aFirstKid)
 {
   WritingMode wm = GetWritingMode();
-  bool isVertical = wm.IsVertical();
   LogicalSize availSize = aButtonReflowState.ComputedSize(wm);
   availSize.BSize(wm) = NS_INTRINSICSIZE;
 
   // Buttons have some bonus renderer-determined border/padding,
   // which occupies part of the button's content-box area:
   const LogicalMargin focusPadding =
     LogicalMargin(wm, mRenderer.GetAddedButtonBorderAndPadding());
 
@@ -278,51 +277,53 @@ nsHTMLButtonControlFrame::ReflowButtonCo
   // Indent the child inside us by the focus border. We must do this separate
   // from the regular border.
   availSize.ISize(wm) -= focusPadding.IStartEnd(wm);
 
   // See whether out availSize's inline-size is big enough.  If it's smaller than
   // our intrinsic min iSize, that means that the kid wouldn't really fit; for a
   // better look in such cases we adjust the available iSize and our inline-start
   // offset to allow the kid to spill start-wards into our padding.
-  nscoord ioffset = focusPadding.IStart(wm) + clbp.IStart(wm);
+  LogicalPoint childPos(wm);
+  childPos.I(wm) = focusPadding.IStart(wm) + clbp.IStart(wm);
   nscoord extraISize = GetMinISize(aButtonReflowState.rendContext) -
     aButtonReflowState.ComputedISize();
   if (extraISize > 0) {
     nscoord extraIStart = extraISize / 2;
     nscoord extraIEnd = extraISize - extraIStart;
     NS_ASSERTION(extraIEnd >=0, "How'd that happen?");
 
     // Do not allow the extras to be bigger than the relevant padding
     const LogicalMargin& padding = aButtonReflowState.ComputedLogicalPadding();
     extraIStart = std::min(extraIStart, padding.IStart(wm));
     extraIEnd = std::min(extraIEnd, padding.IEnd(wm));
-    ioffset -= extraIStart;
+    childPos.I(wm) -= extraIStart;
     availSize.ISize(wm) = availSize.ISize(wm) + extraIStart + extraIEnd;
   }
   availSize.ISize(wm) = std::max(availSize.ISize(wm), 0);
 
   // Give child a clone of the button's reflow state, with height/width reduced
   // by focusPadding, so that descendants with height:100% don't protrude.
   nsHTMLReflowState adjustedButtonReflowState =
     CloneReflowStateWithReducedContentBox(aButtonReflowState,
                                           focusPadding.GetPhysicalMargin(wm));
 
   nsHTMLReflowState contentsReflowState(aPresContext,
                                         adjustedButtonReflowState,
                                         aFirstKid, availSize);
 
   nsReflowStatus contentsReflowStatus;
   nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState);
-  nscoord boffset = focusPadding.BStart(wm) + clbp.BStart(wm);
+  childPos.B(wm) = focusPadding.BStart(wm) + clbp.BStart(wm);
+
+  // We just pass 0 for containerWidth here, as the child will be repositioned
+  // later by FinishReflowChild.
   ReflowChild(aFirstKid, aPresContext,
               contentsDesiredSize, contentsReflowState,
-              isVertical ? boffset : ioffset,
-              isVertical ? ioffset : boffset,
-              0, contentsReflowStatus);
+              wm, childPos, 0, 0, contentsReflowStatus);
   MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus),
              "We gave button-contents frame unconstrained available height, "
              "so it should be complete");
 
   // Compute the button's content-box height:
   nscoord buttonContentBoxBSize = 0;
   if (aButtonReflowState.ComputedBSize() != NS_INTRINSICSIZE) {
     // Button has a fixed block-size -- that's its content-box bSize.
@@ -345,28 +346,29 @@ nsHTMLButtonControlFrame::ReflowButtonCo
   }
 
   // Center child in the block-direction in the button
   // (technically, inside of the button's focus-padding area)
   nscoord extraSpace =
     buttonContentBoxBSize - focusPadding.BStartEnd(wm) -
     contentsDesiredSize.BSize(wm);
 
-  boffset = std::max(0, extraSpace / 2);
+  childPos.B(wm) = std::max(0, extraSpace / 2);
 
-  // Adjust boffset to be in terms of the button's frame-rect, instead of
+  // Adjust childPos.B() to be in terms of the button's frame-rect, instead of
   // its focus-padding rect:
-  boffset += focusPadding.BStart(wm) + clbp.BStart(wm);
+  childPos.B(wm) += focusPadding.BStart(wm) + clbp.BStart(wm);
+
+  nscoord containerWidth = contentsDesiredSize.Width() +
+    clbp.LeftRight(wm) + focusPadding.LeftRight(wm);
 
   // Place the child
   FinishReflowChild(aFirstKid, aPresContext,
                     contentsDesiredSize, &contentsReflowState,
-                    isVertical ? boffset : ioffset,
-                    isVertical ? ioffset : boffset,
-                    0);
+                    wm, childPos, containerWidth, 0);
 
   // Make sure we have a useful 'ascent' value for the child
   if (contentsDesiredSize.BlockStartAscent() ==
       nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
     WritingMode wm = aButtonReflowState.GetWritingMode();
     contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm));
   }
 
@@ -380,17 +382,17 @@ nsHTMLButtonControlFrame::ReflowButtonCo
   //  * Button's ascent is its child's ascent, plus the child's block-offset
   // within our frame... unless it's orthogonal, in which case we'll use the
   // contents inline-size as an approximation for now.
   // XXX is there a better strategy? should we include border-padding?
   if (aButtonDesiredSize.GetWritingMode().IsOrthogonalTo(wm)) {
     aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.ISize(wm));
   } else {
     aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.BlockStartAscent() +
-                                           boffset);
+                                           childPos.B(wm));
   }
 
   aButtonDesiredSize.SetOverflowAreasToDesiredBounds();
 }
 
 nsresult nsHTMLButtonControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
 {
   if (nsGkAtoms::value == aName) {
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2731,17 +2731,17 @@ nsFrame::IsSelectable(bool* aSelectable,
         break;
       default:
         // otherwise return the first value which is not 'auto'
         if (selectStyle == NS_STYLE_USER_SELECT_AUTO) {
           selectStyle = userinterface->mUserSelect;
         }
         break;
     }
-    frame = frame->GetParent();
+    frame = nsLayoutUtils::GetParentOrPlaceholderFor(frame);
   }
 
   // convert internal values to standard values
   if (selectStyle == NS_STYLE_USER_SELECT_AUTO)
     selectStyle = NS_STYLE_USER_SELECT_TEXT;
   else
   if (selectStyle == NS_STYLE_USER_SELECT_MOZ_ALL)
     selectStyle = NS_STYLE_USER_SELECT_ALL;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1130231-1-button-padding-rtl-ref.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html lang="en">
+<head>
+	<meta charset="utf-8">
+	<title>Bug 1130231</title>
+	<style type="text/css">
+	div {
+	    width: 300px;
+	    height: 50px;
+	    border: 1px solid blue;
+	    margin: 10px;
+	    padding: 2px;
+	}
+	.inner {
+		padding-left: 100px;
+		padding-right: 50px;
+		border: 5px solid black;
+		border-left: 20px solid red;
+		border-right: 10px solid green;
+	}
+	</style>
+</head>
+<html>
+<body>
+<div dir=rtl style="text-align:left">
+  <button class="inner">|button|</button>
+</div>
+<div style="text-align:right">
+  <button class="inner">|button|</button>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1130231-1-button-padding-rtl.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html lang="en">
+<head>
+	<meta charset="utf-8">
+	<title>Bug 1130231</title>
+	<style type="text/css">
+	div {
+	    width: 300px;
+	    height: 50px;
+	    border: 1px solid blue;
+	    margin: 10px;
+	    padding: 2px;
+	}
+	.inner {
+		padding-left: 100px;
+		padding-right: 50px;
+		border: 5px solid black;
+		border-left: 20px solid red;
+		border-right: 10px solid green;
+	}
+	</style>
+</head>
+<html>
+<body>
+<div>
+  <button class="inner">|button|</button>
+</div>
+<div dir=rtl>
+  <button class="inner">|button|</button>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1130231-2-button-padding-rtl-ref.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html lang="en">
+<head>
+	<meta charset="utf-8">
+	<title>Bug 1130231</title>
+	<style type="text/css">
+	div {
+	    width: 300px;
+	    height: 50px;
+	    border: 1px solid blue;
+	    margin: 10px;
+	    padding: 2px;
+	}
+	.inner {
+		padding-left: 100px;
+		padding-right: 50px;
+		border: 5px solid black;
+		border-left: 20px solid red;
+		border-right: 10px solid green;
+	}
+	.right {
+		padding-right: 100px;
+		padding-left: 50px;
+	}
+	</style>
+</head>
+<html>
+<body>
+<div>
+  <button class="inner">|button|</button>
+</div>
+<div style="text-align:right">
+  <button class="inner right">|button|</button>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1130231-2-button-padding-rtl.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html lang="en">
+<head>
+	<meta charset="utf-8">
+	<title>Bug 1130231</title>
+	<style type="text/css">
+	div {
+	    width: 300px;
+	    height: 50px;
+	    border: 1px solid blue;
+	    margin: 10px;
+	    padding: 2px;
+	}
+	.inner {
+		-moz-padding-start: 100px;
+		-moz-padding-end: 50px;
+		border: 5px solid black;
+		border-left: 20px solid red;
+		border-right: 10px solid green;
+	}
+	</style>
+</head>
+<html>
+<body>
+<div>
+  <button class="inner">|button|</button>
+</div>
+<div dir=rtl>
+  <button class="inner">|button|</button>
+</div>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1860,8 +1860,10 @@ fuzzy-if(d2d,36,304) HTTP(..) == 1116480
 == 1120431-1.html 1120431-1-ref.html
 == 1120431-2.html 1120431-2-ref.html
 == 1121748-1.html 1121748-1-ref.html
 == 1121748-2.html 1121748-2-ref.html
 == 1127107-1a-nowrap.html 1127107-1-ref.html
 == 1127107-1b-pre.html 1127107-1-ref.html
 == 1127107-2-capitalize.html 1127107-2-capitalize-ref.html
 == 1127679-1a-inline-flex-relpos.html 1127679-1b-inline-flex-relpos.html
+== 1130231-1-button-padding-rtl.html 1130231-1-button-padding-rtl-ref.html
+== 1130231-2-button-padding-rtl.html 1130231-2-button-padding-rtl-ref.html
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1353,18 +1353,17 @@ nsTableFrame::PaintTableBorderBackground
   nsPresContext* presContext = PresContext();
 
   TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table,
                                  presContext, aRenderingContext,
                                  aDirtyRect, aPt, aBGPaintFlags);
   nsMargin deflate = GetDeflationForBackground(presContext);
   // If 'deflate' is (0,0,0,0) then we'll paint the table background
   // in a separate display item, so don't do it here.
-  nsresult rv = painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0));
-  if (NS_FAILED(rv)) return;
+  painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0));
 
   if (StyleVisibility()->IsVisible()) {
     if (!IsBorderCollapse()) {
       Sides skipSides = GetSkipSides();
       nsRect rect(aPt, mRect.Size());
       nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
                                   aDirtyRect, rect, mStyleContext, skipSides);
     }
--- a/layout/tables/nsTablePainter.cpp
+++ b/layout/tables/nsTablePainter.cpp
@@ -71,20 +71,18 @@
    Its principal function is PaintTable, which paints all table element
    backgrounds. The initial code in that method sets up an array of column
    data that caches the background styles and the border sizes for the
    columns and colgroups in TableBackgroundData structs in mCols. Data for
    BC borders are calculated and stashed in a synthesized border style struct
    in the data struct since collapsed borders aren't the same width as style-
    assigned borders. The data struct optimizes by only doing this if there's
    an image background; otherwise we don't care. //XXX should also check background-origin
-   The class then loops through the row groups, rows, and cells. It uses
-   the mRowGroup and mRow TableBackgroundData structs to cache data for
-   the current frame in the loop. At the cell level, it paints the backgrounds,
-   one over the other, inside the cell rect.
+   The class then loops through the row groups, rows, and cells. At the cell
+   level, it paints the backgrounds, one over the other, inside the cell rect.
 
    The exception to this pattern is when a table element creates a (pseudo)
    stacking context. Elements with stacking contexts (e.g., 'opacity' applied)
    are <dfn>passed through</dfn>, which means their data (and their
    descendants' data) are not cached. The full loop is still executed, however,
    so that underlying layers can get painted at the cell level.
 
    The TableBackgroundPainter is then destroyed.
@@ -93,123 +91,83 @@
    painting process, since they were skipped. They call the appropriate
    sub-part of the loop (e.g. PaintRow) which will paint the frame and
    descendants.
    
    XXX views are going 
  */
 
 TableBackgroundPainter::TableBackgroundData::TableBackgroundData()
-  : mFrame(nullptr),
-    mVisible(false),
-    mBorder(nullptr),
-    mSynthBorder(nullptr)
-{
-  MOZ_COUNT_CTOR(TableBackgroundData);
-}
-
-TableBackgroundPainter::TableBackgroundData::~TableBackgroundData()
+  : mFrame(nullptr)
+  , mVisible(false)
+  , mUsesSynthBorder(false)
 {
-  NS_ASSERTION(!mSynthBorder, "must call Destroy before dtor");
-  MOZ_COUNT_DTOR(TableBackgroundData);
-}
-
-void
-TableBackgroundPainter::TableBackgroundData::Destroy(nsPresContext* aPresContext)
-{
-  NS_PRECONDITION(aPresContext, "null prescontext");
-  if (mSynthBorder) {
-    mSynthBorder->Destroy(aPresContext);
-    mSynthBorder = nullptr;
-  }
 }
 
-void
-TableBackgroundPainter::TableBackgroundData::Clear()
-{
-  mRect.SetEmpty();
-  mFrame = nullptr;
-  mBorder = nullptr;
-  mVisible = false;
-}
-
-void
-TableBackgroundPainter::TableBackgroundData::SetFrame(nsIFrame* aFrame)
+TableBackgroundPainter::TableBackgroundData::TableBackgroundData(nsIFrame* aFrame)
+  : mFrame(aFrame)
+  , mRect(aFrame->GetRect())
+  , mVisible(mFrame->IsVisibleForPainting())
+  , mUsesSynthBorder(false)
 {
-  NS_PRECONDITION(aFrame, "null frame");
-  mFrame = aFrame;
-  mRect = aFrame->GetRect();
-}
-
-void
-TableBackgroundPainter::TableBackgroundData::SetData()
-{
-  NS_PRECONDITION(mFrame, "null frame");
-  if (mFrame->IsVisibleForPainting()) {
-    mVisible = true;
-    mBorder = mFrame->StyleBorder();
-  }
-}
-
-void
-TableBackgroundPainter::TableBackgroundData::SetFull(nsIFrame* aFrame)
-{
-  NS_PRECONDITION(aFrame, "null frame");
-  SetFrame(aFrame);
-  SetData();
 }
 
 inline bool
-TableBackgroundPainter::TableBackgroundData::ShouldSetBCBorder()
+TableBackgroundPainter::TableBackgroundData::ShouldSetBCBorder() const
 {
   /* we only need accurate border data when positioning background images*/
   if (!mVisible) {
     return false;
   }
 
   const nsStyleBackground *bg = mFrame->StyleBackground();
   NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
     if (!bg->mLayers[i].mImage.IsEmpty())
       return true;
   }
   return false;
 }
 
-nsresult
-TableBackgroundPainter::TableBackgroundData::SetBCBorder(nsMargin& aBorder,
-                                                         TableBackgroundPainter* aPainter)
+void
+TableBackgroundPainter::TableBackgroundData::SetBCBorder(const nsMargin& aBorder)
 {
-  NS_PRECONDITION(aPainter, "null painter");
-  if (!mSynthBorder) {
-    mSynthBorder = new (aPainter->mPresContext)
-                        nsStyleBorder(aPainter->mZeroBorder);
-    if (!mSynthBorder) return NS_ERROR_OUT_OF_MEMORY;
+  mUsesSynthBorder = true;
+  mSynthBorderWidths = aBorder;
+}
+
+nsStyleBorder
+TableBackgroundPainter::TableBackgroundData::StyleBorder(const nsStyleBorder& aZeroBorder) const
+{
+  MOZ_ASSERT(mVisible, "Don't call StyleBorder on an invisible TableBackgroundData");
+
+  if (mUsesSynthBorder) {
+    nsStyleBorder result = aZeroBorder;
+    NS_FOR_CSS_SIDES(side) {
+      result.SetBorderWidth(side, mSynthBorderWidths.Side(side));
+    }
+    return result;
   }
 
-  NS_FOR_CSS_SIDES(side) {
-    mSynthBorder->SetBorderWidth(side, aBorder.Side(side));
-  }
-  
-  mBorder = mSynthBorder;
-  return NS_OK;
+  MOZ_ASSERT(mFrame);
+
+  return *mFrame->StyleBorder();
 }
 
 TableBackgroundPainter::TableBackgroundPainter(nsTableFrame*        aTableFrame,
                                                Origin               aOrigin,
                                                nsPresContext*       aPresContext,
                                                nsRenderingContext& aRenderingContext,
                                                const nsRect&        aDirtyRect,
                                                const nsPoint&       aRenderPt,
                                                uint32_t             aBGPaintFlags)
   : mPresContext(aPresContext),
     mRenderingContext(aRenderingContext),
     mRenderPt(aRenderPt),
     mDirtyRect(aDirtyRect),
     mOrigin(aOrigin),
-    mCols(nullptr),
     mZeroBorder(aPresContext),
     mBGPaintFlags(aBGPaintFlags)
 {
   MOZ_COUNT_CTOR(TableBackgroundPainter);
 
   NS_FOR_CSS_SIDES(side) {
     mZeroBorder.SetBorderStyle(side, NS_STYLE_BORDER_STYLE_SOLID);
     mZeroBorder.SetBorderWidth(side, 0);
@@ -219,46 +177,27 @@ TableBackgroundPainter::TableBackgroundP
 #ifdef DEBUG
   mCompatMode = mPresContext->CompatibilityMode();
 #endif
   mNumCols = aTableFrame->GetColCount();
 }
 
 TableBackgroundPainter::~TableBackgroundPainter()
 {
-  if (mCols) {
-    TableBackgroundData* lastColGroup = nullptr;
-    for (uint32_t i = 0; i < mNumCols; i++) {
-      if (mCols[i].mColGroup != lastColGroup) {
-        lastColGroup = mCols[i].mColGroup;
-        NS_ASSERTION(mCols[i].mColGroup, "colgroup data should not be null - bug 237421");
-        // we need to wallpaper a over zero pointer deref, bug 237421 will have the real fix
-        if(lastColGroup)
-          lastColGroup->Destroy(mPresContext);
-        delete lastColGroup;
-      }
-      mCols[i].mColGroup = nullptr;
-      mCols[i].mCol.Destroy(mPresContext);
-    }
-    delete [] mCols;
-  }
-  mRowGroup.Destroy(mPresContext);
-  mRow.Destroy(mPresContext);
   MOZ_COUNT_DTOR(TableBackgroundPainter);
 }
 
-nsresult
+void
 TableBackgroundPainter::PaintTableFrame(nsTableFrame*         aTableFrame,
                                         nsTableRowGroupFrame* aFirstRowGroup,
                                         nsTableRowGroupFrame* aLastRowGroup,
                                         const nsMargin&       aDeflate)
 {
-  NS_PRECONDITION(aTableFrame, "null frame");
-  TableBackgroundData tableData;
-  tableData.SetFull(aTableFrame);
+  MOZ_ASSERT(aTableFrame, "null frame");
+  TableBackgroundData tableData(aTableFrame);
   tableData.mRect.MoveTo(0,0); //using table's coords
   tableData.mRect.Deflate(aDeflate);
   if (mIsBorderCollapse && tableData.ShouldSetBCBorder()) {
     if (aFirstRowGroup && aLastRowGroup && mNumCols > 0) {
       //only handle non-degenerate tables; we need a more robust BC model
       //to make degenerate tables' borders reasonable to deal with
       nsMargin border, tempBorder;
       nsTableColFrame* colFrame = aTableFrame->GetColFrame(mNumCols - 1);
@@ -273,210 +212,186 @@ TableBackgroundPainter::PaintTableFrame(
       nsTableRowFrame* rowFrame = aFirstRowGroup->GetFirstRow();
       if (rowFrame) {
         rowFrame->GetContinuousBCBorderWidth(tempBorder);
         border.top = tempBorder.top;
       }
 
       border.left = aTableFrame->GetContinuousLeftBCBorderWidth();
 
-      nsresult rv = tableData.SetBCBorder(border, this);
-      if (NS_FAILED(rv)) {
-        tableData.Destroy(mPresContext);
-        return rv;
-      }
+      tableData.SetBCBorder(border);
     }
   }
   if (tableData.IsVisible()) {
     nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
                                           tableData.mFrame, mDirtyRect,
                                           tableData.mRect + mRenderPt,
                                           tableData.mFrame->StyleContext(),
-                                          *tableData.mBorder,
+                                          tableData.StyleBorder(mZeroBorder),
                                           mBGPaintFlags);
   }
-  tableData.Destroy(mPresContext);
-  return NS_OK;
 }
 
 void
 TableBackgroundPainter::TranslateContext(nscoord aDX,
                                          nscoord aDY)
 {
   mRenderPt += nsPoint(aDX, aDY);
-  if (mCols) {
-    TableBackgroundData* lastColGroup = nullptr;
-    for (uint32_t i = 0; i < mNumCols; i++) {
-      mCols[i].mCol.mRect.MoveBy(-aDX, -aDY);
-      if (lastColGroup != mCols[i].mColGroup) {
-        NS_ASSERTION(mCols[i].mColGroup, "colgroup data should not be null - bug 237421");
-        // we need to wallpaper a over zero pointer deref, bug 237421 will have the real fix
-        if (!mCols[i].mColGroup)
-          return;
-        mCols[i].mColGroup->mRect.MoveBy(-aDX, -aDY);
-        lastColGroup = mCols[i].mColGroup;
-      }
-    }
+  for (auto& col : mCols) {
+    col.mCol.mRect.MoveBy(-aDX, -aDY);
+  }
+  for (auto& colGroup : mColGroups) {
+    colGroup.mRect.MoveBy(-aDX, -aDY);
   }
 }
 
-nsresult
+TableBackgroundPainter::ColData::ColData(nsIFrame* aFrame, TableBackgroundData& aColGroupBGData)
+  : mCol(aFrame)
+  , mColGroup(aColGroupBGData)
+{
+}
+
+void
 TableBackgroundPainter::PaintTable(nsTableFrame*   aTableFrame,
                                    const nsMargin& aDeflate,
                                    bool            aPaintTableBackground)
 {
   NS_PRECONDITION(aTableFrame, "null table frame");
 
   nsTableFrame::RowGroupArray rowGroups;
   aTableFrame->OrderRowGroups(rowGroups);
 
   if (rowGroups.Length() < 1) { //degenerate case
     if (aPaintTableBackground) {
       PaintTableFrame(aTableFrame, nullptr, nullptr, nsMargin(0,0,0,0));
     }
     /* No cells; nothing else to paint */
-    return NS_OK;
+    return;
   }
 
   if (aPaintTableBackground) {
     PaintTableFrame(aTableFrame, rowGroups[0], rowGroups[rowGroups.Length() - 1],
                     aDeflate);
   }
 
   /*Set up column background/border data*/
   if (mNumCols > 0) {
     nsFrameList& colGroupList = aTableFrame->GetColGroups();
     NS_ASSERTION(colGroupList.FirstChild(), "table should have at least one colgroup");
 
-    mCols = new ColData[mNumCols];
-    if (!mCols) return NS_ERROR_OUT_OF_MEMORY;
-
-    TableBackgroundData* cgData = nullptr;
-    nsMargin border;
-    /* BC left borders aren't stored on cols, but the previous column's
-       right border is the next one's left border.*/
-    //Start with table's left border.
-    nscoord lastLeftBorder = aTableFrame->GetContinuousLeftBCBorderWidth();
+    // Collect all col group frames first so that we know how many there are.
+    nsTArray<nsTableColGroupFrame*> colGroupFrames;
     for (nsTableColGroupFrame* cgFrame = static_cast<nsTableColGroupFrame*>(colGroupList.FirstChild());
          cgFrame; cgFrame = static_cast<nsTableColGroupFrame*>(cgFrame->GetNextSibling())) {
 
       if (cgFrame->GetColCount() < 1) {
         //No columns, no cells, so no need for data
         continue;
       }
+      colGroupFrames.AppendElement(cgFrame);
+    }
 
+    // Ensure that mColGroups won't reallocate during the loop below, because
+    // we grab references to its contents and need those to stay valid until
+    // mColGroups is destroyed as part of TablePainter destruction.
+    mColGroups.SetCapacity(colGroupFrames.Length());
+
+    nsMargin border;
+    /* BC left borders aren't stored on cols, but the previous column's
+       right border is the next one's left border.*/
+    //Start with table's left border.
+    nscoord lastLeftBorder = aTableFrame->GetContinuousLeftBCBorderWidth();
+
+    for (nsTableColGroupFrame* cgFrame : colGroupFrames) {
       /*Create data struct for column group*/
-      cgData = new TableBackgroundData;
-      if (!cgData) return NS_ERROR_OUT_OF_MEMORY;
-      cgData->SetFull(cgFrame);
-      if (mIsBorderCollapse && cgData->ShouldSetBCBorder()) {
+      TableBackgroundData& cgData = *mColGroups.AppendElement(TableBackgroundData(cgFrame));
+      if (mIsBorderCollapse && cgData.ShouldSetBCBorder()) {
         border.left = lastLeftBorder;
         cgFrame->GetContinuousBCBorderWidth(border);
-        nsresult rv = cgData->SetBCBorder(border, this);
-        if (NS_FAILED(rv)) {
-          cgData->Destroy(mPresContext);
-          delete cgData;
-          return rv;
-        }
+        cgData.SetBCBorder(border);
       }
-
-      // Boolean that indicates whether mCols took ownership of cgData
-      bool cgDataOwnershipTaken = false;
       
       /*Loop over columns in this colgroup*/
       for (nsTableColFrame* col = cgFrame->GetFirstColumn(); col;
            col = static_cast<nsTableColFrame*>(col->GetNextSibling())) {
-        /*Create data struct for column*/
-        uint32_t colIndex = col->GetColIndex();
-        NS_ASSERTION(colIndex < mNumCols, "prevent array boundary violation");
-        if (mNumCols <= colIndex)
-          break;
-        mCols[colIndex].mCol.SetFull(col);
+        MOZ_ASSERT(size_t(col->GetColIndex()) == mCols.Length());
+        // Store a reference to the colGroup in the ColData element.
+        ColData& colData = *mCols.AppendElement(ColData(col, cgData));
         //Bring column mRect into table's coord system
-        mCols[colIndex].mCol.mRect.MoveBy(cgData->mRect.x, cgData->mRect.y);
-        //link to parent colgroup's data
-        mCols[colIndex].mColGroup = cgData;
-        cgDataOwnershipTaken = true;
+        colData.mCol.mRect.MoveBy(cgData.mRect.x, cgData.mRect.y);
         if (mIsBorderCollapse) {
           border.left = lastLeftBorder;
           lastLeftBorder = col->GetContinuousBCBorderWidth(border);
-          if (mCols[colIndex].mCol.ShouldSetBCBorder()) {
-            nsresult rv = mCols[colIndex].mCol.SetBCBorder(border, this);
-            if (NS_FAILED(rv)) return rv;
+          if (colData.mCol.ShouldSetBCBorder()) {
+            colData.mCol.SetBCBorder(border);
           }
         }
       }
-
-      if (!cgDataOwnershipTaken) {
-        cgData->Destroy(mPresContext);
-        delete cgData;
-      }
     }
   }
 
   for (uint32_t i = 0; i < rowGroups.Length(); i++) {
     nsTableRowGroupFrame* rg = rowGroups[i];
-    mRowGroup.SetFrame(rg);
+    TableBackgroundData rowGroupBGData(rg);
     // Need to compute the right rect via GetOffsetTo, since the row
     // group may not be a child of the table.
-    mRowGroup.mRect.MoveTo(rg->GetOffsetTo(aTableFrame));
+    rowGroupBGData.mRect.MoveTo(rg->GetOffsetTo(aTableFrame));
 
     // We have to draw backgrounds not only within the overflow region of this
     // row group, but also possibly (in the case of column / column group
     // backgrounds) at its pre-relative-positioning location.
     nsRect rgVisualOverflow = rg->GetVisualOverflowRectRelativeToSelf();
     nsRect rgOverflowRect = rgVisualOverflow + rg->GetPosition();
     nsRect rgNormalRect = rgVisualOverflow + rg->GetNormalPosition();
 
     if (rgOverflowRect.Union(rgNormalRect).Intersects(mDirtyRect - mRenderPt)) {
-      nsresult rv = PaintRowGroup(rg, rg->IsPseudoStackingContextFromStyle());
-      if (NS_FAILED(rv)) return rv;
+      PaintRowGroup(rg, rowGroupBGData, rg->IsPseudoStackingContextFromStyle());
     }
   }
-  return NS_OK;
 }
 
-nsresult
+void
+TableBackgroundPainter::PaintRowGroup(nsTableRowGroupFrame* aFrame)
+{
+  PaintRowGroup(aFrame, TableBackgroundData(aFrame), false);
+}
+
+void
 TableBackgroundPainter::PaintRowGroup(nsTableRowGroupFrame* aFrame,
+                                      TableBackgroundData   aRowGroupBGData,
                                       bool                  aPassThrough)
 {
-  NS_PRECONDITION(aFrame, "null frame");
-
-  if (!mRowGroup.mFrame) {
-    mRowGroup.SetFrame(aFrame);
-  }
+  MOZ_ASSERT(aFrame, "null frame");
 
   nsTableRowFrame* firstRow = aFrame->GetFirstRow();
 
   /* Load row group data */
-  if (!aPassThrough) {
-    mRowGroup.SetData();
-    if (mIsBorderCollapse && mRowGroup.ShouldSetBCBorder()) {
+  if (aPassThrough) {
+    aRowGroupBGData.MakeInvisible();
+  } else {
+    if (mIsBorderCollapse && aRowGroupBGData.ShouldSetBCBorder()) {
       nsMargin border;
       if (firstRow) {
         //pick up first row's top border (= rg top border)
         firstRow->GetContinuousBCBorderWidth(border);
         /* (row group doesn't store its top border) */
       }
       //overwrite sides+bottom borders with rg's own
       aFrame->GetContinuousBCBorderWidth(border);
-      nsresult res = mRowGroup.SetBCBorder(border, this);
-      if (!NS_SUCCEEDED(res)) {
-        return res;
-      }
+      aRowGroupBGData.SetBCBorder(border);
     }
-    aPassThrough = !mRowGroup.IsVisible();
+    aPassThrough = !aRowGroupBGData.IsVisible();
   }
 
   /* translate everything into row group coord system*/
   if (eOrigin_TableRowGroup != mOrigin) {
-    TranslateContext(mRowGroup.mRect.x, mRowGroup.mRect.y);
+    TranslateContext(aRowGroupBGData.mRect.x, aRowGroupBGData.mRect.y);
   }
-  nsRect rgRect = mRowGroup.mRect;
-  mRowGroup.mRect.MoveTo(0, 0);
+  nsRect rgRect = aRowGroupBGData.mRect;
+  aRowGroupBGData.mRect.MoveTo(0, 0);
 
   /* Find the right row to start with */
 
   // Note that mDirtyRect  - mRenderPt is guaranteed to be in the row
   // group's coordinate system here, so passing its .y to
   // GetFirstRowContaining is ok.
   nscoord overflowAbove;
   nsIFrame* cursor = aFrame->GetFirstRowContaining(mDirtyRect.y - mRenderPt.y, &overflowAbove);
@@ -494,182 +409,184 @@ TableBackgroundPainter::PaintRowGroup(ns
     // cursor; if we've gotten this far then we've already built the display
     // list for the rowgroup, so not having a cursor means that there's some
     // good reason we don't have a cursor and we shouldn't create one here.
     row = firstRow;
   }
   
   /* Finally paint */
   for (; row; row = row->GetNextRow()) {
-    mRow.SetFrame(row);
+    TableBackgroundData rowBackgroundData(row);
+
     // Be sure to consider our positions both pre- and post-relative
     // positioning, since we potentially need to paint at both places.
-    nscoord rowY = std::min(mRow.mRect.y, row->GetNormalPosition().y);
+    nscoord rowY = std::min(rowBackgroundData.mRect.y, row->GetNormalPosition().y);
 
     // Intersect wouldn't handle rowspans.
     if (cursor &&
         (mDirtyRect.YMost() - mRenderPt.y) <= (rowY - overflowAbove)) {
       // All done; cells originating in later rows can't intersect mDirtyRect.
       break;
     }
     
-    nsresult rv = PaintRow(row, aPassThrough || row->IsPseudoStackingContextFromStyle());
-    if (NS_FAILED(rv)) return rv;
+    PaintRow(row, aRowGroupBGData, rowBackgroundData,
+             aPassThrough || row->IsPseudoStackingContextFromStyle());
   }
 
   /* translate back into table coord system */
   if (eOrigin_TableRowGroup != mOrigin) {
     TranslateContext(-rgRect.x, -rgRect.y);
   }
-  
-  /* unload rg data */
-  mRowGroup.Clear();
-
-  return NS_OK;
 }
 
-nsresult
+void
+TableBackgroundPainter::PaintRow(nsTableRowFrame* aFrame)
+{
+  return PaintRow(aFrame, TableBackgroundData(), TableBackgroundData(aFrame), false);
+}
+
+void
 TableBackgroundPainter::PaintRow(nsTableRowFrame* aFrame,
+                                 const TableBackgroundData& aRowGroupBGData,
+                                 TableBackgroundData aRowBGData,
                                  bool             aPassThrough)
 {
-  NS_PRECONDITION(aFrame, "null frame");
-
-  if (!mRow.mFrame) {
-    mRow.SetFrame(aFrame);
-  }
+  MOZ_ASSERT(aFrame, "null frame");
 
   /* Load row data */
-  if (!aPassThrough) {
-    mRow.SetData();
-    if (mIsBorderCollapse && mRow.ShouldSetBCBorder()) {
+  if (aPassThrough) {
+    aRowBGData.MakeInvisible();
+  } else {
+    if (mIsBorderCollapse && aRowBGData.ShouldSetBCBorder()) {
       nsMargin border;
       nsTableRowFrame* nextRow = aFrame->GetNextRow();
       if (nextRow) { //outer top below us is inner bottom for us
         border.bottom = nextRow->GetOuterTopContBCBorderWidth();
       }
       else { //acquire rg's bottom border
         nsTableRowGroupFrame* rowGroup = static_cast<nsTableRowGroupFrame*>(aFrame->GetParent());
         rowGroup->GetContinuousBCBorderWidth(border);
       }
       //get the rest of the borders; will overwrite all but bottom
       aFrame->GetContinuousBCBorderWidth(border);
 
-      nsresult res = mRow.SetBCBorder(border, this);
-      if (!NS_SUCCEEDED(res)) {
-        return res;
-      }
+      aRowBGData.SetBCBorder(border);
     }
-    aPassThrough = !mRow.IsVisible();
+    aPassThrough = !aRowBGData.IsVisible();
   }
 
   /* Translate */
   if (eOrigin_TableRow == mOrigin) {
     /* If we originate from the row, then make the row the origin. */
-    mRow.mRect.MoveTo(0, 0);
+    aRowBGData.mRect.MoveTo(0, 0);
   }
   //else: Use row group's coord system -> no translation necessary
 
   for (nsTableCellFrame* cell = aFrame->GetFirstCell(); cell; cell = cell->GetNextCell()) {
     nsRect cellBGRect, rowBGRect, rowGroupBGRect, colBGRect;
-    ComputeCellBackgrounds(cell, cellBGRect, rowBGRect,
+    ComputeCellBackgrounds(cell, aRowGroupBGData, aRowBGData,
+                           cellBGRect, rowBGRect,
                            rowGroupBGRect, colBGRect);
 
     // Find the union of all the cell background layers.
     nsRect combinedRect(cellBGRect);
     combinedRect.UnionRect(combinedRect, rowBGRect);
     combinedRect.UnionRect(combinedRect, rowGroupBGRect);
     combinedRect.UnionRect(combinedRect, colBGRect);
 
     if (combinedRect.Intersects(mDirtyRect)) {
       bool passCell = aPassThrough || cell->IsPseudoStackingContextFromStyle();
-      nsresult rv = PaintCell(cell, cellBGRect, rowBGRect, rowGroupBGRect,
-                              colBGRect, passCell);
-      if (NS_FAILED(rv)) return rv;
+      PaintCell(cell, aRowGroupBGData, aRowBGData,
+                cellBGRect, rowBGRect, rowGroupBGRect, colBGRect, passCell);
     }
   }
-
-  /* Unload row data */
-  mRow.Clear();
-  return NS_OK;
 }
 
-nsresult
+void
 TableBackgroundPainter::PaintCell(nsTableCellFrame* aCell,
+                                  const TableBackgroundData& aRowGroupBGData,
+                                  const TableBackgroundData& aRowBGData,
                                   nsRect&           aCellBGRect,
                                   nsRect&           aRowBGRect,
                                   nsRect&           aRowGroupBGRect,
                                   nsRect&           aColBGRect,
                                   bool              aPassSelf)
 {
-  NS_PRECONDITION(aCell, "null frame");
+  MOZ_ASSERT(aCell, "null frame");
 
   const nsStyleTableBorder* cellTableStyle;
   cellTableStyle = aCell->StyleTableBorder();
   if (NS_STYLE_TABLE_EMPTY_CELLS_SHOW != cellTableStyle->mEmptyCells &&
       aCell->GetContentEmpty() && !mIsBorderCollapse) {
-    return NS_OK;
+    return;
   }
 
   int32_t colIndex;
   aCell->GetColIndex(colIndex);
-  NS_ASSERTION(colIndex < int32_t(mNumCols), "prevent array boundary violation");
-  if (int32_t(mNumCols) <= colIndex)
-    return NS_OK;
+  // We're checking mNumCols instead of mCols.Length() here because mCols can
+  // be empty even if mNumCols > 0.
+  NS_ASSERTION(size_t(colIndex) < mNumCols, "out-of-bounds column index");
+  if (size_t(colIndex) >= mNumCols)
+    return;
+
+  // If callers call PaintRowGroup or PaintRow directly, we haven't processed
+  // our columns. Ignore column / col group backgrounds in that case.
+  bool haveColumns = !mCols.IsEmpty();
 
   //Paint column group background
-  if (mCols && mCols[colIndex].mColGroup && mCols[colIndex].mColGroup->IsVisible()) {
+  if (haveColumns && mCols[colIndex].mColGroup.IsVisible()) {
     nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
-                                          mCols[colIndex].mColGroup->mFrame, mDirtyRect,
-                                          mCols[colIndex].mColGroup->mRect + mRenderPt,
-                                          mCols[colIndex].mColGroup->mFrame->StyleContext(),
-                                          *mCols[colIndex].mColGroup->mBorder,
+                                          mCols[colIndex].mColGroup.mFrame, mDirtyRect,
+                                          mCols[colIndex].mColGroup.mRect + mRenderPt,
+                                          mCols[colIndex].mColGroup.mFrame->StyleContext(),
+                                          mCols[colIndex].mColGroup.StyleBorder(mZeroBorder),
                                           mBGPaintFlags, &aColBGRect);
   }
 
   //Paint column background
-  if (mCols && mCols[colIndex].mCol.IsVisible()) {
+  if (haveColumns && mCols[colIndex].mCol.IsVisible()) {
     nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
                                           mCols[colIndex].mCol.mFrame, mDirtyRect,
                                           mCols[colIndex].mCol.mRect + mRenderPt,
                                           mCols[colIndex].mCol.mFrame->StyleContext(),
-                                          *mCols[colIndex].mCol.mBorder,
+                                          mCols[colIndex].mCol.StyleBorder(mZeroBorder),
                                           mBGPaintFlags, &aColBGRect);
   }
 
   //Paint row group background
-  if (mRowGroup.IsVisible()) {
+  if (aRowGroupBGData.IsVisible()) {
     nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
-                                          mRowGroup.mFrame, mDirtyRect,
-                                          mRowGroup.mRect + mRenderPt,
-                                          mRowGroup.mFrame->StyleContext(),
-                                          *mRowGroup.mBorder,
+                                          aRowGroupBGData.mFrame, mDirtyRect,
+                                          aRowGroupBGData.mRect + mRenderPt,
+                                          aRowGroupBGData.mFrame->StyleContext(),
+                                          aRowGroupBGData.StyleBorder(mZeroBorder),
                                           mBGPaintFlags, &aRowGroupBGRect);
   }
 
   //Paint row background
-  if (mRow.IsVisible()) {
+  if (aRowBGData.IsVisible()) {
     nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
-                                          mRow.mFrame, mDirtyRect,
-                                          mRow.mRect + mRenderPt,
-                                          mRow.mFrame->StyleContext(),
-                                          *mRow.mBorder,
+                                          aRowBGData.mFrame, mDirtyRect,
+                                          aRowBGData.mRect + mRenderPt,
+                                          aRowBGData.mFrame->StyleContext(),
+                                          aRowBGData.StyleBorder(mZeroBorder),
                                           mBGPaintFlags, &aRowBGRect);
   }
 
   //Paint cell background in border-collapse unless we're just passing
   if (mIsBorderCollapse && !aPassSelf) {
     aCell->PaintCellBackground(mRenderingContext, mDirtyRect,
                                aCellBGRect.TopLeft(), mBGPaintFlags);
   }
-
-  return NS_OK;
 }
 
 void
 TableBackgroundPainter::ComputeCellBackgrounds(nsTableCellFrame* aCell,
+                                               const TableBackgroundData& aRowGroupBGData,
+                                               const TableBackgroundData& aRowBGData,
                                                nsRect&           aCellBGRect,
                                                nsRect&           aRowBGRect,
                                                nsRect&           aRowGroupBGRect,
                                                nsRect&           aColBGRect)
 {
   // We need to compute table background layer rects for this cell space,
   // adjusted for possible relative positioning. This behavior is not specified
   // at the time of this writing, but the approach below should be web
@@ -689,30 +606,30 @@ TableBackgroundPainter::ComputeCellBackg
   // background layer rect is thus determined by peeling off successive table
   // part layers, removing the contribution of each layer's positioning one by
   // one.  Every rect we generate will be the same size, the size of the cell
   // space.
 
   // We cannot rely on the row group background data to be available, since some
   // callers enter through PaintRow.
   nsIFrame* rowGroupFrame =
-    mRowGroup.mFrame ? mRowGroup.mFrame : mRow.mFrame->GetParent();
+    aRowGroupBGData.mFrame ? aRowGroupBGData.mFrame : aRowBGData.mFrame->GetParent();
 
   // The cell background goes at the cell's position, translated to use the same
-  // coordinate system as mRow.
-  aCellBGRect = aCell->GetRect() + mRow.mRect.TopLeft() + mRenderPt;
+  // coordinate system as aRowBGData.
+  aCellBGRect = aCell->GetRect() + aRowBGData.mRect.TopLeft() + mRenderPt;
 
   // The row background goes at the normal position of the cell, which is to say
   // the position without relative positioning applied.
   aRowBGRect = aCellBGRect + (aCell->GetNormalPosition() - aCell->GetPosition());
 
   // The row group background goes at the position we'd find the cell if neither
   // the cell's relative positioning nor the row's were applied.
   aRowGroupBGRect = aRowBGRect +
-                    (mRow.mFrame->GetNormalPosition() - mRow.mFrame->GetPosition());
+                    (aRowBGData.mFrame->GetNormalPosition() - aRowBGData.mFrame->GetPosition());
 
   // The column and column group backgrounds (they're always at the same
   // location, since relative positioning doesn't apply to columns or column
   // groups) are drawn at the position we'd find the cell if none of the cell's,
   // row's, or row group's relative positioning were applied.
   aColBGRect = aRowGroupBGRect +
              (rowGroupFrame->GetNormalPosition() - rowGroupFrame->GetPosition());
 
--- a/layout/tables/nsTablePainter.h
+++ b/layout/tables/nsTablePainter.h
@@ -76,169 +76,180 @@ class TableBackgroundPainter
       * Table must do a flagged TABLE_BG_PAINT ::Paint call on its
       * children afterwards
       * @param aTableFrame - the table frame
       * @param aDeflate    - deflation needed to bring table's mRect
       *                      to the outer grid lines in border-collapse
       * @param aPaintTableBackground - if true, the table background
       * is included, otherwise it isn't
       */
-    nsresult PaintTable(nsTableFrame* aTableFrame, const nsMargin& aDeflate,
-                        bool aPaintTableBackground);
+    void PaintTable(nsTableFrame* aTableFrame, const nsMargin& aDeflate,
+                    bool aPaintTableBackground);
 
     /** Paint background for the row group and its children down through cells
       * (Cells themselves will only be painted in border collapse)
       * Standards mode only
       * Table Row Group must do a flagged TABLE_BG_PAINT ::Paint call on its
       * children afterwards
       * @param aFrame - the table row group frame
       */
-    nsresult PaintRowGroup(nsTableRowGroupFrame* aFrame)
-    { return PaintRowGroup(aFrame, false); }
+    void PaintRowGroup(nsTableRowGroupFrame* aFrame);
 
     /** Paint background for the row and its children down through cells
       * (Cells themselves will only be painted in border collapse)
       * Standards mode only
       * Table Row must do a flagged TABLE_BG_PAINT ::Paint call on its
       * children afterwards
       * @param aFrame - the table row frame
       */
-    nsresult PaintRow(nsTableRowFrame* aFrame)
-    { return PaintRow(aFrame, false); }
+    void PaintRow(nsTableRowFrame* aFrame);
 
   private:
+    struct TableBackgroundData;
 
     /** Paint table frame's background
       * @param aTableFrame     - the table frame
       * @param aFirstRowGroup  - the first (in layout order) row group
       *                          may be null
       * @param aLastRowGroup   - the last (in layout order) row group
       *                          may be null
       * @param aDeflate        - adjustment to frame's rect (used for quirks BC)
       *                          may be null
       */
-    nsresult PaintTableFrame(nsTableFrame*         aTableFrame,
-                             nsTableRowGroupFrame* aFirstRowGroup,
-                             nsTableRowGroupFrame* aLastRowGroup,
-                             const nsMargin&       aDeflate);
+    void PaintTableFrame(nsTableFrame*         aTableFrame,
+                         nsTableRowGroupFrame* aFirstRowGroup,
+                         nsTableRowGroupFrame* aLastRowGroup,
+                         const nsMargin&       aDeflate);
 
     /* aPassThrough params indicate whether to paint the element or to just
-     * pass through and paint underlying layers only
+     * pass through and paint underlying layers only.
+     * aRowGroupBGData is not a const reference because the function modifies
+     * its copy. Same for aRowBGData in PaintRow.
      * See Public versions for function descriptions
      */
-    nsresult PaintRowGroup(nsTableRowGroupFrame* aFrame,
-                           bool                  aPassThrough);
-    nsresult PaintRow(nsTableRowFrame* aFrame,
-                      bool             aPassThrough);
+    void PaintRowGroup(nsTableRowGroupFrame* aFrame,
+                       TableBackgroundData   aRowGroupBGData,
+                       bool                  aPassThrough);
+
+    void PaintRow(nsTableRowFrame* aFrame,
+                  const TableBackgroundData& aRowGroupBGData,
+                  TableBackgroundData aRowBGData,
+                  bool             aPassThrough);
 
     /** Paint table background layers for this cell space
       * Also paints cell's own background in border-collapse mode
       * @param aCell           - the cell
+      * @param aRowGroupBGData - background drawing info for the row group
+      * @param aRowBGData      - background drawing info for the row
       * @param aCellBGRect     - background rect for the cell
       * @param aRowBGRect      - background rect for the row
       * @param aRowGroupBGRect - background rect for the row group
       * @param aColBGRect      - background rect for the column and column group
       * @param aPassSelf       - pass this cell; i.e. paint only underlying layers
       */
-    nsresult PaintCell(nsTableCellFrame* aCell,
-                       nsRect&           aCellBGRect,
-                       nsRect&           aRowBGRect,
-                       nsRect&           aRowGroupBGRect,
-                       nsRect&           aColBGRect,
-                       bool              aPassSelf);
+    void PaintCell(nsTableCellFrame* aCell,
+                   const TableBackgroundData& aRowGroupBGData,
+                   const TableBackgroundData& aRowBGData,
+                   nsRect&           aCellBGRect,
+                   nsRect&           aRowBGRect,
+                   nsRect&           aRowGroupBGRect,
+                   nsRect&           aColBGRect,
+                   bool              aPassSelf);
 
     /** Compute table background layer positions for this cell space
       * @param aCell              - the cell
+      * @param aRowGroupBGData    - background drawing info for the row group
+      * @param aRowBGData         - background drawing info for the row
       * @param aCellBGRectOut     - outparam: background rect for the cell
       * @param aRowBGRectOut      - outparam: background rect for the row
       * @param aRowGroupBGRectOut - outparam: background rect for the row group
       * @param aColBGRectOut      - outparam: background rect for the column
                                     and column group
       */
     void ComputeCellBackgrounds(nsTableCellFrame* aCell,
+                                const TableBackgroundData& aRowGroupBGData,
+                                const TableBackgroundData& aRowBGData,
                                 nsRect&           aCellBGRect,
                                 nsRect&           aRowBGRect,
                                 nsRect&           aRowGroupBGRect,
                                 nsRect&           aColBGRect);
 
     /** Translate mRenderingContext, mDirtyRect, and mCols' column and
       * colgroup coords
       * @param aDX - origin's x-coord change
       * @param aDY - origin's y-coord change
       */
     void TranslateContext(nscoord aDX,
                           nscoord aDY);
 
-    struct TableBackgroundData;
-    friend struct TableBackgroundData;
     struct TableBackgroundData {
-      nsIFrame*                 mFrame;
-      /** mRect is the rect of mFrame in the current coordinate system */
-      nsRect                    mRect;
-      bool                      mVisible;
-      const nsStyleBorder*      mBorder;
+    public:
+      /**
+       * Construct an empty TableBackgroundData instance, which is invisible.
+       */
+      TableBackgroundData();
+
+      /**
+       * Construct a TableBackgroundData instance for a frame. Visibility will
+       * be derived from the frame and can be overridden using MakeInvisible().
+       */
+      explicit TableBackgroundData(nsIFrame* aFrame);
+
+      /** Destructor */
+      ~TableBackgroundData() {}
 
       /** Data is valid & frame is visible */
       bool IsVisible() const { return mVisible; }
 
-      /** Constructor */
-      TableBackgroundData();
-      /** Destructor */
-      ~TableBackgroundData();
-      /** Destroys synthesized data. MUST be called before destructor
-       *  @param aPresContext - the pres context
-       */
-      void Destroy(nsPresContext* aPresContext);
-
-
-      /** Clear background data */
-      void Clear();
-
-      /** Calculate and set all data values to represent aFrame */
-      void SetFull(nsIFrame* aFrame);
-
-      /** Set frame data (mFrame, mRect) but leave style data empty */
-      void SetFrame(nsIFrame* aFrame);
-
-      /** Calculate the style data for mFrame */
-      void SetData();
+      /** Override visibility of the frame, force it to be invisible */
+      void MakeInvisible() { mVisible = false; }
 
       /** True if need to set border-collapse border; must call SetFull beforehand */
-      bool ShouldSetBCBorder();
+      bool ShouldSetBCBorder() const;
 
       /** Set border-collapse border with aBorderWidth as widths */
-      nsresult SetBCBorder(nsMargin&               aBorderWidth,
-                           TableBackgroundPainter* aPainter);
+      void SetBCBorder(const nsMargin& aBorderWidth);
 
-      private:
-      nsStyleBorder* mSynthBorder;
+      /**
+       * @param  aZeroBorder An nsStyleBorder instance that has been initialized
+       *                     for the right nsPresContext, with all border widths
+       *                     set to zero and border styles set to solid.
+       * @return             The nsStyleBorder that should be used for rendering
+       *                     this background.
+       */
+      nsStyleBorder StyleBorder(const nsStyleBorder& aZeroBorder) const;
+
+      nsIFrame* const mFrame;
+
+      /** mRect is the rect of mFrame in the current coordinate system */
+      nsRect mRect;
+
+    private:
+      nsMargin mSynthBorderWidths;
+      bool mVisible;
+      bool mUsesSynthBorder;
     };
 
-    struct ColData;
-    friend struct ColData;
     struct ColData {
-      TableBackgroundData  mCol;
-      TableBackgroundData* mColGroup; //link to col's parent colgroup's data (owned by painter)
-      ColData() {
-        mColGroup = nullptr;
-      }
+      ColData(nsIFrame* aFrame, TableBackgroundData& aColGroupBGData);
+      TableBackgroundData mCol;
+      TableBackgroundData& mColGroup; // reference to col's parent colgroup's data, owned by TablePainter in mColGroups
     };
 
     nsPresContext*      mPresContext;
     nsRenderingContext& mRenderingContext;
     nsPoint              mRenderPt;
     nsRect               mDirtyRect;
 #ifdef DEBUG
     nsCompatibility      mCompatMode;
 #endif
     bool                 mIsBorderCollapse;
     Origin               mOrigin; //user's table frame type
 
-    ColData*             mCols;  //array of columns' ColData
-    uint32_t             mNumCols;
-    TableBackgroundData  mRowGroup; //current row group
-    TableBackgroundData  mRow;      //current row
+    nsTArray<TableBackgroundData> mColGroups;
+    nsTArray<ColData>    mCols;
+    size_t               mNumCols;
 
     nsStyleBorder        mZeroBorder;  //cached zero-width border
     uint32_t             mBGPaintFlags;
 };
 
 #endif
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -1,15 +1,20 @@
 /*
  * Copyright  2013 Mozilla Foundation
  *
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
+// This enables assert in release, and lets us have debug-only code
+#ifdef NDEBUG
+#define DEBUG
 #undef NDEBUG
+#endif // #ifdef NDEBUG
+
 #if defined(HAVE_CONFIG_H)
 #include "config.h"
 #endif
 #include <assert.h>
 #include <windows.h>
 #include <mmdeviceapi.h>
 #include <windef.h>
 #include <audioclient.h>
@@ -70,28 +75,96 @@ SafeRelease(HANDLE handle)
 template <typename T>
 void SafeRelease(T * ptr)
 {
   if (ptr) {
     ptr->Release();
   }
 }
 
+/* This wraps a critical section to track the owner in debug mode, adapted from
+ * NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
+class owned_critical_section
+{
+public:
+
+  owned_critical_section()
+#ifdef DEBUG
+    : owner(0)
+#endif
+  {
+    InitializeCriticalSection(&critical_section);
+  }
+
+  ~owned_critical_section()
+  {
+    DeleteCriticalSection(&critical_section);
+  }
+
+  void enter()
+  {
+    EnterCriticalSection(&critical_section);
+#ifdef DEBUG
+    assert(owner != GetCurrentThreadId() && "recursive locking");
+    owner = GetCurrentThreadId();
+#endif
+  }
+
+  void leave()
+  {
+#ifdef DEBUG
+    /* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
+    owner = 0;
+#endif
+    LeaveCriticalSection(&critical_section);
+  }
+
+#ifdef DEBUG
+  /* This is guaranteed to have the good behaviour if it succeeds. The behaviour
+   * is undefined otherwise. */
+  void assert_current_thread_owns()
+  {
+    /* This implies owner != 0, because GetCurrentThreadId cannot return 0. */
+    assert(owner == GetCurrentThreadId());
+  }
+#endif
+
+private:
+  CRITICAL_SECTION critical_section;
+#ifdef DEBUG
+  DWORD owner;
+#endif
+};
+
 struct auto_lock {
-  auto_lock(CRITICAL_SECTION * lock)
+  auto_lock(owned_critical_section * lock)
     :lock(lock)
   {
-    EnterCriticalSection(lock);
+    lock->enter();
   }
   ~auto_lock()
   {
-    LeaveCriticalSection(lock);
+    lock->leave();
   }
 private:
-  CRITICAL_SECTION * lock;
+  owned_critical_section * lock;
+};
+
+struct auto_unlock {
+  auto_unlock(owned_critical_section * lock)
+    :lock(lock)
+  {
+    lock->leave();
+  }
+  ~auto_unlock()
+  {
+    lock->enter();
+  }
+private:
+  owned_critical_section * lock;
 };
 
 struct auto_com {
   auto_com()
   : need_uninit(true) {
     HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
     // This is for information purposes only, in anycase, COM is initialized
     // at the end of the constructor.
@@ -181,17 +254,17 @@ struct cubeb_stream
    * function, so the render loop can exit properly. */
   HANDLE shutdown_event;
   /* This is set by WASAPI when we should refill the stream. */
   HANDLE refill_event;
   /* Each cubeb_stream has its own thread. */
   HANDLE thread;
   /* We synthetize our clock from the callbacks. */
   LONG64 clock;
-  CRITICAL_SECTION stream_reset_lock;
+  owned_critical_section * stream_reset_lock;
   /* Maximum number of frames we can be requested in a callback. */
   uint32_t buffer_frame_count;
   /* Resampler instance. Resampling will only happen if necessary. */
   cubeb_resampler * resampler;
   /* Buffer used to downmix or upmix to the number of channels the mixer has.
    * its size is |frames_to_bytes_before_mix(buffer_frame_count)|. */
   float * mix_buffer;
   /* True if the stream is draining. */
@@ -252,17 +325,17 @@ public:
       return S_OK;
     }
 
     auto_com autocom;
 
     /* Close the stream */
     wasapi_stream_stop(stm);
     {
-      auto_lock lock(&stm->stream_reset_lock);
+      auto_lock lock(stm->stream_reset_lock);
       close_wasapi_stream(stm);
       /* Reopen a stream and start it immediately. This will automatically pick the
        * new default device for this role. */
       setup_wasapi_stream(stm);
     }
     wasapi_stream_start(stm);
 
     return S_OK;
@@ -309,16 +382,22 @@ bool should_upmix(cubeb_stream * stream)
   return stream->mix_params.channels > stream->stream_params.channels;
 }
 
 bool should_downmix(cubeb_stream * stream)
 {
   return stream->mix_params.channels < stream->stream_params.channels;
 }
 
+float stream_to_mix_samplerate_ratio(cubeb_stream * stream)
+{
+  auto_lock lock(stream->stream_reset_lock);
+  return float(stream->stream_params.rate) / stream->mix_params.rate;
+}
+
 /* Upmix function, copies a mono channel in two interleaved
  * stereo channel. |out| has to be twice as long as |in| */
 template<typename T>
 void
 mono_to_stereo(T * in, long insamples, T * out)
 {
   int j = 0;
   for (int i = 0; i < insamples; ++i, j += 2) {
@@ -382,19 +461,19 @@ refill(cubeb_stream * stm, float * data,
    * avoid a copy. */
   float * dest;
   if (should_upmix(stm) || should_downmix(stm)) {
     dest = stm->mix_buffer;
   } else {
     dest = data;
   }
 
-  stm->clock = InterlockedAdd64(&stm->clock, frames_needed);
+  long out_frames = cubeb_resampler_fill(stm->resampler, dest, frames_needed);
 
-  long out_frames = cubeb_resampler_fill(stm->resampler, dest, frames_needed);
+  stm->clock = InterlockedAdd64(&stm->clock, frames_needed * stream_to_mix_samplerate_ratio(stm));
 
   /* XXX: Handle this error. */
   if (out_frames < 0) {
     assert(false);
   }
 
   /* Go in draining mode if we got fewer frames than requested. */
   if (out_frames < frames_needed) {
@@ -461,18 +540,20 @@ wasapi_stream_render_loop(LPVOID stream)
       if (FAILED(hr)) {
         LOG("Failed to get padding");
         is_playing = false;
         continue;
       }
       assert(padding <= stm->buffer_frame_count);
 
       if (stm->draining) {
-        stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
-        is_playing = false;
+        if (padding == 0) {
+          stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+          is_playing = false;
+        }
         continue;
       }
 
       long available = stm->buffer_frame_count - padding;
 
       if (available == 0) {
         continue;
       }
@@ -540,23 +621,20 @@ HRESULT register_notification_client(cub
     return hr;
   }
 
   return hr;
 }
 
 HRESULT unregister_notification_client(cubeb_stream * stm)
 {
-  HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
-                                NULL, CLSCTX_INPROC_SERVER,
-                                IID_PPV_ARGS(&stm->device_enumerator));
+  assert(stm);
 
-  if (FAILED(hr)) {
-    LOG("Could not get device enumerator: %x", hr);
-    return hr;
+  if (!stm->device_enumerator) {
+    return S_OK;
   }
 
   stm->device_enumerator->UnregisterEndpointNotificationCallback(stm->notification_client);
 
   SafeRelease(stm->notification_client);
   SafeRelease(stm->device_enumerator);
 
   return S_OK;
@@ -842,18 +920,22 @@ handle_channel_layout(cubeb_stream * stm
 }
 
 int setup_wasapi_stream(cubeb_stream * stm)
 {
   HRESULT hr;
   IMMDevice * device;
   WAVEFORMATEX * mix_format;
 
+  stm->stream_reset_lock->assert_current_thread_owns();
+
   auto_com com;
 
+  assert(!stm->client && "WASAPI stream already setup, close it first.");
+
   hr = get_default_endpoint(&device);
   if (FAILED(hr)) {
     LOG("Could not get default endpoint, error: %x", hr);
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   /* Get a client. We will get all other interfaces we need from
@@ -973,17 +1055,26 @@ wasapi_stream_init(cubeb * context, cube
   stm->context = context;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
   stm->stream_params = stream_params;
   stm->draining = false;
   stm->latency = latency;
   stm->clock = 0;
-  InitializeCriticalSection(&stm->stream_reset_lock);
+
+  /* Null out WASAPI-specific state */
+  stm->resampler = nullptr;
+  stm->client = nullptr;
+  stm->render_client = nullptr;
+  stm->audio_stream_volume = nullptr;
+  stm->device_enumerator = nullptr;
+  stm->notification_client = nullptr;
+
+  stm->stream_reset_lock = new owned_critical_section();
 
   stm->shutdown_event = CreateEvent(NULL, 0, 0, NULL);
   stm->refill_event = CreateEvent(NULL, 0, 0, NULL);
 
   if (!stm->shutdown_event) {
     LOG("Can't create the shutdown event, error: %x.", GetLastError());
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
@@ -991,19 +1082,25 @@ wasapi_stream_init(cubeb * context, cube
 
   if (!stm->refill_event) {
     SafeRelease(stm->shutdown_event);
     LOG("Can't create the refill event, error: %x.", GetLastError());
     wasapi_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
-  rv = setup_wasapi_stream(stm);
-  if (rv != CUBEB_OK) {
-    return rv;
+  {
+    /* Locking here is not stricly necessary, because we don't have a
+       notification client that can reset the stream yet, but it lets us
+       assert that the lock is held in the function. */
+    auto_lock lock(stm->stream_reset_lock);
+    rv = setup_wasapi_stream(stm);
+    if (rv != CUBEB_OK) {
+      return rv;
+    }
   }
 
   hr = register_notification_client(stm);
   if (FAILED(hr)) {
     /* this is not fatal, we can still play audio, but we won't be able
      * to keep using the default audio endpoint if it changes. */
     LOG("failed to register notification client, %x", hr);
   }
@@ -1012,51 +1109,64 @@ wasapi_stream_init(cubeb * context, cube
 
   return CUBEB_OK;
 }
 
 void close_wasapi_stream(cubeb_stream * stm)
 {
   assert(stm);
 
+  stm->stream_reset_lock->assert_current_thread_owns();
+
   SafeRelease(stm->client);
+  stm->client = nullptr;
+
   SafeRelease(stm->render_client);
+  stm->client = nullptr;
 
-  cubeb_resampler_destroy(stm->resampler);
+  if (stm->resampler) {
+    cubeb_resampler_destroy(stm->resampler);
+    stm->resampler = nullptr;
+  }
 
   free(stm->mix_buffer);
+  stm->mix_buffer = nullptr;
 }
 
 void wasapi_stream_destroy(cubeb_stream * stm)
 {
   assert(stm);
 
+  unregister_notification_client(stm);
+
   if (stm->thread) {
     SetEvent(stm->shutdown_event);
     WaitForSingleObject(stm->thread, INFINITE);
     CloseHandle(stm->thread);
     stm->thread = 0;
   }
 
   SafeRelease(stm->shutdown_event);
   SafeRelease(stm->refill_event);
-  DeleteCriticalSection(&stm->stream_reset_lock);
 
-  close_wasapi_stream(stm);
+  {
+    auto_lock lock(stm->stream_reset_lock);
+    close_wasapi_stream(stm);
+  }
 
-  unregister_notification_client(stm);
+  delete stm->stream_reset_lock;
 
   free(stm);
 }
 
 int wasapi_stream_start(cubeb_stream * stm)
 {
   HRESULT hr;
 
-  auto_lock lock(&stm->stream_reset_lock);
+  auto_lock lock(stm->stream_reset_lock);
 
   assert(stm);
 
   stm->thread = (HANDLE) _beginthreadex(NULL, 256 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
   if (stm->thread == NULL) {
     LOG("could not create WASAPI render thread.");
     return CUBEB_ERROR;
   }
@@ -1071,26 +1181,27 @@ int wasapi_stream_start(cubeb_stream * s
 
   return CUBEB_OK;
 }
 
 int wasapi_stream_stop(cubeb_stream * stm)
 {
   assert(stm && stm->shutdown_event);
 
-  auto_lock lock(&stm->stream_reset_lock);
+  auto_lock lock(stm->stream_reset_lock);
 
   SetEvent(stm->shutdown_event);
 
   HRESULT hr = stm->client->Stop();
   if (FAILED(hr)) {
     LOG("could not stop AudioClient");
   }
 
   if (stm->thread) {
+    auto_unlock lock(stm->stream_reset_lock);
     WaitForSingleObject(stm->thread, INFINITE);
     CloseHandle(stm->thread);
     stm->thread = NULL;
   }
 
   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
 
   return CUBEB_OK;
@@ -1104,17 +1215,17 @@ int wasapi_stream_get_position(cubeb_str
 
   return CUBEB_OK;
 }
 
 int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
 {
   assert(stm && latency);
 
-  auto_lock lock(&stm->stream_reset_lock);
+  auto_lock lock(stm->stream_reset_lock);
 
   /* The GetStreamLatency method only works if the
    * AudioClient has been initialized. */
   if (!stm->client) {
     return CUBEB_ERROR;
   }
 
   REFERENCE_TIME latency_hns;
@@ -1127,17 +1238,17 @@ int wasapi_stream_get_latency(cubeb_stre
 
 int wasapi_stream_set_volume(cubeb_stream * stm, float volume)
 {
   HRESULT hr;
   uint32_t channels;
   /* up to 9.1 for now */
   float volumes[10];
 
-  auto_lock lock(&stm->stream_reset_lock);
+  auto_lock lock(stm->stream_reset_lock);
 
   hr = stm->audio_stream_volume->GetChannelCount(&channels);
   if (hr != S_OK) {
     LOG("could not get the channel count: %x", hr);
     return CUBEB_ERROR;
   }
 
   assert(channels <= 10 && "bump the array size");
--- a/media/libstagefright/binding/DecoderData.cpp
+++ b/media/libstagefright/binding/DecoderData.cpp
@@ -228,23 +228,23 @@ MP4Sample::MP4Sample(const MP4Sample& co
 MP4Sample::~MP4Sample()
 {
   if (mMediaBuffer) {
     mMediaBuffer->release();
   }
 }
 
 void
-MP4Sample::Update(int64_t& aMediaTime, int64_t& aTimestampOffset)
+MP4Sample::Update(int64_t& aMediaTime)
 {
   sp<MetaData> m = mMediaBuffer->meta_data();
   // XXXbholley - Why don't we adjust decode_timestamp for aMediaTime?
   // According to k17e, this code path is no longer used - we should probably remove it.
-  decode_timestamp = FindInt64(m, kKeyDecodingTime) + aTimestampOffset;
-  composition_timestamp = FindInt64(m, kKeyTime) - aMediaTime + aTimestampOffset;
+  decode_timestamp = FindInt64(m, kKeyDecodingTime);
+  composition_timestamp = FindInt64(m, kKeyTime) - aMediaTime;
   duration = FindInt64(m, kKeyDuration);
   byte_offset = FindInt64(m, kKey64BitFileOffset);
   is_sync_point = FindInt32(m, kKeyIsSyncFrame);
   data = reinterpret_cast<uint8_t*>(mMediaBuffer->data());
   size = mMediaBuffer->range_length();
 
   crypto.Update(m);
 }
--- a/media/libstagefright/binding/Index.cpp
+++ b/media/libstagefright/binding/Index.cpp
@@ -224,23 +224,22 @@ SampleIterator::GetNextKeyframeTime()
       return moofs[moof].mIndex[sample].mDecodeTime;
     }
     ++sample;
   }
   MOZ_ASSERT(false); // should not be reached.
 }
 
 Index::Index(const stagefright::Vector<MediaSource::Indice>& aIndex,
-             Stream* aSource, uint32_t aTrackId, Microseconds aTimestampOffset,
-             Monitor* aMonitor)
+             Stream* aSource, uint32_t aTrackId, Monitor* aMonitor)
   : mSource(aSource)
   , mMonitor(aMonitor)
 {
   if (aIndex.isEmpty()) {
-    mMoofParser = new MoofParser(aSource, aTrackId, aTimestampOffset, aMonitor);
+    mMoofParser = new MoofParser(aSource, aTrackId, aMonitor);
   } else {
     for (size_t i = 0; i < aIndex.size(); i++) {
       const MediaSource::Indice& indice = aIndex[i];
       Sample sample;
       sample.mByteRange = MediaByteRange(indice.start_offset,
                                          indice.end_offset);
       sample.mCompositionRange = Interval<Microseconds>(indice.start_composition,
                                                         indice.end_composition);
--- a/media/libstagefright/binding/MoofParser.cpp
+++ b/media/libstagefright/binding/MoofParser.cpp
@@ -24,17 +24,17 @@ MoofParser::RebuildFragmentedIndex(
 void
 MoofParser::RebuildFragmentedIndex(BoxContext& aContext)
 {
   for (Box box(&aContext, mOffset); box.IsAvailable(); box = box.Next()) {
     if (box.IsType("moov")) {
       mInitRange = MediaByteRange(0, box.Range().mEnd);
       ParseMoov(box);
     } else if (box.IsType("moof")) {
-      Moof moof(box, mTrex, mMdhd, mEdts, mSinf, mTimestampOffset);
+      Moof moof(box, mTrex, mMdhd, mEdts, mSinf);
 
       if (!mMoofs.IsEmpty()) {
         // Stitch time ranges together in the case of a (hopefully small) time
         // range gap between moofs.
         mMoofs.LastElement().FixRounding(moof);
       }
 
       mMoofs.AppendElement(moof);
@@ -209,18 +209,19 @@ MoofParser::ParseEncrypted(Box& aBox)
 
       if (mSinf.IsValid()) {
         break;
       }
     }
   }
 }
 
-Moof::Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, Microseconds aTimestampOffset) :
-    mRange(aBox.Range()), mTimestampOffset(aTimestampOffset), mMaxRoundingError(0)
+Moof::Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf)
+  : mRange(aBox.Range())
+  , mMaxRoundingError(0)
 {
   for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
     if (box.IsType("traf")) {
       ParseTraf(box, aTrex, aMdhd, aEdts, aSinf);
     }
   }
   ProcessCenc();
 }
@@ -394,20 +395,20 @@ Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, 
     if (flags & 0x800) {
       ctsOffset = reader->Read32();
     }
 
     Sample sample;
     sample.mByteRange = MediaByteRange(offset, offset + sampleSize);
     offset += sampleSize;
 
-    sample.mDecodeTime = aMdhd.ToMicroseconds(decodeTime) + mTimestampOffset;
+    sample.mDecodeTime = aMdhd.ToMicroseconds(decodeTime);
     sample.mCompositionRange = Interval<Microseconds>(
-      aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset - aEdts.mMediaStart) + mTimestampOffset,
-      aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset + sampleDuration - aEdts.mMediaStart) + mTimestampOffset);
+      aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset - aEdts.mMediaStart),
+      aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset + sampleDuration - aEdts.mMediaStart));
     decodeTime += sampleDuration;
 
     sample.mSync = !(sampleFlags & 0x1010000);
 
     mIndex.AppendElement(sample);
 
     mMdatRange = mMdatRange.Extents(sample.mByteRange);
   }
@@ -504,17 +505,18 @@ Trex::Trex(Box& aBox)
   mTrackId = reader->ReadU32();
   mDefaultSampleDescriptionIndex = reader->ReadU32();
   mDefaultSampleDuration = reader->ReadU32();
   mDefaultSampleSize = reader->ReadU32();
   mDefaultSampleFlags = reader->ReadU32();
   mValid = true;
 }
 
-Tfhd::Tfhd(Box& aBox, Trex& aTrex) : Trex(aTrex)
+Tfhd::Tfhd(Box& aBox, Trex& aTrex)
+  : Trex(aTrex)
 {
   MOZ_ASSERT(aBox.IsType("tfhd"));
   MOZ_ASSERT(aBox.Parent()->IsType("traf"));
   MOZ_ASSERT(aBox.Parent()->Parent()->IsType("moof"));
 
   BoxReader reader(aBox);
   if (!reader->CanReadType<uint32_t>()) {
     return;
--- a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
@@ -152,17 +152,17 @@ public:
 typedef int64_t Microseconds;
 
 class MP4Sample
 {
 public:
   MP4Sample();
   MP4Sample(const MP4Sample& copy);
   virtual ~MP4Sample();
-  void Update(int64_t& aMediaTime, int64_t& aTimestampOffset);
+  void Update(int64_t& aMediaTime);
   void Pad(size_t aPaddingBytes);
 
   stagefright::MediaBuffer* mMediaBuffer;
 
   Microseconds decode_timestamp;
   Microseconds composition_timestamp;
   Microseconds duration;
   int64_t byte_offset;
--- a/media/libstagefright/binding/include/mp4_demuxer/Index.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h
@@ -33,18 +33,17 @@ private:
 };
 
 class Index
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Index)
 
   Index(const stagefright::Vector<stagefright::MediaSource::Indice>& aIndex,
-        Stream* aSource, uint32_t aTrackId, Microseconds aTimestampOffset,
-        Monitor* aMonitor);
+        Stream* aSource, uint32_t aTrackId, Monitor* aMonitor);
 
   void UpdateMoofIndex(const nsTArray<mozilla::MediaByteRange>& aByteRanges);
   Microseconds GetEndCompositionIfBuffered(
     const nsTArray<mozilla::MediaByteRange>& aByteRanges);
   void ConvertByteRangesToTimeRanges(
     const nsTArray<mozilla::MediaByteRange>& aByteRanges,
     nsTArray<Interval<Microseconds>>* aTimeRanges);
   uint64_t GetEvictionOffset(Microseconds aTime);
--- a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
@@ -163,17 +163,17 @@ private:
   int64_t mMoofOffset;
   Saiz& mSaiz;
   Saio& mSaio;
 };
 
 class Moof : public Atom
 {
 public:
-  Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, Microseconds aTimestampOffset);
+  Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf);
   bool GetAuxInfo(AtomType aType, nsTArray<MediaByteRange>* aByteRanges);
   void FixRounding(const Moof& aMoof);
 
   mozilla::MediaByteRange mRange;
   mozilla::MediaByteRange mMdatRange;
   Interval<Microseconds> mTimeRange;
   nsTArray<Sample> mIndex;
 
@@ -181,27 +181,27 @@ public:
   nsTArray<Saio> mSaios;
 
 private:
   void ParseTraf(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf);
   void ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd, Edts& aEdts);
   void ParseSaiz(Box& aBox);
   void ParseSaio(Box& aBox);
   bool ProcessCenc();
-  Microseconds mTimestampOffset;
   uint64_t mMaxRoundingError;
 };
 
 class MoofParser
 {
 public:
-  MoofParser(Stream* aSource, uint32_t aTrackId,
-             Microseconds aTimestampOffset, Monitor* aMonitor)
-    : mSource(aSource), mOffset(0), mTimestampOffset(aTimestampOffset),
-      mTrex(aTrackId), mMonitor(aMonitor)
+  MoofParser(Stream* aSource, uint32_t aTrackId, Monitor* aMonitor)
+    : mSource(aSource)
+    , mOffset(0)
+    , mTrex(aTrackId)
+    , mMonitor(aMonitor)
   {
     // Setting the mTrex.mTrackId to 0 is a nasty work around for calculating
     // the composition range for MSE. We need an array of tracks.
   }
   void RebuildFragmentedIndex(
     const nsTArray<mozilla::MediaByteRange>& aByteRanges);
   void RebuildFragmentedIndex(BoxContext& aContext);
   Interval<Microseconds> GetCompositionRange(
@@ -218,17 +218,16 @@ public:
   void ParseEncrypted(Box& aBox);
   void ParseSinf(Box& aBox);
 
   bool BlockingReadNextMoof();
 
   mozilla::MediaByteRange mInitRange;
   nsRefPtr<Stream> mSource;
   uint64_t mOffset;
-  Microseconds mTimestampOffset;
   nsTArray<uint64_t> mMoofOffsets;
   Mdhd mMdhd;
   Trex mTrex;
   Tfdt mTfdt;
   Edts mEdts;
   Sinf mSinf;
   Monitor* mMonitor;
   nsTArray<Moof>& Moofs() { mMonitor->AssertCurrentThreadOwns(); return mMoofs; }
--- a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
@@ -38,17 +38,17 @@ protected:
   virtual ~Stream() {}
 };
 
 enum TrackType { kVideo = 1, kAudio };
 
 class MP4Demuxer
 {
 public:
-  explicit MP4Demuxer(Stream* aSource, Microseconds aTimestampOffset, Monitor* aMonitor);
+  explicit MP4Demuxer(Stream* aSource, Monitor* aMonitor);
   ~MP4Demuxer();
 
   bool Init();
   Microseconds Duration();
   bool CanSeek();
 
   bool HasValidAudio();
   bool HasValidVideo();
@@ -81,16 +81,15 @@ private:
   AudioDecoderConfig mAudioConfig;
   VideoDecoderConfig mVideoConfig;
   CryptoFile mCrypto;
 
   nsAutoPtr<StageFrightPrivate> mPrivate;
   nsRefPtr<Stream> mSource;
   nsTArray<mozilla::MediaByteRange> mCachedByteRanges;
   nsTArray<Interval<Microseconds>> mCachedTimeRanges;
-  Microseconds mTimestampOffset;
   Monitor* mMonitor;
   Microseconds mNextKeyframeTime;
 };
 
 } // namespace mozilla
 
 #endif // MP4_DEMUXER_H_
--- a/media/libstagefright/binding/mp4_demuxer.cpp
+++ b/media/libstagefright/binding/mp4_demuxer.cpp
@@ -68,20 +68,21 @@ public:
   virtual uint32_t flags() { return kWantsPrefetching | kIsHTTPBasedSource; }
 
   virtual status_t reconnectAtOffset(off64_t offset) { return NO_ERROR; }
 
 private:
   nsRefPtr<Stream> mSource;
 };
 
-MP4Demuxer::MP4Demuxer(Stream* source, Microseconds aTimestampOffset, Monitor* aMonitor)
-  : mPrivate(new StageFrightPrivate()), mSource(source),
-    mTimestampOffset(aTimestampOffset), mMonitor(aMonitor),
-    mNextKeyframeTime(-1)
+MP4Demuxer::MP4Demuxer(Stream* source, Monitor* aMonitor)
+  : mPrivate(new StageFrightPrivate())
+  , mSource(source)
+  , mMonitor(aMonitor)
+  , mNextKeyframeTime(-1)
 {
   mPrivate->mExtractor = new MPEG4Extractor(new DataSourceAdapter(source));
 }
 
 MP4Demuxer::~MP4Demuxer()
 {
   if (mPrivate->mAudio.get()) {
     mPrivate->mAudio->stop();
@@ -116,31 +117,31 @@ MP4Demuxer::Init()
       sp<MediaSource> track = e->getTrack(i);
       if (track->start() != OK) {
         return false;
       }
       mPrivate->mAudio = track;
       mAudioConfig.Update(metaData, mimeType);
       nsRefPtr<Index> index = new Index(mPrivate->mAudio->exportIndex(),
                                         mSource, mAudioConfig.mTrackId,
-                                        mTimestampOffset, mMonitor);
+                                        mMonitor);
       mPrivate->mIndexes.AppendElement(index);
       if (index->IsFragmented()) {
         mPrivate->mAudioIterator = new SampleIterator(index);
       }
     } else if (!mPrivate->mVideo.get() && !strncmp(mimeType, "video/", 6)) {
       sp<MediaSource> track = e->getTrack(i);
       if (track->start() != OK) {
         return false;
       }
       mPrivate->mVideo = track;
       mVideoConfig.Update(metaData, mimeType);
       nsRefPtr<Index> index = new Index(mPrivate->mVideo->exportIndex(),
                                         mSource, mVideoConfig.mTrackId,
-                                        mTimestampOffset, mMonitor);
+                                        mMonitor);
       mPrivate->mIndexes.AppendElement(index);
       if (index->IsFragmented()) {
         mPrivate->mVideoIterator = new SampleIterator(index);
       }
     }
   }
   sp<MetaData> metaData = e->getMetaData();
   mCrypto.Update(metaData);
@@ -227,17 +228,17 @@ MP4Demuxer::DemuxAudioSample()
   status_t status =
     mPrivate->mAudio->read(&sample->mMediaBuffer, &mPrivate->mAudioOptions);
   mPrivate->mAudioOptions.clearSeekTo();
 
   if (status < 0) {
     return nullptr;
   }
 
-  sample->Update(mAudioConfig.media_time, mTimestampOffset);
+  sample->Update(mAudioConfig.media_time);
 
   return sample.forget();
 }
 
 MP4Sample*
 MP4Demuxer::DemuxVideoSample()
 {
   mMonitor->AssertCurrentThreadOwns();
@@ -260,17 +261,17 @@ MP4Demuxer::DemuxVideoSample()
   status_t status =
     mPrivate->mVideo->read(&sample->mMediaBuffer, &mPrivate->mVideoOptions);
   mPrivate->mVideoOptions.clearSeekTo();
 
   if (status < 0) {
     return nullptr;
   }
 
-  sample->Update(mVideoConfig.media_time, mTimestampOffset);
+  sample->Update(mVideoConfig.media_time);
   sample->extra_data = mVideoConfig.extra_data;
 
   return sample.forget();
 }
 
 void
 MP4Demuxer::UpdateIndex(const nsTArray<mozilla::MediaByteRange>& aByteRanges)
 {
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -3516,16 +3516,18 @@ status_t MPEG4Source::read(
         ssize_t num_bytes_read = 0;
         int32_t drm = 0;
         bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
         if (usesDRM) {
             num_bytes_read =
                 mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
         } else {
             if (!ensureSrcBufferAllocated(size)) {
+                ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
+                      size, mSrcBackend.Length());
                 return ERROR_MALFORMED;
             }
             num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
         }
 
         if (num_bytes_read < (ssize_t)size) {
             mBuffer->release();
             mBuffer = NULL;
@@ -3871,16 +3873,18 @@ status_t MPEG4Source::fragmentedRead(
         ssize_t num_bytes_read = 0;
         int32_t drm = 0;
         bool usesDRM = (mFormat->findInt32(kKeyIsDRM, &drm) && drm != 0);
         if (usesDRM) {
             num_bytes_read =
                 mDataSource->readAt(offset, (uint8_t*)mBuffer->data(), size);
         } else {
             if (!ensureSrcBufferAllocated(size)) {
+                ALOGE("Error insufficient memory, requested %u bytes (had:%u)",
+                      size, mSrcBackend.Length());
                 return ERROR_MALFORMED;
             }
             num_bytes_read = mDataSource->readAt(offset, mSrcBuffer, size);
         }
 
         if (num_bytes_read < (ssize_t)size) {
             mBuffer->release();
             mBuffer = NULL;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -304,18 +304,16 @@ static nsresult InitNSSInContent()
 #endif // MOZILLA_INTERNAL_API
 
 namespace mozilla {
   class DataChannel;
 }
 
 class nsIDOMDataChannel;
 
-static const int MEDIA_STREAM_MUTE = 0x80;
-
 PRLogModuleInfo *signalingLogInfo() {
   static PRLogModuleInfo *logModuleInfo = nullptr;
   if (!logModuleInfo) {
     logModuleInfo = PR_NewLogModule("signaling");
   }
   return logModuleInfo;
 }
 
@@ -1005,49 +1003,16 @@ PeerConnectionImpl::ConfigureJsepSession
 
 RefPtr<DtlsIdentity> const
 PeerConnectionImpl::GetIdentity() const
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   return mIdentity;
 }
 
-nsresult
-PeerConnectionImpl::CreateFakeMediaStream(uint32_t aHint, DOMMediaStream** aRetval)
-{
-  MOZ_ASSERT(aRetval);
-  PC_AUTO_ENTER_API_CALL(false);
-
-  bool mute = false;
-
-  // Hack to allow you to mute the stream
-  if (aHint & MEDIA_STREAM_MUTE) {
-    mute = true;
-    aHint &= ~MEDIA_STREAM_MUTE;
-  }
-
-  nsRefPtr<DOMMediaStream> stream = MakeMediaStream(aHint);
-  if (!stream) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (!mute) {
-    if (aHint & DOMMediaStream::HINT_CONTENTS_AUDIO) {
-      new Fake_AudioGenerator(stream);
-    } else {
-#ifdef MOZILLA_INTERNAL_API
-    new Fake_VideoGenerator(stream);
-#endif
-    }
-  }
-
-  stream.forget(aRetval);
-  return NS_OK;
-}
-
 // Data channels won't work without a window, so in order for the C++ unit
 // tests to work (it doesn't have a window available) we ifdef the following
 // two implementations.
 NS_IMETHODIMP
 PeerConnectionImpl::EnsureDataConnection(uint16_t aNumstreams)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -316,19 +316,16 @@ public:
   nsCOMPtr<nsIEventTarget> GetSTSThread() {
     PC_AUTO_ENTER_API_CALL_NO_CHECK();
     return mSTSThread;
   }
 
   // Get the DTLS identity (local side)
   mozilla::RefPtr<DtlsIdentity> const GetIdentity() const;
 
-  // Create a fake media stream
-  nsresult CreateFakeMediaStream(uint32_t hint, mozilla::DOMMediaStream** retval);
-
   nsPIDOMWindow* GetWindow() const {
     PC_AUTO_ENTER_API_CALL_NO_CHECK();
     return mWindow;
   }
 
   // Initialize PeerConnection from an IceConfiguration object (unit-tests)
   nsresult Initialize(PeerConnectionObserver& aObserver,
                       nsGlobalWindow* aWindow,
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -52,133 +52,16 @@ struct RTCOutboundRTPStreamStats;
 #include "MediaPipeline.h"
 
 namespace mozilla {
 
 class PeerConnectionImpl;
 class PeerConnectionMedia;
 class PCUuidGenerator;
 
-/* Temporary for providing audio data */
-class Fake_AudioGenerator {
- public:
-  explicit Fake_AudioGenerator(DOMMediaStream* aStream) : mStream(aStream), mCount(0) {
-    mTimer = do_CreateInstance("@mozilla.org/timer;1");
-    MOZ_ASSERT(mTimer);
-
-    // Make a track
-    mozilla::AudioSegment *segment = new mozilla::AudioSegment();
-    mStream->GetStream()->AsSourceStream()->AddAudioTrack(1, 16000, 0, segment);
-
-    // Set the timer
-    mTimer->InitWithFuncCallback(Callback, this, 100, nsITimer::TYPE_REPEATING_PRECISE);
-  }
-
-  static void Callback(nsITimer* timer, void *arg) {
-    Fake_AudioGenerator* gen = static_cast<Fake_AudioGenerator*>(arg);
-
-    nsRefPtr<mozilla::SharedBuffer> samples = mozilla::SharedBuffer::Create(1600 * sizeof(int16_t));
-    int16_t* data = static_cast<int16_t*>(samples->Data());
-    for (int i=0; i<1600; i++) {
-      data[i] = ((gen->mCount % 8) * 4000) - (7*4000)/2;
-      ++gen->mCount;
-    }
-
-    mozilla::AudioSegment segment;
-    nsAutoTArray<const int16_t*,1> channelData;
-    channelData.AppendElement(data);
-    segment.AppendFrames(samples.forget(), channelData, 1600);
-    gen->mStream->GetStream()->AsSourceStream()->AppendToTrack(1, &segment);
-  }
-
- private:
-  nsCOMPtr<nsITimer> mTimer;
-  nsRefPtr<DOMMediaStream> mStream;
-  int mCount;
-};
-
-/* Temporary for providing video data */
-#ifdef MOZILLA_INTERNAL_API
-class Fake_VideoGenerator {
- public:
-  typedef mozilla::gfx::IntSize IntSize;
-
-  explicit Fake_VideoGenerator(DOMMediaStream* aStream) {
-    mStream = aStream;
-    mCount = 0;
-    mTimer = do_CreateInstance("@mozilla.org/timer;1");
-    MOZ_ASSERT(mTimer);
-
-    // Make a track
-    mozilla::VideoSegment *segment = new mozilla::VideoSegment();
-    mStream->GetStream()->AsSourceStream()->AddTrack(1, 0, segment);
-    mStream->GetStream()->AsSourceStream()->AdvanceKnownTracksTime(mozilla::STREAM_TIME_MAX);
-
-    // Set the timer. Set to 10 fps.
-    mTimer->InitWithFuncCallback(Callback, this, 100, nsITimer::TYPE_REPEATING_SLACK);
-  }
-
-  static void Callback(nsITimer* timer, void *arg) {
-    Fake_VideoGenerator* gen = static_cast<Fake_VideoGenerator*>(arg);
-
-    const uint32_t WIDTH = 640;
-    const uint32_t HEIGHT = 480;
-
-    // Allocate a single blank Image
-    nsRefPtr<mozilla::layers::ImageContainer> container =
-      mozilla::layers::LayerManager::CreateImageContainer();
-
-    nsRefPtr<mozilla::layers::Image> image =
-      container->CreateImage(mozilla::ImageFormat::PLANAR_YCBCR);
-
-    int len = ((WIDTH * HEIGHT) * 3 / 2);
-    mozilla::layers::PlanarYCbCrImage* planar =
-      static_cast<mozilla::layers::PlanarYCbCrImage*>(image.get());
-    uint8_t* frame = (uint8_t*) PR_Malloc(len);
-    ++gen->mCount;
-    memset(frame, (gen->mCount / 8) & 0xff, len); // Rotating colors
-
-    const uint8_t lumaBpp = 8;
-    const uint8_t chromaBpp = 4;
-
-    mozilla::layers::PlanarYCbCrData data;
-    data.mYChannel = frame;
-    data.mYSize = IntSize(WIDTH, HEIGHT);
-    data.mYStride = (int32_t) (WIDTH * lumaBpp / 8.0);
-    data.mCbCrStride = (int32_t) (WIDTH * chromaBpp / 8.0);
-    data.mCbChannel = frame + HEIGHT * data.mYStride;
-    data.mCrChannel = data.mCbChannel + HEIGHT * data.mCbCrStride / 2;
-    data.mCbCrSize = IntSize(WIDTH / 2, HEIGHT / 2);
-    data.mPicX = 0;
-    data.mPicY = 0;
-    data.mPicSize = IntSize(WIDTH, HEIGHT);
-    data.mStereoMode = mozilla::StereoMode::MONO;
-
-    // SetData copies data, so we can free the frame
-    planar->SetData(data);
-    PR_Free(frame);
-
-    // AddTrack takes ownership of segment
-    mozilla::VideoSegment *segment = new mozilla::VideoSegment();
-    // 10 fps.
-    segment->AppendFrame(image.forget(),
-                         gen->mStream->GetStream()->GraphRate() / 10,
-                         IntSize(WIDTH, HEIGHT));
-
-    gen->mStream->GetStream()->AsSourceStream()->AppendToTrack(1, segment);
-  }
-
- private:
-  nsCOMPtr<nsITimer> mTimer;
-  nsRefPtr<DOMMediaStream> mStream;
-  int mCount;
-};
-#endif
-
-
 class SourceStreamInfo {
 public:
   SourceStreamInfo(DOMMediaStream* aMediaStream,
                    PeerConnectionMedia *aParent,
                    const std::string& aId)
       : mMediaStream(aMediaStream),
         mParent(aParent),
         mId(aId) {
--- a/mobile/android/base/gfx/ScrollbarLayer.java
+++ b/mobile/android/base/gfx/ScrollbarLayer.java
@@ -6,25 +6,33 @@
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.util.FloatUtils;
 
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.opengl.GLES20;
+import android.util.Log;
 
 import java.nio.FloatBuffer;
+import java.nio.ByteBuffer;
 
 public class ScrollbarLayer extends TileLayer {
+    private static final String LOGTAG = "GeckoScrollbarLayer";
+
     public static final long FADE_DELAY = 500; // milliseconds before fade-out starts
     private static final float FADE_AMOUNT = 0.03f; // how much (as a percent) the scrollbar should fade per frame
 
     private final boolean mVertical;
     private float mOpacity;
+    private final Rect mDirtyRect;
+    private IntSize mSize;
+
+    private int[] mTextureIDs;
 
     // To avoid excessive GC, declare some objects here that would otherwise
     // be created and destroyed frequently during draw().
     private final RectF mBarRectF;
     private final Rect mBarRect;
     private final float[] mCoords;
     private final RectF mCapRectF;
 
@@ -62,16 +70,18 @@ public class ScrollbarLayer extends Tile
         super(new BufferedImage(scrollbarImage), TileLayer.PaintMode.NORMAL);
         mRenderer = renderer;
         mVertical = vertical;
 
         mBarRectF = new RectF();
         mBarRect = new Rect();
         mCoords = new float[20];
         mCapRectF = new RectF();
+        mDirtyRect = new Rect();
+        mSize = new IntSize(0, 0);
 
         mTexHeight = scrollbarImage.getHeight();
         mTexWidth = scrollbarImage.getWidth();
 
         if (mVertical) {
             mBarWidth = imageSize.width;
             mCapLength = imageSize.height / 2;
             mStartCapTexCoords = new Rect(0, mTexHeight - mCapLength, imageSize.width, mTexHeight);
@@ -289,9 +299,130 @@ public class ScrollbarLayer extends Tile
         float barStart = ((viewport.left - context.offset.x - pageRect.left) * (viewport.width() / pageRect.width())) + mCapLength;
         float barEnd = ((viewport.right - context.offset.x - pageRect.left) * (viewport.width() / pageRect.width())) - mCapLength;
         if (barStart > barEnd) {
             float middle = (barStart + barEnd) / 2.0f;
             barStart = barEnd = middle;
         }
         dest.set(barStart, viewport.height() - mBarWidth, barEnd, viewport.height());
     }
+
+    private void validateTexture() {
+        /* Calculate the ideal texture size. This must be a power of two if
+         * the texture is repeated or OpenGL ES 2.0 isn't supported, as
+         * OpenGL ES 2.0 is required for NPOT texture support (without
+         * extensions), but doesn't support repeating NPOT textures.
+         *
+         * XXX Currently, we don't pick a GLES 2.0 context, so always round.
+         */
+        IntSize textureSize = mImage.getSize().nextPowerOfTwo();
+
+        if (!textureSize.equals(mSize)) {
+            mSize = textureSize;
+
+            // Delete the old texture
+            if (mTextureIDs != null) {
+                TextureReaper.get().add(mTextureIDs);
+                mTextureIDs = null;
+
+                // Free the texture immediately, so we don't incur a
+                // temporarily increased memory usage.
+                TextureReaper.get().reap();
+            }
+        }
+    }
+
+    @Override
+    protected void performUpdates(RenderContext context) {
+        super.performUpdates(context);
+
+        // Reallocate the texture if the size has changed
+        validateTexture();
+
+        // Don't do any work if the image has an invalid size.
+        if (!mImage.getSize().isPositive())
+            return;
+
+        // If we haven't allocated a texture, assume the whole region is dirty
+        if (mTextureIDs == null) {
+            uploadFullTexture();
+        } else {
+            uploadDirtyRect(mDirtyRect);
+        }
+
+        mDirtyRect.setEmpty();
+    }
+
+    private void uploadFullTexture() {
+        IntSize bufferSize = mImage.getSize();
+        uploadDirtyRect(new Rect(0, 0, bufferSize.width, bufferSize.height));
+    }
+
+    private void uploadDirtyRect(Rect dirtyRect) {
+        // If we have nothing to upload, just return for now
+        if (dirtyRect.isEmpty())
+            return;
+
+        // It's possible that the buffer will be null, check for that and return
+        ByteBuffer imageBuffer = mImage.getBuffer();
+        if (imageBuffer == null)
+            return;
+
+        if (mTextureIDs == null) {
+            mTextureIDs = new int[1];
+            GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
+        }
+
+        int imageFormat = mImage.getFormat();
+        BufferedImageGLInfo glInfo = new BufferedImageGLInfo(imageFormat);
+
+        bindAndSetGLParameters();
+
+        // XXX TexSubImage2D is too broken to rely on on Adreno, and very slow
+        //     on other chipsets, so we always upload the entire buffer.
+        IntSize bufferSize = mImage.getSize();
+        if (mSize.equals(bufferSize)) {
+            GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width,
+                                mSize.height, 0, glInfo.format, glInfo.type, imageBuffer);
+        } else {
+            // Our texture has been expanded to the next power of two.
+            // XXX We probably never want to take this path, so throw an exception.
+            throw new RuntimeException("Buffer/image size mismatch in TileLayer!");
+        }
+    }
+
+    private void bindAndSetGLParameters() {
+        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]);
+        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
+                               GLES20.GL_LINEAR);
+        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
+                               GLES20.GL_LINEAR);
+
+        int repeatMode = repeats() ? GLES20.GL_REPEAT : GLES20.GL_CLAMP_TO_EDGE;
+        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, repeatMode);
+        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, repeatMode);
+    }
+
+    @Override
+    public void destroy() {
+        try {
+            if (mImage != null) {
+                mImage.destroy();
+            }
+        } catch (Exception ex) {
+            Log.e(LOGTAG, "error clearing buffers: ", ex);
+        }
+    }
+
+    protected int getTextureID() { return mTextureIDs[0]; }
+    protected boolean initialized() { return mImage != null && mTextureIDs != null; }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mTextureIDs != null)
+                TextureReaper.get().add(mTextureIDs);
+        } finally {
+            super.finalize();
+        }
+    }
 }
--- a/mobile/android/base/gfx/TileLayer.java
+++ b/mobile/android/base/gfx/TileLayer.java
@@ -2,176 +2,38 @@
  * 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/. */
 
 package org.mozilla.gecko.gfx;
 
 import android.graphics.Rect;
 import android.opengl.GLES20;
-import android.util.Log;
-
-import java.nio.ByteBuffer;
 
 /**
  * Base class for tile layers, which encapsulate the logic needed to draw textured tiles in OpenGL
  * ES.
  */
 public abstract class TileLayer extends Layer {
     private static final String LOGTAG = "GeckoTileLayer";
 
-    private final Rect mDirtyRect;
-    private IntSize mSize;
-    private int[] mTextureIDs;
-
     protected final BufferedImage mImage;
 
     public enum PaintMode { NORMAL, REPEAT, STRETCH };
     private PaintMode mPaintMode;
 
     public TileLayer(BufferedImage image, PaintMode paintMode) {
         super(image.getSize());
 
         mPaintMode = paintMode;
         mImage = image;
-        mSize = new IntSize(0, 0);
-        mDirtyRect = new Rect();
     }
 
     protected boolean repeats() { return mPaintMode == PaintMode.REPEAT; }
     protected boolean stretches() { return mPaintMode == PaintMode.STRETCH; }
-    protected int getTextureID() { return mTextureIDs[0]; }
-    protected boolean initialized() { return mImage != null && mTextureIDs != null; }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mTextureIDs != null)
-                TextureReaper.get().add(mTextureIDs);
-        } finally {
-            super.finalize();
-        }
-    }
-
-    public void destroy() {
-        try {
-            if (mImage != null) {
-                mImage.destroy();
-            }
-        } catch (Exception ex) {
-            Log.e(LOGTAG, "error clearing buffers: ", ex);
-        }
-    }
+    public abstract void destroy();
 
     public void setPaintMode(PaintMode mode) {
         mPaintMode = mode;
     }
-
-    /**
-     * Invalidates the entire buffer so that it will be uploaded again. Only valid inside a
-     * transaction.
-     */
-
-    public void invalidate() {
-        if (!inTransaction())
-            throw new RuntimeException("invalidate() is only valid inside a transaction");
-        IntSize bufferSize = mImage.getSize();
-        mDirtyRect.set(0, 0, bufferSize.width, bufferSize.height);
-    }
-
-    private void validateTexture() {
-        /* Calculate the ideal texture size. This must be a power of two if
-         * the texture is repeated or OpenGL ES 2.0 isn't supported, as
-         * OpenGL ES 2.0 is required for NPOT texture support (without
-         * extensions), but doesn't support repeating NPOT textures.
-         *
-         * XXX Currently, we don't pick a GLES 2.0 context, so always round.
-         */
-        IntSize textureSize = mImage.getSize().nextPowerOfTwo();
-
-        if (!textureSize.equals(mSize)) {
-            mSize = textureSize;
-
-            // Delete the old texture
-            if (mTextureIDs != null) {
-                TextureReaper.get().add(mTextureIDs);
-                mTextureIDs = null;
-
-                // Free the texture immediately, so we don't incur a
-                // temporarily increased memory usage.
-                TextureReaper.get().reap();
-            }
-        }
-    }
-
-    @Override
-    protected void performUpdates(RenderContext context) {
-        super.performUpdates(context);
-
-        // Reallocate the texture if the size has changed
-        validateTexture();
-
-        // Don't do any work if the image has an invalid size.
-        if (!mImage.getSize().isPositive())
-            return;
-
-        // If we haven't allocated a texture, assume the whole region is dirty
-        if (mTextureIDs == null) {
-            uploadFullTexture();
-        } else {
-            uploadDirtyRect(mDirtyRect);
-        }
-
-        mDirtyRect.setEmpty();
-    }
-
-    private void uploadFullTexture() {
-        IntSize bufferSize = mImage.getSize();
-        uploadDirtyRect(new Rect(0, 0, bufferSize.width, bufferSize.height));
-    }