Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 16 Jul 2014 17:08:26 -0700
changeset 216343 8e8f3ba646554c13de1f26140d8a954b79d7549b
parent 216270 4024d8019701d7704807d451172a65008b0702cf (current diff)
parent 216342 80af4d90c3d570f428d842d8147adcdca91fa6eb (diff)
child 216407 a74600665875202a1990416598444deb807d3a5f
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c a=merge
browser/app/profile/firefox.js
browser/components/about/AboutRedirector.cpp
media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.cc
security/sandbox/chromium/base/base_paths.cc
security/sandbox/chromium/base/base_paths_win.cc
security/sandbox/chromium/base/command_line.cc
security/sandbox/chromium/base/command_line.h
security/sandbox/chromium/base/critical_closure.h
security/sandbox/chromium/base/environment.h
security/sandbox/chromium/base/file_util.cc
security/sandbox/chromium/base/file_version_info.h
security/sandbox/chromium/base/file_version_info_win.h
security/sandbox/chromium/base/format_macros.h
security/sandbox/chromium/base/logging.cc
security/sandbox/chromium/base/logging_win.cc
security/sandbox/chromium/base/logging_win.h
security/sandbox/chromium/base/observer_list.h
security/sandbox/chromium/base/observer_list_threadsafe.h
security/sandbox/chromium/base/os_compat_nacl.h
security/sandbox/chromium/base/path_service.cc
security/sandbox/chromium/base/pending_task.h
security/sandbox/chromium/base/platform_file.cc
security/sandbox/chromium/base/profiler/alternate_timer.h
security/sandbox/chromium/base/profiler/tracked_time.h
security/sandbox/chromium/base/run_loop.h
security/sandbox/chromium/base/sys_info.h
security/sandbox/chromium/base/thread_task_runner_handle.h
security/sandbox/chromium/base/threading/thread_local_storage.h
security/sandbox/chromium/base/tracking_info.h
testing/marionette/client/marionette/scripts/runemu.py
testing/marionette/client/marionette/scripts/runemu.sh
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1024707 - It seems like changing 3 uuids is just too much!
\ No newline at end of file
+Bug 1038799 - And be wary of your ccache too.
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -659,16 +659,22 @@ pref("javascript.options.mem.gc_high_fre
 pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 300);
 pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 120);
 pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 40);
 pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 0);
 pref("javascript.options.mem.gc_low_frequency_heap_growth", 120);
 pref("javascript.options.mem.high_water_mark", 6);
 pref("javascript.options.mem.gc_allocation_threshold_mb", 1);
 pref("javascript.options.mem.gc_decommit_threshold_mb", 1);
+#ifdef JSGC_GENERATIONAL
+pref("javascript.options.mem.gc_min_empty_chunk_count", 1);
+#else
+pref("javascript.options.mem.gc_min_empty_chunk_count", 0);
+#endif
+pref("javascript.options.mem.gc_max_empty_chunk_count", 2);
 
 // Show/Hide scrollbars when active/inactive
 pref("ui.showHideScrollbars", 1);
 pref("ui.useOverlayScrollbars", 1);
 
 // Enable the ProcessPriorityManager, and give processes with no visible
 // documents a 1s grace period before they're eligible to be marked as
 // background. Background processes that are perceivable due to playing
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1506,21 +1506,26 @@ pref("pdfjs.previousHandler.alwaysAskBef
 pref("shumway.disabled", true);
 #endif
 
 // The maximum amount of decoded image data we'll willingly keep around (we
 // might keep around more than this, but we'll try to get down to this value).
 // (This is intentionally on the high side; see bug 746055.)
 pref("image.mem.max_decoded_image_kb", 256000);
 
-#ifdef MOZ_LOOP
+// Enable by default on nightly and aurora.
+#ifndef RELEASE_BUILD
+pref("loop.enabled", true);
+#else
+pref("loop.enabled", false);
+#endif
+
 pref("loop.server", "https://loop.services.mozilla.com");
 pref("loop.do_not_disturb", false);
 pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/Firefox-Long.ogg");
-#endif
 
 // serverURL to be assigned by services team
 pref("services.push.serverURL", "wss://push.services.mozilla.com/");
 
 // Default social providers
 pref("social.manifest.facebook", "{\"origin\":\"https://www.facebook.com\",\"name\":\"Facebook Share\",\"shareURL\":\"https://www.facebook.com/sharer/sharer.php?u=%{url}\",\"iconURL\":\"data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAX0lEQVQ4jWP4%2F%2F8%2FAyUYTFhHzjgDxP9JxGeQDSBVMxgTbUBCxer%2Fr999%2BQ8DJBuArJksA9A10s8AXIBoA0B%2BR%2FY%2FjD%2BEwoBoA1yT5v3PbdmCE8MAshhID%2FUMoDgzUYIBj0Cgi7ar4coAAAAASUVORK5CYII%3D\",\"icon32URL\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAADbklEQVRYCc1Xv08UQRj99tctexAuCEFjRE0kGBEtLDSGqIWNxkYKbTAxNlY2JhaGWltNtNFeKgsKKxITK43/gCYW+IsoRhA4D47bH7fn9+bcvdm5JR7sefolC3Ozu9978+bNN7PayUv3HN3umdY0Y6IWBtSJ0HSTarXqTOiuTep6Lj+tdxAcA8RAgSmwdd2aCDs0clldYALb/FvgYVhjmfliVA2XpjEgWo0Attn42Z6WH1RFor5ehwo9XQIUZMoVn4qlCoVMSo62EvD8Kh0b3U2Xz43R2PBO6mUCGDlAf65V6MadZzT/rUimoccc2kYA4BfPHqJb105RzjJigKhRq9kEJUBIjgYVuXeL7SAI6eD+Abp5dTwVHOmEHxT50d8WBYJqSOdPj5BjW8gZR8UNqFR2xagx/65XFYaMH+BGWwiYpi4UkBPPLxTp9v1Z+lHc4DWvCQXWmIy6EjITgKowVd5Jjv7N3Hd6y5esigoOwpkJIAmMpZpLJGdiaaC4F0UmAj6bD84GCEwmB/qxMmRilmnwb/mpjAocHh4UEoNAt5NLZB7oy9OJo0PxqkAtePdhiSqunyC1LQUwWMPQaOr6GRre258Ajn4cP7KHcEXhsxpXbj+lT19X2TMNGTLVAcjcalS8gDwsQ2UOMhH4k8FkcrEn5E5ub2sKohxLK2VR77Hl9RUcsrgeRIEiVOT6z+tDbIeLy+vk+kGTCbXxycet6xhl//3f6bJEkdHYhA+mLtDIvoH4ieev5+juoxdk5+pjhALYEdXIpEB5w+NlSKSzqVQ/+H7IO6BLtl3fngGMiqhGJgIwlM6qpyUGFjySdk8m0Zg0ubeD7X9OIDEFajltRQgUJaUKx69tdgaQa0FMADuahZPMFtcEwNPm2hA7ZI5sK4aoE2NvYI+o8hkCIe7CwTv68zS0q9Dk5vpbm/8FXxitSzmMFHpsGj0wyLUheTwD2Y9fVgh1Ae0EPUgD9241ZEnld+v5kgnVZ/8fE0brVh5BK+1oCqKKF72Dk7HwBsssB/pklU1dfChy3S659H5+uelgIb+8WRv1/uGTV9Sdb5wJFlfW6fPCalMhwhSU1j2xKwKbP838GcOwJja4TqO0bjdmXxYTy1EYjFdCWoCEYZhseH/GDL3yJPHnuW6YmT7P1SlIA4768Hke4vOcsX8BE346lLHhDUQAAAAASUVORK5CYII=\", \"icon64URL\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAACNNJREFUeNrtm3tw1NUVxz/399hHHkgCaCBGEFEEREVFYFQcSoOKdkZay4z+4dDpYIsjHWx1WoTMhFi1gzBSpVgVGbU4U1sHfPESKODwEEnRYDFAICEIeZIQshs2u/v73ds/drMsyW7YLEkl2Z6Z32yy+9v7u+fc7znne8+5KzgvAjDunzlv0M13PjDZ6c4cARj0WhEoaZ1tOn3yq9XLf/tNU0O1D5Ad7wq/OpxpaXOL1j5uZAwuaGlVgwNBhULRm0XXBG6HZrlNa9uRrzfM+3DlgjIgGMsA7rl/XDdHOnNf9vosTfVuvTsaQhdkZ4iykh2rHtqydvkxwI58BhjTfv7MmP55E9/1nLNdfU15ACkVvoAaMCRvRPa+re9+DgTaPjMAx+DrJv3M67Mz+6LybWLb4NfTHhxzx31DDhZvOtqGAgNwWbjGICV9XQJB0e/KobcOP1i8qTzaAEYgaDtNU/V5A9hSaUFLuQEt2gVQSgml+j4CUAIppYgK/m0GkCjZ9xGAUNAu0LUhgJRAAAIVzwBSqVRQH4hlAClRKZAFhOgEASoFECBR8QwgUyQGdJT/B8HzCEiBNKhUJzEgBYIgQsTJAkohe9oFZHgHKvQoHtZ9K3tewfiixXABLdoFeuSSEmkF+PH4QTz7+M3o+ENptzvGtS36uSwmjMpAYF10XCllHCYoe84FlLS555Zs5jx6J6ahY+iCl98pJiDNS1hwSZop+cm91zJmxEBefGsPlu1AxKC67V3gf5oGlZSMuz6Dp2fdhWnoAEwaN5T5hsYLb+4hKB1dcgelFDpB8ifk8thDt3DO5+fZxRvxBV0IjQR0EB3KfD1GhJS0GZnnYuGcKTgdF9ZWx4/No/BJjUUrdtJqm4iL+K5SCmSAiWMHMevhcQzNzaa6ron5SzfQ7HeiaSKx+au4m6HupcJKSYZdZVI4dypuV2yo3zoql0VP3cOiFV/Q4jdiGkGhQFqMGpbJL346kbE3DEYIQWNTC39Ysp4Gr4HQtZDyiRhA0NlmSHZbRM7pr1H0m6lckeHqdGXG3jCYoqfupeC17bT49fNRXIFSNrkDTGbNGM9dtw1D10M1DI/Xx3NLP6OqETRdDy1eglPT4rqA7K56gCIrXfHCvHwGZqUnBMtR113FS/N+xHPLtuJpDa1mVobg0emjmX7vqEjsUErhaw1Q8Mo6yk4F0A1HeOW7kIlFx/u7jworRabLpmhuPjmD+iG7YNDrrhnIS09P5cW/buOeO67lkftvJt3tDE06PE7Qsnh++QYOHPOim86wcVUS0+whJug0ghTMmcK1V2eH8m2UHP++nrwhAyIwjiXDcrN5vXAGhqGhFBeMYUvJ0re2sPfgGTTDGUZrEogVopMgmGQWUCgMEWTBE5MZPSLngnGqas/w9j92s31fJfmThvPM7HyMMKRj+qgmOiBHSsnr73/B5r1V6A53KD4k3bFS8dNgckFQoWPxu1kTGDc6N7JqzR4ff/+smE+2H8FSLkx3FluLawhaG3n2iXwcppGoV/Hemt18tK0c3UwLIfUS2nVafBeQJNUXkAF+/dht3H37cKSUBC2bTTv+w98++YazPg1dT0NoIUhruoMd+2sJrtjI7381rQM3iCVrN33N++tL0c30xFNdp0GQeFRYQhcRIO0gv5xxE/fdfSO2bfNVSTmrPtxLZW0A3XSh6VporaLG1XQHu0pOU/TaOhY8+QAuZ3w6vHnnQd74536EkZ50wOsSE0zcugolbWZOG8GM/LGUVdSw6sM97D/UgGa60QxXzMJDZAq6yb7SJgr//CkLn5pOmsvR4Z5dxUdZ9t6XoKfFjNyXkqZjuoBUCpGgCyhp8eDdQ5k++UaWrdrMlr2VSFxoZlpE8YtNWGgGXx9ppnDZpxTMfTCS8gAOlJ5g8ds7kCItTIi6j6FqMVxAC2sV2RB1ekmLCTcNpH+myeyFH7BxT1Voopoe4RKJXQqhmxw45mXh0o/xeH0opThcXs2iv2wmoFyhAnbC4yX+3PgISMDShrA5XHGa3d9UITRniIeTfHASmsGhEz7mL/mI2TPv4sU3t+KzHAnu7JKpCosOkcSIICAB5hZE0OiRCM0Iwb0b6LPQdI5W+Zn/yucoYYayRk+16eK1xqRMDAHtA0r3lep0lNAjO8kfpCpMqpTF4xZEUqA7rIlOCiKpgADVWXc4FQwgEfHPB5AiByTixIDUCYJx+wJoqdIcJV5VOAWygEZcF7BToT2upFDKtuz2BrAtf8v3mju972cBJX2exso6ok6N64BhOtM11xXXPBz6v6340PcuO+DZfaJkzWqgqY3L64Bqaaz0ZV45Mkc308dG2kd97FLSaq4v317gazr5HeCLRoACFTxbfeBw+oDhWYYj4/rw+30H+rb/VMPxXQsbKnbuABqJOi4vogyRiRB5/XNvvz3zytFTDEf61eF9b0dCKTS36c4afymTsgLeQ9Ly13X/aYnzE1Uy6PV7679trNy1xe+tKwPqAH/0Vla0qw65gH7AFeG/Y3Uy9P45o0bm3PTIaplM6lTK9jWf/OBUyQcrpdXaTIyfsXQb9QcLaAn7vJd2vxY5XxBpo8pwDmgFGsLKx1oeh8OVmUUSLXUlrWZPzbdLag9v+BjUqfDzepKAyDDcZbznGHG+1NmqSKHpVlfbadJqLW+o2LHobNX+PUB1WPkfnHwYyTmX6lI7Lehr3F576NM/+T3V3wH17f2w1xkg2ggXuSvga6p8p+bgmpVKWpXAmVh+2AsNEKogdYYAJa0GT03J4obyf60HTgKe6PTTqw0QOpcQ3wXs4LlDZyq2FXrrS4uBmjDxuCw3G5eIgA46yeC5ho11pWsWW35PWTibBC4Xf+9eBLRPg0q2+s5UvHG6bMNqJYPHw7nXutxZYvIIiMoCSgbrPVX/fv7syS+3AKfC5MOmF4iRpP6RjrId8O5vrNhS1NpUWQLUholUr6muXEoatP3emrWNR9e/avk9R8P+HuxNypPkrk93pGdnK0VtXemaN6UdOHo55vdE5b/0NKx+K4AxtAAAAABJRU5ErkJggg==\", \"description\":\"Easily share the web to your Facebook friends.\",\"author\":\"Facebook\",\"homepageURL\":\"https://www.facebook.com\",\"builtin\":\"true\",\"version\":1}");
 
 pref("social.sidebar.unload_timeout_ms", 10000);
--- a/browser/base/content/browser-loop.js
+++ b/browser/base/content/browser-loop.js
@@ -31,12 +31,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                            "about:looppanel", null, callback);
     },
 
     /**
      * Triggers the initialization of the loop service.  Called by
      * delayedStartup.
      */
     initialize: function() {
+      if (!Services.prefs.getBoolPref("loop.enabled")) {
+        CustomizableUI.getWidget("loop-call-button").forWindow(window).node.hidden = true;
+        return;
+      }
+
       MozLoopService.initialize();
     },
   };
 })();
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -167,19 +167,17 @@ let gInitialPages = [
   "about:sessionrestore"
 ];
 
 #include browser-addons.js
 #include browser-customization.js
 #include browser-feeds.js
 #include browser-fullScreen.js
 #include browser-fullZoom.js
-#ifdef MOZ_LOOP
 #include browser-loop.js
-#endif
 #include browser-places.js
 #include browser-plugins.js
 #include browser-safebrowsing.js
 #include browser-social.js
 #include browser-tabPreviews.js
 #include browser-tabview.js
 #include browser-thumbnails.js
 #include browser-webrtcUI.js
@@ -1183,19 +1181,17 @@ var gBrowserInit = {
     gSyncUI.init();
     gFxAccounts.init();
 #endif
 
 #ifdef MOZ_DATA_REPORTING
     gDataNotificationInfoBar.init();
 #endif
 
-#ifdef MOZ_LOOP
     LoopUI.initialize();
-#endif
 
     gBrowserThumbnails.init();
 
     // Add Devtools menuitems and listeners
     gDevToolsBrowser.registerBrowserWindow(window);
 
     window.addEventListener("mousemove", MousePosTracker, false);
     window.addEventListener("dragover", MousePosTracker, false);
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -262,29 +262,27 @@
            side="right"
            type="arrow"
            hidden="true"
            flip="slide"
            rolluponmousewheel="true"
            noautofocus="true"
            position="topcenter topright"/>
 
-#ifdef MOZ_LOOP
     <panel id="loop-notification-panel"
            class="loop-panel social-panel"
            type="arrow"
            hidden="true"
            noautofocus="true"/>
 
     <panel id="loop-panel"
            class="loop-panel social-panel"
            type="arrow"
            orient="horizontal"
            hidden="true"/>
-#endif
 
     <menupopup id="toolbar-context-menu"
                onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator'));">
       <menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
                 accesskey="&customizeMenu.moveToPanel.accesskey;"
                 label="&customizeMenu.moveToPanel.label;"
                 contexttype="toolbaritem"
                 class="customize-context-moveToPanel"/>
@@ -668,21 +666,17 @@
            Should you need to add items to the toolbar here, make sure to also add them
            to the default placements of buttons in CustomizableUI.jsm, so the
            customization code doesn't get confused.
       -->
     <toolbar id="nav-bar" class="toolbar-primary chromeclass-toolbar"
              aria-label="&navbarCmd.label;"
              fullscreentoolbar="true" mode="icons" customizable="true"
              iconsize="small"
-#ifdef MOZ_LOOP
              defaultset="urlbar-container,search-container,webrtc-status-button,bookmarks-menu-button,downloads-button,home-button,loop-call-button,social-share-button,social-toolbar-item"
-#else
-             defaultset="urlbar-container,search-container,webrtc-status-button,bookmarks-menu-button,downloads-button,home-button,social-share-button,social-toolbar-item"
-#endif
              customizationtarget="nav-bar-customization-target"
              overflowable="true"
              overflowbutton="nav-bar-overflow-button"
              overflowtarget="widget-overflow-list"
              overflowpanel="widget-overflow"
              context="toolbar-context-menu">
 
       <hbox id="nav-bar-customization-target" flex="1">
@@ -806,33 +800,16 @@
                        tooltiptext="&webrtcIndicatorButton.tooltip;"
                        cui-areatype="toolbar"
                        overflows="false">
           <menupopup onpopupshowing="WebrtcIndicator.fillPopup(this);"
                      onpopuphiding="WebrtcIndicator.clearPopup(this);"
                      oncommand="WebrtcIndicator.menuCommand(event.target);"/>
         </toolbarbutton>
 
-#ifdef MOZ_LOOP
-        <!-- XXX Bug 1013989 will provide a label for the button -->
-        <!-- This uses badged to be compatible with the social api code it shares.
-             We may also want it to be badged in the future, for notification
-             purposes. -->
-        <toolbarbutton id="loop-call-button"
-                       class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       persist="class"
-                       type="badged"
-                       removable="true"
-                       tooltiptext="&loopCallButton.tooltip;"
-                       oncommand="LoopUI.openCallPanel(event);"
-                       cui-areatype="toolbar"
-                       >
-        </toolbarbutton>
-#endif
-
         <toolbarbutton id="bookmarks-menu-button"
                        class="toolbarbutton-1 chromeclass-toolbar-additional"
                        persist="class"
                        removable="true"
                        type="menu-button"
                        label="&bookmarksMenuButton.label;"
                        tooltip="dynamic-shortcut-tooltip"
                        anchor="dropmarker"
@@ -952,16 +929,31 @@
                        ondragenter="homeButtonObserver.onDragOver(event)"
                        ondrop="homeButtonObserver.onDrop(event)"
                        ondragexit="homeButtonObserver.onDragExit(event)"
                        key="goHome"
                        onclick="BrowserGoHome(event);"
                        cui-areatype="toolbar"
                        aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
 
+        <!-- XXX Bug 1013989 will provide a label for the button -->
+        <!-- This uses badged to be compatible with the social api code it shares.
+             We may also want it to be badged in the future, for notification
+             purposes. -->
+        <toolbarbutton id="loop-call-button"
+                       class="toolbarbutton-1 chromeclass-toolbar-additional"
+                       persist="class"
+                       type="badged"
+                       removable="true"
+                       tooltiptext="&loopCallButton.tooltip;"
+                       oncommand="LoopUI.openCallPanel(event);"
+                       cui-areatype="toolbar"
+                       >
+        </toolbarbutton>
+
 
         <toolbarbutton id="social-share-button"
                        class="toolbarbutton-1 chromeclass-toolbar-additional"
                        label="&sharePageCmd.label;"
                        tooltiptext="&sharePageCmd.label;"
                        cui-areatype="toolbar"
                        removable="true"
                        hidden="true"
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -95,30 +95,28 @@ static RedirEntry kRedirMap[] = {
     nsIAboutModule::ALLOW_SCRIPT },
 #endif
   { "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
   { "app-manager", "chrome://browser/content/devtools/app-manager/index.xul",
     nsIAboutModule::ALLOW_SCRIPT },
   { "customizing", "chrome://browser/content/customizableui/aboutCustomizing.xul",
     nsIAboutModule::ALLOW_SCRIPT },
-#ifdef MOZ_LOOP
   { "loopconversation", "chrome://browser/content/loop/conversation.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT |
     nsIAboutModule::ENABLE_INDEXED_DB },
   { "looppanel", "chrome://browser/content/loop/panel.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT |
     nsIAboutModule::ENABLE_INDEXED_DB,
     // Shares an IndexedDB origin with about:loopconversation.
     "loopconversation" },
-#endif
 };
 static const int kRedirTotal = ArrayLength(kRedirMap);
 
 static nsAutoCString
 GetAboutModuleName(nsIURI *aURI)
 {
   nsAutoCString path;
   aURI->GetPath(path);
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -107,20 +107,18 @@ static const mozilla::Module::ContractID
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "accounts", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_HEALTHREPORT
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "app-manager", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "customizing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
-#ifdef MOZ_LOOP
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "looppanel", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "loopconversation", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
-#endif
 #if defined(XP_WIN)
     { NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
 #elif defined(XP_MACOSX)
     { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
 #endif
     { nullptr }
 };
 
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -136,19 +136,17 @@
                      label="&showAllBookmarks2.label;"
                      class="subviewbutton panel-subview-footer"
                      command="Browser:ShowAllBookmarks"
                      onclick="PanelUI.hide();"/>
     </panelview>
 
     <panelview id="PanelUI-socialapi" flex="1"/>
 
-#ifdef MOZ_LOOP
     <panelview id="PanelUI-loopapi" flex="1"/>
-#endif
 
     <panelview id="PanelUI-feeds" flex="1" oncommand="FeedHandler.subscribeToFeed(null, event);">
       <label value="&feedsMenu.label;" class="panel-subview-header"/>
     </panelview>
 
     <panelview id="PanelUI-helpView" flex="1" class="PanelUI-subView">
       <label value="&helpMenu.label;" class="panel-subview-header"/>
       <vbox id="PanelUI-helpItems" class="panel-subview-body"/>
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -201,19 +201,17 @@ let CustomizableUIInternal = {
       overflowable: true,
       defaultPlacements: [
         "urlbar-container",
         "search-container",
         "webrtc-status-button",
         "bookmarks-menu-button",
         "downloads-button",
         "home-button",
-#ifdef MOZ_LOOP
         "loop-call-button",
-#endif
         "social-share-button",
       ],
       defaultCollapsed: false,
     }, true);
 #ifndef XP_MACOSX
     this.registerArea(CustomizableUI.AREA_MENUBAR, {
       legacy: true,
       type: CustomizableUI.TYPE_TOOLBAR,
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -444,16 +444,21 @@ let MozLoopServiceInternal = {
  * Public API
  */
 this.MozLoopService = {
   /**
    * Initialized the loop service, and starts registration with the
    * push and loop servers.
    */
   initialize: function() {
+    // Don't do anything if loop is not enabled.
+    if (!Services.prefs.getBoolPref("loop.enabled")) {
+      return;
+    }
+
     // If expiresTime is in the future then kick-off registration.
     if (MozLoopServiceInternal.urlExpiryTimeIsInFuture()) {
       this._startInitializeTimer();
     }
   },
 
   /**
    * Internal function, exposed for testing purposes only. Used to start the
@@ -476,16 +481,21 @@ this.MozLoopService = {
    * with the Loop server. It will return early if already registered.
    *
    * @param {Object} mockPushHandler Optional, test-only mock push handler. Used
    *                                 to allow mocking of the MozLoopPushHandler.
    * @returns {Promise} a promise that is resolved with no params on completion, or
    *          rejected with an error code or string.
    */
   register: function(mockPushHandler) {
+    // Don't do anything if loop is not enabled.
+    if (!Services.prefs.getBoolPref("loop.enabled")) {
+      throw new Error("Loop is not enabled");
+    }
+
     return MozLoopServiceInternal.promiseRegisteredWithServers(mockPushHandler);
   },
 
   /**
    * Used to note a call url expiry time. If the time is later than the current
    * latest expiry time, then the stored expiry time is increased. For times
    * sooner, this function is a no-op; this ensures we always have the latest
    * expiry time for a url.
--- a/browser/components/loop/content/js/client.js
+++ b/browser/components/loop/content/js/client.js
@@ -30,23 +30,16 @@ loop.Client = (function($) {
       this.mozLoop = navigator.mozLoop;
     }
 
     this.settings = settings;
   }
 
   Client.prototype = {
     /**
-     * Converts from hours to seconds
-     */
-    _hoursToSeconds: function(value) {
-      return value * 60 * 60;
-    },
-
-    /**
      * Validates a data object to confirm it has the specified properties.
      *
      * @param  {Object} The data object to verify
      * @param  {Array} The list of properties to verify within the object
      * @return This returns either the specific property if only one
      *         property is specified, or it returns all properties
      */
     _validate: function(data, properties) {
@@ -119,18 +112,17 @@ loop.Client = (function($) {
           // XXX Support an alternate call_url property for
           // backwards compatibility whilst we switch over servers.
           // Bug 1033988 will want to remove these two lines.
           if (urlData.call_url)
             urlData.callUrl = urlData.call_url;
 
           cb(null, this._validate(urlData, expectedCallUrlProperties));
 
-          var expiresHours = this._hoursToSeconds(urlData.expiresAt);
-          this.mozLoop.noteCallUrlExpiry(expiresHours);
+          this.mozLoop.noteCallUrlExpiry(urlData.expiresAt);
         } catch (err) {
           console.log("Error requesting call info", err);
           cb(err);
         }
       });
     },
 
     /**
--- a/browser/components/loop/content/shared/js/models.js
+++ b/browser/components/loop/content/shared/js/models.js
@@ -9,16 +9,17 @@ loop.shared = loop.shared || {};
 loop.shared.models = (function() {
   "use strict";
 
   /**
    * Conversation model.
    */
   var ConversationModel = Backbone.Model.extend({
     defaults: {
+      connected:    false,     // Session connected flag
       ongoing:      false,     // Ongoing call flag
       callerId:     undefined, // Loop caller id
       loopToken:    undefined, // Loop conversation token
       loopVersion:  undefined, // Loop version for /calls/ information. This
                                // is the version received from the push
                                // notification and is used by the server to
                                // determine the pending calls
       sessionId:    undefined, // OT session id
@@ -34,30 +35,52 @@ loop.shared.models = (function() {
 
     /**
      * SDK session object.
      * @type {XXX}
      */
     session: undefined,
 
     /**
+     * Pending call timeout value.
+     * @type {Number}
+     */
+    pendingCallTimeout: undefined,
+
+    /**
+     * Pending call timer.
+     * @type {Number}
+     */
+    _pendingCallTimer: undefined,
+
+    /**
      * Constructor.
      *
-     * Required options:
-     * - {OT} sdk: SDK object.
+     * Options:
+     *
+     * Required:
+     * - {OT} sdk: OT SDK object.
+     *
+     * Optional:
+     * - {Number} pendingCallTimeout: Pending call timeout in milliseconds
+     *                                (default: 20000).
      *
      * @param  {Object} attributes Attributes object.
      * @param  {Object} options    Options object.
      */
     initialize: function(attributes, options) {
       options = options || {};
       if (!options.sdk) {
         throw new Error("missing required sdk");
       }
       this.sdk = options.sdk;
+      this.pendingCallTimeout = options.pendingCallTimeout || 20000;
+
+      // Ensure that any pending call timer is cleared on disconnect/error
+      this.on("session:ended session:error", this._clearPendingCallTimer, this);
     },
 
     /**
      * Initiates a conversation, requesting call session information to the Loop
      * server and updates appropriately the current model attributes with the
      * data.
      *
      * Available options:
@@ -74,31 +97,48 @@ loop.shared.models = (function() {
      *
      * - `session:ready` when the session information have been successfully
      *   retrieved from the server;
      * - `session:error` when the request failed.
      *
      * @param {Object} options Options object
      */
     initiate: function(options) {
+      options = options || {};
+
+      // Outgoing call has never reached destination, closing - see bug 1020448
+      function handleOutgoingCallTimeout() {
+        /*jshint validthis:true */
+        if (!this.get("ongoing")) {
+          this.trigger("timeout").endSession();
+        }
+      }
+
       function handleResult(err, sessionData) {
         /*jshint validthis:true */
+        this._clearPendingCallTimer();
+
         if (err) {
           this.trigger("session:error", new Error(
             "Retrieval of session information failed: HTTP " + err));
           return;
         }
 
-        // XXX For incoming calls we might have more than one call queued.
-        // For now, we'll just assume the first call is the right information.
-        // We'll probably really want to be getting this data from the
-        // background worker on the desktop client.
-        // Bug 990714 should fix this.
-        if (!options.outgoing)
+        if (options.outgoing) {
+          // Setup pending call timeout.
+          this._pendingCallTimer = setTimeout(
+            handleOutgoingCallTimeout.bind(this), this.pendingCallTimeout);
+        } else {
+          // XXX For incoming calls we might have more than one call queued.
+          // For now, we'll just assume the first call is the right information.
+          // We'll probably really want to be getting this data from the
+          // background worker on the desktop client.
+          // Bug 990714 should fix this.
           sessionData = sessionData[0];
+        }
 
         this.setReady(sessionData);
       }
 
       if (options.outgoing) {
         options.client.requestCallInfo(this.get("loopToken"), options.callType,
           handleResult.bind(this));
       }
@@ -151,18 +191,27 @@ loop.shared.models = (function() {
                            this._onConnectCompletion.bind(this));
     },
 
     /**
      * Ends current session.
      */
     endSession: function() {
       this.session.disconnect();
-      this.once("session:ended", this.stopListening, this);
-      this.set("ongoing", false);
+      this.set("ongoing", false)
+          .once("session:ended", this.stopListening, this);
+    },
+
+    /**
+     * Clears current pending call timer, if any.
+     */
+    _clearPendingCallTimer: function() {
+      if (this._pendingCallTimer) {
+        clearTimeout(this._pendingCallTimer);
+      }
     },
 
     /**
      * Manages connection status
      * triggers apropriate event for connection error/success
      * http://tokbox.com/opentok/tutorials/connect-session/js/
      * http://tokbox.com/opentok/tutorials/hello-world/js/
      * http://tokbox.com/opentok/libraries/client/js/reference/SessionConnectEvent.html
@@ -170,62 +219,68 @@ loop.shared.models = (function() {
      * @param {error|null} error
      */
     _onConnectCompletion: function(error) {
       if (error) {
         this.trigger("session:connection-error", error);
         this.endSession();
       } else {
         this.trigger("session:connected");
-        this.set("ongoing", true);
+        this.set("connected", true);
       }
     },
 
     /**
      * New created streams are available.
      * http://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html
      *
      * @param  {StreamEvent} event
      */
     _streamCreated: function(event) {
-      this.trigger("session:stream-created", event);
+      this.set("ongoing", true)
+          .trigger("session:stream-created", event);
     },
 
     /**
      * Local user hung up.
      * http://tokbox.com/opentok/libraries/client/js/reference/SessionDisconnectEvent.html
      *
      * @param  {SessionDisconnectEvent} event
      */
     _sessionDisconnected: function(event) {
-      this.trigger("session:ended");
-      this.set("ongoing", false);
+      this.set("connected", false)
+          .set("ongoing", false)
+          .trigger("session:ended");
     },
 
     /**
      * Peer hung up. Disconnects local session.
      * http://tokbox.com/opentok/libraries/client/js/reference/ConnectionEvent.html
      *
      * @param  {ConnectionEvent} event
      */
     _connectionDestroyed: function(event) {
-      this.trigger("session:peer-hungup", {
-        connectionId: event.connection.connectionId
-      });
+      this.set("connected", false)
+          .set("ongoing", false)
+          .trigger("session:peer-hungup", {
+            connectionId: event.connection.connectionId
+          });
       this.endSession();
     },
 
     /**
      * Network was disconnected.
      * http://tokbox.com/opentok/libraries/client/js/reference/ConnectionEvent.html
      *
      * @param {ConnectionEvent} event
      */
     _networkDisconnected: function(event) {
-      this.trigger("session:network-disconnected");
+      this.set("connected", false)
+          .set("ongoing", false)
+          .trigger("session:network-disconnected");
       this.endSession();
     },
   });
 
   /**
    * Notification model.
    */
   var NotificationModel = Backbone.Model.extend({
--- a/browser/components/loop/standalone/Makefile
+++ b/browser/components/loop/standalone/Makefile
@@ -1,13 +1,14 @@
 # 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/.
 
 LOOP_SERVER_URL := $(shell echo $${LOOP_SERVER_URL-http://localhost:5000})
+LOOP_PENDING_CALL_TIMEOUT := $(shell echo $${LOOP_PENDING_CALL_TIMEOUT-20000})
 NODE_LOCAL_BIN=./node_modules/.bin
 
 install:
 	@npm install
 
 test:
 	@echo "Not implemented yet."
 
@@ -16,9 +17,12 @@ lint:
 
 runserver: config
 	@node server.js
 
 frontend:
 	@echo "Not implemented yet."
 
 config:
-	@echo "var loop = loop || {};\nloop.config = {serverUrl: '`echo $(LOOP_SERVER_URL)`'};" > content/config.js
+	@echo "var loop = loop || {};" > content/config.js
+	@echo "loop.config = loop.config || {};" >> content/config.js
+	@echo "loop.config.serverUrl          = '`echo $(LOOP_SERVER_URL)`';" >> content/config.js
+	@echo "loop.config.pendingCallTimeout = `echo $(LOOP_PENDING_CALL_TIMEOUT)`;" >> content/config.js
--- a/browser/components/loop/standalone/README.md
+++ b/browser/components/loop/standalone/README.md
@@ -11,21 +11,25 @@ Installation
 
     $ make install
 
 Configuration
 -------------
 
 You will need to generate a configuration file, you can do so with:
 
-	$ make config
+    $ make config
+
+It will read the configuration from the following env variables and generate the
+appropriate configuration file:
 
-It will read the configuration from the `LOOP_SERVER_URL` env variable and
-generate the appropriate configuration file. This setting defines the root url
-of the loop server, without trailing slash.
+- `LOOP_SERVER_URL` defines the root url of the loop server, without trailing
+  slash (default: `http://localhost:5000`).
+- `LOOP_PENDING_CALL_TIMEOUT` defines the amount of time a pending outgoing call
+  should be considered timed out, in milliseconds (default: `20000`).
 
 Usage
 -----
 
 For development, run a local static file server:
 
     $ make runserver
 
--- a/browser/components/loop/standalone/content/js/webapp.js
+++ b/browser/components/loop/standalone/content/js/webapp.js
@@ -114,16 +114,18 @@ loop.webapp = (function($, _, OT) {
       "unsupportedBrowser":  "unsupportedBrowser",
       "call/ongoing/:token": "loadConversation",
       "call/:token":         "initiate"
     },
 
     initialize: function() {
       // Load default view
       this.loadView(new HomeView());
+
+      this.listenTo(this._conversation, "timeout", this._onTimeout);
     },
 
     /**
      * @override {loop.shared.router.BaseConversationRouter.startCall}
      */
     startCall: function() {
       if (!this._conversation.get("loopToken")) {
         this._notifier.errorL10n("missing_conversation_info");
@@ -141,16 +143,20 @@ loop.webapp = (function($, _, OT) {
     endCall: function() {
       var route = "home";
       if (this._conversation.get("loopToken")) {
         route = "call/" + this._conversation.get("loopToken");
       }
       this.navigate(route, {trigger: true});
     },
 
+    _onTimeout: function() {
+      this._notifier.errorL10n("call_timeout_notification_text");
+    },
+
     /**
      * Default entry point.
      */
     home: function() {
       this.loadView(new HomeView());
     },
 
     unsupportedDevice: function() {
@@ -208,18 +214,21 @@ loop.webapp = (function($, _, OT) {
   };
 
   /**
    * App initialization.
    */
   function init() {
     var helper = new WebappHelper();
     router = new WebappRouter({
-      conversation: new sharedModels.ConversationModel({}, {sdk: OT}),
-      notifier: new sharedViews.NotificationListView({el: "#messages"})
+      notifier: new sharedViews.NotificationListView({el: "#messages"}),
+      conversation: new sharedModels.ConversationModel({}, {
+        sdk: OT,
+        pendingCallTimeout: loop.config.pendingCallTimeout
+      })
     });
     Backbone.history.start();
     if (helper.isIOS(navigator.platform)) {
       router.navigate("unsupportedDevice", {trigger: true});
     } else if (!OT.checkSystemRequirements()) {
       router.navigate("unsupportedBrowser", {trigger: true});
     }
   }
--- a/browser/components/loop/standalone/content/l10n/data.ini
+++ b/browser/components/loop/standalone/content/l10n/data.ini
@@ -1,10 +1,11 @@
 [en]
 call_has_ended=Your call has ended.
+call_timeout_notification_text=Your call did not go through.
 missing_conversation_info=Missing conversation information.
 network_disconnected=The network connection terminated abruptly.
 peer_ended_conversation=Your peer ended the conversation.
 unable_retrieve_call_info=Unable to retrieve conversation information.
 hangup_button_title=Hangup
 mute_local_audio_button_title=Mute your audio
 unmute_local_audio_button_title=Unute your audio
 mute_local_video_button_title=Mute your video
@@ -16,16 +17,17 @@ powered_by_webrtc=The audio and video co
 use_latest_firefox.innerHTML=To use Loop, please use the latest version of <a href="{{ff_url}}">Firefox</a>.
 incompatible_device=Incompatible device
 sorry_device_unsupported=Sorry, Loop does not currently support your device.
 use_firefox_windows_mac_linux=Please open this page using the latest Firefox on Windows, Android, Mac or Linux.
 connection_error_see_console_notification=Call failed; see console for details.
 
 [fr]
 call_has_ended=L'appel est terminé.
+call_timeout_notification_text=Votre appel n'a pas abouti.
 missing_conversation_info=Informations de communication manquantes.
 network_disconnected=La connexion réseau semble avoir été interrompue.
 peer_ended_conversation=Votre correspondant a mis fin à la communication.
 unable_retrieve_call_info=Impossible de récupérer les informations liées à cet appel.
 hangup_button_title=Terminer l'appel
 mute_local_audio_button_title=Couper la diffusion audio
 unmute_local_audio_button_title=Reprendre la diffusion audio
 mute_local_video_button_title=Couper la diffusion vidéo
--- a/browser/components/loop/test/desktop-local/client_test.js
+++ b/browser/components/loop/test/desktop-local/client_test.js
@@ -86,30 +86,29 @@ describe("loop.Client", function() {
         client.requestCallUrl("foo", callback);
 
         sinon.assert.calledWithExactly(callback, null, callUrlData);
       });
 
       it("should note the call url expiry when the request succeeds", function() {
         var callUrlData = {
           "callUrl": "fakeCallUrl",
-          "expiresAt": 60
+          "expiresAt": 6000
         };
 
         // Sets up the hawkRequest stub to trigger the callback with no error
         // and the url.
         hawkRequestStub.callsArgWith(3, null,
                                      JSON.stringify(callUrlData));
 
         client.requestCallUrl("foo", callback);
 
-        // expiresAt is in hours, and noteCallUrlExpiry wants seconds.
         sinon.assert.calledOnce(mozLoop.noteCallUrlExpiry);
         sinon.assert.calledWithExactly(mozLoop.noteCallUrlExpiry,
-          60 * 60 * 60);
+          6000);
       });
 
       it("should send an error when the request fails", function() {
         // Sets up the hawkRequest stub to trigger the callback with
         // an error
         hawkRequestStub.callsArgWith(3, fakeErrorRes);
 
         client.requestCallUrl("foo", callback);
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -91,17 +91,20 @@ describe("loop.conversation", function()
       sinon.assert.calledOnce(Backbone.history.start);
     });
   });
 
   describe("ConversationRouter", function() {
     var conversation;
 
     beforeEach(function() {
-      conversation = new loop.shared.models.ConversationModel({}, {sdk: {}});
+      conversation = new loop.shared.models.ConversationModel({}, {
+        sdk: {},
+        pendingCallTimeout: 1000
+      });
       sandbox.stub(conversation, "initiate");
     });
 
     describe("Routes", function() {
       var router;
 
       beforeEach(function() {
         router = new ConversationRouter({
@@ -280,17 +283,20 @@ describe("loop.conversation", function()
       });
     });
   });
 
   describe("IncomingCallView", function() {
     var conversation, view;
 
     beforeEach(function() {
-      conversation = new loop.shared.models.ConversationModel({}, {sdk: {}});
+      conversation = new loop.shared.models.ConversationModel({}, {
+        sdk: {},
+        pendingCallTimeout: 1000
+      });
       view = new loop.conversation.IncomingCallView({model: conversation});
     });
 
     describe("#handleAccept", function() {
       it("should trigger an 'accept' conversation model event" ,
         function(done) {
           conversation.once("accept", function() {
             done();
--- a/browser/components/loop/test/shared/models_test.js
+++ b/browser/components/loop/test/shared/models_test.js
@@ -9,16 +9,17 @@ var expect = chai.expect;
 describe("loop.shared.models", function() {
   "use strict";
 
   var sharedModels = loop.shared.models,
       sandbox, fakeXHR, requests = [], fakeSDK, fakeSession, fakeSessionData;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
+    sandbox.useFakeTimers();
     fakeXHR = sandbox.useFakeXMLHttpRequest();
     requests = [];
     // https://github.com/cjohansen/Sinon.JS/issues/393
     fakeXHR.xhr.onCreate = function(xhr) {
       requests.push(xhr);
     };
     fakeSessionData = {
       sessionId:    "sessionId",
@@ -41,38 +42,52 @@ describe("loop.shared.models", function(
   afterEach(function() {
     sandbox.restore();
   });
 
   describe("ConversationModel", function() {
     describe("#initialize", function() {
       it("should require a sdk option", function() {
         expect(function() {
-          new sharedModels.ConversationModel();
+          new sharedModels.ConversationModel({}, {});
         }).to.Throw(Error, /missing required sdk/);
       });
+
+      it("should accept a pendingCallTimeout option", function() {
+        expect(new sharedModels.ConversationModel({}, {
+          sdk: {},
+          pendingCallTimeout: 1000
+        }).pendingCallTimeout).eql(1000);
+      });
     });
 
     describe("constructed", function() {
       var conversation, fakeClient, fakeBaseServerUrl,
           requestCallInfoStub, requestCallsInfoStub;
 
       beforeEach(function() {
-        conversation = new sharedModels.ConversationModel({}, {sdk: fakeSDK});
+        conversation = new sharedModels.ConversationModel({}, {
+          sdk: fakeSDK,
+          pendingCallTimeout: 1000
+        });
         conversation.set("loopToken", "fakeToken");
         fakeBaseServerUrl = "http://fakeBaseServerUrl";
         fakeClient = {
           requestCallInfo: sandbox.stub(),
           requestCallsInfo: sandbox.stub()
         };
         requestCallInfoStub = fakeClient.requestCallInfo;
         requestCallsInfoStub = fakeClient.requestCallsInfo;
       });
 
       describe("#initiate", function() {
+        beforeEach(function() {
+          sandbox.stub(conversation, "endSession");
+        });
+
         it("call requestCallInfo on the client for outgoing calls",
           function() {
             conversation.initiate({
               client: fakeClient,
               outgoing: true,
               callType: "audio"
             });
 
@@ -134,16 +149,45 @@ describe("loop.shared.models", function(
           conversation.on("session:error", function(err) {
             expect(err.message).to.match(/failed: HTTP 400 Bad Request; fake/);
             done();
           }).initiate({
             client: fakeClient,
             outgoing: true
           });
         });
+
+        it("should end the session on outgoing call timeout", function() {
+          requestCallInfoStub.callsArgWith(2, null, fakeSessionData);
+
+          conversation.initiate({
+            client: fakeClient,
+            outgoing: true
+          });
+
+          sandbox.clock.tick(1001);
+
+          sinon.assert.calledOnce(conversation.endSession);
+        });
+
+        it("should trigger a `timeout` event on outgoing call timeout",
+          function(done) {
+            requestCallInfoStub.callsArgWith(2, null, fakeSessionData);
+
+            conversation.once("timeout", function() {
+              done();
+            });
+
+            conversation.initiate({
+              client: fakeClient,
+              outgoing: true
+            });
+
+            sandbox.clock.tick(1001);
+          });
       });
 
       describe("#setReady", function() {
         it("should update conversation session information", function() {
           conversation.setReady(fakeSessionData);
 
           expect(conversation.get("sessionId")).eql("sessionId");
           expect(conversation.get("sessionToken")).eql("sessionToken");
@@ -156,18 +200,21 @@ describe("loop.shared.models", function(
           }).setReady(fakeSessionData);
         });
       });
 
       describe("#startSession", function() {
         var model;
 
         beforeEach(function() {
+          sandbox.stub(sharedModels.ConversationModel.prototype,
+                       "_clearPendingCallTimer");
           model = new sharedModels.ConversationModel(fakeSessionData, {
-            sdk: fakeSDK
+            sdk: fakeSDK,
+            pendingCallTimeout: 1000
           });
           model.startSession();
         });
 
         it("should start a session", function() {
           sinon.assert.calledOnce(fakeSDK.initSession);
         });
 
@@ -177,26 +224,26 @@ describe("loop.shared.models", function(
           model.startSession();
 
           sinon.assert.calledOnce(fakeSession.connect);
           sinon.assert.calledWithExactly(fakeSession.connect,
                         sinon.match.string, sinon.match.string,
                         sinon.match.func);
         });
 
-        it("should set ongoing to true when no error is called back",
+        it("should set connected to true when no error is called back",
             function() {
               fakeSession.connect = function(key, token, cb) {
                 cb(null);
               };
-              sinon.stub(model, "set");
+              sandbox.stub(model, "set");
 
               model.startSession();
 
-              sinon.assert.calledWith(model.set, "ongoing", true);
+              sinon.assert.calledWith(model.set, "connected", true);
             });
 
         it("should trigger session:connected when no error is called back",
             function() {
               fakeSession.connect = function(key, token, cb) {
                 cb(null);
               };
               sandbox.stub(model, "trigger");
@@ -210,17 +257,17 @@ describe("loop.shared.models", function(
 
           it("should trigger a fail event when an error is called back",
             function() {
               fakeSession.connect = function(key, token, cb) {
                 cb({
                   error: true
                 });
               };
-              sinon.stub(model, "endSession");
+              sandbox.stub(model, "endSession");
 
               model.startSession();
 
               sinon.assert.calledOnce(model.endSession);
               sinon.assert.calledWithExactly(model.endSession);
             });
 
           it("should trigger session:connection-error event when an error is" +
@@ -234,32 +281,59 @@ describe("loop.shared.models", function(
 
               model.startSession();
 
               sinon.assert.calledOnce(model.trigger);
               sinon.assert.calledWithExactly(model.trigger,
                           "session:connection-error", sinon.match.object);
             });
 
+          it("should set the connected attr to true on connection completed",
+            function() {
+              fakeSession.connect = function(key, token, cb) {
+                cb();
+              };
+
+              model.startSession();
+
+              expect(model.get("connected")).eql(true);
+            });
+
           it("should trigger a session:ended event on sessionDisconnected",
             function(done) {
               model.once("session:ended", function(){ done(); });
 
               fakeSession.trigger("sessionDisconnected", {reason: "ko"});
             });
 
+          it("should set the connected attribute to false on sessionDisconnected",
+            function() {
+              fakeSession.trigger("sessionDisconnected", {reason: "ko"});
+
+              expect(model.get("connected")).eql(false);
+            });
+
           it("should set the ongoing attribute to false on sessionDisconnected",
-            function(done) {
-              model.once("session:ended", function() {
-                expect(model.get("ongoing")).eql(false);
-                done();
-              });
+            function() {
+              fakeSession.trigger("sessionDisconnected", {reason: "ko"});
+
+              expect(model.get("ongoing")).eql(false);
+            });
+
+          it("should clear a pending timer on session:ended", function() {
+            model.trigger("session:ended");
 
-              fakeSession.trigger("sessionDisconnected", {reason: "ko"});
-            });
+            sinon.assert.calledOnce(model._clearPendingCallTimer);
+          });
+
+          it("should clear a pending timer on session:error", function() {
+            model.trigger("session:error");
+
+            sinon.assert.calledOnce(model._clearPendingCallTimer);
+          });
 
           describe("connectionDestroyed event received", function() {
             var fakeEvent = {reason: "ko", connection: {connectionId: 42}};
 
             it("should trigger a session:peer-hungup model event",
               function(done) {
                 model.once("session:peer-hungup", function(event) {
                   expect(event.connectionId).eql(42);
@@ -299,27 +373,34 @@ describe("loop.shared.models", function(
         });
       });
 
       describe("#endSession", function() {
         var model;
 
         beforeEach(function() {
           model = new sharedModels.ConversationModel(fakeSessionData, {
-            sdk: fakeSDK
+            sdk: fakeSDK,
+            pendingCallTimeout: 1000
           });
           model.startSession();
         });
 
         it("should disconnect current session", function() {
           model.endSession();
 
           sinon.assert.calledOnce(fakeSession.disconnect);
         });
 
+        it("should set the connected attribute to false", function() {
+          model.endSession();
+
+          expect(model.get("connected")).eql(false);
+        });
+
         it("should set the ongoing attribute to false", function() {
           model.endSession();
 
           expect(model.get("ongoing")).eql(false);
         });
 
         it("should stop listening to session events once the session is " +
            "actually disconnected", function() {
--- a/browser/components/loop/test/shared/router_test.js
+++ b/browser/components/loop/test/shared/router_test.js
@@ -97,17 +97,20 @@ describe("loop.shared.router", function(
 
     beforeEach(function() {
       TestRouter = loop.shared.router.BaseConversationRouter.extend({
         startCall: sandbox.spy(),
         endCall: sandbox.spy()
       });
       conversation = new loop.shared.models.ConversationModel({
         loopToken: "fakeToken"
-      }, {sdk: {}});
+      }, {
+        sdk: {},
+        pendingCallTimeout: 1000
+      });
     });
 
     describe("#constructor", function() {
       it("should require a ConversationModel instance", function() {
         expect(function() {
           new TestRouter();
         }).to.Throw(Error, /missing required conversation/);
       });
--- a/browser/components/loop/test/shared/views_test.js
+++ b/browser/components/loop/test/shared/views_test.js
@@ -200,17 +200,18 @@ describe("loop.shared.views", function()
         publishAudio: sandbox.spy(),
         publishVideo: sandbox.spy()
       };
       fakeSDK = {
         initPublisher: sandbox.stub().returns(fakePublisher),
         initSession: sandbox.stub().returns(fakeSession)
       };
       model = new sharedModels.ConversationModel(fakeSessionData, {
-        sdk: fakeSDK
+        sdk: fakeSDK,
+        pendingCallTimeout: 1000
       });
     });
 
     describe("#componentDidMount", function() {
       it("should start a session", function() {
         sandbox.stub(model, "startSession");
 
         mountTestComponent({sdk: fakeSDK, model: model});
--- a/browser/components/loop/test/standalone/webapp_test.js
+++ b/browser/components/loop/test/standalone/webapp_test.js
@@ -18,20 +18,22 @@ describe("loop.webapp", function() {
     sandbox = sinon.sandbox.create();
     notifier = {
       notify: sandbox.spy(),
       warn: sandbox.spy(),
       warnL10n: sandbox.spy(),
       error: sandbox.spy(),
       errorL10n: sandbox.spy(),
     };
+    loop.config.pendingCallTimeout = 1000;
   });
 
   afterEach(function() {
     sandbox.restore();
+    delete loop.config.pendingCallTimeout;
   });
 
   describe("#init", function() {
     var WebappRouter;
 
     beforeEach(function() {
       WebappRouter = loop.webapp.WebappRouter;
       sandbox.stub(WebappRouter.prototype, "navigate");
@@ -64,17 +66,20 @@ describe("loop.webapp", function() {
                                      "unsupportedBrowser", {trigger: true});
     });
   });
 
   describe("WebappRouter", function() {
     var router, conversation;
 
     beforeEach(function() {
-      conversation = new sharedModels.ConversationModel({}, {sdk: {}});
+      conversation = new sharedModels.ConversationModel({}, {
+        sdk: {},
+        pendingCallTimeout: 1000
+      });
       router = new loop.webapp.WebappRouter({
         conversation: conversation,
         notifier: notifier
       });
       sandbox.stub(router, "loadView");
       sandbox.stub(router, "loadReactComponent");
       sandbox.stub(router, "navigate");
     });
@@ -248,32 +253,37 @@ describe("loop.webapp", function() {
         });
     });
   });
 
   describe("ConversationFormView", function() {
     var conversation;
 
     beforeEach(function() {
-      conversation = new sharedModels.ConversationModel({}, {sdk: {}});
+      conversation = new sharedModels.ConversationModel({}, {
+        sdk: {},
+        pendingCallTimeout: 1000});
     });
 
     describe("#initialize", function() {
       it("should require a conversation option", function() {
         expect(function() {
           new loop.webapp.WebappRouter();
         }).to.Throw(Error, /missing required conversation/);
       });
     });
 
     describe("#initiate", function() {
       var conversation, initiate, view, fakeSubmitEvent;
 
       beforeEach(function() {
-        conversation = new sharedModels.ConversationModel({}, {sdk: {}});
+        conversation = new sharedModels.ConversationModel({}, {
+          sdk: {},
+          pendingCallTimeout: 1000
+        });
         view = new loop.webapp.ConversationFormView({
           model: conversation,
           notifier: notifier
         });
         fakeSubmitEvent = {preventDefault: sinon.spy()};
         initiate = sinon.stub(conversation, "initiate");
       });
 
@@ -302,17 +312,20 @@ describe("loop.webapp", function() {
     });
 
     describe("Events", function() {
       var conversation, view;
 
       beforeEach(function() {
         conversation = new sharedModels.ConversationModel({
           loopToken: "fake"
-        }, {sdk: {}});
+        }, {
+          sdk: {},
+          pendingCallTimeout: 1000
+        });
         view = new loop.webapp.ConversationFormView({
           model: conversation,
           notifier: notifier
         });
       });
 
       it("should trigger a notication when a session:error model event is " +
          " received", function() {
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -5,31 +5,29 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 PARALLEL_DIRS += [
     'about',
     'customizableui',
     'dirprovider',
     'downloads',
     'feeds',
+    'loop',
     'places',
     'preferences',
     'privatebrowsing',
     'search',
     'sessionstore',
     'shell',
     'sidebar',
     'tabview',
     'translation',
     'migration',
 ]
 
-if CONFIG['MOZ_LOOP']:
-    PARALLEL_DIRS += ['loop']
-
 DIRS += ['build']
 
 XPIDL_SOURCES += [
     'nsIBrowserGlue.idl',
     'nsIBrowserHandler.idl',
 ]
 
 XPIDL_MODULE = 'browsercompsbase'
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1599,22 +1599,20 @@ richlistitem[type~="action"][actiontype=
   -moz-image-region: rect(28px, 28px, 42px, 14px);
 }
 
 /* Popup blocker button */
 #page-report-button {
   list-style-image: url("chrome://browser/skin/Info.png");
 }
 
-%ifdef MOZ_LOOP
 /* Loop */
 #loop-call-button {
   list-style-image: url("chrome://global/skin/loop/loop-call.png");
 }
-%endif
 
 /* social share panel */
 
 .social-share-frame {
   background: linear-gradient(to bottom, rgba(242,242,242,.99), rgba(242,242,242,.95));
   border-left: 1px solid #f8f8f8;
   width: 330px;
   height: 150px;
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2221,23 +2221,21 @@ richlistitem[type~="action"][actiontype=
   }
 
   #page-report-button:hover:active,
   #page-report-button[open="true"] {
     -moz-image-region: rect(0, 64px, 32px, 32px);
   }
 }
 
-%ifdef MOZ_LOOP
 /* Loop */
 /* XXX - probably need retina images here */
 #loop-call-button {
   list-style-image: url("chrome://global/skin/loop/loop-call.png");
 }
-%endif
 
 /* social share panel */
 .social-share-frame {
   background: linear-gradient(to bottom, rgba(242,242,242,.99), rgba(242,242,242,.95));
   border-left: 1px solid #f8f8f8;
   width: 330px;
   height: 150px;
   /* we resize our panels dynamically, make it look nice */
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1535,23 +1535,20 @@ richlistitem[type~="action"][actiontype=
   -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
 #page-report-button:hover:active,
 #page-report-button[open="true"] {
   -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
-
-%ifdef MOZ_LOOP
 /* Loop */
 #loop-call-button {
   list-style-image: url("chrome://global/skin/loop/loop-call.png");
 }
-%endif
 
 /* social share panel */
 
 #social-share-panel > iframe {
   background: linear-gradient(to bottom, #f0f4f7, #fafbfc);
   width: 300px;
   height: 150px;
 }
--- a/config/system-headers
+++ b/config/system-headers
@@ -1239,16 +1239,17 @@ wx/toolbar.h
 wx/wx.h
 wx/xrc/xmlres.h
 X11/cursorfont.h
 X11/extensions/Print.h
 X11/extensions/shape.h
 X11/extensions/scrnsaver.h
 X11/extensions/XShm.h
 X11/extensions/Xrender.h
+X11/extensions/Xfixes.h
 X11/extensions/Xdamage.h
 X11/extensions/Xcomposite.h
 X11/Intrinsic.h
 X11/keysymdef.h
 X11/keysym.h
 X11/Shell.h
 X11/StringDefs.h
 X11/Xatom.h
--- a/configure.in
+++ b/configure.in
@@ -3887,17 +3887,16 @@ VPX_AS_CONVERSION=
 VPX_ASM_SUFFIX=
 VPX_X86_ASM=
 VPX_ARM_ASM=
 LIBJPEG_TURBO_AS=
 LIBJPEG_TURBO_ASFLAGS=
 LIBJPEG_TURBO_X86_ASM=
 LIBJPEG_TURBO_X64_ASM=
 LIBJPEG_TURBO_ARM_ASM=
-MOZ_LOOP=$NIGHTLY_BUILD
 MOZ_PERMISSIONS=1
 MOZ_PLACES=1
 MOZ_SOCIAL=1
 MOZ_PREF_EXTENSIONS=1
 MOZ_PROFILELOCKING=1
 MOZ_REFLOW_PERF=
 MOZ_SAFE_BROWSING=
 MOZ_HELP_VIEWER=
@@ -8172,17 +8171,17 @@ if test "$MOZ_TREE_CAIRO"; then
     AC_SUBST(QUARTZ_FONT_FEATURE)
     AC_SUBST(PNG_FUNCTIONS_FEATURE)
     AC_SUBST(QT_SURFACE_FEATURE)
     AC_SUBST(TEE_SURFACE_FEATURE)
 
     MOZ_CAIRO_OSLIBS='${CAIRO_FT_OSLIBS}'
 
     if test "$MOZ_X11"; then
-        MOZ_CAIRO_OSLIBS="$MOZ_CAIRO_OSLIBS $XLDFLAGS -lXrender"
+        MOZ_CAIRO_OSLIBS="$MOZ_CAIRO_OSLIBS $XLDFLAGS -lXext -lXdamage -lXfixes -lXcomposite -lXrender"
     fi
 
     CAIRO_FEATURES_H=gfx/cairo/cairo/src/cairo-features.h
 else
     PKG_CHECK_MODULES(CAIRO, cairo >= $CAIRO_VERSION)
     MOZ_CAIRO_CFLAGS="$CAIRO_CFLAGS"
     MOZ_CAIRO_LIBS="$CAIRO_LIBS"
     PKG_CHECK_MODULES(CAIRO_TEE, cairo-tee >= $CAIRO_VERSION)
@@ -8345,22 +8344,16 @@ MOZ_ARG_DISABLE_BOOL(ctypes,
 [  --disable-ctypes        Disable js-ctypes],
     BUILD_CTYPES=,
     BUILD_CTYPES=1)
 AC_SUBST(BUILD_CTYPES)
 if test "$BUILD_CTYPES"; then
     AC_DEFINE(BUILD_CTYPES)
 fi
 
-dnl Build Loop if required
-AC_SUBST(MOZ_LOOP)
-if test "$MOZ_LOOP"; then
-  AC_DEFINE(MOZ_LOOP)
-fi
-
 dnl Build Places if required
 if test "$MOZ_PLACES"; then
   AC_DEFINE(MOZ_PLACES)
 fi
 
 dnl Build SocialAPI if required
 if test "$MOZ_SOCIAL"; then
   AC_DEFINE(MOZ_SOCIAL)
--- a/content/base/public/nsIContentSecurityPolicy.idl
+++ b/content/base/public/nsIContentSecurityPolicy.idl
@@ -12,17 +12,17 @@ interface nsIPrincipal;
 
 /**
  * nsIContentSecurityPolicy
  * Describes an XPCOM component used to model and enforce CSPs.  Instances of
  * this class may have multiple policies within them, but there should only be
  * one of these per document/principal.
  */
 
-[scriptable, uuid(15c409c5-ebf8-457c-a8dd-5b169ca0b218)]
+[scriptable, uuid(3e923bf6-a974-4f3b-91c4-b4fd48b37732)]
 interface nsIContentSecurityPolicy : nsISerializable
 {
 
   /**
    * Set to true when the CSP has been read in and parsed and is ready to
    * enforce.  This is a barrier for the nsDocument so it doesn't load any
    * sub-content until either it knows that a CSP is ready or will not be used.
    */
@@ -216,26 +216,14 @@ interface nsIContentSecurityPolicy : nsI
    */
   short shouldLoad(in nsContentPolicyType aContentType,
                    in nsIURI          aContentLocation,
                    in nsIURI          aRequestOrigin,
                    in nsISupports     aContext,
                    in ACString        aMimeTypeGuess,
                    in nsISupports     aExtra);
 
-  /**
-   * Delegate method called by the service when sub-elements of the protected
-   * document are being processed.  Given a bit of information about the request,
-   * decides whether or not the policy is satisfied.
-   */
-  short shouldProcess(in nsContentPolicyType   aContentType,
-                      in nsIURI          aContentLocation,
-                      in nsIURI          aRequestOrigin,
-                      in nsISupports     aContext,
-                      in ACString        aMimeType,
-                      in nsISupports     aExtra);
-
 %{ C++
 // nsIObserver topic to fire when the policy encounters a violation.
 #define CSP_VIOLATION_TOPIC "csp-on-violate-policy"
 %}
 
 };
--- a/content/base/src/ImportManager.cpp
+++ b/content/base/src/ImportManager.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ImportManager.h"
 
 #include "mozilla/EventListenerManager.h"
 #include "HTMLLinkElement.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
+#include "nsCrossSiteListenerProxy.h"
 #include "nsIChannel.h"
 #include "nsIChannelPolicy.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMEvent.h"
 #include "nsIPrincipal.h"
@@ -54,16 +55,17 @@ NS_INTERFACE_MAP_BEGIN(ImportLoader)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(ImportLoader)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImportLoader)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImportLoader)
 
 NS_IMPL_CYCLE_COLLECTION(ImportLoader,
                          mDocument,
+                         mImportParent,
                          mLinks)
 
 ImportLoader::ImportLoader(nsIURI* aURI, nsIDocument* aImportParent)
   : mURI(aURI)
   , mImportParent(aImportParent)
   , mReady(false)
   , mStopped(false)
   , mBlockingScripts(false)
@@ -127,18 +129,18 @@ public:
     MOZ_ASSERT(mNode);
   }
 
   NS_IMETHOD Run() {
     return nsContentUtils::DispatchTrustedEvent(mNode->OwnerDoc(),
                                                 mNode,
                                                 mSuccess ? NS_LITERAL_STRING("load")
                                                          : NS_LITERAL_STRING("error"),
-                                                /* aCanBubble = */ true,
-                                                /* aCancelable = */ true);
+                                                /* aCanBubble = */ false,
+                                                /* aCancelable = */ false);
   }
 
 private:
   nsCOMPtr<nsINode> mNode;
   bool mSuccess;
 };
 
 void
@@ -172,90 +174,108 @@ ImportLoader::Error(bool aUnblockScripts
   mStopped = true;
   uint32_t count = mLinks.Count();
   for (uint32_t i = 0; i < count; i++) {
     DispatchErrorEvent(mLinks[i]);
   }
   if (aUnblockScripts) {
     UnblockScripts();
   }
-
   ReleaseResources();
 }
 
 // Release all the resources we don't need after there is no more
 // data available on the channel, and the parser is done.
 void ImportLoader::ReleaseResources()
 {
   mParserStreamListener = nullptr;
-  mChannel = nullptr;
   mImportParent = nullptr;
 }
 
 void
 ImportLoader::Open()
 {
   AutoError ae(this, false);
   // Imports should obey to the master documents CSP.
   nsCOMPtr<nsIDocument> master = mImportParent->MasterDocument();
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(master);
   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
+
   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
                                           mURI,
                                           principal,
                                           mImportParent,
                                           NS_LITERAL_CSTRING("text/html"),
                                           /* extra = */ nullptr,
                                           &shouldLoad,
                                           nsContentUtils::GetContentPolicy(),
                                           nsContentUtils::GetSecurityManager());
   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
     NS_WARN_IF_FALSE(NS_CP_ACCEPTED(shouldLoad), "ImportLoader rejected by CSP");
     return;
   }
 
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  rv = secMan->CheckLoadURIWithPrincipal(principal, mURI,
+                                         nsIScriptSecurityManager::STANDARD);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
   nsCOMPtr<nsILoadGroup> loadGroup = mImportParent->GetDocumentLoadGroup();
   nsCOMPtr<nsIChannelPolicy> channelPolicy;
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   rv = principal->GetCsp(getter_AddRefs(csp));
   NS_ENSURE_SUCCESS_VOID(rv);
 
   if (csp) {
     channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
     channelPolicy->SetContentSecurityPolicy(csp);
     channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SUBDOCUMENT);
   }
-  rv = NS_NewChannel(getter_AddRefs(mChannel),
+  nsCOMPtr<nsIChannel> channel;
+  rv = NS_NewChannel(getter_AddRefs(channel),
                      mURI,
                      /* ioService = */ nullptr,
                      loadGroup,
                      /* callbacks = */ nullptr,
                      nsIRequest::LOAD_BACKGROUND,
                      channelPolicy);
   NS_ENSURE_SUCCESS_VOID(rv);
 
-  mChannel->AsyncOpen(this, nullptr);
+  // Init CORSListenerProxy and omit credentials.
+  nsRefPtr<nsCORSListenerProxy> corsListener =
+    new nsCORSListenerProxy(this, principal,
+                            /* aWithCredentials */ false);
+  rv = corsListener->Init(channel, true);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  rv = channel->AsyncOpen(corsListener, nullptr);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
   BlockScripts();
   ae.Pass();
 }
 
 NS_IMETHODIMP
 ImportLoader::OnDataAvailable(nsIRequest* aRequest,
                               nsISupports* aContext,
                               nsIInputStream* aStream,
                               uint64_t aOffset,
                               uint32_t aCount)
 {
   MOZ_ASSERT(mParserStreamListener);
 
   AutoError ae(this);
-  nsresult rv = mParserStreamListener->OnDataAvailable(mChannel, aContext,
-                                                       aStream, aOffset,
-                                                       aCount);
+  nsresult rv;
+  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mParserStreamListener->OnDataAvailable(channel, aContext,
+                                              aStream, aOffset,
+                                              aCount);
   NS_ENSURE_SUCCESS(rv, rv);
   ae.Pass();
   return rv;
 }
 
 NS_IMETHODIMP
 ImportLoader::HandleEvent(nsIDOMEvent *aEvent)
 {
@@ -273,48 +293,66 @@ ImportLoader::OnStopRequest(nsIRequest* 
                             nsISupports* aContext,
                             nsresult aStatus)
 {
   // OnStartRequest throws a special error code to let us know that we
   // shouldn't do anything else.
   if (aStatus == NS_ERROR_DOM_ABORT_ERR) {
     // We failed in OnStartRequest, nothing more to do (we've already
     // dispatched an error event) just return here.
-    MOZ_ASSERT(!mChannel);
+    MOZ_ASSERT(mStopped);
     return NS_OK;
   }
 
-  MOZ_ASSERT(aRequest == mChannel,
-             "Wrong channel something went horribly wrong");
-
   if (mParserStreamListener) {
     mParserStreamListener->OnStopRequest(aRequest, aContext, aStatus);
   }
 
+  if (!mDocument) {
+    // If at this point we don't have a document, then the error was
+    // already reported.
+    return NS_ERROR_DOM_ABORT_ERR;
+  }
+
   nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mDocument);
   EventListenerManager* manager = eventTarget->GetOrCreateListenerManager();
   manager->AddEventListenerByType(this,
                                   NS_LITERAL_STRING("DOMContentLoaded"),
                                   TrustedEventsAtSystemGroupBubble());
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ImportLoader::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
 {
-  MOZ_ASSERT(aRequest == mChannel,
-             "Wrong channel, something went horribly wrong");
-
   AutoError ae(this);
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mImportParent);
   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
-  mChannel->SetOwner(principal);
+  if (!sop) {
+    return NS_ERROR_DOM_ABORT_ERR;
+  }
+
+  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+  if (!channel) {
+    return NS_ERROR_DOM_ABORT_ERR;
+  }
+
+  if (nsContentUtils::IsSystemPrincipal(principal)) {
+    // We should never import non-system documents and run their scripts with system principal!
+    nsCOMPtr<nsIPrincipal> channelPrincipal;
+    nsContentUtils::GetSecurityManager()->GetChannelPrincipal(channel,
+                                                              getter_AddRefs(channelPrincipal));
+    if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  channel->SetOwner(principal);
 
   nsAutoCString type;
-  mChannel->GetContentType(type);
+  channel->GetContentType(type);
   if (!type.EqualsLiteral("text/html")) {
     NS_WARNING("ImportLoader wrong content type");
     return NS_ERROR_DOM_ABORT_ERR;
   }
 
   // The scope object is same for all the imports in an import tree,
   // let's get it form the import parent.
   nsCOMPtr<nsIGlobalObject> global = mImportParent->GetScopeObject();
@@ -327,21 +365,25 @@ ImportLoader::OnStartRequest(nsIRequest*
                                   DocumentFlavorHTML);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
 
   // The imported document must know which master document it belongs to.
   mDocument = do_QueryInterface(importDoc);
   nsCOMPtr<nsIDocument> master = mImportParent->MasterDocument();
   mDocument->SetMasterDocument(master);
 
-  // We have to connect the blank document we created with the channel we opened.
+  // We have to connect the blank document we created with the channel we opened,
+  // and create its own LoadGroup for it.
   nsCOMPtr<nsIStreamListener> listener;
   nsCOMPtr<nsILoadGroup> loadGroup;
-  mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
-  rv = mDocument->StartDocumentLoad("import", mChannel, loadGroup,
+  channel->GetLoadGroup(getter_AddRefs(loadGroup));
+  nsCOMPtr<nsILoadGroup> newLoadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
+  NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
+  newLoadGroup->SetLoadGroup(loadGroup);
+  rv = mDocument->StartDocumentLoad("import", channel, newLoadGroup,
                                     nullptr, getter_AddRefs(listener),
                                     true);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
 
   // Let's start parser.
   mParserStreamListener = listener;
   rv = listener->OnStartRequest(aRequest, aContext);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
--- a/content/base/src/ImportManager.h
+++ b/content/base/src/ImportManager.h
@@ -112,17 +112,16 @@ private:
 
   // While the document is being loaded we must block scripts
   // on the import parent document.
   void BlockScripts();
   void UnblockScripts();
 
   nsCOMPtr<nsIDocument> mDocument;
   nsCOMPtr<nsIURI> mURI;
-  nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsIStreamListener> mParserStreamListener;
   nsCOMPtr<nsIDocument> mImportParent;
   // List of the LinkElements that are referring to this import
   // we need to keep track of them so we can fire event on them.
   nsCOMArray<nsINode> mLinks;
   bool mReady;
   bool mStopped;
   bool mBlockingScripts;
--- a/content/base/src/nsCSPContext.cpp
+++ b/content/base/src/nsCSPContext.cpp
@@ -204,29 +204,16 @@ nsCSPContext::ShouldLoad(nsContentPolicy
   nsAutoCString spec;
   aContentLocation->GetSpec(spec);
   CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, decision: %s, aContentLocation: %s", *outDecision ? "load" : "deny", spec.get()));
   }
 #endif
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsCSPContext::ShouldProcess(nsContentPolicyType aContentType,
-                            nsIURI*             aContentLocation,
-                            nsIURI*             aRequestOrigin,
-                            nsISupports*        aRequestContext,
-                            const nsACString&   aMimeType,
-                            nsISupports*        aExtra,
-                            int16_t*            outDecision)
-{
-  *outDecision = nsIContentPolicy::ACCEPT;
-  return NS_OK;
-}
-
 /* ===== nsISupports implementation ========== */
 
 NS_IMPL_CLASSINFO(nsCSPContext,
                   nullptr,
                   nsIClassInfo::MAIN_THREAD_ONLY,
                   NS_CSPCONTEXT_CID)
 
 NS_IMPL_ISUPPORTS_CI(nsCSPContext,
--- a/content/base/src/nsCSPService.cpp
+++ b/content/base/src/nsCSPService.cpp
@@ -213,66 +213,17 @@ CSPService::ShouldProcess(uint32_t      
                           const nsACString &aMimeTypeGuess,
                           nsISupports      *aExtra,
                           nsIPrincipal     *aRequestPrincipal,
                           int16_t          *aDecision)
 {
   if (!aContentLocation)
     return NS_ERROR_FAILURE;
 
-  // default decision is to accept the item
   *aDecision = nsIContentPolicy::ACCEPT;
-
-  // No need to continue processing if CSP is disabled
-  if (!sCSPEnabled)
-    return NS_OK;
-
-  // find the nsDocument that initiated this request and see if it has a
-  // CSP policy object
-  nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
-  nsCOMPtr<nsIPrincipal> principal;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  if (node) {
-    principal = node->NodePrincipal();
-    principal->GetCsp(getter_AddRefs(csp));
-
-    if (csp) {
-#ifdef PR_LOGGING
-      {
-        uint32_t numPolicies = 0;
-        nsresult rv = csp->GetPolicyCount(&numPolicies);
-        if (NS_SUCCEEDED(rv)) {
-          for (uint32_t i=0; i<numPolicies; i++) {
-            nsAutoString policy;
-            csp->GetPolicy(i, policy);
-            PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-                   ("shouldProcess - document has policy[%d]: %s", i,
-                   NS_ConvertUTF16toUTF8(policy).get()));
-          }
-        }
-      }
-#endif
-      // obtain the enforcement decision
-      csp->ShouldProcess(aContentType,
-                         aContentLocation,
-                         aRequestOrigin,
-                         aRequestContext,
-                         aMimeTypeGuess,
-                         aExtra,
-                         aDecision);
-    }
-  }
-#ifdef PR_LOGGING
-  else {
-    nsAutoCString uriSpec;
-    aContentLocation->GetSpec(uriSpec);
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-           ("COULD NOT get nsINode for location: %s", uriSpec.get()));
-  }
-#endif
   return NS_OK;
 }
 
 /* nsIChannelEventSink implementation */
 NS_IMETHODIMP
 CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
                                    nsIChannel *newChannel,
                                    uint32_t flags,
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_imports_redirect.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<body>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_imports_redirect.html^headers^
@@ -0,0 +1,2 @@
+HTTP 301 Moved Permanently
+Location: http://mochi.test:8888/tests/content/html/content/test/file_imports_redirected.html
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_imports_redirected.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script> var redirected = true; </script>
+</body>
+</html>
--- a/content/html/content/test/mochitest.ini
+++ b/content/html/content/test/mochitest.ini
@@ -143,16 +143,19 @@ support-files =
   file_iframe_sandbox_window_form_fail.html
   file_iframe_sandbox_window_form_pass.html
   file_iframe_sandbox_window_navigation_fail.html
   file_iframe_sandbox_window_navigation_pass.html
   file_iframe_sandbox_window_top_navigation_pass.html
   file_iframe_sandbox_window_top_navigation_fail.html
   file_iframe_sandbox_worker.js
   file_imports_basics.html
+  file_imports_redirect.html
+  file_imports_redirect.html^headers^
+  file_imports_redirected.html
   file_srcdoc-2.html
   file_srcdoc.html
   form_submit_server.sjs
   image.png
   image-allow-credentials.png
   image-allow-credentials.png^headers^
   nnc_lockup.gif
   reflect.js
@@ -449,16 +452,18 @@ skip-if = buildapp == 'b2g' # b2g(multip
 [test_iframe_sandbox_popups_inheritance.html]
 skip-if = buildapp == 'b2g' || e10s # b2g(multiple concurrent window.open()s fail on B2G) b2g-debug(multiple concurrent window.open()s fail on B2G) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_iframe_sandbox_redirect.html]
 [test_iframe_sandbox_same_origin.html]
 [test_iframe_sandbox_workers.html]
 [test_img_attributes_reflection.html]
 [test_imageSrcSet.html]
 [test_imports_basics.html]
+[test_imports_redirect.html]
+[test_imports_nonhttp.html]
 [test_li_attributes_reflection.html]
 [test_link_attributes_reflection.html]
 [test_link_sizes.html]
 [test_map_attributes_reflection.html]
 [test_meta_attributes_reflection.html]
 [test_mod_attributes_reflection.html]
 [test_mozaudiochannel.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(Perma-orange on debug emulator) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_imports_nonhttp.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1016875
+-->
+<head>
+  <title>Test for Bug 1016875</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1016875">Mozilla Bug 1016875</a>
+
+  <script type="text/javascript">
+    //<![CDATA[
+    SimpleTest.waitForExplicitFinish();
+    var counter = 0;
+    function loaded() {
+      if ( counter++ == 1 ) {
+        ok(document.getElementById("import1").import.getElementById("div1"),
+           "import document was loaded");
+        ok(document.getElementById("import2").import.getElementById("div1"),
+           "import document was loaded");
+        SimpleTest.finish();
+      }
+    }
+    function failed() {
+      ok(false, "Import should not have failed")
+      SimpleTest.finish();
+    }
+    var link = document.createElement("link");
+    link.setAttribute("id", "import1");
+    link.setAttribute("rel", "import");
+    var stringDoc = "<!DOCTYPE html><html><body><div id='div1'></div></body></html>";
+    var encoded = btoa(stringDoc);
+    var dataurl = "data:text/html;base64," + encoded;
+    link.setAttribute("href", dataurl);
+    link.onload = loaded;
+    link.onerror = failed;
+    document.body.appendChild(link);
+
+    var link = document.createElement("link");
+    link.setAttribute("id", "import2");
+    link.setAttribute("rel", "import");
+    var blob = new Blob([stringDoc], {type : 'text/html'});
+    var objectURL = URL.createObjectURL(blob);
+    link.setAttribute("href", objectURL);
+    link.onload = loaded;
+    link.onerror = failed;
+    document.body.appendChild(link);
+    //]]>
+  </script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_imports_redirect.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1016875
+-->
+<head>
+  <title>Test for Bug 1016875</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1016875">Mozilla Bug 1016875</a>
+
+  <script type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+    var loadEventFired = false;
+    var errorEventFired = false;
+    var counter = 0;
+    function loaded() {
+      loadEventFired = true;
+    }
+    function failed() {
+      errorEventFired = true;
+    }
+  </script>
+
+  <link rel="import" href="http://mochi.test:8888/tests/content/html/content/test/file_imports_redirect.html" id="import" onload="loaded()" onerror="failed()"></link>
+
+  <script type="text/javascript">
+    ok(loadEventFired, "Load event was fired");
+    ok(!errorEventFired, "Error event was not fired, redirection worked");
+    ok(redirected, "Script of the target of redirection was executed");
+
+    SimpleTest.finish();
+  </script>
+
+</body>
+</html>
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -400,17 +400,16 @@ skip-if = appname == "seamonkey" || tool
 [test_playback.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' # Disabled on Android & B2G due to bug 668973
 [test_playback_errors.html]
 [test_playback_rate.html]
 [test_playback_rate_playpause.html]
 [test_played.html]
 skip-if = true # bug 1021794
 [test_preload_actions.html]
-skip-if = toolkit == 'android' # bug 886188
 [test_preload_attribute.html]
 [test_preload_suspend.html]
 skip-if = true # bug 493692
 [test_progress.html]
 [test_reactivate.html]
 [test_readyState.html]
 [test_referer.html]
 [test_replay_metadata.html]
--- a/content/media/webrtc/MediaEngine.h
+++ b/content/media/webrtc/MediaEngine.h
@@ -4,16 +4,17 @@
 
 #ifndef MEDIAENGINE_H_
 #define MEDIAENGINE_H_
 
 #include "mozilla/RefPtr.h"
 #include "nsIDOMFile.h"
 #include "DOMMediaStream.h"
 #include "MediaStreamGraph.h"
+#include "mozilla/dom/MediaStreamTrackBinding.h"
 
 namespace mozilla {
 
 struct VideoTrackConstraintsN;
 struct AudioTrackConstraintsN;
 
 /**
  * Abstract interface for managing audio and video devices. Each platform
@@ -49,21 +50,23 @@ public:
   static const int DEFAULT_43_VIDEO_WIDTH = 640;
   static const int DEFAULT_43_VIDEO_HEIGHT = 480;
   static const int DEFAULT_169_VIDEO_WIDTH = 1280;
   static const int DEFAULT_169_VIDEO_HEIGHT = 720;
   static const int DEFAULT_AUDIO_TIMER_MS = 10;
 
   /* Populate an array of video sources in the nsTArray. Also include devices
    * that are currently unavailable. */
-  virtual void EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >*) = 0;
+  virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
+                                     nsTArray<nsRefPtr<MediaEngineVideoSource> >*) = 0;
 
   /* Populate an array of audio sources in the nsTArray. Also include devices
    * that are currently unavailable. */
-  virtual void EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >*) = 0;
+  virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
+                                     nsTArray<nsRefPtr<MediaEngineAudioSource> >*) = 0;
 
 protected:
   virtual ~MediaEngine() {}
 };
 
 /**
  * Common abstract base class for audio and video sources.
  */
@@ -177,16 +180,19 @@ private:
   }
 };
 
 class MediaEngineVideoSource : public MediaEngineSource
 {
 public:
   virtual ~MediaEngineVideoSource() {}
 
+  virtual const dom::MediaSourceEnum GetMediaSource() {
+      return dom::MediaSourceEnum::Camera;
+  }
   /* This call reserves but does not start the device. */
   virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs) = 0;
 };
 
 /**
  * Audio source and friends.
  */
--- a/content/media/webrtc/MediaEngineDefault.cpp
+++ b/content/media/webrtc/MediaEngineDefault.cpp
@@ -473,34 +473,43 @@ MediaEngineDefaultAudioSource::Notify(ns
   channels.AppendElement(dest);
   segment.AppendFrames(buffer.forget(), channels, AUDIO_FRAME_LENGTH);
   mSource->AppendToTrack(mTrackID, &segment);
 
   return NS_OK;
 }
 
 void
-MediaEngineDefault::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources) {
+MediaEngineDefault::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
+                                          nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources) {
   MutexAutoLock lock(mMutex);
 
+  // only supports camera sources (for now).  See Bug 1038241
+  if (aMediaSource != dom::MediaSourceEnum::Camera) {
+    return;
+  }
+
   // We once had code here to find a VideoSource with the same settings and re-use that.
   // This no longer is possible since the resolution is being set in Allocate().
 
   nsRefPtr<MediaEngineVideoSource> newSource = new MediaEngineDefaultVideoSource();
   mVSources.AppendElement(newSource);
   aVSources->AppendElement(newSource);
 
   return;
 }
 
 void
-MediaEngineDefault::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources) {
+MediaEngineDefault::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
+                                          nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources) {
   MutexAutoLock lock(mMutex);
   int32_t len = mASources.Length();
 
+  // aMediaSource is ignored for audio devices (for now).
+
   for (int32_t i = 0; i < len; i++) {
     nsRefPtr<MediaEngineAudioSource> source = mASources.ElementAt(i);
     if (source->IsAvailable()) {
       aASources->AppendElement(source);
     }
   }
 
   // All streams are currently busy, just make a new one.
--- a/content/media/webrtc/MediaEngineDefault.h
+++ b/content/media/webrtc/MediaEngineDefault.h
@@ -133,18 +133,20 @@ protected:
 
 class MediaEngineDefault : public MediaEngine
 {
 public:
   MediaEngineDefault()
   : mMutex("mozilla::MediaEngineDefault")
   {}
 
-  virtual void EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
-  virtual void EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
+  virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
+                                     nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
+  virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
+                                     nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
 
 private:
   ~MediaEngineDefault() {}
 
   Mutex mMutex;
   // protected with mMutex:
 
   nsTArray<nsRefPtr<MediaEngineVideoSource> > mVSources;
--- a/content/media/webrtc/MediaEngineWebRTC.cpp
+++ b/content/media/webrtc/MediaEngineWebRTC.cpp
@@ -40,22 +40,25 @@ GetUserMediaLog()
 #endif
 
 #undef LOG
 #define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args)
 
 namespace mozilla {
 
 MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
-  : mMutex("mozilla::MediaEngineWebRTC")
-  , mVideoEngine(nullptr)
-  , mVoiceEngine(nullptr)
-  , mVideoEngineInit(false)
-  , mAudioEngineInit(false)
-  , mHasTabVideoSource(false)
+    : mMutex("mozilla::MediaEngineWebRTC")
+    , mScreenEngine(nullptr)
+    , mAppEngine(nullptr)
+    , mVideoEngine(nullptr)
+    , mVoiceEngine(nullptr)
+    , mVideoEngineInit(false)
+    , mAudioEngineInit(false)
+    , mScreenEngineInit(false)
+    , mAppEngineInit(false)
 {
 #ifndef MOZ_B2G_CAMERA
   nsCOMPtr<nsIComponentRegistrar> compMgr;
   NS_GetComponentRegistrar(getter_AddRefs(compMgr));
   if (compMgr) {
     compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource);
   }
 #else
@@ -64,21 +67,28 @@ MediaEngineWebRTC::MediaEngineWebRTC(Med
   // XXX
   gFarendObserver = new AudioOutputObserver();
 
   NS_NewNamedThread("AudioGUM", getter_AddRefs(mThread));
   MOZ_ASSERT(mThread);
 }
 
 void
-MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
+MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
+                                         nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
 {
-#ifdef MOZ_B2G_CAMERA
+  // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
 
+ #ifdef MOZ_B2G_CAMERA
+  if (aMediaSource != dom::MediaSourceEnum::Camera) {
+    // only supports camera sources
+    return;
+  }
+
   /**
    * We still enumerate every time, in case a new device was plugged in since
    * the last call. TODO: Verify that WebRTC actually does deal with hotplugging
    * new devices (with or without new engine creation) and accordingly adjust.
    * Enumeration is not neccessary if GIPS reports the same set of devices
    * for a given instance of the engine. Likewise, if a device was plugged out,
    * mVideoSources must be updated.
    */
@@ -97,58 +107,86 @@ MediaEngineWebRTC::EnumerateVideoDevices
     }
 
     nsRefPtr<MediaEngineWebRTCVideoSource> vSource;
     NS_ConvertUTF8toUTF16 uuid(cameraName);
     if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) {
       // We've already seen this device, just append.
       aVSources->AppendElement(vSource.get());
     } else {
-      vSource = new MediaEngineWebRTCVideoSource(i);
+      vSource = new MediaEngineWebRTCVideoSource(i, aMediaSource);
       mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
       aVSources->AppendElement(vSource);
     }
   }
 
   return;
 #else
   ScopedCustomReleasePtr<webrtc::ViEBase> ptrViEBase;
   ScopedCustomReleasePtr<webrtc::ViECapture> ptrViECapture;
-
-  // We spawn threads to handle gUM runnables, so we must protect the member vars
-  MutexAutoLock lock(mMutex);
+  webrtc::Config configSet;
+  webrtc::VideoEngine *videoEngine = nullptr;
+  bool *videoEngineInit = nullptr;
 
 #ifdef MOZ_WIDGET_ANDROID
   // get the JVM
   JavaVM *jvm = mozilla::AndroidBridge::Bridge()->GetVM();
 
   if (webrtc::VideoEngine::SetAndroidObjects(jvm) != 0) {
     LOG(("VieCapture:SetAndroidObjects Failed"));
     return;
   }
 #endif
-  if (!mVideoEngine) {
-    if (!(mVideoEngine = webrtc::VideoEngine::Create())) {
-      return;
-    }
+
+  switch (aMediaSource) {
+    case dom::MediaSourceEnum::Application:
+      mAppEngineConfig.Set<webrtc::CaptureDeviceInfo>(
+          new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Application));
+      if (!mAppEngine) {
+        if (!(mAppEngine = webrtc::VideoEngine::Create(mAppEngineConfig))) {
+          return;
+        }
+      }
+      videoEngine = mAppEngine;
+      videoEngineInit = &mAppEngineInit;
+      break;
+    case dom::MediaSourceEnum::Screen:
+      mScreenEngineConfig.Set<webrtc::CaptureDeviceInfo>(
+          new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Screen));
+      if (!mScreenEngine) {
+        if (!(mScreenEngine = webrtc::VideoEngine::Create(mScreenEngineConfig))) {
+          return;
+        }
+      }
+      videoEngine = mScreenEngine;
+      videoEngineInit = &mScreenEngineInit;
+      break;
+    case dom::MediaSourceEnum::Camera:
+      // fall through
+    default:
+      if (!mVideoEngine) {
+        if (!(mVideoEngine = webrtc::VideoEngine::Create())) {
+          return;
+        }
+      }
+      videoEngine = mVideoEngine;
+      videoEngineInit = &mVideoEngineInit;
+      break;
   }
 
-  ptrViEBase = webrtc::ViEBase::GetInterface(mVideoEngine);
+  ptrViEBase = webrtc::ViEBase::GetInterface(videoEngine);
   if (!ptrViEBase) {
     return;
   }
+  if (ptrViEBase->Init() < 0) {
+    return;
+  }
+  *videoEngineInit = true;
 
-  if (!mVideoEngineInit) {
-    if (ptrViEBase->Init() < 0) {
-      return;
-    }
-    mVideoEngineInit = true;
-  }
-
-  ptrViECapture = webrtc::ViECapture::GetInterface(mVideoEngine);
+  ptrViECapture = webrtc::ViECapture::GetInterface(videoEngine);
   if (!ptrViECapture) {
     return;
   }
 
   /**
    * We still enumerate every time, in case a new device was plugged in since
    * the last call. TODO: Verify that WebRTC actually does deal with hotplugging
    * new devices (with or without new engine creation) and accordingly adjust.
@@ -202,31 +240,32 @@ MediaEngineWebRTC::EnumerateVideoDevices
     }
 
     nsRefPtr<MediaEngineWebRTCVideoSource> vSource;
     NS_ConvertUTF8toUTF16 uuid(uniqueId);
     if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) {
       // We've already seen this device, just append.
       aVSources->AppendElement(vSource.get());
     } else {
-      vSource = new MediaEngineWebRTCVideoSource(mVideoEngine, i);
+      vSource = new MediaEngineWebRTCVideoSource(videoEngine, i, aMediaSource);
       mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
       aVSources->AppendElement(vSource);
     }
   }
 
   if (mHasTabVideoSource)
     aVSources->AppendElement(new MediaEngineTabVideoSource());
 
   return;
 #endif
 }
 
 void
-MediaEngineWebRTC::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources)
+MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
+                                         nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources)
 {
   ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
   ScopedCustomReleasePtr<webrtc::VoEHardware> ptrVoEHw;
   // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
 
 #ifdef MOZ_WIDGET_ANDROID
   jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef();
@@ -318,24 +357,33 @@ MediaEngineWebRTC::Shutdown()
 
   // Clear callbacks before we go away since the engines may outlive us
   if (mVideoEngine) {
     mVideoSources.Clear();
     mVideoEngine->SetTraceCallback(nullptr);
     webrtc::VideoEngine::Delete(mVideoEngine);
   }
 
+  if (mScreenEngine) {
+    webrtc::VideoEngine::Delete(mScreenEngine);
+  }
+  if (mAppEngine) {
+    webrtc::VideoEngine::Delete(mAppEngine);
+  }
+
   if (mVoiceEngine) {
     mAudioSources.Clear();
     mVoiceEngine->SetTraceCallback(nullptr);
     webrtc::VoiceEngine::Delete(mVoiceEngine);
   }
 
   mVideoEngine = nullptr;
   mVoiceEngine = nullptr;
+  mScreenEngine = nullptr;
+  mAppEngine = nullptr;
 
   if (mThread) {
     mThread->Shutdown();
     mThread = nullptr;
   }
 }
 
 }
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -23,19 +23,19 @@
 #include "VideoUtils.h"
 #include "MediaEngine.h"
 #include "VideoSegment.h"
 #include "AudioSegment.h"
 #include "StreamBuffer.h"
 #include "MediaStreamGraph.h"
 
 #include "MediaEngineWrapper.h"
-
+#include "mozilla/dom/MediaStreamTrackBinding.h"
 // WebRTC library includes follow
-
+#include "webrtc/common.h"
 // Audio Engine
 #include "webrtc/voice_engine/include/voe_base.h"
 #include "webrtc/voice_engine/include/voe_codec.h"
 #include "webrtc/voice_engine/include/voe_hardware.h"
 #include "webrtc/voice_engine/include/voe_network.h"
 #include "webrtc/voice_engine/include/voe_audio_processing.h"
 #include "webrtc/voice_engine/include/voe_volume_control.h"
 #include "webrtc/voice_engine/include/voe_external_media.h"
@@ -90,22 +90,24 @@ class MediaEngineWebRTCVideoSource : pub
                                    , public CameraControlListener
                                    , public mozilla::hal::ScreenConfigurationObserver
 #else
                                    , public webrtc::ExternalRenderer
 #endif
 {
 public:
 #ifdef MOZ_B2G_CAMERA
-  MediaEngineWebRTCVideoSource(int aIndex)
+  MediaEngineWebRTCVideoSource(int aIndex,
+                               dom::MediaSourceEnum aMediaSource = dom::MediaSourceEnum::Camera)
     : mCameraControl(nullptr)
     , mCallbackMonitor("WebRTCCamera.CallbackMonitor")
     , mRotation(0)
     , mBackCamera(false)
     , mCaptureIndex(aIndex)
+    , mMediaSource(aMediaSource)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
     , mHeight(0)
     , mHasDirectListeners(false)
     , mInitDone(false)
     , mInSnapshotMode(false)
     , mSnapshotPath(nullptr)
   {
@@ -119,21 +121,23 @@ public:
                            void *handle);
   /**
    * Does DeliverFrame() support a null buffer and non-null handle
    * (video texture)?
    * XXX Investigate!  Especially for Android/B2G
    */
   virtual bool IsTextureSupported() { return false; }
 
-  MediaEngineWebRTCVideoSource(webrtc::VideoEngine* aVideoEnginePtr, int aIndex)
+  MediaEngineWebRTCVideoSource(webrtc::VideoEngine* aVideoEnginePtr, int aIndex,
+                               dom::MediaSourceEnum aMediaSource = dom::MediaSourceEnum::Camera)
     : mVideoEngine(aVideoEnginePtr)
     , mCaptureIndex(aIndex)
     , mFps(-1)
     , mMinFps(-1)
+    , mMediaSource(aMediaSource)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
     , mHeight(0)
     , mHasDirectListeners(false)
     , mInitDone(false)
     , mInSnapshotMode(false)
     , mSnapshotPath(nullptr) {
     MOZ_ASSERT(aVideoEnginePtr);
@@ -160,16 +164,20 @@ public:
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime);
 
   virtual bool IsFake() {
     return false;
   }
 
+  virtual const dom::MediaSourceEnum GetMediaSource() {
+    return mMediaSource;
+  }
+
 #ifndef MOZ_B2G_CAMERA
   NS_DECL_THREADSAFE_ISUPPORTS
 #else
   // We are subclassed from CameraControlListener, which implements a
   // threadsafe reference-count for us.
   NS_DECL_ISUPPORTS_INHERITED
 
   void OnHardwareStateChange(HardwareState aState);
@@ -234,16 +242,17 @@ private:
   webrtc::ViECapture* mViECapture;
   webrtc::ViERender* mViERender;
 #endif
   webrtc::CaptureCapability mCapability; // Doesn't work on OS X.
 
   int mCaptureIndex;
   int mFps; // Track rate (30 fps by default)
   int mMinFps; // Min rate we want to accept
+  dom::MediaSourceEnum mMediaSource; // source of media (camera | application | screen)
 
   // mMonitor protects mImage access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack() and
   // image changes).  Note that mSources is not accessed from other threads
   // for video and is not protected.
   Monitor mMonitor; // Monitor for processing WebRTC frames.
   int mWidth, mHeight;
   nsRefPtr<layers::Image> mImage;
@@ -376,40 +385,49 @@ class MediaEngineWebRTC : public MediaEn
 {
 public:
   MediaEngineWebRTC(MediaEnginePrefs &aPrefs);
 
   // Clients should ensure to clean-up sources video/audio sources
   // before invoking Shutdown on this class.
   void Shutdown();
 
-  virtual void EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
-  virtual void EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
-
+  virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
+                                    nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
+  virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
+                                    nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
 private:
   ~MediaEngineWebRTC() {
     Shutdown();
 #ifdef MOZ_B2G_CAMERA
     AsyncLatencyLogger::Get()->Release();
 #endif
     // XXX
     gFarendObserver = nullptr;
   }
 
   nsCOMPtr<nsIThread> mThread;
 
   Mutex mMutex;
+
   // protected with mMutex:
-
+  webrtc::VideoEngine* mScreenEngine;
+  webrtc::VideoEngine* mAppEngine;
   webrtc::VideoEngine* mVideoEngine;
   webrtc::VoiceEngine* mVoiceEngine;
 
+  // specialized configurations
+  webrtc::Config mAppEngineConfig;
+  webrtc::Config mScreenEngineConfig;
+
   // Need this to avoid unneccesary WebRTC calls while enumerating.
   bool mVideoEngineInit;
   bool mAudioEngineInit;
+  bool mScreenEngineInit;
+  bool mAppEngineInit;
   bool mHasTabVideoSource;
 
   // Store devices we've already seen in a hashtable for quick return.
   // Maps UUID to MediaEngineSource (one set for audio, one for video).
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCVideoSource > mVideoSources;
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCAudioSource > mAudioSources;
 };
 
--- a/content/media/webrtc/MediaTrackConstraints.h
+++ b/content/media/webrtc/MediaTrackConstraints.h
@@ -37,16 +37,29 @@ public:
         auto value = ToEnum(array[i]);
         if (value != Kind::Other) {
           mRequireN.AppendElement(value);
         } else {
           mUnsupportedRequirement = true;
         }
       }
     }
+
+    // treat MediaSource special because it's always required
+    mRequired.mMediaSource = mMediaSource;
+
+    if (mMediaSource != dom::MediaSourceEnum::Camera && mAdvanced.WasPassed()) {
+      // iterate through advanced, forcing mediaSource to match "root"
+      auto& array = mAdvanced.Value();
+      for (uint32_t i = 0; i < array.Length(); i++) {
+        if (array[i].mMediaSource == dom::MediaSourceEnum::Camera) {
+          array[i].mMediaSource = mMediaSource;
+        }
+      }
+    }
   }
 protected:
   MediaTrackConstraintSet& Triage(const Kind kind) {
     if (mRequireN.IndexOf(kind) != mRequireN.NoIndex) {
       return mRequired;
     } else {
       mNonrequired.AppendElement(MediaTrackConstraintSet());
       return mNonrequired[mNonrequired.Length()-1];
@@ -80,14 +93,17 @@ struct VideoTrackConstraintsN :
                            dom::SupportedVideoConstraintsValues::strings) {
     if (mFacingMode.WasPassed()) {
       Triage(Kind::FacingMode).mFacingMode.Construct(mFacingMode.Value());
     }
     // Reminder: add handling for new constraints both here & SatisfyConstraintSet
     Triage(Kind::Width).mWidth = mWidth;
     Triage(Kind::Height).mHeight = mHeight;
     Triage(Kind::FrameRate).mFrameRate = mFrameRate;
+
+    // treat MediaSource special because it's always required
+    mRequired.mMediaSource = mMediaSource;
   }
 };
 
 }
 
 #endif /* MEDIATRACKCONSTRAINTS_H_ */
--- a/dom/base/MessagePort.cpp
+++ b/dom/base/MessagePort.cpp
@@ -180,20 +180,22 @@ PostMessageReadTransferStructuredClone(J
   NS_ASSERTION(scInfo, "Must have scInfo!");
 
   if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
     MessagePort* port = static_cast<MessagePort*>(data);
     port->BindToOwner(scInfo->mPort->GetOwner());
     scInfo->mPorts.Put(port, nullptr);
 
     JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx));
-    if (JS_WrapObject(aCx, &obj)) {
-      MOZ_ASSERT(port->GetOwner() == scInfo->mPort->GetOwner());
-      returnObject.set(obj);
+    if (!obj || !JS_WrapObject(aCx, &obj)) {
+      return false;
     }
+
+    MOZ_ASSERT(port->GetOwner() == scInfo->mPort->GetOwner());
+    returnObject.set(obj);
     return true;
   }
 
   return false;
 }
 
 static bool
 PostMessageTransferStructuredClone(JSContext* aCx,
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -3012,16 +3012,24 @@ nsJSContext::EnsureStatics()
 
   Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
                                        "javascript.options.mem.gc_decommit_threshold_mb",
                                        (void *)JSGC_DECOMMIT_THRESHOLD);
 
   Preferences::RegisterCallbackAndCall(SetIncrementalCCPrefChangedCallback,
                                        "dom.cycle_collector.incremental");
 
+  Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
+                                       "javascript.options.mem.gc_min_empty_chunk_count",
+                                       (void *)JSGC_MIN_EMPTY_CHUNK_COUNT);
+
+  Preferences::RegisterCallbackAndCall(SetMemoryGCPrefChangedCallback,
+                                       "javascript.options.mem.gc_max_empty_chunk_count",
+                                       (void *)JSGC_MAX_EMPTY_CHUNK_COUNT);
+
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (!obs) {
     MOZ_CRASH();
   }
 
   Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
                                "javascript.options.gc_on_memory_pressure",
                                true);
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -396,58 +396,26 @@ child:
     LoadURL(nsCString uri);
 
     CacheFileDescriptor(nsString path, FileDescriptor fd);
 
     UpdateDimensions(nsRect rect, nsIntSize size, ScreenOrientation orientation) compress;
 
     UpdateFrame(FrameMetrics frame);
 
-    /**
-     * Acknowledge the receipt of a scroll offset update from the content
-     * process. This is used to manage concurrent scroll updates from many
-     * sources.
-     */
+    // The following methods correspond to functions on the GeckoContentController
+    // interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
+    // in that file for these functions.
     AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
-
-    /**
-     * Requests handling of a double tap. |point| is in CSS pixels, relative to
-     * the scroll offset. This message is expected to round-trip back to
-     * ZoomToRect() with a rect indicating where we should zoom to.
-     */
     HandleDoubleTap(CSSPoint point, ScrollableLayerGuid aGuid);
-
-    /**
-     * Requests handling of a single tap. |point| is in CSS pixels, relative to
-     * the scroll offset. This message is expected to send a "mousedown" and
-     * "mouseup" series of events at this point.
-     */
     HandleSingleTap(CSSPoint point, ScrollableLayerGuid aGuid);
+    HandleLongTap(CSSPoint point, ScrollableLayerGuid aGuid);
+    HandleLongTapUp(CSSPoint point, ScrollableLayerGuid aGuid);
+    NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
 
-    /**
-     * Requests handling of a long tap. |point| is in CSS pixels, relative to
-     * the scroll offset. This message is expected to send a "contextmenu"
-     * events at this point.
-     */
-    HandleLongTap(CSSPoint point, ScrollableLayerGuid aGuid);
-
-    /**
-     * Requests handling of releasing a long tap. |aPoint| is in CSS pixels,
-     * relative to the current scroll offset. In the case the "contextmenu"
-     * event generated by the preceding HandleLongTap call was not handled,
-     * this message is expected to generate a "mousedown" and "mouseup"
-     * series of events
-     */
-    HandleLongTapUp(CSSPoint point, ScrollableLayerGuid aGuid);
-
-    /**
-     * Notifies the child about various APZ state changes.
-     * See GeckoContentController::NotifyAPZStateChange() for details.
-     */
-    NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
 
     /**
      * Sending an activate message moves focus to the child.
      */
     Activate();
 
     Deactivate();
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -697,19 +697,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mTapHoldTimer(nullptr)
   , mAppPackageFileDescriptorRecved(false)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
-  , mContextMenuHandled(false)
-  , mLongTapEventHandled(false)
-  , mWaitingTouchListeners(false)
+  , mPendingTouchPreventedResponse(false)
   , mIgnoreKeyPressEvent(false)
   , mActiveElementManager(new ActiveElementManager())
   , mHasValidInnerSize(false)
   , mUniqueId(0)
 {
   if (!sActiveDurationMsSet) {
     Preferences::AddIntVarCache(&sActiveDurationMs,
                                 "ui.touch_activation.duration_ms",
@@ -1800,50 +1798,40 @@ TabChild::FireSingleTapEvent(LayoutDevic
 
 bool
 TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
-  mContextMenuHandled =
+  bool eventHandled =
       DispatchMouseEvent(NS_LITERAL_STRING("contextmenu"),
                          APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid),
                          2, 1, 0, false,
                          nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
 
   // If no one handle context menu, fire MOZLONGTAP event
-  if (!mContextMenuHandled) {
+  if (!eventHandled) {
     LayoutDevicePoint currentPoint =
       APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();
     int time = 0;
     nsEventStatus status =
       DispatchSynthesizedMouseEvent(NS_MOUSE_MOZLONGTAP, time, currentPoint, mWidget);
-    mLongTapEventHandled = (status == nsEventStatus_eConsumeNoDefault);
+    eventHandled = (status == nsEventStatus_eConsumeNoDefault);
   }
 
-  SendContentReceivedTouch(aGuid, mContextMenuHandled || mLongTapEventHandled);
+  SendContentReceivedTouch(aGuid, eventHandled);
 
   return true;
 }
 
 bool
 TabChild::RecvHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
-  if (mContextMenuHandled) {
-    mContextMenuHandled = false;
-    return true;
-  }
-
-  if (mLongTapEventHandled) {
-    mLongTapEventHandled = false;
-    return true;
-  }
-
   RecvHandleSingleTap(aPoint, aGuid);
   return true;
 }
 
 bool
 TabChild::RecvNotifyAPZStateChange(const ViewID& aViewId,
                                    const APZStateChange& aChange,
                                    const int& aArg)
@@ -2090,43 +2078,42 @@ TabChild::RecvRealTouchEvent(const Widge
     UpdateTapState(localEvent, status);
     return true;
   }
 
   if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
   }
 
-  nsCOMPtr<nsPIDOMWindow> outerWindow = do_GetInterface(WebNavigation());
-  nsCOMPtr<nsPIDOMWindow> innerWindow = outerWindow ? outerWindow->GetCurrentInnerWindow() : nullptr;
-
-  if (!innerWindow || (!innerWindow->HasTouchEventListeners() &&
-                       !innerWindow->MayHaveTouchCaret())) {
-    SendContentReceivedTouch(aGuid, false);
-    return true;
-  }
-
   bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
                           localEvent.mFlags.mMultipleActionsPrevented;
   switch (aEvent.message) {
   case NS_TOUCH_START: {
+    if (mPendingTouchPreventedResponse) {
+      // We can enter here if we get two TOUCH_STARTs in a row and didn't
+      // respond to the first one. Respond to it now.
+      SendContentReceivedTouch(mPendingTouchPreventedGuid, false);
+      mPendingTouchPreventedResponse = false;
+    }
     if (isTouchPrevented) {
       SendContentReceivedTouch(aGuid, isTouchPrevented);
     } else {
-      mWaitingTouchListeners = true;
+      mPendingTouchPreventedResponse = true;
+      mPendingTouchPreventedGuid = aGuid;
     }
     break;
   }
 
   case NS_TOUCH_MOVE:
   case NS_TOUCH_END:
   case NS_TOUCH_CANCEL: {
-    if (mWaitingTouchListeners) {
-      SendContentReceivedTouch(aGuid, isTouchPrevented);
-      mWaitingTouchListeners = false;
+    if (mPendingTouchPreventedResponse) {
+      MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
+      SendContentReceivedTouch(mPendingTouchPreventedGuid, isTouchPrevented);
+      mPendingTouchPreventedResponse = false;
     }
     break;
   }
 
   default:
     NS_WARNING("Unknown touch event type");
   }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -581,19 +581,18 @@ private:
     nsAutoTArray<nsAutoPtr<CachedFileDescriptorInfo>, 1>
         mCachedFileDescriptorInfos;
     nscolor mLastBackgroundColor;
     bool mDidFakeShow;
     bool mNotified;
     bool mTriedBrowserInit;
     ScreenOrientation mOrientation;
     bool mUpdateHitRegion;
-    bool mContextMenuHandled;
-    bool mLongTapEventHandled;
-    bool mWaitingTouchListeners;
+    bool mPendingTouchPreventedResponse;
+    ScrollableLayerGuid mPendingTouchPreventedGuid;
     void FireSingleTapEvent(LayoutDevicePoint aPoint);
 
     bool mIgnoreKeyPressEvent;
     nsRefPtr<ActiveElementManager> mActiveElementManager;
     bool mHasValidInnerSize;
     uint64_t mUniqueId;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -305,16 +305,19 @@ VideoDevice::VideoDevice(MediaEngineVide
   }
 #endif // MOZ_B2G_CAMERA
 
   // Kludge to test user-facing cameras on OSX.
   if (mName.Find(NS_LITERAL_STRING("Face")) != -1) {
     mHasFacingMode = true;
     mFacingMode = dom::VideoFacingModeEnum::User;
   }
+
+  // dom::MediaSourceEnum::Camera;
+  mMediaSource = aSource->GetMediaSource();
 }
 
 AudioDevice::AudioDevice(MediaEngineAudioSource* aSource)
   : MediaDevice(aSource) {}
 
 NS_IMETHODIMP
 MediaDevice::GetName(nsAString& aName)
 {
@@ -356,16 +359,25 @@ MediaDevice::GetFacingMode(nsAString& aF
     aFacingMode.Assign(NS_ConvertUTF8toUTF16(
         dom::VideoFacingModeEnumValues::strings[uint32_t(mFacingMode)].value));
   } else {
     aFacingMode.Truncate(0);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+MediaDevice::GetMediaSource(nsAString& aMediaSource)
+{
+
+  aMediaSource.Assign(NS_ConvertUTF8toUTF16(
+    dom::MediaSourceEnumValues::strings[uint32_t(mMediaSource)].value));
+  return NS_OK;
+}
+
 MediaEngineVideoSource*
 VideoDevice::GetSource()
 {
   return static_cast<MediaEngineVideoSource*>(&*mSource);
 }
 
 MediaEngineAudioSource*
 AudioDevice::GetSource()
@@ -709,24 +721,29 @@ GetInvariant(const OwningBooleanOrMediaT
  */
 
 // Reminder: add handling for new constraints both here and in GetSources below!
 
 static bool SatisfyConstraintSet(const MediaEngineVideoSource *,
                                  const MediaTrackConstraintSet &aConstraints,
                                  nsIMediaDevice &aCandidate)
 {
+  nsString s;
   if (aConstraints.mFacingMode.WasPassed()) {
-    nsString s;
     aCandidate.GetFacingMode(s);
     if (!s.EqualsASCII(dom::VideoFacingModeEnumValues::strings[
         uint32_t(aConstraints.mFacingMode.Value())].value)) {
       return false;
     }
   }
+  aCandidate.GetMediaSource(s);
+  if (!s.EqualsASCII(dom::MediaSourceEnumValues::strings[
+      uint32_t(aConstraints.mMediaSource)].value)) {
+    return false;
+  }
   // TODO: Add more video-specific constraints
   return true;
 }
 
 static bool SatisfyConstraintSet(const MediaEngineAudioSource *,
                                  const MediaTrackConstraintSet &aConstraints,
                                  nsIMediaDevice &aCandidate)
 {
@@ -737,28 +754,28 @@ static bool SatisfyConstraintSet(const M
 typedef nsTArray<nsCOMPtr<nsIMediaDevice> > SourceSet;
 
 // Source getter that constrains list returned
 
 template<class SourceType, class ConstraintsType>
 static SourceSet *
   GetSources(MediaEngine *engine,
              ConstraintsType &aConstraints,
-             void (MediaEngine::* aEnumerate)(nsTArray<nsRefPtr<SourceType> >*),
+             void (MediaEngine::* aEnumerate)(dom::MediaSourceEnum, nsTArray<nsRefPtr<SourceType> >*),
              const char* media_device_name = nullptr)
 {
   ScopedDeletePtr<SourceSet> result(new SourceSet);
 
   const SourceType * const type = nullptr;
   nsString deviceName;
   // First collect sources
   SourceSet candidateSet;
   {
     nsTArray<nsRefPtr<SourceType> > sources;
-    (engine->*aEnumerate)(&sources);
+    (engine->*aEnumerate)(aConstraints.mMediaSource, &sources);
     /**
       * We're allowing multiple tabs to access the same camera for parity
       * with Chrome.  See bug 811757 for some of the issues surrounding
       * this decision.  To disallow, we'd filter by IsAvailable() as we used
       * to.
       */
     for (uint32_t len = sources.Length(), i = 0; i < len; i++) {
       sources[i]->GetName(deviceName);
@@ -1013,18 +1030,18 @@ public:
 
   nsresult
   SelectDevice(MediaEngine* backend)
   {
     MOZ_ASSERT(mSuccess);
     MOZ_ASSERT(mError);
     if (mConstraints.mPicture || IsOn(mConstraints.mVideo)) {
       VideoTrackConstraintsN constraints(GetInvariant(mConstraints.mVideo));
-      ScopedDeletePtr<SourceSet> sources (GetSources(backend, constraints,
-          &MediaEngine::EnumerateVideoDevices));
+      ScopedDeletePtr<SourceSet> sources(GetSources(backend, constraints,
+                               &MediaEngine::EnumerateVideoDevices));
 
       if (!sources->Length()) {
         Fail(NS_LITERAL_STRING("NO_DEVICES_FOUND"));
         return NS_ERROR_FAILURE;
       }
       // Pick the first available device.
       mVideoDevice = do_QueryObject((*sources)[0]);
       LOG(("Selected video device"));
@@ -1175,27 +1192,28 @@ public:
       backend = new MediaEngineDefault();
     else
       backend = mManager->GetBackend(mWindowId);
 
     ScopedDeletePtr<SourceSet> final(new SourceSet);
     if (IsOn(mConstraints.mVideo)) {
       VideoTrackConstraintsN constraints(GetInvariant(mConstraints.mVideo));
       ScopedDeletePtr<SourceSet> s(GetSources(backend, constraints,
-          &MediaEngine::EnumerateVideoDevices,
-          mLoopbackVideoDevice.get()));
+              &MediaEngine::EnumerateVideoDevices,
+              mLoopbackVideoDevice.get()));
       final->MoveElementsFrom(*s);
     }
     if (IsOn(mConstraints.mAudio)) {
       AudioTrackConstraintsN constraints(GetInvariant(mConstraints.mAudio));
       ScopedDeletePtr<SourceSet> s (GetSources(backend, constraints,
           &MediaEngine::EnumerateAudioDevices,
           mLoopbackAudioDevice.get()));
       final->MoveElementsFrom(*s);
     }
+
     NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId,
                                                               mSuccess, mError,
                                                               final.forget()));
     // DeviceSuccessCallbackRunnable should have taken these.
     MOZ_ASSERT(!mSuccess && !mError);
     return NS_OK;
   }
 
@@ -1460,44 +1478,50 @@ MediaManager::GetUserMedia(bool aPrivile
     runnable = new GetUserMediaRunnable(c, onSuccess.forget(),
       onError.forget(), windowID, listener, mPrefs, new MediaEngineDefault());
   } else {
     // Stream from default device from WebRTC backend.
     runnable = new GetUserMediaRunnable(c, onSuccess.forget(),
       onError.forget(), windowID, listener, mPrefs);
   }
 
+  // deny screensharing request if support is disabled
+  if (c.mVideo.IsMediaTrackConstraints() &&
+      !Preferences::GetBool("media.getusermedia.screensharing.enabled", false)) {
+    auto& tc = c.mVideo.GetAsMediaTrackConstraints();
+    if (tc.mMediaSource != dom::MediaSourceEnum::Camera) {
+      return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
+    }
+  }
+
 #ifdef MOZ_B2G_CAMERA
   if (mCameraManager == nullptr) {
     mCameraManager = nsDOMCameraManager::CreateInstance(aWindow);
   }
 #endif
 
 #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
   if (c.mPicture) {
     // ShowFilePickerForMimeType() must run on the Main Thread! (on Android)
     NS_DispatchToMainThread(runnable);
     return NS_OK;
   }
 #endif
   nsIURI* docURI = aWindow->GetDocumentURI();
-#ifdef MOZ_LOOP
-  {
-    bool isLoop = false;
-    nsCOMPtr<nsIURI> loopURI;
-    nsresult rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation");
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = docURI->EqualsExceptRef(loopURI, &isLoop);
-    NS_ENSURE_SUCCESS(rv, rv);
 
-    if (isLoop) {
-      aPrivileged = true;
-    }
+  bool isLoop = false;
+  nsCOMPtr<nsIURI> loopURI;
+  nsresult rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation");
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = docURI->EqualsExceptRef(loopURI, &isLoop);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (isLoop) {
+    aPrivileged = true;
   }
-#endif
 
   // XXX No full support for picture in Desktop yet (needs proper UI)
   if (aPrivileged ||
       (c.mFake && !Preferences::GetBool("media.navigator.permission.fake"))) {
     mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
   } else {
     bool isHTTPS = false;
     if (docURI) {
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -481,16 +481,17 @@ public:
 
 protected:
   virtual ~MediaDevice() {}
   MediaDevice(MediaEngineSource* aSource);
   nsString mName;
   nsString mID;
   bool mHasFacingMode;
   dom::VideoFacingModeEnum mFacingMode;
+  dom::MediaSourceEnum mMediaSource;
   nsRefPtr<MediaEngineSource> mSource;
 };
 
 class VideoDevice : public MediaDevice
 {
 public:
   VideoDevice(MediaEngineVideoSource* aSource);
   NS_IMETHOD GetType(nsAString& aType);
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -353,24 +353,26 @@ RTCPeerConnection.prototype = {
       throw new this._win.DOMError("",
           "RTCPeerConnection is gone (did you enter Offline mode?)");
     }
     return this._pc;
   },
 
   callCB: function(callback, arg) {
     if (callback) {
-      try {
-        callback(arg);
-      } catch(e) {
-        // A content script (user-provided) callback threw an error. We don't
-        // want this to take down peerconnection, but we still want the user
-        // to see it, so we catch it, report it, and move on.
-        this.logErrorAndCallOnError(e.message, e.fileName, e.lineNumber);
-      }
+      this._win.setTimeout(() => {
+        try {
+          callback(arg);
+        } catch(e) {
+          // A content script (user-provided) callback threw an error. We don't
+          // want this to take down peerconnection, but we still want the user
+          // to see it, so we catch it, report it, and move on.
+          this.logErrorAndCallOnError(e.message, e.fileName, e.lineNumber);
+        }
+      }, 0);
     }
   },
 
   _initIdp: function() {
     let prefName = "media.peerconnection.identity.timeout";
     let idpTimeout = Services.prefs.getIntPref(prefName);
     let warningFunc = this.logWarning.bind(this);
     this._localIdp = new PeerConnectionIdp(this._win, idpTimeout, warningFunc,
--- a/dom/media/nsIDOMNavigatorUserMedia.idl
+++ b/dom/media/nsIDOMNavigatorUserMedia.idl
@@ -8,16 +8,17 @@
 
 [scriptable, builtinclass, uuid(4af2bdb7-1547-4d10-8886-02a78c3c0b83)]
 interface nsIMediaDevice : nsISupports
 {
   readonly attribute DOMString type;
   readonly attribute DOMString name;
   readonly attribute DOMString id;
   readonly attribute DOMString facingMode;
+  readonly attribute DOMString mediaSource;
 };
 
 [scriptable, function, uuid(24544878-d35e-4962-8c5f-fb84e97bdfee)]
 interface nsIGetUserMediaDevicesSuccessCallback : nsISupports
 {
   void onSuccess(in nsIVariant devices);
 };
 
--- a/dom/webidl/Constraints.webidl
+++ b/dom/webidl/Constraints.webidl
@@ -9,16 +9,22 @@
 
 enum VideoFacingModeEnum {
     "user",
     "environment",
     "left",
     "right"
 };
 
+enum MediaSourceEnum {
+    "camera",
+    "screen",
+    "application"
+};
+
 dictionary ConstrainLongRange {
     long min = -2147483647; // +1 works around windows compiler bug
     long max = 2147483647;
 };
 
 dictionary ConstrainDoubleRange {
     unrestricted double min = -Infinity;
     unrestricted double max = Infinity;
--- a/dom/webidl/MediaTrackConstraintSet.webidl
+++ b/dom/webidl/MediaTrackConstraintSet.webidl
@@ -8,28 +8,34 @@
  */
 
 enum SupportedVideoConstraints {
     "other",
     "facingMode",
     "width",
     "height",
     "frameRate",
+    "mediaSource"
 };
 
 enum SupportedAudioConstraints {
     "other"
 };
 
+
 dictionary MediaTrackConstraintSet {
     ConstrainLongRange width;
     ConstrainLongRange height;
     ConstrainDoubleRange frameRate;
     ConstrainVideoFacingMode facingMode;
+    ConstrainMediaSource mediaSource = "camera";
 };
 
 // TODO: Bug 995352 can't nest unions
 //typedef (long or ConstrainLongRange) ConstrainLong;
 //typedef (double or ConstrainDoubleRange) ConstrainDouble;
 
 typedef VideoFacingModeEnum ConstrainVideoFacingMode;
+typedef MediaSourceEnum ConstrainMediaSource;
+
 // TODO: Bug 767924 sequences in unions
 //typedef (VideoFacingModeEnum or sequence<VideoFacingModeEnum>) ConstrainVideoFacingMode;
+//typedef (MediaSourceEnum or sequence<MediaSourceEnum>) ConstrainMediaSource;
\ No newline at end of file
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -589,16 +589,28 @@ LoadJSGCMemoryOptions(const char* aPrefN
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_mark_slice");
     if (memPrefName == matchName || (!rts && index == 11)) {
       bool prefValue = GetWorkerPref(matchName, false);
       UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE,
                                  prefValue ? 0 : 1);
       continue;
     }
 
+    matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_min_empty_chunk_count");
+    if (memPrefName == matchName || (!rts && index == 12)) {
+      UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MIN_EMPTY_CHUNK_COUNT);
+      continue;
+    }
+
+    matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_max_empty_chunk_count");
+    if (memPrefName == matchName || (!rts && index == 13)) {
+      UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MAX_EMPTY_CHUNK_COUNT);
+      continue;
+    }
+
 #ifdef DEBUG
     nsAutoCString message("Workers don't support the 'mem.");
     message.Append(memPrefName);
     message.AppendLiteral("' preference!");
     NS_WARNING(message.get());
 #endif
   }
 }
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -63,17 +63,18 @@ public:
                              int32_t aModifiers,
                              const ScrollableLayerGuid& aGuid) = 0;
 
   /**
    * Requests handling of releasing a long tap. |aPoint| is in CSS pixels,
    * relative to the current scroll offset. HandleLongTapUp will always be
    * preceeded by HandleLongTap. However not all calls to HandleLongTap will
    * be followed by a HandleLongTapUp (for example, if the user drags
-   * around between the long-tap and lifting their finger).
+   * around between the long-tap and lifting their finger, or if content
+   * notifies the APZ that the long-tap event was prevent-defaulted).
    */
   virtual void HandleLongTapUp(const CSSPoint& aPoint,
                                int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) = 0;
 
   /**
    * Requests sending a mozbrowserasyncscroll domevent to embedder.
    * |aContentRect| is in CSS pixels, relative to the current cssPage.
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -10,16 +10,17 @@
 #include <algorithm>                    // for max, min
 #include "AnimationCommon.h"            // for ComputedTimingFunction
 #include "AsyncPanZoomController.h"     // for AsyncPanZoomController, etc
 #include "Compositor.h"                 // for Compositor
 #include "CompositorParent.h"           // for CompositorParent
 #include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "GestureEventListener.h"       // for GestureEventListener
 #include "InputData.h"                  // for MultiTouchInput, etc
+#include "TouchBlockState.h"            // for TouchBlockState
 #include "Units.h"                      // for CSSRect, CSSPoint, etc
 #include "UnitTransforms.h"             // for TransformTo
 #include "base/message_loop.h"          // for MessageLoop
 #include "base/task.h"                  // for NewRunnableMethod, etc
 #include "base/tracked.h"               // for FROM_HERE
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "gfxTypes.h"                   // for gfxFloat
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
@@ -312,24 +313,16 @@ typedef GeckoContentController::APZState
  * documentation for the skate size multipliers above).
  *
  * "apz.zoom_animation_duration_ms"
  * This controls how long the zoom-to-rect animation takes.
  * Units: ms
  */
 
 /**
- * Default touch behavior (is used when not touch behavior is set).
- */
-static const uint32_t DefaultTouchBehavior = AllowedTouchBehavior::VERTICAL_PAN |
-                                             AllowedTouchBehavior::HORIZONTAL_PAN |
-                                             AllowedTouchBehavior::PINCH_ZOOM |
-                                             AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
-
-/**
  * Angle from axis within which we stay axis-locked
  */
 static const double AXIS_LOCK_ANGLE = M_PI / 6.0; // 30 degrees
 
 /**
  * The distance in inches the user must pan before axis lock can be broken
  */
 static const float AXIS_BREAKOUT_THRESHOLD = 1.0f/32.0f;
@@ -407,16 +400,36 @@ static uint32_t sAsyncPanZoomControllerC
 static TimeStamp
 GetFrameTime() {
   if (sFrameTime.IsNull()) {
     return TimeStamp::Now();
   }
   return sFrameTime;
 }
 
+static PRThread* sControllerThread;
+
+static void
+AssertOnControllerThread() {
+  if (!AsyncPanZoomController::GetThreadAssertionsEnabled()) {
+    return;
+  }
+
+  static bool sControllerThreadDetermined = false;
+  if (!sControllerThreadDetermined) {
+    // Technically this may not actually pick up the correct controller thread,
+    // if the first call to this method happens from a non-controller thread.
+    // If the assertion below fires, it is possible that it is because
+    // sControllerThread is not actually the controller thread.
+    sControllerThread = PR_GetCurrentThread();
+    sControllerThreadDetermined = true;
+  }
+  MOZ_ASSERT(sControllerThread == PR_GetCurrentThread());
+}
+
 class FlingAnimation: public AsyncPanZoomAnimation {
 public:
   FlingAnimation(AsyncPanZoomController& aApzc,
                  bool aApplyAcceleration,
                  bool aAllowOverscroll)
     : AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval()))
     , mApzc(aApzc)
     , mAllowOverscroll(aAllowOverscroll)
@@ -692,27 +705,26 @@ AsyncPanZoomController::AsyncPanZoomCont
                                                GestureBehavior aGestures)
   :  mLayersId(aLayersId),
      mPaintThrottler(GetFrameTime()),
      mGeckoContentController(aGeckoContentController),
      mRefPtrMonitor("RefPtrMonitor"),
      mSharingFrameMetricsAcrossProcesses(false),
      mMonitor("AsyncPanZoomController"),
      mState(NOTHING),
-     mContentResponseTimeoutTask(nullptr),
      mX(MOZ_THIS_IN_INITIALIZER_LIST()),
      mY(MOZ_THIS_IN_INITIALIZER_LIST()),
      mPanDirRestricted(false),
      mZoomConstraints(false, false, MIN_ZOOM, MAX_ZOOM),
      mLastSampleTime(GetFrameTime()),
      mLastAsyncScrollTime(GetFrameTime()),
      mLastAsyncScrollOffset(0, 0),
      mCurrentAsyncScrollOffset(0, 0),
      mAsyncScrollTimeoutTask(nullptr),
-     mHandlingTouchQueue(false),
+     mTouchBlockBalance(0),
      mTreeManager(aTreeManager),
      mScrollParentId(FrameMetrics::NULL_SCROLL_ID),
      mAPZCId(sAsyncPanZoomControllerCount++),
      mSharedFrameMetricsBuffer(nullptr),
      mSharedLock(nullptr)
 {
   MOZ_COUNT_CTOR(AsyncPanZoomController);
 
@@ -754,16 +766,18 @@ AsyncPanZoomController::GetGestureEventL
   return listener.forget();
 }
 
 void
 AsyncPanZoomController::Destroy()
 {
   CancelAnimation();
 
+  mTouchBlockQueue.Clear();
+
   { // scope the lock
     MonitorAutoLock lock(mRefPtrMonitor);
     mGeckoContentController = nullptr;
     mGestureEventListener = nullptr;
   }
   mPrevSibling = nullptr;
   mLastChild = nullptr;
   mParent = nullptr;
@@ -797,52 +811,67 @@ AsyncPanZoomController::GetTouchStartTol
 }
 
 /* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
 {
   return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
 }
 
 nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
-  if (aEvent.mInputType == MULTITOUCH_INPUT &&
-      aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
-    // Starting a new touch block, clear old touch block state.
-    mTouchBlockState = TouchBlockState();
+  AssertOnControllerThread();
+
+  if (aEvent.mInputType != MULTITOUCH_INPUT) {
+    return HandleInputEvent(aEvent);
   }
 
-  // If we may have touch listeners and touch action property is enabled, we
-  // enable the machinery that allows touch listeners to preventDefault any touch inputs
-  // and also waits for the allowed touch behavior values to be received from the outside.
-  // This should not happen unless there are actually touch listeners and touch-action property
-  // enable as it introduces potentially unbounded lag because it causes a round-trip through
-  // content.  Usually, if content is responding in a timely fashion, this only introduces a
-  // nearly constant few hundred ms of lag.
-  if ((mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.mMayHaveTouchCaret) &&
-      aEvent.mInputType == MULTITOUCH_INPUT &&
-      (mState == NOTHING || mState == TOUCHING || IsPanningState(mState))) {
-    const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
-    if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
-      SetState(WAITING_CONTENT_RESPONSE);
+  TouchBlockState* block = nullptr;
+  if (aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
+    block = StartNewTouchBlock(false);
+    APZC_LOG("%p started new touch block %p\n", this, block);
+    if (mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.mMayHaveTouchCaret) {
+      // Content may intercept the touch events and prevent-default them. So we schedule
+      // a timeout to give content time to do that.
+      ScheduleContentResponseTimeout();
+    } else {
+      // Content won't prevent-default this, so we can just pretend like we scheduled
+      // a timeout and it expired. Note that we will still receive a ContentReceivedTouch
+      // callback for this block, and so we need to make sure we adjust the touch balance.
+      APZC_LOG("%p not waiting for content response on block %p\n", this, block);
+      mTouchBlockBalance++;
+      block->TimeoutContentResponse();
     }
+  } else if (mTouchBlockQueue.IsEmpty()) {
+    NS_WARNING("Received a non-start touch event while no touch blocks active!");
+  } else {
+    // this touch is part of the most-recently created block
+    block = mTouchBlockQueue.LastElement().get();
+    APZC_LOG("%p received new event in block %p\n", this, block);
   }
 
-  if (mState == WAITING_CONTENT_RESPONSE || mHandlingTouchQueue) {
-    if (aEvent.mInputType == MULTITOUCH_INPUT) {
-      const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
-      mTouchQueue.AppendElement(multiTouchInput);
-
-      SetContentResponseTimer();
-    }
+  if (!block) {
     return nsEventStatus_eIgnore;
   }
 
-  return HandleInputEvent(aEvent);
+  if (block == CurrentTouchBlock() && block->IsReadyForHandling()) {
+    APZC_LOG("%p's current touch block is ready with preventdefault %d\n",
+        this, block->IsDefaultPrevented());
+    if (block->IsDefaultPrevented()) {
+      return nsEventStatus_eIgnore;
+    }
+    return HandleInputEvent(aEvent);
+  }
+
+  // Otherwise, add it to the queue for the touch block
+  block->AddEvent(aEvent.AsMultiTouchInput());
+  return nsEventStatus_eConsumeDoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
+  AssertOnControllerThread();
+
   nsEventStatus rv = nsEventStatus_eIgnore;
 
   switch (aEvent.mInputType) {
   case MULTITOUCH_INPUT: {
     const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
 
     nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
     if (listener) {
@@ -879,16 +908,18 @@ nsEventStatus AsyncPanZoomController::Ha
   default: NS_WARNING("Unhandled input event"); break;
   }
 
   return rv;
 }
 
 nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent)
 {
+  AssertOnControllerThread();
+
   nsEventStatus rv = nsEventStatus_eIgnore;
 
   switch (aEvent.mInputType) {
   case PINCHGESTURE_INPUT: {
     const PinchGestureInput& pinchGestureInput = aEvent.AsPinchGestureInput();
     switch (pinchGestureInput.mType) {
       case PinchGestureInput::PINCHGESTURE_START: rv = OnScaleBegin(pinchGestureInput); break;
       case PinchGestureInput::PINCHGESTURE_SCALE: rv = OnScale(pinchGestureInput); break;
@@ -952,17 +983,16 @@ nsEventStatus AsyncPanZoomController::On
     }
     case TOUCHING:
     case PANNING:
     case PANNING_LOCKED_X:
     case PANNING_LOCKED_Y:
     case CROSS_SLIDING_X:
     case CROSS_SLIDING_Y:
     case PINCHING:
-    case WAITING_CONTENT_RESPONSE:
       NS_WARNING("Received impossible touch in OnTouchStart");
       break;
     default:
       NS_WARNING("Unhandled case in OnTouchStart");
       break;
   }
 
   return nsEventStatus_eConsumeNoDefault;
@@ -987,19 +1017,17 @@ nsEventStatus AsyncPanZoomController::On
     case TOUCHING: {
       float panThreshold = GetTouchStartTolerance();
       UpdateWithTouchAtDevicePoint(aEvent);
 
       if (PanDistance() < panThreshold) {
         return nsEventStatus_eIgnore;
       }
 
-      if (gfxPrefs::TouchActionEnabled() &&
-          (GetTouchBehavior(0) & AllowedTouchBehavior::VERTICAL_PAN) &&
-          (GetTouchBehavior(0) & AllowedTouchBehavior::HORIZONTAL_PAN)) {
+      if (gfxPrefs::TouchActionEnabled() && CurrentTouchBlock()->TouchActionAllowsPanningXY()) {
         // User tries to trigger a touch behavior. If allowed touch behavior is vertical pan
         // + horizontal pan (touch-action value is equal to AUTO) we can return ConsumeNoDefault
         // status immediately to trigger cancel event further. It should happen independent of
         // the parent type (whether it is scrolling or not).
         StartPanning(aEvent);
         return nsEventStatus_eConsumeNoDefault;
       }
 
@@ -1012,17 +1040,16 @@ nsEventStatus AsyncPanZoomController::On
       TrackTouch(aEvent);
       return nsEventStatus_eConsumeNoDefault;
 
     case PINCHING:
       // The scale gesture listener should have handled this.
       NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
       return nsEventStatus_eIgnore;
 
-    case WAITING_CONTENT_RESPONSE:
     case SNAP_BACK:  // Should not receive a touch-move in the SNAP_BACK state
                      // as touch blocks that begin in an overscrolled state
                      // are ignored.
       NS_WARNING("Received impossible touch in OnTouchMove");
       break;
   }
 
   return nsEventStatus_eConsumeNoDefault;
@@ -1087,17 +1114,16 @@ nsEventStatus AsyncPanZoomController::On
     return nsEventStatus_eConsumeNoDefault;
 
   case PINCHING:
     SetState(NOTHING);
     // Scale gesture listener should have handled this.
     NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
     return nsEventStatus_eIgnore;
 
-  case WAITING_CONTENT_RESPONSE:
   case SNAP_BACK:  // Should not receive a touch-move in the SNAP_BACK state
                    // as touch blocks that begin in an overscrolled state
                    // are ignored.
     NS_WARNING("Received impossible touch in OnTouchEnd");
     break;
   }
 
   return nsEventStatus_eConsumeNoDefault;
@@ -1108,34 +1134,36 @@ nsEventStatus AsyncPanZoomController::On
   OnTouchEndOrCancel();
   SetState(NOTHING);
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
 
-  if (!TouchActionAllowPinchZoom()) {
+  // Note that there may not be a touch block at this point, if we received the
+  // PinchGestureEvent directly from widget code without any touch events.
+  if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
     return nsEventStatus_eIgnore;
   }
 
   if (!mZoomConstraints.mAllowZoom) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
   SetState(PINCHING);
   mLastZoomFocus = ToParentLayerCoords(aEvent.mFocusPoint) - mFrameMetrics.mCompositionBounds.TopLeft();
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale in state %d\n", this, mState);
 
-  if (!TouchActionAllowPinchZoom()) {
+  if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
     return nsEventStatus_eIgnore;
   }
 
   if (mState != PINCHING) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
   float prevSpan = aEvent.mPreviousSpan;
@@ -1208,17 +1236,17 @@ nsEventStatus AsyncPanZoomController::On
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale-end in state %d\n", this, mState);
 
-  if (!TouchActionAllowPinchZoom()) {
+  if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
     return nsEventStatus_eIgnore;
   }
 
   SetState(NOTHING);
 
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
@@ -1348,18 +1376,18 @@ nsEventStatus AsyncPanZoomController::On
 
 nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a long-press in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
     CSSPoint geckoScreenPoint;
     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
-      SetState(WAITING_CONTENT_RESPONSE);
-      SetContentResponseTimer();
+      StartNewTouchBlock(true);
+      ScheduleContentResponseTimeout();
       controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid());
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
@@ -1386,50 +1414,50 @@ nsEventStatus AsyncPanZoomController::Ge
       // calling controller->HandleSingleTap directly might mean that content receives
       // the single tap message before the corresponding touch-up. To avoid that we
       // schedule the singletap message to run on the next spin of the event loop.
       // See bug 965381 for the issue this was causing.
       controller->PostDelayedTask(
         NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
                           geckoScreenPoint, modifiers, GetGuid()),
         0);
-      mTouchBlockState.mSingleTapOccurred = true;
+      CurrentTouchBlock()->SetSingleTapOccurred();
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
 void AsyncPanZoomController::OnTouchEndOrCancel() {
   if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
     controller->NotifyAPZStateChange(
-        GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred);
+        GetGuid(), APZStateChange::EndTouch, CurrentTouchBlock()->SingleTapOccurred());
   }
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
   // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
   // sending event to content
-  if (!(mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom())) {
+  if (!(mZoomConstraints.mAllowDoubleTapZoom && CurrentTouchBlock()->TouchActionAllowsDoubleTapZoom())) {
     return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
   return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
 }
 
 nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a double-tap in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
-    if (mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom()) {
+    if (mZoomConstraints.mAllowDoubleTapZoom && CurrentTouchBlock()->TouchActionAllowsDoubleTapZoom()) {
       int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
       CSSPoint geckoScreenPoint;
       if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
         controller->HandleDoubleTap(geckoScreenPoint, modifiers, GetGuid());
       }
     }
     return nsEventStatus_eConsumeNoDefault;
   }
@@ -1446,48 +1474,48 @@ float AsyncPanZoomController::PanDistanc
   ReentrantMonitorAutoEnter lock(mMonitor);
   return NS_hypot(mX.PanDistance(), mY.PanDistance());
 }
 
 const ScreenPoint AsyncPanZoomController::GetVelocityVector() {
   return ScreenPoint(mX.GetVelocity(), mY.GetVelocity());
 }
 
-void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle, TouchBehaviorFlags aBehavior) {
+void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle) {
   // Handling of cross sliding will need to be added in this method after touch-action released
   // enabled by default.
-  if ((aBehavior & AllowedTouchBehavior::VERTICAL_PAN) && (aBehavior & AllowedTouchBehavior::HORIZONTAL_PAN)) {
+  if (CurrentTouchBlock()->TouchActionAllowsPanningXY()) {
     if (mX.CanScrollNow() && mY.CanScrollNow()) {
       if (IsCloseToHorizontal(aAngle, AXIS_LOCK_ANGLE)) {
         mY.SetAxisLocked(true);
         SetState(PANNING_LOCKED_X);
       } else if (IsCloseToVertical(aAngle, AXIS_LOCK_ANGLE)) {
         mX.SetAxisLocked(true);
         SetState(PANNING_LOCKED_Y);
       } else {
         SetState(PANNING);
       }
     } else if (mX.CanScrollNow() || mY.CanScrollNow()) {
       SetState(PANNING);
     } else {
       SetState(NOTHING);
     }
-  } else if (aBehavior & AllowedTouchBehavior::HORIZONTAL_PAN) {
+  } else if (CurrentTouchBlock()->TouchActionAllowsPanningX()) {
     // Using bigger angle for panning to keep behavior consistent
     // with IE.
     if (IsCloseToHorizontal(aAngle, ALLOWED_DIRECT_PAN_ANGLE)) {
       mY.SetAxisLocked(true);
       SetState(PANNING_LOCKED_X);
       mPanDirRestricted = true;
     } else {
       // Don't treat these touches as pan/zoom movements since 'touch-action' value
       // requires it.
       SetState(NOTHING);
     }
-  } else if (aBehavior & AllowedTouchBehavior::VERTICAL_PAN) {
+  } else if (CurrentTouchBlock()->TouchActionAllowsPanningY()) {
     if (IsCloseToVertical(aAngle, ALLOWED_DIRECT_PAN_ANGLE)) {
       mX.SetAxisLocked(true);
       SetState(PANNING_LOCKED_Y);
       mPanDirRestricted = true;
     } else {
       SetState(NOTHING);
     }
   } else {
@@ -1556,17 +1584,17 @@ nsEventStatus AsyncPanZoomController::St
   // so the page won't jump when we start panning.
   mX.StartTouch(point.x, aEvent.mTime);
   mY.StartTouch(point.y, aEvent.mTime);
 
   double angle = atan2(dy, dx); // range [-pi, pi]
   angle = fabs(angle); // range [0, pi]
 
   if (gfxPrefs::TouchActionEnabled()) {
-    HandlePanningWithTouchAction(angle, GetTouchBehavior(0));
+    HandlePanningWithTouchAction(angle);
   } else {
     if (GetAxisLockMode() == FREE) {
       SetState(PANNING);
     } else {
       HandlePanning(angle);
     }
   }
 
@@ -2453,117 +2481,174 @@ void AsyncPanZoomController::ZoomToRect(
         endZoomToMetrics.GetZoom()));
 
     // Schedule a repaint now, so the new displayport will be painted before the
     // animation finishes.
     RequestContentRepaint(endZoomToMetrics);
   }
 }
 
-void AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
-  mTouchBlockState.mPreventDefaultSet = true;
-  mTouchBlockState.mPreventDefault = aPreventDefault;
-  CheckContentResponse();
+void
+AsyncPanZoomController::ScheduleContentResponseTimeout() {
+  APZC_LOG("%p scheduling content response timeout\n", this);
+  PostDelayedTask(
+    NewRunnableMethod(this, &AsyncPanZoomController::ContentResponseTimeout),
+    gfxPrefs::APZContentResponseTimeout());
 }
 
-void AsyncPanZoomController::CheckContentResponse() {
-  bool canProceedToTouchState = true;
-
-  if (mFrameMetrics.mMayHaveTouchListeners ||
-      mFrameMetrics.mMayHaveTouchCaret) {
-    canProceedToTouchState &= mTouchBlockState.mPreventDefaultSet;
-  }
-
-  if (gfxPrefs::TouchActionEnabled()) {
-    canProceedToTouchState &= mTouchBlockState.mAllowedTouchBehaviorSet;
-  }
-
-  if (!canProceedToTouchState) {
-    return;
+void
+AsyncPanZoomController::ContentResponseTimeout() {
+  AssertOnControllerThread();
+
+  mTouchBlockBalance++;
+  APZC_LOG("%p got a content response timeout; balance %d\n", this, mTouchBlockBalance);
+  if (mTouchBlockBalance > 0) {
+    // Find the first touch block in the queue that hasn't already received
+    // the content response timeout callback, and notify it.
+    bool found = false;
+    for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
+      if (mTouchBlockQueue[i]->TimeoutContentResponse()) {
+        found = true;
+        break;
+      }
+    }
+    if (found) {
+      ProcessPendingInputBlocks();
+    } else {
+      NS_WARNING("APZC received more ContentResponseTimeout calls than it has unprocessed touch blocks\n");
+    }
   }
-
-  if (mContentResponseTimeoutTask) {
-    mContentResponseTimeoutTask->Cancel();
-    mContentResponseTimeoutTask = nullptr;
-  }
-
-  if (mState == WAITING_CONTENT_RESPONSE) {
-    if (!mTouchBlockState.mPreventDefault) {
-      SetState(NOTHING);
-    }
-
-    mHandlingTouchQueue = true;
-
-    while (!mTouchQueue.IsEmpty()) {
-      if (!mTouchBlockState.mPreventDefault) {
-        HandleInputEvent(mTouchQueue[0]);
-      }
-
-      if (mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_END ||
-          mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_CANCEL) {
-        mTouchQueue.RemoveElementAt(0);
+}
+
+void
+AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
+  AssertOnControllerThread();
+
+  mTouchBlockBalance--;
+  APZC_LOG("%p got a content response; balance %d\n", this, mTouchBlockBalance);
+  if (mTouchBlockBalance < 0) {
+    // Find the first touch block in the queue that hasn't already received
+    // its response from content, and notify it.
+    bool found = false;
+    for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
+      if (mTouchBlockQueue[i]->SetContentResponse(aPreventDefault)) {
+        found = true;
         break;
       }
-
-      mTouchQueue.RemoveElementAt(0);
     }
-
-    mHandlingTouchQueue = false;
+    if (found) {
+      ProcessPendingInputBlocks();
+    } else {
+      NS_WARNING("APZC received more ContentReceivedTouch calls than it has unprocessed touch blocks\n");
+    }
   }
 }
 
-bool AsyncPanZoomController::TouchActionAllowPinchZoom() {
-  if (!gfxPrefs::TouchActionEnabled()) {
-    return true;
-  }
-  // Pointer events specification implies all touch points to allow zoom
-  // to perform it.
-  for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
-    if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
-      return false;
+void
+AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
+  AssertOnControllerThread();
+
+  bool found = false;
+  for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
+    if (mTouchBlockQueue[i]->SetAllowedTouchBehaviors(aBehaviors)) {
+      found = true;
+      break;
     }
   }
-  return true;
+  if (found) {
+    ProcessPendingInputBlocks();
+  } else {
+    NS_WARNING("APZC received more SetAllowedTouchBehavior calls than it has unprocessed touch blocks\n");
+  }
 }
 
-bool AsyncPanZoomController::TouchActionAllowDoubleTapZoom() {
-  if (!gfxPrefs::TouchActionEnabled()) {
-    return true;
+void
+AsyncPanZoomController::ProcessPendingInputBlocks() {
+  AssertOnControllerThread();
+
+  while (true) {
+    TouchBlockState* curBlock = CurrentTouchBlock();
+    if (!curBlock->IsReadyForHandling()) {
+      break;
+    }
+
+    APZC_LOG("%p processing input block %p; preventDefault %d\n",
+        this, curBlock, curBlock->IsDefaultPrevented());
+    if (curBlock->IsDefaultPrevented()) {
+      SetState(NOTHING);
+      curBlock->DropEvents();
+    } else {
+      while (curBlock->HasEvents()) {
+        HandleInputEvent(curBlock->RemoveFirstEvent());
+      }
+    }
+    MOZ_ASSERT(!curBlock->HasEvents());
+
+    if (mTouchBlockQueue.Length() == 1) {
+      // If |curBlock| is the only touch block in the queue, then it is still
+      // active and we cannot remove it yet. We only know that a touch block is
+      // over when we start the next one. This block will be removed by the code
+      // in StartNewTouchBlock, where new touch blocks are added.
+      break;
+    }
+
+    // If we get here, we know there are more touch blocks in the queue after
+    // |curBlock|, so we can remove |curBlock| and try to process the next one.
+    APZC_LOG("%p discarding depleted touch block %p\n", this, curBlock);
+    mTouchBlockQueue.RemoveElementAt(0);
   }
-  for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
-    if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
-      return false;
+}
+
+TouchBlockState*
+AsyncPanZoomController::StartNewTouchBlock(bool aCopyAllowedTouchBehaviorFromCurrent)
+{
+  TouchBlockState* newBlock = new TouchBlockState();
+  if (gfxPrefs::TouchActionEnabled() && aCopyAllowedTouchBehaviorFromCurrent) {
+    newBlock->CopyAllowedTouchBehaviorsFrom(*CurrentTouchBlock());
+  }
+
+  // We're going to start a new block, so clear out any depleted blocks at the head of the queue.
+  // See corresponding comment in ProcessPendingInputBlocks.
+  while (!mTouchBlockQueue.IsEmpty()) {
+    if (mTouchBlockQueue[0]->IsReadyForHandling() && !mTouchBlockQueue[0]->HasEvents()) {
+      APZC_LOG("%p discarding depleted touch block %p\n", this, mTouchBlockQueue[0]);
+      mTouchBlockQueue.RemoveElementAt(0);
+    } else {
+      break;
     }
   }
-  return true;
+
+  // Add the new block to the queue.
+  mTouchBlockQueue.AppendElement(newBlock);
+  return newBlock;
 }
 
-AsyncPanZoomController::TouchBehaviorFlags
-AsyncPanZoomController::GetTouchBehavior(uint32_t touchIndex) {
-  if (touchIndex < mTouchBlockState.mAllowedTouchBehaviors.Length()) {
-    return mTouchBlockState.mAllowedTouchBehaviors[touchIndex];
-  }
-  return DefaultTouchBehavior;
+TouchBlockState*
+AsyncPanZoomController::CurrentTouchBlock()
+{
+  AssertOnControllerThread();
+
+  MOZ_ASSERT(!mTouchBlockQueue.IsEmpty());
+  return mTouchBlockQueue[0].get();
+}
+
+bool
+AsyncPanZoomController::HasReadyTouchBlock()
+{
+  return !mTouchBlockQueue.IsEmpty() && mTouchBlockQueue[0]->IsReadyForHandling();
 }
 
 AsyncPanZoomController::TouchBehaviorFlags
 AsyncPanZoomController::GetAllowedTouchBehavior(ScreenIntPoint& aPoint) {
   // Here we need to perform a hit testing over the touch-action regions attached to the
   // layer associated with current apzc.
   // Currently they are in progress, for more info see bug 928833.
   return AllowedTouchBehavior::UNKNOWN;
 }
 
-void AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
-  mTouchBlockState.mAllowedTouchBehaviors.Clear();
-  mTouchBlockState.mAllowedTouchBehaviors.AppendElements(aBehaviors);
-  mTouchBlockState.mAllowedTouchBehaviorSet = true;
-  CheckContentResponse();
-}
-
 void AsyncPanZoomController::SetState(PanZoomState aNewState) {
 
   PanZoomState oldState;
 
   // Intentional scoping for mutex
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
     oldState = mState;
@@ -2577,37 +2662,23 @@ void AsyncPanZoomController::SetState(Pa
     } else if (IsTransformingState(oldState) && !IsTransformingState(aNewState)) {
       controller->NotifyAPZStateChange(
           GetGuid(), APZStateChange::TransformEnd);
     }
   }
 }
 
 bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
-  return !(aState == NOTHING || aState == TOUCHING || aState == WAITING_CONTENT_RESPONSE);
+  return !(aState == NOTHING || aState == TOUCHING);
 }
 
 bool AsyncPanZoomController::IsPanningState(PanZoomState aState) {
   return (aState == PANNING || aState == PANNING_LOCKED_X || aState == PANNING_LOCKED_Y);
 }
 
-void AsyncPanZoomController::SetContentResponseTimer() {
-  if (!mContentResponseTimeoutTask) {
-    mContentResponseTimeoutTask =
-      NewRunnableMethod(this, &AsyncPanZoomController::TimeoutContentResponse);
-
-    PostDelayedTask(mContentResponseTimeoutTask, gfxPrefs::APZContentResponseTimeout());
-  }
-}
-
-void AsyncPanZoomController::TimeoutContentResponse() {
-  mContentResponseTimeoutTask = nullptr;
-  ContentReceivedTouch(false);
-}
-
 void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) {
   APZC_LOG("%p updating zoom constraints to %d %d %f %f\n", this, aConstraints.mAllowZoom,
     aConstraints.mAllowDoubleTapZoom, aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
   if (IsNaN(aConstraints.mMinZoom.scale) || IsNaN(aConstraints.mMaxZoom.scale)) {
     NS_WARNING("APZC received zoom constraints with NaN values; dropping...\n");
     return;
   }
   // inf float values and other bad cases should be sanitized by the code below.
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -10,16 +10,17 @@
 #include "CrossProcessMutex.h"
 #include "mozilla/layers/GeckoContentController.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/Atomics.h"
 #include "InputData.h"
 #include "Axis.h"
 #include "TaskThrottler.h"
 #include "gfx3DMatrix.h"
 
 #include "base/message_loop.h"
 
@@ -36,16 +37,17 @@ namespace layers {
 struct ScrollableLayerGuid;
 class CompositorParent;
 class GestureEventListener;
 class ContainerLayer;
 class PCompositorParent;
 struct ViewTransform;
 class AsyncPanZoomAnimation;
 class FlingAnimation;
+class TouchBlockState;
 
 /**
  * Controller for all panning and zooming logic. Any time a user input is
  * detected and it must be processed in some way to affect what the user sees,
  * it goes through here. Listens for any input event from InputData and can
  * optionally handle WidgetGUIEvent-derived touch events, but this must be done
  * on the main thread. Note that this class completely cross-platform.
  *
@@ -106,35 +108,30 @@ public:
   // These methods must only be called on the controller/UI thread.
   //
 
   /**
    * General handler for incoming input events. Manipulates the frame metrics
    * based on what type of input it is. For example, a PinchGestureEvent will
    * cause scaling. This should only be called externally to this class.
    * HandleInputEvent() should be used internally.
+   * This function returns nsEventStatus_eIgnore for events that are ignored,
+   * and nsEventStatus_eConsumeDoDefault for events that are queued for
+   * processing pending a content response.
    */
   nsEventStatus ReceiveInputEvent(const InputData& aEvent);
 
   /**
    * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
    * in. The actual animation is done on the compositor thread after being set
    * up.
    */
   void ZoomToRect(CSSRect aRect);
 
   /**
-   * If we have touch listeners, this should always be called when we know
-   * definitively whether or not content has preventDefaulted any touch events
-   * that have come in. If |aPreventDefault| is true, any touch events in the
-   * queue will be discarded.
-   */
-  void ContentReceivedTouch(bool aPreventDefault);
-
-  /**
    * Updates any zoom constraints contained in the <meta name="viewport"> tag.
    */
   void UpdateZoomConstraints(const ZoomConstraints& aConstraints);
 
   /**
    * Return the zoom constraints last set for this APZC (in the constructor
    * or in UpdateZoomConstraints()).
    */
@@ -299,25 +296,16 @@ public:
    * contains info about allowed touch behavior. If regions info isn't enough it returns
    * UNKNOWN value and we should switch to the fallback approach - asking content.
    * TODO: for now it's only a stub and returns hardcoded magic value. As soon as bug 928833
    * is done we should integrate its logic here.
    */
   TouchBehaviorFlags GetAllowedTouchBehavior(ScreenIntPoint& aPoint);
 
   /**
-   * Sets allowed touch behavior for current touch session.
-   * This method is invoked by the APZCTreeManager which in its turn invoked by
-   * the widget after performing touch-action values retrieving.
-   * Must be called after receiving the TOUCH_START even that started the
-   * touch session.
-   */
-  void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
-
-  /**
    * Returns whether this APZC is for an element marked with the 'scrollgrab'
    * attribute.
    */
   bool HasScrollgrab() const { return mFrameMetrics.GetHasScrollgrab(); }
 
   /**
    * Returns whether this APZC has room to be panned (in any direction).
    */
@@ -342,20 +330,16 @@ protected:
 
     CROSS_SLIDING_X,          /* Panning disabled while user does a horizontal gesture
                                  on a vertically-scrollable view. This used for the
                                  Windows Metro "cross-slide" gesture. */
     CROSS_SLIDING_Y,          /* as above for Y axis */
 
     PINCHING,                 /* nth touch-start, where n > 1. this mode allows pan and zoom */
     ANIMATING_ZOOM,           /* animated zoom to a new rect */
-    WAITING_CONTENT_RESPONSE, /* a state halfway between NOTHING and TOUCHING - the user has
-                                 put a finger down, but we don't yet know if a touch listener has
-                                 prevented the default actions yet and the allowed touch behavior
-                                 was not set yet. we still need to abort animations. */
     SNAP_BACK,                /* snap-back animation to relieve overscroll */
   };
 
   // Protected destructor, to discourage deletion outside of Release():
   ~AsyncPanZoomController();
 
   /**
    * Helper method for touches beginning. Sets everything up for panning and any
@@ -482,17 +466,17 @@ protected:
    * gets only the first one and assumes the rest are either missing or not
    * relevant.
    */
   ScreenIntPoint& GetFirstTouchScreenPoint(const MultiTouchInput& aEvent);
 
   /**
    * Sets the panning state basing on the pan direction angle and current touch-action value.
    */
-  void HandlePanningWithTouchAction(double angle, TouchBehaviorFlags value);
+  void HandlePanningWithTouchAction(double angle);
 
   /**
    * Sets the panning state ignoring the touch action value.
    */
   void HandlePanning(double angle);
 
   /**
    * Update the panning state and axis locks.
@@ -539,98 +523,24 @@ protected:
 
   /**
    * Gets the current frame metrics. This is *not* the Gecko copy stored in the
    * layers code.
    */
   const FrameMetrics& GetFrameMetrics() const;
 
   /**
-   * Sets the timer for content response to a series of touch events, if it
-   * hasn't been already. This is to prevent us from batching up touch events
-   * indefinitely in the case that content doesn't respond with whether or not
-   * it wants to preventDefault. When the timer is fired, the touch event queue
-   * will be flushed.
-   */
-  void SetContentResponseTimer();
-
-  /**
-   * Timeout function for content response. This should be called on a timer
-   * after we get our first touch event in a batch, under the condition that we
-   * waiting for response from content. If a notification comes indicating whether or not
-   * content preventDefaulted a series of touch events and touch behavior values are
-   * set before the timeout, the timeout should be cancelled.
-   */
-  void TimeoutContentResponse();
-
-  /**
    * Timeout function for mozbrowserasyncscroll event. Because we throttle
    * mozbrowserasyncscroll events in some conditions, this function ensures
    * that the last mozbrowserasyncscroll event will be fired after a period of
    * time.
    */
   void FireAsyncScrollOnTimeout();
 
 private:
-  // State related to a single touch block. Does not persist across touch blocks.
-  struct TouchBlockState {
-
-    TouchBlockState()
-      :  mAllowedTouchBehaviorSet(false),
-         mPreventDefault(false),
-         mPreventDefaultSet(false),
-         mSingleTapOccurred(false)
-    {}
-
-    // Values of allowed touch behavior for touch points of this touch block.
-    // Since there are maybe a few current active touch points per time (multitouch case)
-    // and each touch point should have its own value of allowed touch behavior- we're
-    // keeping an array of allowed touch behavior values, not the single value.
-    nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
-
-    // Specifies whether mAllowedTouchBehaviors is set for this touch events block.
-    bool mAllowedTouchBehaviorSet;
-
-    // Flag used to specify that content prevented the default behavior of this
-    // touch events block.
-    bool mPreventDefault;
-
-    // Specifies whether mPreventDefault property is set for this touch events block.
-    bool mPreventDefaultSet;
-
-    // Specifies whether a single tap event was generated during this touch block.
-    bool mSingleTapOccurred;
-  };
-
-  /*
-   * Returns whether current touch behavior values allow pinch-zooming.
-   */
-  bool TouchActionAllowPinchZoom();
-
-  /*
-   * Returns whether current touch behavior values allow double-tap-zooming.
-   */
-  bool TouchActionAllowDoubleTapZoom();
-
-  /*
-   * Returns allowed touch behavior from the mAllowedTouchBehavior array.
-   * In case apzc didn't receive touch behavior values within the timeout
-   * it returns default value.
-   */
-  TouchBehaviorFlags GetTouchBehavior(uint32_t touchIndex);
-
-  /**
-   * To move from the WAITING_CONTENT_RESPONSE state to TOUCHING one we need two
-   * conditions set: get content listeners response (whether they called preventDefault)
-   * and get allowed touch behaviors.
-   * This method checks both conditions and changes (or not changes) state
-   * appropriately.
-   */
-  void CheckContentResponse();
-
   /**
    * Helper to set the current state. Holds the monitor before actually setting
    * it and fires content controller events based on state changes. Always set
    * the state using this call, do not set it directly.
    */
   void SetState(PanZoomState aState);
 
   /**
@@ -729,20 +639,16 @@ private:
   // If we don't do this check, we don't get a ShadowLayersUpdated back.
   FrameMetrics mLastPaintRequestMetrics;
   // The last metrics that we actually sent to Gecko. This allows us to transform
   // inputs into a coordinate space that Gecko knows about. This assumes the pipe
   // through which input events and repaint requests are sent to Gecko operates
   // in a FIFO manner.
   FrameMetrics mLastDispatchedPaintMetrics;
 
-  nsTArray<MultiTouchInput> mTouchQueue;
-
-  CancelableTask* mContentResponseTimeoutTask;
-
   AxisX mX;
   AxisY mY;
 
   // This flag is set to true when we are in a axis-locked pan as a result of
   // the touch-action CSS property.
   bool mPanDirRestricted;
 
   // Most up-to-date constraints on zooming. These should always be reasonable
@@ -766,29 +672,111 @@ private:
   // The current offset drawn on the screen, it may not be sent since we have
   // throttling policy for mozbrowserasyncscroll event.
   CSSPoint mCurrentAsyncScrollOffset;
 
   // The delay task triggered by the throttling mozbrowserasyncscroll event
   // ensures the last mozbrowserasyncscroll event is always been fired.
   CancelableTask* mAsyncScrollTimeoutTask;
 
-  // Flag used to determine whether or not we should try to enter the
-  // WAITING_LISTENERS state. This is used in the case that we are processing a
-  // queued up event block. If set, this means that we are handling this queue
-  // and we don't want to queue the events back up again.
-  bool mHandlingTouchQueue;
-
-  // Stores information about the current touch block.
-  TouchBlockState mTouchBlockState;
-
   nsRefPtr<AsyncPanZoomAnimation> mAnimation;
 
   friend class Axis;
 
+  /* ===================================================================
+   * The functions and members in this section are used to manage
+   * blocks of touch events and the state needed to deal with content
+   * listeners.
+   */
+public:
+  /**
+   * This function is invoked by the APZCTreeManager which in turn is invoked
+   * by the widget when web content decides whether or not it wants to
+   * cancel a block of events. This automatically gets applied to the next
+   * block of events that has not yet been responded to. This function MUST
+   * be invoked exactly once for each touch block.
+   */
+  void ContentReceivedTouch(bool aPreventDefault);
+
+  /**
+   * Sets allowed touch behavior for current touch session.
+   * This method is invoked by the APZCTreeManager which in its turn invoked by
+   * the widget after performing touch-action values retrieving.
+   * Must be called after receiving the TOUCH_START even that started the
+   * touch session.
+   */
+  void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
+
+private:
+  void ScheduleContentResponseTimeout();
+  void ContentResponseTimeout();
+  /**
+   * Processes any pending input blocks that are ready for processing. There
+   * must be at least one input block in the queue when this function is called.
+   */
+  void ProcessPendingInputBlocks();
+  TouchBlockState* StartNewTouchBlock(bool aCopyAllowedTouchBehaviorFromCurrent);
+  TouchBlockState* CurrentTouchBlock();
+  bool HasReadyTouchBlock();
+
+private:
+  // The queue of touch blocks that have not yet been processed by this APZC.
+  // This member must only be accessed on the controller/UI thread.
+  nsTArray<UniquePtr<TouchBlockState>> mTouchBlockQueue;
+
+  // This variable requires some explanation. Strap yourself in.
+  //
+  // For each block of events, we do two things: (1) send the events to gecko and expect
+  // exactly one call to ContentReceivedTouch in return, and (2) kick off a timeout
+  // that triggers in case we don't hear from web content in a timely fashion.
+  // Since events are constantly coming in, we need to be able to handle more than one
+  // block of input events sitting in the queue.
+  //
+  // There are ordering restrictions on events that we can take advantage of, and that
+  // we need to abide by. Blocks of events in the queue will always be in the order that
+  // the user generated them. Responses we get from content will be in the same order as
+  // as the blocks of events in the queue. The timeout callbacks that have been posted
+  // will also fire in the same order as the blocks of events in the queue.
+  // HOWEVER, we may get multiple responses from content interleaved with multiple
+  // timeout expirations, and that interleaving is not predictable.
+  //
+  // Therefore, we need to make sure that for each block of events, we process the queued
+  // events exactly once, either when we get the response from content, or when the
+  // timeout expires (whichever happens first). There is no way to associate the timeout
+  // or response from content with a particular block of events other than via ordering.
+  //
+  // So, what we do to accomplish this is to track a "touch block balance", which is the
+  // number of timeout expirations that have fired, minus the number of content responses
+  // that have been received. (Think "balance" as in teeter-totter balance). This
+  // value is:
+  // - zero when we are in a state where the next content response we expect to receive
+  //   and the next timeout expiration we expect to fire both correspond to the next
+  //   unprocessed block of events in the queue.
+  // - negative when we are in a state where we have received more content responses than
+  //   timeout expirations. This means that the next content repsonse we receive will
+  //   correspond to the first unprocessed block, but the next n timeout expirations need
+  //   to be ignored as they are for blocks we have already processed. (n is the absolute
+  //   value of the balance.)
+  // - positive when we are in a state where we have received more timeout expirations
+  //   than content responses. This means that the next timeout expiration that we will
+  //   receive will correspond to the first unprocessed block, but the next n content
+  //   responses need to be ignored as they are for blocks we have already processed.
+  //   (n is the absolute value of the balance.)
+  //
+  // Note that each touch block internally carries flags that indicate whether or not it
+  // has received a content response and/or timeout expiration. However, we cannot rely
+  // on that alone to deliver these notifications to the right input block, because
+  // once an input block has been processed, it can potentially be removed from the queue.
+  // Therefore the information in that block is lost. An alternative approach would
+  // be to keep around those blocks until they have received both the content response
+  // and timeout expiration, but that involves a higher level of memory usage.
+  //
+  // This member must only be accessed on the controller/UI thread.
+  int32_t mTouchBlockBalance;
+
 
   /* ===================================================================
    * The functions and members in this section are used to manage
    * fling animations and handling overscroll during a fling.
    */
 public:
   /**
    * Take over a fling with the given velocity from another APZC. Used for
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/TouchBlockState.cpp
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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 "TouchBlockState.h"
+#include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
+#include "gfxPrefs.h"                       // for gfxPrefs
+
+#define TBS_LOG(...)
+// #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Default touch behavior (is used when no touch behavior is set).
+ */
+static const TouchBlockState::TouchBehaviorFlags kDefaultTouchBehavior =
+    AllowedTouchBehavior::VERTICAL_PAN |
+    AllowedTouchBehavior::HORIZONTAL_PAN |
+    AllowedTouchBehavior::PINCH_ZOOM |
+    AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
+
+TouchBlockState::TouchBlockState()
+  : mAllowedTouchBehaviorSet(false)
+  , mPreventDefault(false)
+  , mContentResponded(false)
+  , mContentResponseTimerExpired(false)
+  , mSingleTapOccurred(false)
+{
+  TBS_LOG("Creating %p\n", this);
+}
+
+bool
+TouchBlockState::SetContentResponse(bool aPreventDefault)
+{
+  if (mContentResponded) {
+    return false;
+  }
+  TBS_LOG("%p got content response %d with timer expired %d\n",
+    this, aPreventDefault, mContentResponseTimerExpired);
+  if (!mContentResponseTimerExpired) {
+    mPreventDefault = aPreventDefault;
+  }
+  mContentResponded = true;
+  return true;
+}
+
+bool
+TouchBlockState::TimeoutContentResponse()
+{
+  if (mContentResponseTimerExpired) {
+    return false;
+  }
+  TBS_LOG("%p got content timer expired with response received %d\n",
+    this, mContentResponded);
+  if (!mContentResponded) {
+    mPreventDefault = false;
+  }
+  mContentResponseTimerExpired = true;
+  return true;
+}
+
+bool
+TouchBlockState::SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors)
+{
+  if (mAllowedTouchBehaviorSet) {
+    return false;
+  }
+  TBS_LOG("%p got allowed touch behaviours for %d points\n", this, aBehaviors.Length());
+  mAllowedTouchBehaviors.AppendElements(aBehaviors);
+  mAllowedTouchBehaviorSet = true;
+  return true;
+}
+
+bool
+TouchBlockState::CopyAllowedTouchBehaviorsFrom(const TouchBlockState& aOther)
+{
+  TBS_LOG("%p copying allowed touch behaviours from %p\n", this, &aOther);
+  MOZ_ASSERT(aOther.mAllowedTouchBehaviorSet);
+  return SetAllowedTouchBehaviors(aOther.mAllowedTouchBehaviors);
+}
+
+bool
+TouchBlockState::IsReadyForHandling() const
+{
+  // TODO: for long-tap blocks we probably don't need the touch behaviour?
+  if (gfxPrefs::TouchActionEnabled() && !mAllowedTouchBehaviorSet) {
+    return false;
+  }
+  if (!mContentResponded && !mContentResponseTimerExpired) {
+    return false;
+  }
+  return true;
+}
+
+bool
+TouchBlockState::IsDefaultPrevented() const
+{
+  MOZ_ASSERT(mContentResponded || mContentResponseTimerExpired);
+  return mPreventDefault;
+}
+
+void
+TouchBlockState::SetSingleTapOccurred()
+{
+  TBS_LOG("%p setting single-tap occurred\n", this);
+  mSingleTapOccurred = true;
+}
+
+bool
+TouchBlockState::SingleTapOccurred() const
+{
+  return mSingleTapOccurred;
+}
+
+bool
+TouchBlockState::HasEvents() const
+{
+  return !mEvents.IsEmpty();
+}
+
+void
+TouchBlockState::AddEvent(const MultiTouchInput& aEvent)
+{
+  TBS_LOG("%p adding event of type %d\n", this, aEvent.mType);
+  mEvents.AppendElement(aEvent);
+}
+
+void
+TouchBlockState::DropEvents()
+{
+  TBS_LOG("%p dropping %d events\n", this, mEvents.Length());
+  mEvents.Clear();
+}
+
+MultiTouchInput
+TouchBlockState::RemoveFirstEvent()
+{
+  MOZ_ASSERT(!mEvents.IsEmpty());
+  TBS_LOG("%p returning first of %d events\n", this, mEvents.Length());
+  MultiTouchInput event = mEvents[0];
+  mEvents.RemoveElementAt(0);
+  return event;
+}
+
+bool
+TouchBlockState::TouchActionAllowsPinchZoom() const
+{
+  if (!gfxPrefs::TouchActionEnabled()) {
+    return true;
+  }
+  // Pointer events specification requires that all touch points allow zoom.
+  for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
+    if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool
+TouchBlockState::TouchActionAllowsDoubleTapZoom() const
+{
+  if (!gfxPrefs::TouchActionEnabled()) {
+    return true;
+  }
+  for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
+    if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool
+TouchBlockState::TouchActionAllowsPanningX() const
+{
+  if (!gfxPrefs::TouchActionEnabled()) {
+    return true;
+  }
+  if (mAllowedTouchBehaviors.IsEmpty()) {
+    return false;
+  }
+  TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
+  return (flags & AllowedTouchBehavior::HORIZONTAL_PAN);
+}
+
+bool
+TouchBlockState::TouchActionAllowsPanningY() const
+{
+  if (!gfxPrefs::TouchActionEnabled()) {
+    return true;
+  }
+  if (mAllowedTouchBehaviors.IsEmpty()) {
+    return false;
+  }
+  TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
+  return (flags & AllowedTouchBehavior::VERTICAL_PAN);
+}
+
+bool
+TouchBlockState::TouchActionAllowsPanningXY() const
+{
+  if (!gfxPrefs::TouchActionEnabled()) {
+    return true;
+  }
+  if (mAllowedTouchBehaviors.IsEmpty()) {
+    return false;
+  }
+  TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
+  return (flags & AllowedTouchBehavior::HORIZONTAL_PAN)
+      && (flags & AllowedTouchBehavior::VERTICAL_PAN);
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/TouchBlockState.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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 mozilla_layers_TouchBlockState_h
+#define mozilla_layers_TouchBlockState_h
+
+#include "nsTArray.h"                       // for nsTArray
+#include "InputData.h"                      // for MultiTouchInput
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This class represents a single touch block. A touch block is
+ * a set of touch events that can be cancelled by web content via
+ * touch event listeners.
+ *
+ * Every touch-start event creates a new touch block. In this case, the
+ * touch block consists of the touch-start, followed by all touch events
+ * up to but not including the next touch-start (except in the case where
+ * a long-tap happens, see below). Note that in particular we cannot know
+ * when a touch block ends until the next one is started. Most touch
+ * blocks are created by receipt of a touch-start event.
+ *
+ * Every long-tap event also creates a new touch block, since it can also
+ * be consumed by web content. In this case, when the long-tap event is
+ * dispatched to web content, a new touch block is started to hold the remaining
+ * touch events, up to but not including the next touch start (or long-tap).
+ *
+ * Conceptually, each touch block can be cancelled by web content, and
+ * this information is stored in the mPreventDefault flag. Because web
+ * content runs on the Gecko main thread, we cannot always wait for web content's
+ * response. Instead, there is a timeout that sets this flag in the case
+ * where web content doesn't respond in time. The mContentResponded
+ * and mContentResponseTimerExpired flags indicate which of these scenarios
+ * occurred.
+ *
+ * Additionally, if touch-action is enabled, each touch block should
+ * have a set of allowed touch behavior flags; one for each touch point.
+ * This also requires running code on the Gecko main thread, and so may
+ * be populated with some latency. The mAllowedTouchBehaviorSet and
+ * mAllowedTouchBehaviors variables track this information.
+ */
+class TouchBlockState
+{
+public:
+  typedef uint32_t TouchBehaviorFlags;
+
+  TouchBlockState();
+
+  /**
+   * Record whether or not content cancelled this block of events.
+   * @param aPreventDefault true iff the block is cancelled.
+   * @return false if this block has already received a response from
+   *         web content, true if not.
+   */
+  bool SetContentResponse(bool aPreventDefault);
+  /**
+   * Record that content didn't respond in time.
+   * @return false if this block already timed out, true if not.
+   */
+  bool TimeoutContentResponse();
+  /**
+   * Set the allowed touch behavior flags for this block.
+   * @return false if this block already has these flags set, true if not.
+   */
+  bool SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors);
+  /**
+   * Copy the allowed touch behavior flags from another block.
+   * @return false if this block already has these flags set, true if not.
+   */
+  bool CopyAllowedTouchBehaviorsFrom(const TouchBlockState& aOther);
+
+  /**
+   * @return true iff this block has received all the information needed
+   *         to properly dispatch the events in the block.
+   */
+  bool IsReadyForHandling() const;
+  /**
+   * @return true iff web content cancelled this block of events.
+   */
+  bool IsDefaultPrevented() const;
+
+  /**
+   * Set a flag that indicates that this touch block triggered a single tap event.
+   */
+  void SetSingleTapOccurred();
+  /**
+   * @return true iff SetSingleTapOccurred was previously called on this block.
+   */
+  bool SingleTapOccurred() const;
+
+  /**
+   * @return true iff there are pending events in this touch block.
+   */
+  bool HasEvents() const;
+  /**
+   * Add a new touch event to the queue of events in this input block.
+   */
+  void AddEvent(const MultiTouchInput& aEvent);
+  /**
+   * Throw away all the events in this input block.
+   */
+  void DropEvents();
+  /**
+   * @return the first event in the queue. The event is removed from the queue
+   *         before it is returned.
+   */
+  MultiTouchInput RemoveFirstEvent();
+
+  /**
+   * @return false iff touch-action is enabled and the allowed touch behaviors for
+   *         this touch block do not allow pinch-zooming.
+   */
+  bool TouchActionAllowsPinchZoom() const;
+  /**
+   * @return false iff touch-action is enabled and the allowed touch behaviors for
+   *         this touch block do not allow double-tap zooming.
+   */
+  bool TouchActionAllowsDoubleTapZoom() const;
+  /**
+   * @return false iff touch-action is enabled and the allowed touch behaviors for
+   *         the first touch point do not allow panning in the specified direction(s).
+   */
+  bool TouchActionAllowsPanningX() const;
+  bool TouchActionAllowsPanningY() const;
+  bool TouchActionAllowsPanningXY() const;
+
+private:
+  nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
+  bool mAllowedTouchBehaviorSet;
+  bool mPreventDefault;
+  bool mContentResponded;
+  bool mContentResponseTimerExpired;
+  bool mSingleTapOccurred;
+  nsTArray<MultiTouchInput> mEvents;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_TouchBlockState_h
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -230,16 +230,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
         ]
 
 UNIFIED_SOURCES += [
     'apz/src/APZCTreeManager.cpp',
     'apz/src/AsyncPanZoomController.cpp',
     'apz/src/Axis.cpp',
     'apz/src/GestureEventListener.cpp',
     'apz/src/TaskThrottler.cpp',
+    'apz/src/TouchBlockState.cpp',
     'apz/testutil/APZTestData.cpp',
     'apz/util/ActiveElementManager.cpp',
     'apz/util/APZCCallbackHelper.cpp',
     'basic/BasicCanvasLayer.cpp',
     'basic/BasicColorLayer.cpp',
     'basic/BasicCompositor.cpp',
     'basic/BasicContainerLayer.cpp',
     'basic/BasicImages.cpp',
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -98,16 +98,18 @@ if CONFIG['INTEL_ARCHITECTURE'] and CONF
     SOURCES['trunk/src/opts/SkBlitRect_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
     SOURCES['trunk/src/opts/SkBlitRow_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
     SOURCES['trunk/src/opts/SkBlurImage_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
     SOURCES['trunk/src/opts/SkMorphology_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
     SOURCES['trunk/src/opts/SkUtils_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
 elif CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC'] and CONFIG['BUILD_ARM_NEON']:
     DEFINES['__ARM_HAVE_OPTIONAL_NEON_SUPPORT'] = 1
     DEFINES['USE_ANDROID_NDK_CPU_FEATURES'] = 0
+elif CONFIG['CLANG_CL']:
+    SOURCES['trunk/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['-mssse3']
 
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 DEFINES['GR_IMPLEMENTATION'] = 1
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-overloaded-virtual',
         '-Wno-unused-function',
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -906,16 +906,18 @@ if CONFIG['INTEL_ARCHITECTURE'] and CONF
     SOURCES['trunk/src/opts/SkBlitRect_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
     SOURCES['trunk/src/opts/SkBlitRow_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
     SOURCES['trunk/src/opts/SkBlurImage_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
     SOURCES['trunk/src/opts/SkMorphology_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
     SOURCES['trunk/src/opts/SkUtils_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
 elif CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC'] and CONFIG['BUILD_ARM_NEON']:
     DEFINES['__ARM_HAVE_OPTIONAL_NEON_SUPPORT'] = 1
     DEFINES['USE_ANDROID_NDK_CPU_FEATURES'] = 0
+elif CONFIG['CLANG_CL']:
+    SOURCES['trunk/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['-mssse3']
 
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 DEFINES['GR_IMPLEMENTATION'] = 1
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-overloaded-virtual',
         '-Wno-unused-function',
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -132,17 +132,22 @@ public:
     : AsyncPanZoomController(aLayersId, aTreeManager, aMcc, aBehavior)
   {}
 
   void SetFrameMetrics(const FrameMetrics& metrics) {
     ReentrantMonitorAutoEnter lock(mMonitor);
     mFrameMetrics = metrics;
   }
 
-  FrameMetrics GetFrameMetrics() {
+  FrameMetrics& GetFrameMetrics() {
+    ReentrantMonitorAutoEnter lock(mMonitor);
+    return mFrameMetrics;
+  }
+
+  const FrameMetrics& GetFrameMetrics() const {
     ReentrantMonitorAutoEnter lock(mMonitor);
     return mFrameMetrics;
   }
 
   void AssertStateIsReset() {
     ReentrantMonitorAutoEnter lock(mMonitor);
     EXPECT_EQ(NOTHING, mState);
   }
@@ -195,21 +200,19 @@ protected:
     apzc->SetFrameMetrics(TestFrameMetrics());
   }
 
   virtual void TearDown()
   {
     apzc->Destroy();
   }
 
-  void UseTouchListenerMetrics()
+  void SetMayHaveTouchListeners()
   {
-    FrameMetrics frameMetrics(TestFrameMetrics());
-    frameMetrics.mMayHaveTouchListeners = true;
-    apzc->SetFrameMetrics(frameMetrics);
+    apzc->GetFrameMetrics().mMayHaveTouchListeners = true;
   }
 
   void MakeApzcZoomable()
   {
     apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25f), CSSToScreenScale(4.0f)));
   }
 
   void MakeApzcUnzoomable()
@@ -344,28 +347,31 @@ ApzcPanAndCheckStatus(AsyncPanZoomContro
                       bool expectIgnoredPan,
                       bool hasTouchListeners,
                       nsTArray<uint32_t>* aAllowedTouchBehaviors)
 {
   nsEventStatus statuses[4]; // down, move, move, up
   ApzcPan(aApzc, aTreeManager, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses);
 
   nsEventStatus touchStartStatus;
-  if (hasTouchListeners) {
+  if (hasTouchListeners || gfxPrefs::TouchActionEnabled()) {
     // APZC shouldn't consume the start event now, instead queueing it up
-    // waiting for content's response.
-    touchStartStatus = nsEventStatus_eIgnore;
+    // waiting for content's response and/or allowed behavior.
+    touchStartStatus = nsEventStatus_eConsumeDoDefault;
   } else {
     // APZC should go into the touching state and therefore consume the event.
     touchStartStatus = nsEventStatus_eConsumeNoDefault;
   }
   EXPECT_EQ(touchStartStatus, statuses[0]);
 
   nsEventStatus touchMoveStatus;
-  if (expectIgnoredPan) {
+  if (hasTouchListeners) {
+    // APZC will queue up this event while waiting for content's response.
+    touchMoveStatus = nsEventStatus_eConsumeDoDefault;
+  } else if (expectIgnoredPan) {
     // APZC should ignore panning, be in TOUCHING state and therefore return eIgnore.
     // The same applies to all consequent touch move events.
     touchMoveStatus = nsEventStatus_eIgnore;
   } else {
     // APZC should go into the panning state and therefore consume the event.
     touchMoveStatus = nsEventStatus_eConsumeNoDefault;
   }
   EXPECT_EQ(touchMoveStatus, statuses[1]);
@@ -487,28 +493,32 @@ ApzcPinchWithTouchInputAndCheckStatus(As
 class APZCPinchTester : public APZCBasicTester {
 public:
   APZCPinchTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
     : APZCBasicTester(aGestureBehavior)
   {
   }
 
 protected:
-  void DoPinchTest(bool aShouldTriggerPinch,
-                   nsTArray<uint32_t> *aAllowedTouchBehaviors = nullptr)
+  FrameMetrics GetPinchableFrameMetrics()
   {
     FrameMetrics fm;
     fm.mViewport = CSSRect(0, 0, 980, 480);
     fm.mCompositionBounds = ParentLayerRect(200, 200, 100, 200);
     fm.mScrollableRect = CSSRect(0, 0, 980, 1000);
     fm.SetScrollOffset(CSSPoint(300, 300));
     fm.SetZoom(CSSToScreenScale(2.0));
-    apzc->SetFrameMetrics(fm);
     // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
+    return fm;
+  }
 
+  void DoPinchTest(bool aShouldTriggerPinch,
+                   nsTArray<uint32_t> *aAllowedTouchBehaviors = nullptr)
+  {
+    apzc->SetFrameMetrics(GetPinchableFrameMetrics());
     MakeApzcZoomable();
 
     if (aShouldTriggerPinch) {
       EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
       EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
     } else {
       EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtMost(2));
       EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
@@ -516,17 +526,17 @@ protected:
 
     int touchInputId = 0;
     if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
       ApzcPinchWithTouchInputAndCheckStatus(apzc, 250, 300, 1.25, touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
     } else {
       ApzcPinchWithPinchInputAndCheckStatus(apzc, 250, 300, 1.25, aShouldTriggerPinch);
     }
 
-    fm = apzc->GetFrameMetrics();
+    FrameMetrics fm = apzc->GetFrameMetrics();
 
     if (aShouldTriggerPinch) {
       // the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
       EXPECT_EQ(2.5f, fm.GetZoom().scale);
       EXPECT_EQ(305, fm.GetScrollOffset().x);
       EXPECT_EQ(310, fm.GetScrollOffset().y);
     } else {
       // The frame metrics should stay the same since touch-action:none makes
@@ -599,16 +609,42 @@ TEST_F(APZCPinchGestureDetectorTester, P
 TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNotAllowZoom) {
   SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
   nsTArray<uint32_t> behaviors;
   behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
   behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
   DoPinchTest(false, &behaviors);
 }
 
+TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) {
+  FrameMetrics originalMetrics = GetPinchableFrameMetrics();
+  apzc->SetFrameMetrics(originalMetrics);
+
+  SetMayHaveTouchListeners();
+  MakeApzcZoomable();
+
+  int touchInputId = 0;
+  ApzcPinchWithTouchInput(apzc, 250, 300, 1.25, touchInputId);
+
+  // Send the prevent-default notification for the touch block
+  apzc->ContentReceivedTouch(true);
+
+  // Run all pending tasks (this should include at least the
+  // prevent-default timer).
+  EXPECT_LE(1, mcc->RunThroughDelayedTasks());
+
+  // verify the metrics didn't change (i.e. the pinch was ignored)
+  FrameMetrics fm = apzc->GetFrameMetrics();
+  EXPECT_EQ(originalMetrics.GetZoom().scale, fm.GetZoom().scale);
+  EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
+  EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
+
+  apzc->AssertStateIsReset();
+}
+
 TEST_F(APZCBasicTester, Overzoom) {
   // the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
   FrameMetrics fm;
   fm.mViewport = CSSRect(0, 0, 100, 100);
   fm.mCompositionBounds = ParentLayerRect(0, 0, 100, 100);
   fm.mScrollableRect = CSSRect(0, 0, 125, 150);
   fm.SetScrollOffset(CSSPoint(10, 0));
   fm.SetZoom(CSSToScreenScale(1.0));
@@ -773,16 +809,45 @@ protected:
 
     // Pan back
     ApzcPanAndCheckStatus(apzc, tm, time, touchEnd, touchStart, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
     apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
     EXPECT_EQ(ScreenPoint(), pointOut);
     EXPECT_EQ(ViewTransform(), viewTransformOut);
   }
+
+  void DoPanWithPreventDefaultTest()
+  {
+    SetMayHaveTouchListeners();
+
+    int time = 0;
+    int touchStart = 50;
+    int touchEnd = 10;
+    ScreenPoint pointOut;
+    ViewTransform viewTransformOut;
+
+    // Pan down
+    nsTArray<uint32_t> allowedTouchBehaviors;
+    allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
+    ApzcPanAndCheckStatus(apzc, tm, time, touchStart, touchEnd, true, true, &allowedTouchBehaviors);
+
+    // Send the signal that content has handled and preventDefaulted the touch
+    // events. This flushes the event queue.
+    apzc->ContentReceivedTouch(true);
+    // Run all pending tasks (this should include at least the
+    // prevent-default timer).
+    EXPECT_LE(1, mcc->RunThroughDelayedTasks());
+
+    apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
+    EXPECT_EQ(ScreenPoint(), pointOut);
+    EXPECT_EQ(ViewTransform(), viewTransformOut);
+
+    apzc->AssertStateIsReset();
+  }
 };
 
 TEST_F(APZCPanningTester, Pan) {
   DoPanTest(true, mozilla::layers::AllowedTouchBehavior::NONE);
 }
 
 // In the each of the following 4 pan tests we are performing two pan gestures: vertical pan from top
 // to bottom and back - from bottom to top.
@@ -805,38 +870,24 @@ TEST_F(APZCPanningTester, PanWithTouchAc
   DoPanTest(false, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN);
 }
 
 TEST_F(APZCPanningTester, PanWithTouchActionPanY) {
   SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
   DoPanTest(true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
 }
 
-TEST_F(APZCBasicTester, PanWithPreventDefault) {
+TEST_F(APZCPanningTester, PanWithPreventDefaultAndTouchAction) {
   SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
-  UseTouchListenerMetrics();
-
-  int time = 0;
-  int touchStart = 50;
-  int touchEnd = 10;
-  ScreenPoint pointOut;
-  ViewTransform viewTransformOut;
+  DoPanWithPreventDefaultTest();
+}
 
-  // Pan down
-  nsTArray<uint32_t> allowedTouchBehaviors;
-  allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
-  ApzcPanAndCheckStatus(apzc, tm, time, touchStart, touchEnd, true, true, &allowedTouchBehaviors);
-
-  // Send the signal that content has handled and preventDefaulted the touch
-  // events. This flushes the event queue.
-  apzc->ContentReceivedTouch(true);
-
-  apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
-  EXPECT_EQ(ScreenPoint(), pointOut);
-  EXPECT_EQ(ViewTransform(), viewTransformOut);
+TEST_F(APZCPanningTester, PanWithPreventDefault) {
+  SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
+  DoPanWithPreventDefaultTest();
 }
 
 TEST_F(APZCBasicTester, Fling) {
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   int time = 0;
   int touchStart = 50;
@@ -1031,64 +1082,75 @@ TEST_F(APZCGestureDetectorTester, Medium
 class APZCLongPressTester : public APZCGestureDetectorTester {
 protected:
   void DoLongPressTest(uint32_t aBehavior) {
     MakeApzcUnzoomable();
 
     int time = 0;
 
     nsEventStatus status = ApzcDown(apzc, 10, 10, time);
-    EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
+    if (gfxPrefs::TouchActionEnabled()) {
+      // If touch-action is enabled, then the event is queued until the
+      // allowed touch behavior is set.
+      EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+    } else {
+      // Otherwise, it is processed immediately.
+      EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
+    }
 
-    // SetAllowedTouchBehavior() must be called after sending touch-start.
-    nsTArray<uint32_t> allowedTouchBehaviors;
-    allowedTouchBehaviors.AppendElement(aBehavior);
-    apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
+    if (gfxPrefs::TouchActionEnabled()) {
+      // SetAllowedTouchBehavior() must be called after sending touch-start.
+      nsTArray<uint32_t> allowedTouchBehaviors;
+      allowedTouchBehaviors.AppendElement(aBehavior);
+      apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
+    }
+    // Have content "respond" to the touchstart
+    apzc->ContentReceivedTouch(false);
 
     MockFunction<void(std::string checkPointName)> check;
 
     {
       InSequence s;
 
       EXPECT_CALL(check, Call("preHandleLongTap"));
       EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
       EXPECT_CALL(check, Call("postHandleLongTap"));
 
       EXPECT_CALL(check, Call("preHandleLongTapUp"));
       EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
       EXPECT_CALL(check, Call("postHandleLongTapUp"));
     }
 
+    // There is a longpress event scheduled on a timeout
     mcc->CheckHasDelayedTask();
 
     // Manually invoke the longpress while the touch is currently down.
     check.Call("preHandleLongTap");
     mcc->RunDelayedTask();
     check.Call("postHandleLongTap");
 
     // Destroy pending MAX_TAP timeout task
     mcc->DestroyOldestTask();
-    // There should be a TimeoutContentResponse task in the queue still
-    // Clear the waiting-for-content timeout task, then send the signal that
-    // content has handled this long tap. This takes the place of the
-    // "contextmenu" event.
+
+    // Dispatching the longpress event starts a new touch block, which
+    // needs a new content response and also has a pending timeout task
+    // in the queue. Deal with those here. We do the content response first
+    // with preventDefault=false, and then we run the timeout task which
+    // "loses the race" and does nothing.
+    apzc->ContentReceivedTouch(false);
     mcc->CheckHasDelayedTask();
-    mcc->ClearDelayedTask();
-    apzc->ContentReceivedTouch(true);
+    mcc->RunDelayedTask();
 
     time += 1000;
 
+    // Finally, simulate lifting the finger. Since the long-press wasn't
+    // prevent-defaulted, we should get a long-tap-up event.
+    check.Call("preHandleLongTapUp");
     status = ApzcUp(apzc, 10, 10, time);
     EXPECT_EQ(nsEventStatus_eIgnore, status);
-
-    // To get a LongTapUp event, we must kick APZC to flush its event queue. This
-    // would normally happen if we had a (Tab|RenderFrame)(Parent|Child)
-    // mechanism.
-    check.Call("preHandleLongTapUp");
-    apzc->ContentReceivedTouch(false);
     check.Call("postHandleLongTapUp");
 
     apzc->AssertStateIsReset();
   }
 
   void DoLongPressPreventDefaultTest(uint32_t aBehavior) {
     MakeApzcUnzoomable();
 
@@ -1096,22 +1158,33 @@ protected:
     EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
 
     int touchX = 10,
         touchStartY = 10,
         touchEndY = 50;
 
     int time = 0;
     nsEventStatus status = ApzcDown(apzc, touchX, touchStartY, time);
-    EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
+    if (gfxPrefs::TouchActionEnabled()) {
+      // If touch-action is enabled, then the event is queued until the
+      // allowed touch behavior is set.
+      EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
+    } else {
+      // Otherwise, it is processed immediately.
+      EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
+    }
 
-    // SetAllowedTouchBehavior() must be called after sending touch-start.
-    nsTArray<uint32_t> allowedTouchBehaviors;
-    allowedTouchBehaviors.AppendElement(aBehavior);
-    apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
+    if (gfxPrefs::TouchActionEnabled()) {
+      // SetAllowedTouchBehavior() must be called after sending touch-start.
+      nsTArray<uint32_t> allowedTouchBehaviors;
+      allowedTouchBehaviors.AppendElement(aBehavior);
+      apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
+    }
+    // Have content "respond" to the touchstart
+    apzc->ContentReceivedTouch(false);
 
     MockFunction<void(std::string checkPointName)> check;
 
     {
       InSequence s;
 
       EXPECT_CALL(check, Call("preHandleLongTap"));
       EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(touchX, touchStartY), 0, apzc->GetGuid())).Times(1);
@@ -1122,38 +1195,37 @@ protected:
 
     // Manually invoke the longpress while the touch is currently down.
     check.Call("preHandleLongTap");
     mcc->RunDelayedTask();
     check.Call("postHandleLongTap");
 
     // Destroy pending MAX_TAP timeout task
     mcc->DestroyOldestTask();
-    // Clear the waiting-for-content timeout task, then send the signal that
-    // content has handled this long tap. This takes the place of the
-    // "contextmenu" event.
-    mcc->ClearDelayedTask();
+
+    // There should be a TimeoutContentResponse task in the queue still,
+    // waiting for the response from the longtap event dispatched above.
+    // Send the signal that content has handled the long-tap, and then run
+    // the timeout task (it will be a no-op because the content "wins" the
+    // race. This takes the place of the "contextmenu" event.
     apzc->ContentReceivedTouch(true);
+    mcc->CheckHasDelayedTask();
+    mcc->RunDelayedTask();
 
     time += 1000;
 
     MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, time, TimeStamp(), 0);
     mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
     status = apzc->ReceiveInputEvent(mti);
     EXPECT_EQ(nsEventStatus_eIgnore, status);
 
     EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(0);
     status = ApzcUp(apzc, touchX, touchEndY, time);
     EXPECT_EQ(nsEventStatus_eIgnore, status);
 
-    // Flush the event queue. Once the "contextmenu" event is handled, any touch
-    // events that come from the same series of start->n*move->end events should
-    // be discarded, even if only the "contextmenu" event is preventDefaulted.
-    apzc->ContentReceivedTouch(false);
-
     ScreenPoint pointOut;
     ViewTransform viewTransformOut;
     apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
     EXPECT_EQ(ScreenPoint(), pointOut);
     EXPECT_EQ(ViewTransform(), viewTransformOut);
 
     apzc->AssertStateIsReset();
@@ -1177,16 +1249,128 @@ TEST_F(APZCLongPressTester, LongPressPre
 
 TEST_F(APZCLongPressTester, LongPressPreventDefaultWithTouchAction) {
   SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
   DoLongPressPreventDefaultTest(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
                                 | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
                                 | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
 }
 
+static void
+ApzcDoubleTap(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime,
+              nsEventStatus (*aOutEventStatuses)[4] = nullptr)
+{
+  nsEventStatus status = ApzcDown(aApzc, aX, aY, aTime);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[0] = status;
+  }
+  aTime += 10;
+  status = ApzcUp(aApzc, aX, aY, aTime);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[1] = status;
+  }
+  aTime += 10;
+  status = ApzcDown(aApzc, aX, aY, aTime);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[2] = status;
+  }
+  aTime += 10;
+  status = ApzcUp(aApzc, aX, aY, aTime);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[3] = status;
+  }
+}
+
+static void
+ApzcDoubleTapAndCheckStatus(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime)
+{
+  nsEventStatus statuses[4];
+  ApzcDoubleTap(aApzc, aX, aY, aTime, &statuses);
+  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
+  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
+  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]);
+  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[3]);
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTap) {
+  SetMayHaveTouchListeners();
+  MakeApzcZoomable();
+
+  EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
+  EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+
+  int time = 0;
+  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
+
+  // responses to the two touchstarts
+  apzc->ContentReceivedTouch(false);
+  apzc->ContentReceivedTouch(false);
+
+  while (mcc->RunThroughDelayedTasks());
+
+  apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
+  SetMayHaveTouchListeners();
+  MakeApzcUnzoomable();
+
+  EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(2);
+  EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
+
+  int time = 0;
+  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
+
+  // responses to the two touchstarts
+  apzc->ContentReceivedTouch(false);
+  apzc->ContentReceivedTouch(false);
+
+  while (mcc->RunThroughDelayedTasks());
+
+  apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) {
+  SetMayHaveTouchListeners();
+  MakeApzcZoomable();
+
+  EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
+  EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
+
+  int time = 0;
+  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
+
+  // responses to the two touchstarts
+  apzc->ContentReceivedTouch(true);
+  apzc->ContentReceivedTouch(false);
+
+  while (mcc->RunThroughDelayedTasks());
+
+  apzc->AssertStateIsReset();
+}
+
+TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
+  SetMayHaveTouchListeners();
+  MakeApzcZoomable();
+
+  EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
+  EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
+
+  int time = 0;
+  ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time);
+
+  // responses to the two touchstarts
+  apzc->ContentReceivedTouch(true);
+  apzc->ContentReceivedTouch(true);
+
+  while (mcc->RunThroughDelayedTasks());
+
+  apzc->AssertStateIsReset();
+}
+
 // Layer tree for HitTesting1
 static already_AddRefed<mozilla::layers::Layer>
 CreateTestLayerTree1(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Layer> >& aLayers) {
   const char* layerTreeSyntax = "c(ttcc)";
   // LayerID                     0 1234
   nsIntRegion layerVisibleRegion[] = {
     nsIntRegion(nsIntRect(0,0,100,100)),
     nsIntRegion(nsIntRect(0,0,100,100)),
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -95,16 +95,19 @@ AssertGCThingHasType(js::gc::Cell *cell,
 } /* namespace js */
 
 namespace JS {
 struct Zone;
 
 /* Default size for the generational nursery in bytes. */
 const uint32_t DefaultNurseryBytes = 16 * 1024 * 1024;
 
+/* Default maximum heap size in bytes to pass to JS_NewRuntime(). */
+const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
+
 /*
  * We cannot expose the class hierarchy: the implementation is hidden. Instead
  * we provide cast functions with strong debug-mode assertions.
  */
 static MOZ_ALWAYS_INLINE js::gc::Cell *
 AsCell(JSObject *obj)
 {
     js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(obj);
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -207,18 +207,20 @@ struct GCSizes
 // This class holds information about the memory taken up by identical copies of
 // a particular string.  Multiple JSStrings may have their sizes aggregated
 // together into one StringInfo object.  Note that two strings with identical
 // chars will not be aggregated together if one is a short string and the other
 // is not.
 struct StringInfo
 {
 #define FOR_EACH_SIZE(macro) \
-    macro(Strings, IsLiveGCThing,  gcHeap) \
-    macro(Strings, NotLiveGCThing, mallocHeap) \
+    macro(Strings, IsLiveGCThing,  gcHeapLatin1) \
+    macro(Strings, IsLiveGCThing,  gcHeapTwoByte) \
+    macro(Strings, NotLiveGCThing, mallocHeapLatin1) \
+    macro(Strings, NotLiveGCThing, mallocHeapTwoByte)
 
     StringInfo()
       : FOR_EACH_SIZE(ZERO_SIZE)
         numCopies(0)
     {}
 
     void add(const StringInfo &other) {
         FOR_EACH_SIZE(ADD_OTHER_SIZE);
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -672,22 +672,117 @@ static bool
 regexp_test_impl(JSContext *cx, CallArgs args)
 {
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     RegExpRunStatus status = ExecuteRegExp(cx, args, matches);
     args.rval().setBoolean(status == RegExpRunStatus_Success);
     return status != RegExpRunStatus_Error;
 }
 
+static inline bool
+StringHasDotStar(HandleLinearString str, size_t index)
+{
+    // Return whether the portion of the string at the specified index is '.*'
+    return str->latin1OrTwoByteChar(index) == '.' && str->latin1OrTwoByteChar(index + 1) == '*';
+}
+
+static bool
+TryFillRegExpTestCache(JSContext *cx, HandleObject regexp, RegExpTestCache &cache,
+                       MutableHandleObject result)
+{
+    cache.purge();
+
+    // test() on global RegExps uses the lastIndex in a fashion that is
+    // incompatible with the cache.
+    if (regexp->as<RegExpObject>().global())
+        return true;
+
+    RootedAtom source(cx, regexp->as<RegExpObject>().getSource());
+
+    // Try to strip a leading '.*' from the RegExp, but only if it is not
+    // followed by a '?' (which will affect how the .* is parsed).
+    if (source->length() >= 3 &&
+        StringHasDotStar(source, 0) &&
+        source->latin1OrTwoByteChar(2) != '?')
+    {
+        source = AtomizeSubstring(cx, source, 2, source->length() - 2);
+        if (!source)
+            return false;
+    }
+
+    // Try to strip a trailing '.*' from the RegExp, but only if it does not
+    // have any other meta characters (to be sure we are not affecting how the
+    // RegExp will be parsed).
+    if (source->length() >= 3 &&
+        StringHasDotStar(source, source->length() - 2) &&
+        !StringHasRegExpMetaChars(source, 0, 2))
+    {
+        source = AtomizeSubstring(cx, source, 0, source->length() - 2);
+        if (!source)
+            return false;
+    }
+
+    if (source == regexp->as<RegExpObject>().getSource()) {
+        // We weren't able to remove a leading or trailing .*
+        return true;
+    }
+
+    RegExpObjectBuilder builder(cx);
+
+    result.set(builder.build(source, regexp->as<RegExpObject>().getFlags()));
+    if (!result)
+        return false;
+
+    cache.fill(&regexp->as<RegExpObject>(), &result->as<RegExpObject>());
+    return true;
+}
+
 /* Separate interface for use by IonMonkey. */
 bool
 js::regexp_test_raw(JSContext *cx, HandleObject regexp, HandleString input, bool *result)
 {
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, matches, UpdateRegExpStatics);
+
+    RegExpTestCache &cache = cx->runtime()->regExpTestCache;
+
+    RootedObject alternate(cx);
+    if (regexp == cache.key ||
+        (cache.key &&
+         regexp->as<RegExpObject>().getSource() == cache.key->getSource() &&
+         regexp->as<RegExpObject>().getFlags() == cache.key->getFlags()))
+    {
+        alternate = cache.value;
+    } else {
+        if (!TryFillRegExpTestCache(cx, regexp, cache, &alternate))
+            return false;
+    }
+
+    RegExpRunStatus status;
+    if (alternate) {
+        // The alternate RegExp is simpler and should execute faster than the
+        // original one, so use it instead.
+        status = ExecuteRegExp(cx, alternate, input, matches, DontUpdateRegExpStatics);
+
+        if (status == RegExpRunStatus_Success) {
+            // Update the RegExpStatics to reflect the original RegExp we were
+            // trying to execute, and not the alternate one.
+            RegExpStatics *res = cx->global()->getRegExpStatics(cx);
+            if (!res)
+                return RegExpRunStatus_Error;
+
+            RegExpGuard shared(cx);
+            if (!regexp->as<RegExpObject>().getShared(cx, &shared))
+                return RegExpRunStatus_Error;
+
+            res->updateLazily(cx, &input->asLinear(), shared.re(), 0);
+        }
+    } else {
+        status = ExecuteRegExp(cx, regexp, input, matches, UpdateRegExpStatics);
+    }
+
     *result = (status == RegExpRunStatus_Success);
     return status != RegExpRunStatus_Error;
 }
 
 bool
 js::regexp_test(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -286,17 +286,19 @@ static const struct ParamPair {
     const char      *name;
     JSGCParamKey    param;
 } paramMap[] = {
     {"maxBytes",            JSGC_MAX_BYTES },
     {"maxMallocBytes",      JSGC_MAX_MALLOC_BYTES},
     {"gcBytes",             JSGC_BYTES},
     {"gcNumber",            JSGC_NUMBER},
     {"sliceTimeBudget",     JSGC_SLICE_TIME_BUDGET},
-    {"markStackLimit",      JSGC_MARK_STACK_LIMIT}
+    {"markStackLimit",      JSGC_MARK_STACK_LIMIT},
+    {"minEmptyChunkCount",  JSGC_MIN_EMPTY_CHUNK_COUNT},
+    {"maxEmptyChunkCount",  JSGC_MAX_EMPTY_CHUNK_COUNT}
 };
 
 // Keep this in sync with above params.
 #define GC_PARAMETER_ARGS_LIST "maxBytes, maxMallocBytes, gcBytes, gcNumber, sliceTimeBudget, or markStackLimit"
 
 static bool
 GCParameter(JSContext *cx, unsigned argc, Value *vp)
 {
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -838,16 +838,17 @@ typedef ReadBarriered<GlobalObject*> Rea
 typedef ReadBarriered<JSFunction*> ReadBarrieredFunction;
 typedef ReadBarriered<JSObject*> ReadBarrieredObject;
 typedef ReadBarriered<ScriptSourceObject*> ReadBarrieredScriptSourceObject;
 typedef ReadBarriered<Shape*> ReadBarrieredShape;
 typedef ReadBarriered<UnownedBaseShape*> ReadBarrieredUnownedBaseShape;
 typedef ReadBarriered<jit::JitCode*> ReadBarrieredJitCode;
 typedef ReadBarriered<types::TypeObject*> ReadBarrieredTypeObject;
 typedef ReadBarriered<JSAtom*> ReadBarrieredAtom;
+typedef ReadBarriered<JS::Symbol*> ReadBarrieredSymbol;
 
 typedef ReadBarriered<Value> ReadBarrieredValue;
 
 // A pre- and post-barriered Value that is specialized to be aware that it
 // resides in a slots or elements vector. This allows it to be relocated in
 // memory, but with substantially less overhead than a RelocatablePtr.
 class HeapSlot : public BarrieredBase<Value>
 {
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -475,16 +475,18 @@ class GCRuntime
     uint64_t              highFrequencyLowLimitBytes;
     uint64_t              highFrequencyHighLimitBytes;
     double                highFrequencyHeapGrowthMax;
     double                highFrequencyHeapGrowthMin;
     double                lowFrequencyHeapGrowth;
     bool                  dynamicHeapGrowth;
     bool                  dynamicMarkSlice;
     uint64_t              decommitThreshold;
+    unsigned              minEmptyChunkCount;
+    unsigned              maxEmptyChunkCount;
 
     /* During shutdown, the GC needs to clean up every possible object. */
     bool                  cleanUpEverything;
 
     /*
      * The gray bits can become invalid if UnmarkGray overflows the stack. A
      * full GC will reset this bit, since it fills in all the gray bits.
      */
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -724,22 +724,24 @@ gc::MarkIdRootRange(JSTracer *trc, size_
 static inline void
 MarkValueInternal(JSTracer *trc, Value *v)
 {
     if (v->isMarkable()) {
         JS_ASSERT(v->toGCThing());
         void *thing = v->toGCThing();
         trc->setTracingLocation((void *)v);
         MarkKind(trc, &thing, v->gcKind());
-        if (v->isString())
+        if (v->isString()) {
             v->setString((JSString *)thing);
-        else if (v->isSymbol())
+        } else if (v->isObject()) {
+            v->setObjectOrNull((JSObject *)thing);
+        } else {
+            JS_ASSERT(v->isSymbol());
             v->setSymbol((JS::Symbol *)thing);
-        else
-            v->setObjectOrNull((JSObject *)thing);
+        }
     } else {
         /* Unset realLocation manually if we do not call MarkInternal. */
         trc->unsetTracingLocation();
     }
 }
 
 void
 gc::MarkValue(JSTracer *trc, BarrieredBase<Value> *v, const char *name)
@@ -795,37 +797,47 @@ bool
 gc::IsValueMarked(Value *v)
 {
     JS_ASSERT(v->isMarkable());
     bool rv;
     if (v->isString()) {
         JSString *str = (JSString *)v->toGCThing();
         rv = IsMarked<JSString>(&str);
         v->setString(str);
-    } else {
+    } else if (v->isObject()) {
         JSObject *obj = (JSObject *)v->toGCThing();
         rv = IsMarked<JSObject>(&obj);
         v->setObject(*obj);
+    } else {
+        JS_ASSERT(v->isSymbol());
+        JS::Symbol *sym = v->toSymbol();
+        rv = IsMarked<JS::Symbol>(&sym);
+        v->setSymbol(sym);
     }
     return rv;
 }
 
 bool
 gc::IsValueAboutToBeFinalized(Value *v)
 {
     JS_ASSERT(v->isMarkable());
     bool rv;
     if (v->isString()) {
         JSString *str = (JSString *)v->toGCThing();
         rv = IsAboutToBeFinalized<JSString>(&str);
         v->setString(str);
-    } else {
+    } else if (v->isObject()) {
         JSObject *obj = (JSObject *)v->toGCThing();
         rv = IsAboutToBeFinalized<JSObject>(&obj);
         v->setObject(*obj);
+    } else {
+        JS_ASSERT(v->isSymbol());
+        JS::Symbol *sym = v->toSymbol();
+        rv = IsAboutToBeFinalized<JS::Symbol>(&sym);
+        v->setSymbol(sym);
     }
     return rv;
 }
 
 /*** Slot Marking ***/
 
 bool
 gc::IsSlotMarked(HeapSlot *s)
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -823,16 +823,20 @@ js::Nursery::collect(JSRuntime *rt, JS::
     TIME_START(markDebugger);
     Debugger::markAll(&trc);
     TIME_END(markDebugger);
 
     TIME_START(clearNewObjectCache);
     rt->newObjectCache.clearNurseryObjects(rt);
     TIME_END(clearNewObjectCache);
 
+    TIME_START(clearRegExpTestCache);
+    rt->regExpTestCache.purge();
+    TIME_END(clearRegExpTestCache);
+
     // Most of the work is done here. This loop iterates over objects that have
     // been moved to the major heap. If these objects have any outgoing pointers
     // to the nursery, then those nursery objects get moved as well, until no
     // objects are left to move. That is, we iterate to a fixed point.
     TIME_START(collectToFP);
     TenureCountCache tenureCounts;
     collectToFixedPoint(&trc, tenureCounts);
     TIME_END(collectToFP);
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -246,17 +246,17 @@ gc::GCRuntime::startVerifyPreBarriers()
 
     verifyPreData = trc;
     incrementalState = MARK;
     marker.start();
 
     rt->setNeedsBarrier(true);
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         PurgeJITCaches(zone);
-        zone->setNeedsBarrier(true, Zone::UpdateIon);
+        zone->setNeedsBarrier(true, Zone::UpdateJit);
         zone->allocator.arenas.purge();
     }
 
     return;
 
 oom:
     incrementalState = NO_INCREMENTAL;
     js_delete(trc);
@@ -331,17 +331,17 @@ gc::GCRuntime::endVerifyPreBarriers()
 
     bool compartmentCreated = false;
 
     /* We need to disable barriers before tracing, which may invoke barriers. */
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         if (!zone->needsBarrier())
             compartmentCreated = true;
 
-        zone->setNeedsBarrier(false, Zone::UpdateIon);
+        zone->setNeedsBarrier(false, Zone::UpdateJit);
         PurgeJITCaches(zone);
     }
     rt->setNeedsBarrier(false);
 
     /*
      * We need to bump gcNumber so that the methodjit knows that jitcode has
      * been discarded.
      */
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -37,17 +37,17 @@ JS::Zone::Zone(JSRuntime *rt)
     usedByExclusiveThread(false),
     scheduledForDestruction(false),
     maybeAlive(true),
     active(false),
     jitZone_(nullptr),
     gcState_(NoGC),
     gcScheduled_(false),
     gcPreserveCode_(false),
-    ionUsingBarriers_(false)
+    jitUsingBarriers_(false)
 {
     /* Ensure that there are no vtables to mess us up here. */
     JS_ASSERT(reinterpret_cast<JS::shadow::Zone *>(this) ==
               static_cast<JS::shadow::Zone *>(this));
 
     setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9);
 }
 
@@ -63,22 +63,22 @@ Zone::~Zone()
 }
 
 bool Zone::init()
 {
     return gcZoneGroupEdges.init();
 }
 
 void
-Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon)
+Zone::setNeedsBarrier(bool needs, ShouldUpdateJit updateJit)
 {
 #ifdef JS_ION
-    if (updateIon == UpdateIon && needs != ionUsingBarriers_) {
+    if (updateJit == UpdateJit && needs != jitUsingBarriers_) {
         jit::ToggleBarriers(this, needs);
-        ionUsingBarriers_ = needs;
+        jitUsingBarriers_ = needs;
     }
 #endif
 
     if (needs && runtimeFromMainThread()->isAtomsZone(this))
         JS_ASSERT(!runtimeFromMainThread()->exclusiveThreadsPresent());
 
     JS_ASSERT_IF(needs, canCollect());
     needsBarrier_ = needs;
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -182,18 +182,18 @@ struct Zone : public JS::shadow::Zone,
     // possibly at other times too.
     uint64_t gcNumber();
 
     bool compileBarriers() const { return compileBarriers(needsBarrier()); }
     bool compileBarriers(bool needsBarrier) const {
         return needsBarrier || runtimeFromMainThread()->gcZeal() == js::gc::ZealVerifierPreValue;
     }
 
-    enum ShouldUpdateIon { DontUpdateIon, UpdateIon };
-    void setNeedsBarrier(bool needs, ShouldUpdateIon updateIon);
+    enum ShouldUpdateJit { DontUpdateJit, UpdateJit };
+    void setNeedsBarrier(bool needs, ShouldUpdateJit updateJit);
     const bool *addressOfNeedsBarrier() const { return &needsBarrier_; }
 
     js::jit::JitZone *getJitZone(JSContext *cx) { return jitZone_ ? jitZone_ : createJitZone(cx); }
     js::jit::JitZone *jitZone() { return jitZone_; }
 
 #ifdef DEBUG
     // For testing purposes, return the index of the zone group which this zone
     // was swept in in the last GC.
@@ -269,17 +269,17 @@ struct Zone : public JS::shadow::Zone,
     mozilla::DebugOnly<unsigned> gcLastZoneGroupIndex;
 
   private:
     js::jit::JitZone *jitZone_;
 
     GCState gcState_;
     bool gcScheduled_;
     bool gcPreserveCode_;
-    bool ionUsingBarriers_;
+    bool jitUsingBarriers_;
 
     friend bool js::CurrentThreadCanAccessZone(Zone *zone);
     friend class js::gc::GCRuntime;
 };
 
 } // namespace JS
 
 namespace js {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/regexp-removed-dot-star.js
@@ -0,0 +1,49 @@
+
+// Test that removal of leading or trailing .* from RegExp test() calls
+// does not affect lastMatch or other RegExpStatics info.
+
+function first(input) {
+    var re = /.*b(cd)/;
+    for (var i = 0; i < 10; i++)
+	re.test(input);
+}
+
+first("1234\nabcdefg\n1234");
+assertEq(RegExp.lastMatch, "abcd");
+assertEq(RegExp.$1, "cd");
+assertEq(RegExp.input, "1234\nabcdefg\n1234");
+assertEq(RegExp.leftContext, "1234\n");
+assertEq(RegExp.rightContext, "efg\n1234");
+assertEq(RegExp.lastParen, "cd");
+
+
+// Test that removal of leading or trailing .* from RegExp test() calls
+// does not affect lastMatch or other RegExpStatics info.
+
+function second(input) {
+    var re = /bcd.*/;
+    for (var i = 0; i < 10; i++)
+	re.test(input);
+}
+
+second("1234\nabcdefg\n1234");
+assertEq(RegExp.lastMatch, "bcdefg");
+assertEq(RegExp.$1, "");
+assertEq(RegExp.input, "1234\nabcdefg\n1234");
+assertEq(RegExp.leftContext, "1234\na");
+assertEq(RegExp.rightContext, "\n1234");
+assertEq(RegExp.lastParen, "");
+
+function third(input) {
+    var re = /.*bcd.*/;
+    for (var i = 0; i < 10; i++)
+	re.test(input);
+}
+
+third("1234\nabcdefg\n1234");
+assertEq(RegExp.lastMatch, "abcdefg");
+assertEq(RegExp.$1, "");
+assertEq(RegExp.input, "1234\nabcdefg\n1234");
+assertEq(RegExp.leftContext, "1234\n");
+assertEq(RegExp.rightContext, "\n1234");
+assertEq(RegExp.lastParen, "");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1017141.js
@@ -0,0 +1,25 @@
+var min = gcparam('minEmptyChunkCount');
+var max = gcparam('maxEmptyChunkCount');
+
+gcparam('minEmptyChunkCount', 10);
+gcparam('maxEmptyChunkCount', 20);
+assertEq(gcparam('minEmptyChunkCount'), 10);
+assertEq(gcparam('maxEmptyChunkCount'), 20);
+gc();
+
+/* We maintain the invariant that maxEmptyChunkCount >= minEmptyChunkCount. */
+gcparam('minEmptyChunkCount', 30);
+assertEq(gcparam('minEmptyChunkCount'), 30);
+assertEq(gcparam('maxEmptyChunkCount'), 30);
+gc();
+
+gcparam('maxEmptyChunkCount', 5);
+assertEq(gcparam('minEmptyChunkCount'), 5);
+assertEq(gcparam('maxEmptyChunkCount'), 5);
+gc();
+
+gcparam('minEmptyChunkCount', min);
+gcparam('maxEmptyChunkCount', max);
+assertEq(gcparam('minEmptyChunkCount'), min);
+assertEq(gcparam('maxEmptyChunkCount'), max);
+gc();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1035371.js
@@ -0,0 +1,4 @@
+x = function() {};
+y = new WeakMap;
+selectforgc({});;
+y.set(x, Symbol());
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1039516.js
@@ -0,0 +1,6 @@
+gczeal(9);
+Symbol.for("a");
+gcslice(1);
+var a = Symbol.for("a");
+gcslice();
+print(Symbol.keyFor(a));
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -5543,18 +5543,17 @@ ICSetElem_TypedArray::Compiler::generate
     regs = availableGeneralRegs(0);
     regs.takeUnchecked(obj);
     regs.takeUnchecked(key);
     regs.take(scratchReg);
     Register secondScratch = regs.takeAny();
 
     if (type_ == Scalar::Float32 || type_ == Scalar::Float64) {
         masm.ensureDouble(value, FloatReg0, &failure);
-        if (LIRGenerator::allowFloat32Optimizations() &&
-            type_ == Scalar::Float32)
+        if (type_ == Scalar::Float32)
         {
             masm.convertDoubleToFloat32(FloatReg0, ScratchFloat32Reg);
             masm.storeToTypedFloatArray(type_, ScratchFloat32Reg, dest);
         } else {
             masm.storeToTypedFloatArray(type_, FloatReg0, dest);
         }
         EmitReturnFromIC(masm);
     } else if (type_ == Scalar::Uint8Clamped) {
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -947,21 +947,16 @@ TypeAnalyzer::graphContainsFloat32()
         }
     }
     return false;
 }
 
 bool
 TypeAnalyzer::tryEmitFloatOperations()
 {
-    // Backends that currently don't know how to generate Float32 specialized instructions
-    // shouldn't run this pass and just let all instructions as specialized for Double.
-    if (!LIRGenerator::allowFloat32Optimizations())
-        return true;
-
     // Asm.js uses the ahead of time type checks to specialize operations, no need to check
     // them again at this point.
     if (mir->compilingAsmJS())
         return true;
 
     // Check ahead of time that there is at least one definition typed as Float32, otherwise we
     // don't need this pass.
     if (!graphContainsFloat32())
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7554,17 +7554,17 @@ MIRTypeForTypedArrayRead(Scalar::Type ar
       case Scalar::Uint8Clamped:
       case Scalar::Int16:
       case Scalar::Uint16:
       case Scalar::Int32:
         return MIRType_Int32;
       case Scalar::Uint32:
         return observedDouble ? MIRType_Double : MIRType_Int32;
       case Scalar::Float32:
-        return (LIRGenerator::allowFloat32Optimizations()) ? MIRType_Float32 : MIRType_Double;
+        return MIRType_Float32;
       case Scalar::Float64:
         return MIRType_Double;
       default:
         break;
     }
     MOZ_ASSUME_UNREACHABLE("Unknown typed array type");
 }
 
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -3730,28 +3730,20 @@ GenerateSetTypedArrayElement(JSContext *
     masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elements);
 
     // Set the value.
     Scalar::Type arrayType = tarr->type();
     int width = Scalar::byteSize(arrayType);
     BaseIndex target(elements, index, ScaleFromElemWidth(width));
 
     if (arrayType == Scalar::Float32) {
-        FloatRegister ftemp;
-        if (LIRGenerator::allowFloat32Optimizations()) {
-            JS_ASSERT(tempFloat32 != InvalidFloatReg);
-            if (!masm.convertConstantOrRegisterToFloat(cx, value, tempFloat32, &failures))
-                return false;
-            ftemp = tempFloat32;
-        } else {
-            if (!masm.convertConstantOrRegisterToDouble(cx, value, tempDouble, &failures))
-                return false;
-            ftemp = tempDouble;
-        }
-        masm.storeToTypedFloatArray(arrayType, ftemp, target);
+        JS_ASSERT(tempFloat32 != InvalidFloatReg);
+        if (!masm.convertConstantOrRegisterToFloat(cx, value, tempFloat32, &failures))
+            return false;
+        masm.storeToTypedFloatArray(arrayType, tempFloat32, target);
     } else if (arrayType == Scalar::Float64) {
         if (!masm.convertConstantOrRegisterToDouble(cx, value, tempDouble, &failures))
             return false;
         masm.storeToTypedFloatArray(arrayType, tempDouble, target);
     } else {
         // On x86 we only have 6 registers available to use, so reuse the object
         // register to compute the intermediate value to store and restore it
         // afterwards.
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -273,26 +273,17 @@ MacroAssembler::moveNurseryPtr(ImmMaybeN
 }
 
 template<typename S, typename T>
 static void
 StoreToTypedFloatArray(MacroAssembler &masm, int arrayType, const S &value, const T &dest)
 {
     switch (arrayType) {
       case Scalar::Float32:
-        if (LIRGenerator::allowFloat32Optimizations()) {
-            masm.storeFloat32(value, dest);
-        } else {
-#ifdef JS_MORE_DETERMINISTIC
-            // See the comment in TypedArrayObjectTemplate::doubleToNative.
-            masm.canonicalizeDouble(value);
-#endif
-            masm.convertDoubleToFloat32(value, ScratchFloat32Reg);
-            masm.storeFloat32(ScratchFloat32Reg, dest);
-        }
+        masm.storeFloat32(value, dest);
         break;
       case Scalar::Float64:
 #ifdef JS_MORE_DETERMINISTIC
         // See the comment in TypedArrayObjectTemplate::doubleToNative.
         masm.canonicalizeDouble(value);
 #endif
         masm.storeDouble(value, dest);
         break;
@@ -345,23 +336,18 @@ MacroAssembler::loadFromTypedArray(Scala
 
             // Bail out if the value doesn't fit into a signed int32 value. This
             // is what allows MLoadTypedArrayElement to have a type() of
             // MIRType_Int32 for UInt32 array loads.
             branchTest32(Assembler::Signed, dest.gpr(), dest.gpr(), fail);
         }
         break;
       case Scalar::Float32:
-        if (LIRGenerator::allowFloat32Optimizations()) {
-            loadFloat32(src, dest.fpu());
-            canonicalizeFloat(dest.fpu());
-        } else {
-            loadFloatAsDouble(src, dest.fpu());
-            canonicalizeDouble(dest.fpu());
-        }
+        loadFloat32(src, dest.fpu());
+        canonicalizeFloat(dest.fpu());
         break;
       case Scalar::Float64:
         loadDouble(src, dest.fpu());
         canonicalizeDouble(dest.fpu());
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
     }
@@ -409,18 +395,17 @@ MacroAssembler::loadFromTypedArray(Scala
             // Bailout if the value does not fit in an int32.
             branchTest32(Assembler::Signed, temp, temp, fail);
             tagValue(JSVAL_TYPE_INT32, temp, dest);
         }
         break;
       case Scalar::Float32:
         loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloat32Reg), dest.scratchReg(),
                            nullptr);
-        if (LIRGenerator::allowFloat32Optimizations())
-            convertFloat32ToDouble(ScratchFloat32Reg, ScratchDoubleReg);
+        convertFloat32ToDouble(ScratchFloat32Reg, ScratchDoubleReg);
         boxDouble(ScratchDoubleReg, dest);
         break;
       case Scalar::Float64:
         loadFromTypedArray(arrayType, src, AnyRegister(ScratchDoubleReg), dest.scratchReg(),
                            nullptr);
         boxDouble(ScratchDoubleReg, dest);
         break;
       default:
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -506,19 +506,16 @@ LIRGenerator::visitUnreachable(MUnreacha
 }
 
 bool
 LIRGenerator::visitAssertFloat32(MAssertFloat32 *assertion)
 {
     MIRType type = assertion->input()->type();
     DebugOnly<bool> checkIsFloat32 = assertion->mustBeFloat32();
 
-    if (!allowFloat32Optimizations())
-        return true;
-
     if (type != MIRType_Value && !js_JitOptions.eagerCompilation) {
         JS_ASSERT_IF(checkIsFloat32, type == MIRType_Float32);
         JS_ASSERT_IF(!checkIsFloat32, type != MIRType_Float32);
     }
     return true;
 }
 
 bool
@@ -2846,21 +2843,18 @@ LIRGenerator::visitLoadTypedArrayElement
 
 bool
 LIRGenerator::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins)
 {
     JS_ASSERT(ins->elements()->type() == MIRType_Elements);
     JS_ASSERT(ins->index()->type() == MIRType_Int32);
 
     if (ins->isFloatArray()) {
-        DebugOnly<bool> optimizeFloat32 = allowFloat32Optimizations();
-        JS_ASSERT_IF(optimizeFloat32 && ins->arrayType() == Scalar::Float32,
-                     ins->value()->type() == MIRType_Float32);
-        JS_ASSERT_IF(!optimizeFloat32 || ins->arrayType() == Scalar::Float64,
-                     ins->value()->type() == MIRType_Double);
+        JS_ASSERT_IF(ins->arrayType() == Scalar::Float32, ins->value()->type() == MIRType_Float32);
+        JS_ASSERT_IF(ins->arrayType() == Scalar::Float64, ins->value()->type() == MIRType_Double);
     } else {
         JS_ASSERT(ins->value()->type() == MIRType_Int32);
     }
 
     LUse elements = useRegister(ins->elements());
     LAllocation index = useRegisterOrConstant(ins->index());
     LAllocation value;
 
@@ -2875,21 +2869,18 @@ LIRGenerator::visitStoreTypedArrayElemen
 bool
 LIRGenerator::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins)
 {
     JS_ASSERT(ins->elements()->type() == MIRType_Elements);
     JS_ASSERT(ins->index()->type() == MIRType_Int32);
     JS_ASSERT(ins->length()->type() == MIRType_Int32);
 
     if (ins->isFloatArray()) {
-        DebugOnly<bool> optimizeFloat32 = allowFloat32Optimizations();
-        JS_ASSERT_IF(optimizeFloat32 && ins->arrayType() == Scalar::Float32,
-                     ins->value()->type() == MIRType_Float32);
-        JS_ASSERT_IF(!optimizeFloat32 || ins->arrayType() == Scalar::Float64,
-                     ins->value()->type() == MIRType_Double);
+        JS_ASSERT_IF(ins->arrayType() == Scalar::Float32, ins->value()->type() == MIRType_Float32);
+        JS_ASSERT_IF(ins->arrayType() == Scalar::Float64, ins->value()->type() == MIRType_Double);
     } else {
         JS_ASSERT(ins->value()->type() == MIRType_Int32);
     }
 
     LUse elements = useRegister(ins->elements());
     LAllocation length = useAnyOrConstant(ins->length());
     LAllocation index = useRegisterOrConstant(ins->index());
     LAllocation value;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1029,19 +1029,16 @@ IonBuilder::inlineMathImul(CallInfo &cal
     current->add(ins);
     current->push(ins);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineMathFRound(CallInfo &callInfo)
 {
-    if (!LIRGenerator::allowFloat32Optimizations())
-        return InliningStatus_NotInlined;
-
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     // MIRType can't be Float32, as this point, as getInlineReturnType uses JSVal types
     // to infer the returned MIR type.
     types::TemporaryTypeSet *returned = getInlineReturnTypeSet();
     if (returned->empty()) {
         // As there's only one possible returned type, just add it to the observed
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -772,25 +772,21 @@ StoreTypedArrayPolicy::adjustValueInput(
             ins->block()->insertBefore(ins, value->toInstruction());
         }
         break;
       case Scalar::Uint8Clamped:
         // IonBuilder should have inserted ClampToUint8.
         JS_ASSERT(value->type() == MIRType_Int32);
         break;
       case Scalar::Float32:
-        if (LIRGenerator::allowFloat32Optimizations()) {
-            if (value->type() != MIRType_Float32) {
-                value = MToFloat32::New(alloc, value);
-                ins->block()->insertBefore(ins, value->toInstruction());
-            }
-            break;
+        if (value->type() != MIRType_Float32) {
+            value = MToFloat32::New(alloc, value);
+            ins->block()->insertBefore(ins, value->toInstruction());
         }
-        // Fallthrough: if the LIRGenerator cannot directly store Float32, it will expect the
-        // stored value to be a double.
+        break;
       case Scalar::Float64:
         if (value->type() != MIRType_Double) {
             value = MToDouble::New(alloc, value);
             ins->block()->insertBefore(ins, value->toInstruction());
         }
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid array type");
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -86,20 +86,16 @@ class LIRGeneratorARM : public LIRGenera
     bool visitGuardObjectType(MGuardObjectType *ins);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
     bool visitForkJoinGetSlice(MForkJoinGetSlice *ins);
-
-    static bool allowFloat32Optimizations() {
-        return true;
-    }
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Lowering_arm_h */
--- a/js/src/jit/mips/Lowering-mips.h
+++ b/js/src/jit/mips/Lowering-mips.h
@@ -86,20 +86,16 @@ class LIRGeneratorMIPS : public LIRGener
     bool visitGuardObjectType(MGuardObjectType *ins);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
     bool visitForkJoinGetSlice(MForkJoinGetSlice *ins);
-
-    static bool allowFloat32Optimizations() {
-        return true;
-    }
 };
 
 typedef LIRGeneratorMIPS LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_Lowering_mips_h */
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -201,21 +201,16 @@ class LIRGeneratorShared : public MInstr
         return false;
     }
 
     // Whether to generate typed array accesses on statically known objects.
     static bool allowStaticTypedArrayAccesses() {
         return false;
     }
 
-     // Whether we can emit Float32 specific optimizations.
-    static bool allowFloat32Optimizations() {
-       return false;
-    }
-
     // Whether we can inline ForkJoinGetSlice.
     static bool allowInlineForkJoinGetSlice() {
         return false;
     }
 
 };
 
 } // namespace jit
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -49,20 +49,16 @@ class LIRGeneratorX64 : public LIRGenera
     bool visitReturn(MReturn *ret);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
 
-    static bool allowFloat32Optimizations() {
-        return true;
-    }
-
     static bool allowInlineForkJoinGetSlice() {
         return true;
     }
 };
 
 typedef LIRGeneratorX64 LIRGeneratorSpecific;
 
 } // namespace jit
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -59,19 +59,16 @@ class LIRGeneratorX86 : public LIRGenera
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }
 
     static bool allowStaticTypedArrayAccesses() {
         return true;
     }
-    static bool allowFloat32Optimizations() {
-        return true;
-    }
     static bool allowInlineForkJoinGetSlice() {
         return true;
     }
 };
 
 typedef LIRGeneratorX86 LIRGeneratorSpecific;
 
 } // namespace js
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2140,17 +2140,26 @@ typedef enum JSGCParamKey {
     /* Lower limit after which we limit the heap growth. */
     JSGC_ALLOCATION_THRESHOLD = 19,
 
     /*
      * We decommit memory lazily. If more than this number of megabytes is
      * available to be decommitted, then JS_MaybeGC will trigger a shrinking GC
      * to decommit it.
      */
-    JSGC_DECOMMIT_THRESHOLD = 20
+    JSGC_DECOMMIT_THRESHOLD = 20,
+
+    /*
+     * We try to keep at least this many unused chunks in the free chunk pool at
+     * all times, even after a shrinking GC.
+     */
+    JSGC_MIN_EMPTY_CHUNK_COUNT = 21,
+
+    /* We never keep more than this many unused chunks in the free chunk pool. */
+    JSGC_MAX_EMPTY_CHUNK_COUNT = 22
 } JSGCParamKey;
 
 extern JS_PUBLIC_API(void)
 JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value);
 
 extern JS_PUBLIC_API(uint32_t)
 JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key);
 
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -468,16 +468,32 @@ js::AtomizeString(ExclusiveContext *cx, 
 
     JS::AutoCheckCannotGC nogc;
     return linear->hasLatin1Chars()
            ? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), ib)
            : AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), ib);
 }
 
 JSAtom *
+js::AtomizeSubstring(ExclusiveContext *cx, JSString *str, size_t start, size_t length,
+                     InternBehavior ib /* = DoNotInternAtom */)
+{
+    JS_ASSERT(start + length <= str->length());
+
+    JSLinearString *linear = str->ensureLinear(cx);
+    if (!linear)
+        return nullptr;
+
+    JS::AutoCheckCannotGC nogc;
+    return linear->hasLatin1Chars()
+           ? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc) + start, length, ib)
+           : AtomizeAndCopyChars(cx, linear->twoByteChars(nogc) + start, length, ib);
+}
+
+JSAtom *
 js::Atomize(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavior ib)
 {
     CHECK_REQUEST(cx);
 
     if (!JSString::validateLength(cx, length))
         return nullptr;
 
     if (EnableLatin1Strings) {
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -204,16 +204,20 @@ Atomize(ExclusiveContext *cx, const char
 template <typename CharT>
 extern JSAtom *
 AtomizeChars(ExclusiveContext *cx, const CharT *chars, size_t length,
              js::InternBehavior ib = js::DoNotInternAtom);
 
 extern JSAtom *
 AtomizeString(ExclusiveContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom);
 
+extern JSAtom *
+AtomizeSubstring(ExclusiveContext *cx, JSString *str, size_t start, size_t length,
+                 InternBehavior ib = DoNotInternAtom);
+
 template <AllowGC allowGC>
 extern JSAtom *
 ToAtom(ExclusiveContext *cx, typename MaybeRooted<Value, allowGC>::HandleType v);
 
 enum XDRMode {
     XDR_ENCODE,
     XDR_DECODE
 };
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -233,28 +233,16 @@ using mozilla::Swap;
 using JS::AutoGCRooter;
 
 /* Perform a Full GC every 20 seconds if MaybeGC is called */
 static const uint64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
 
 /* Increase the IGC marking slice time if we are in highFrequencyGC mode. */
 static const int IGC_MARK_SLICE_MULTIPLIER = 2;
 
-#ifdef JSGC_GENERATIONAL
-static const unsigned MIN_EMPTY_CHUNK_COUNT = 1;
-#else
-static const unsigned MIN_EMPTY_CHUNK_COUNT = 0;
-#endif
-
-#if defined(ANDROID) || defined(MOZ_B2G)
-static const unsigned MAX_EMPTY_CHUNK_COUNT = 2;
-#else
-static const unsigned MAX_EMPTY_CHUNK_COUNT = 30;
-#endif
-
 const AllocKind gc::slotsToThingKind[] = {
     /* 0 */  FINALIZE_OBJECT0,  FINALIZE_OBJECT2,  FINALIZE_OBJECT2,  FINALIZE_OBJECT4,
     /* 4 */  FINALIZE_OBJECT4,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,
     /* 8 */  FINALIZE_OBJECT8,  FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12,
     /* 12 */ FINALIZE_OBJECT12, FINALIZE_OBJECT16, FINALIZE_OBJECT16, FINALIZE_OBJECT16,
     /* 16 */ FINALIZE_OBJECT16
 };
 
@@ -690,38 +678,40 @@ Chunk *
 GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
 {
     /*
      * Return old empty chunks to the system while preserving the order of
      * other chunks in the list. This way, if the GC runs several times
      * without emptying the list, the older chunks will stay at the tail
      * and are more likely to reach the max age.
      */
+    JS_ASSERT(maxEmptyChunkCount >= minEmptyChunkCount);
     Chunk *freeList = nullptr;
     unsigned freeChunkCount = 0;
     for (ChunkPool::Enum e(chunkPool); !e.empty(); ) {
         Chunk *chunk = e.front();
         JS_ASSERT(chunk->unused());
         JS_ASSERT(!chunkSet.has(chunk));
-        if (releaseAll || freeChunkCount >= MAX_EMPTY_CHUNK_COUNT ||
-            (freeChunkCount >= MIN_EMPTY_CHUNK_COUNT &&
+        if (releaseAll || freeChunkCount >= maxEmptyChunkCount ||
+            (freeChunkCount >= minEmptyChunkCount &&
              (shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
         {
             e.removeAndPopFront();
             prepareToFreeChunk(chunk->info);
             chunk->info.next = freeList;
             freeList = chunk;
         } else {
             /* Keep the chunk but increase its age. */
             ++freeChunkCount;
             ++chunk->info.age;
             e.popFront();
         }
     }
-    JS_ASSERT_IF(shrinkBuffers, chunkPool.getEmptyCount() <= MIN_EMPTY_CHUNK_COUNT);
+    JS_ASSERT(chunkPool.getEmptyCount() <= maxEmptyChunkCount);
+    JS_ASSERT_IF(shrinkBuffers, chunkPool.getEmptyCount() <= minEmptyChunkCount);
     JS_ASSERT_IF(releaseAll, chunkPool.getEmptyCount() == 0);
     return freeList;
 }
 
 void
 GCRuntime::freeChunkList(Chunk *chunkListHead)
 {
     while (Chunk *chunk = chunkListHead) {
@@ -1027,17 +1017,17 @@ inline bool
 GCRuntime::wantBackgroundAllocation() const
 {
     /*
      * To minimize memory waste we do not want to run the background chunk
      * allocation if we have empty chunks or when the runtime needs just few
      * of them.
      */
     return helperState.canBackgroundAllocate() &&
-           chunkPool.getEmptyCount() < MIN_EMPTY_CHUNK_COUNT &&
+           chunkPool.getEmptyCount() < minEmptyChunkCount &&
            chunkSet.count() >= 4;
 }
 
 class js::gc::AutoMaybeStartBackgroundAllocation
 {
   private:
     JSRuntime *runtime;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
@@ -1132,16 +1122,18 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     highFrequencyLowLimitBytes(100 * 1024 * 1024),
     highFrequencyHighLimitBytes(500 * 1024 * 1024),
     highFrequencyHeapGrowthMax(3.0),
     highFrequencyHeapGrowthMin(1.5),
     lowFrequencyHeapGrowth(1.5),
     dynamicHeapGrowth(false),
     dynamicMarkSlice(false),
     decommitThreshold(32 * 1024 * 1024),
+    minEmptyChunkCount(1),
+    maxEmptyChunkCount(30),
     cleanUpEverything(false),
     grayBitsValid(false),
     isNeeded(0),
     number(0),
     startNumber(0),
     isFull(false),
     triggerReason(JS::gcreason::NO_REASON),
 #ifdef DEBUG
@@ -1451,16 +1443,26 @@ GCRuntime::setParameter(JSGCParamKey key
         dynamicMarkSlice = value;
         break;
       case JSGC_ALLOCATION_THRESHOLD:
         allocThreshold = value * 1024 * 1024;
         break;
       case JSGC_DECOMMIT_THRESHOLD:
         decommitThreshold = value * 1024 * 1024;
         break;
+      case JSGC_MIN_EMPTY_CHUNK_COUNT:
+        minEmptyChunkCount = value;
+        if (minEmptyChunkCount > maxEmptyChunkCount)
+            maxEmptyChunkCount = minEmptyChunkCount;
+        break;
+      case JSGC_MAX_EMPTY_CHUNK_COUNT:
+        maxEmptyChunkCount = value;
+        if (minEmptyChunkCount > maxEmptyChunkCount)
+            minEmptyChunkCount = maxEmptyChunkCount;
+        break;
       default:
         JS_ASSERT(key == JSGC_MODE);
         mode = JSGCMode(value);
         JS_ASSERT(mode == JSGC_MODE_GLOBAL ||
                   mode == JSGC_MODE_COMPARTMENT ||
                   mode == JSGC_MODE_INCREMENTAL);
         return;
     }
@@ -1499,16 +1501,20 @@ GCRuntime::getParameter(JSGCParamKey key
       case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
         return uint32_t(lowFrequencyHeapGrowth * 100);
       case JSGC_DYNAMIC_HEAP_GROWTH:
         return dynamicHeapGrowth;
       case JSGC_DYNAMIC_MARK_SLICE:
         return dynamicMarkSlice;
       case JSGC_ALLOCATION_THRESHOLD:
         return allocThreshold / 1024 / 1024;
+      case JSGC_MIN_EMPTY_CHUNK_COUNT:
+        return minEmptyChunkCount;
+      case JSGC_MAX_EMPTY_CHUNK_COUNT:
+        return maxEmptyChunkCount;
       default:
         JS_ASSERT(key == JSGC_NUMBER);
         return uint32_t(number);
     }
 }
 
 void
 GCRuntime::setMarkStackLimit(size_t limit)
@@ -3034,16 +3040,17 @@ PurgeRuntime(JSRuntime *rt)
     rt->interpreterStack().purge(rt);
 
     rt->gsnCache.purge();
     rt->scopeCoordinateNameCache.purge();
     rt->newObjectCache.purge();
     rt->nativeIterCache.purge();
     rt->uncompressedSourceCache.purge();
     rt->evalCache.clear();
+    rt->regExpTestCache.purge();
 
     if (!rt->hasActiveCompilations())
         rt->parseMapPool().purgeAll();
 }
 
 bool
 GCRuntime::shouldPreserveJITCode(JSCompartment *comp, int64_t currentTime,
                                  JS::gcreason::Reason reason)
@@ -3837,17 +3844,17 @@ GCRuntime::getNextZoneGroup()
     if (!isIncremental)
         ComponentFinder<Zone>::mergeGroups(currentZoneGroup);
 
     if (abortSweepAfterCurrentGroup) {
         JS_ASSERT(!isIncremental);
         for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
             JS_ASSERT(!zone->gcNextGraphComponent);
             JS_ASSERT(zone->isGCMarking());
-            zone->setNeedsBarrier(false, Zone::UpdateIon);
+            zone->setNeedsBarrier(false, Zone::UpdateJit);
             zone->setGCState(Zone::NoGC);
             zone->gcGrayRoots.clearAndFree();
         }
         rt->setNeedsBarrier(false);
         AssertNeedsBarrierFlagsConsistent(rt);
 
         for (GCCompartmentGroupIter comp(rt); !comp.done(); comp.next()) {
             ArrayBufferObject::resetArrayBufferList(comp);
@@ -4652,17 +4659,17 @@ GCRuntime::resetIncrementalGC(const char
 
         for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
             ArrayBufferObject::resetArrayBufferList(c);
             ResetGrayList(c);
         }
 
         for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
             JS_ASSERT(zone->isGCMarking());
-            zone->setNeedsBarrier(false, Zone::UpdateIon);
+            zone->setNeedsBarrier(false, Zone::UpdateJit);
             zone->setGCState(Zone::NoGC);
         }
         rt->setNeedsBarrier(false);
         AssertNeedsBarrierFlagsConsistent(rt);
 
         incrementalState = NO_INCREMENTAL;
 
         JS_ASSERT(!marker.shouldCheckCompartments());
@@ -4733,36 +4740,36 @@ AutoGCSlice::AutoGCSlice(JSRuntime *rt)
         /*
          * Clear needsBarrier early so we don't do any write barriers during
          * GC. We don't need to update the Ion barriers (which is expensive)
          * because Ion code doesn't run during GC. If need be, we'll update the
          * Ion barriers in ~AutoGCSlice.
          */
         if (zone->isGCMarking()) {
             JS_ASSERT(zone->needsBarrier());
-            zone->setNeedsBarrier(false, Zone::DontUpdateIon);
+            zone->setNeedsBarrier(false, Zone::DontUpdateJit);
         } else {
             JS_ASSERT(!zone->needsBarrier());
         }
     }
     rt->setNeedsBarrier(false);
     AssertNeedsBarrierFlagsConsistent(rt);
 }
 
 AutoGCSlice::~AutoGCSlice()
 {
     /* We can't use GCZonesIter if this is the end of the last slice. */
     bool haveBarriers = false;
     for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) {
         if (zone->isGCMarking()) {
-            zone->setNeedsBarrier(true, Zone::UpdateIon);
+            zone->setNeedsBarrier(true, Zone::UpdateJit);
             zone->allocator.arenas.prepareForIncrementalGC(runtime);
             haveBarriers = true;
         } else {
-            zone->setNeedsBarrier(false, Zone::UpdateIon);
+            zone->setNeedsBarrier(false, Zone::UpdateJit);
         }
     }
     runtime->setNeedsBarrier(haveBarriers);
     AssertNeedsBarrierFlagsConsistent(runtime);
 }
 
 void
 GCRuntime::pushZealSelectedObjects()
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3651,26 +3651,20 @@ JSScript::makeTypes(JSContext *cx)
 
     unsigned count = TypeScript::NumTypeSets(this);
 
     TypeScript *typeScript = (TypeScript *)
         cx->calloc_(TypeScript::SizeIncludingTypeArray(count));
     if (!typeScript)
         return false;
 
-    new(typeScript) TypeScript();
-
-    TypeSet *typeArray = typeScript->typeArray();
-
-    for (unsigned i = 0; i < count; i++)
-        new (&typeArray[i]) StackTypeSet();
-
     types = typeScript;
 
 #ifdef DEBUG
+    StackTypeSet *typeArray = typeScript->typeArray();
     for (unsigned i = 0; i < nTypeSets(); i++) {
         InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u",
                   InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
                   i, id());
     }
     TypeSet *thisTypes = TypeScript::ThisTypes(this);
     InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u",
               InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1986,23 +1986,25 @@ HasRegExpMetaChars(const CharT *chars, s
     for (size_t i = 0; i < length; ++i) {
         if (IsRegExpMetaChar(chars[i]))
             return true;
     }
     return false;
 }
 
 bool
-js::StringHasRegExpMetaChars(JSLinearString *str)
+js::StringHasRegExpMetaChars(JSLinearString *str, size_t beginOffset, size_t endOffset)
 {
+    JS_ASSERT(beginOffset + endOffset <= str->length());
+
     AutoCheckCannotGC nogc;
     if (str->hasLatin1Chars())
-        return HasRegExpMetaChars(str->latin1Chars(nogc), str->length());
-
-    return HasRegExpMetaChars(str->twoByteChars(nogc), str->length());
+        return HasRegExpMetaChars(str->latin1Chars(nogc) + beginOffset, str->length() - beginOffset - endOffset);
+
+    return HasRegExpMetaChars(str->twoByteChars(nogc) + beginOffset, str->length() - beginOffset - endOffset);
 }
 
 namespace {
 
 /*
  * StringRegExpGuard factors logic out of String regexp operations.
  *
  * |optarg| indicates in which argument position RegExp flags will be found, if
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -257,18 +257,20 @@ StringEqualsAscii(JSLinearString *str, c
 
 /* Return true if the string contains a pattern anywhere inside it. */
 extern bool
 StringHasPattern(JSLinearString *text, const jschar *pat, uint32_t patlen);
 
 extern int
 StringFindPattern(JSLinearString *text, JSLinearString *pat, size_t start);
 
+// Whether the string contains any RegExp meta characters (., *, and so forth).
+// Searches the range [beginOffset, length - endOffset>.
 extern bool
-StringHasRegExpMetaChars(JSLinearString *str);
+StringHasRegExpMetaChars(JSLinearString *str, size_t beginOffset = 0, size_t endOffset = 0);
 
 template <typename Char1, typename Char2>
 inline bool
 EqualChars(const Char1 *s1, const Char2 *s2, size_t len);
 
 template <typename Char1>
 inline bool
 EqualChars(const Char1 *s1, const Char1 *s2, size_t len)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6414,17 +6414,17 @@ main(int argc, char **argv, char **envp)
         return 1;
 
     size_t nurseryBytes = JS::DefaultNurseryBytes;
 #ifdef JSGC_GENERATIONAL
     nurseryBytes = op.getIntOption("nursery-size") * 1024L * 1024L;
 #endif
 
     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
-    rt = JS_NewRuntime(32L * 1024L * 1024L, nurseryBytes);
+    rt = JS_NewRuntime(JS::DefaultHeapMaxBytes, nurseryBytes);
     if (!rt)
         return 1;
 
     JS::SetOutOfMemoryCallback(rt, my_OOMCallback, nullptr);
     if (!SetRuntimeOptions(rt, op))
         return 1;
 
     gInterruptFunc.construct(rt, NullValue());
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -375,18 +375,23 @@ StatsCellCallback(JSRuntime *rt, void *d
         }
         break;
       }
 
       case JSTRACE_STRING: {
         JSString *str = static_cast<JSString *>(thing);
 
         JS::StringInfo info;
-        info.gcHeap = thingSize;
-        info.mallocHeap = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        if (str->hasLatin1Chars()) {
+            info.gcHeapLatin1 = thingSize;
+            info.mallocHeapLatin1 = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        } else {
+            info.gcHeapTwoByte = thingSize;
+            info.mallocHeapTwoByte = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        }
         info.numCopies = 1;
 
         zStats->stringInfo.add(info);
 
         // The primary use case for anonymization is automated crash submission
         // (to help detect OOM crashes). In that case, we don't want to pay the
         // memory cost required to do notable string detection.
         if (granularity == FineGrained && !closure->anonymize) {
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -345,16 +345,40 @@ class NewObjectCache
         js_memcpy(dst, src, gc::Arena::thingSize(kind));
 #ifdef JSGC_GENERATIONAL
         Shape::writeBarrierPost(dst->shape_, &dst->shape_);
         types::TypeObject::writeBarrierPost(dst->type_, &dst->type_);
 #endif
     }
 };
 
+class RegExpObject;
+
+// One slot cache for speeding up RegExp.test() executions, by stripping
+// unnecessary leading or trailing .* from the RegExp.
+struct RegExpTestCache
+{
+    RegExpObject *key;
+    RegExpObject *value;
+
+    RegExpTestCache()
+      : key(nullptr), value(nullptr)
+    {}
+
+    void purge() {
+        key = nullptr;
+        value = nullptr;
+    }
+
+    void fill(RegExpObject *key, RegExpObject *value) {
+        this->key = key;
+        this->value = value;
+    }
+};
+
 /*
  * A FreeOp can do one thing: free memory. For convenience, it has delete_
  * convenience methods that also call destructors.
  *
  * FreeOp is passed to finalizers and other sweep-phase hooks so that we do not
  * need to pass a JSContext to those hooks.
  */
 class FreeOp : public JSFreeOp {
@@ -1105,16 +1129,17 @@ struct JSRuntime : public JS::shadow::Ru
 
     js::GSNCache        gsnCache;
     js::ScopeCoordinateNameCache scopeCoordinateNameCache;
     js::NewObjectCache  newObjectCache;
     js::NativeIterCache nativeIterCache;
     js::UncompressedSourceCache uncompressedSourceCache;
     js::EvalCache       evalCache;
     js::LazyScriptCache lazyScriptCache;
+    js::RegExpTestCache regExpTestCache;
 
     js::CompressedSourceSet compressedSourceSet;
     js::DateTimeInfo    dateTimeInfo;
 
     // Pool of maps used during parse/emit. This may be modified by threads
     // with an ExclusiveContext and requires a lock. Active compilations
     // prevent the pool from being purged during GCs.
   private:
--- a/js/src/vm/Symbol.h
+++ b/js/src/vm/Symbol.h
@@ -78,17 +78,17 @@ struct HashSymbolsByDescription
 };
 
 /*
  * Hash table that implements the symbol registry.
  *
  * This must be a typedef for the benefit of GCC 4.4.6 (used to build B2G for Ice
  * Cream Sandwich).
  */
-typedef HashSet<JS::Symbol *, HashSymbolsByDescription, SystemAllocPolicy> SymbolHashSet;
+typedef HashSet<ReadBarrieredSymbol, HashSymbolsByDescription, SystemAllocPolicy> SymbolHashSet;
 
 /*
  * The runtime-wide symbol registry, used to implement Symbol.for().
  *
  * ES6 draft rev 25 (2014 May 22) calls this the GlobalSymbolRegistry List. In
  * our implementation, it is not global. There is one per JSRuntime. The
  * symbols in the symbol registry, like all symbols, are allocated in the atoms
  * compartment and can be directly referenced from any compartment. They are
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1910,62 +1910,88 @@ ReportZoneStats(const JS::ZoneStats &zSt
         // "string(length=".  If we report these as notable, then we'll create
         // even more notable strings the next time we open about:memory (unless
         // there's a GC in the meantime), and so on ad infinitum.
         //
         // To avoid cluttering up about:memory like this, we stick notable
         // strings which contain "string(length=" into their own bucket.
 #       define STRING_LENGTH "string(length="
         if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) {
-            stringsNotableAboutMemoryGCHeap += info.gcHeap;
-            stringsNotableAboutMemoryMallocHeap += info.mallocHeap;
+            stringsNotableAboutMemoryGCHeap += info.gcHeapLatin1;
+            stringsNotableAboutMemoryGCHeap += info.gcHeapTwoByte;
+            stringsNotableAboutMemoryMallocHeap += info.mallocHeapLatin1;
+            stringsNotableAboutMemoryMallocHeap += info.mallocHeapTwoByte;
             continue;
         }
 
         // Escape / to \ before we put notableString into the memory reporter
         // path, because we don't want any forward slashes in the string to
         // count as path separators.
         nsCString escapedString(notableString);
         escapedString.ReplaceSubstring("/", "\\");
 
         bool truncated = notableString.Length() < info.length;
 
         nsCString path = pathPrefix +
             nsPrintfCString("strings/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)/",
                             info.length, info.numCopies, escapedString.get(),
                             truncated ? " (truncated)" : "");
 
-        if (info.gcHeap > 0) {
-            REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap"),
-                info.gcHeap,
-                "Strings. " MAYBE_INLINE);
+        if (info.gcHeapLatin1 > 0) {
+            REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap/latin1"),
+                info.gcHeapLatin1,
+                "Latin1 strings. " MAYBE_INLINE);
+        }
+
+        if (info.gcHeapTwoByte > 0) {
+            REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap/two-byte"),
+                info.gcHeapTwoByte,
+                "TwoByte strings. " MAYBE_INLINE);
         }
 
-        if (info.mallocHeap > 0) {
-            REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap"),
-                KIND_HEAP, info.mallocHeap,
-                "Non-inline string characters. " MAYBE_OVERALLOCATED);
+        if (info.mallocHeapLatin1 > 0) {
+            REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap/latin1"),
+                KIND_HEAP, info.mallocHeapLatin1,
+                "Non-inline Latin1 string characters. " MAYBE_OVERALLOCATED);
+        }
+
+        if (info.mallocHeapTwoByte > 0) {
+            REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap/two-byte"),
+                KIND_HEAP, info.mallocHeapTwoByte,
+                "Non-inline TwoByte string characters. " MAYBE_OVERALLOCATED);
         }
     }
 
     nsCString nonNotablePath = pathPrefix;
     nonNotablePath += (zStats.isTotals || anonymize)
                     ? NS_LITERAL_CSTRING("strings/")
                     : NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/");
 
-    if (zStats.stringInfo.gcHeap > 0) {
-        REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap"),
-            zStats.stringInfo.gcHeap,
-            "Strings. " MAYBE_INLINE);
+    if (zStats.stringInfo.gcHeapLatin1 > 0) {
+        REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap/latin1"),
+            zStats.stringInfo.gcHeapLatin1,
+            "Latin1 strings. " MAYBE_INLINE);
+    }
+
+    if (zStats.stringInfo.gcHeapTwoByte > 0) {
+        REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap/two-byte"),
+            zStats.stringInfo.gcHeapTwoByte,
+            "TwoByte strings. " MAYBE_INLINE);
     }
 
-    if (zStats.stringInfo.mallocHeap > 0) {
-        REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap"),
-            KIND_HEAP, zStats.stringInfo.mallocHeap,
-            "Non-inline string characters. " MAYBE_OVERALLOCATED);
+    if (zStats.stringInfo.mallocHeapLatin1 > 0) {
+        REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap/latin1"),
+            KIND_HEAP, zStats.stringInfo.mallocHeapLatin1,
+            "Non-inline Latin1 string characters. " MAYBE_OVERALLOCATED);
+    }
+
+    if (zStats.stringInfo.mallocHeapTwoByte > 0) {
+        REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap/two-byte"),
+            KIND_HEAP, zStats.stringInfo.mallocHeapTwoByte,
+            "Non-inline TwoByte string characters. " MAYBE_OVERALLOCATED);
     }
 
     if (stringsNotableAboutMemoryGCHeap > 0) {
         MOZ_ASSERT(!zStats.isTotals);
         REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/gc-heap"),
             stringsNotableAboutMemoryGCHeap,
             "Strings that contain the characters '" STRING_LENGTH "', which "
             "are probably from about:memory itself." MAYBE_INLINE
@@ -3064,17 +3090,17 @@ class XPCJSSourceHook: public js::Source
 };
 
 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
     xpc::WrapperFactory::Rewrap,
     xpc::WrapperFactory::PrepareForWrapping
 };
 
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
-   : CycleCollectedJSRuntime(nullptr, 32L * 1024L * 1024L),
+   : CycleCollectedJSRuntime(nullptr, JS::DefaultHeapMaxBytes),
    mJSContextStack(new XPCJSContextStack(MOZ_THIS_IN_INITIALIZER_LIST())),
    mCallContext(nullptr),
    mAutoRoots(nullptr),
    mResolveName(JSID_VOID),
    mResolvingWrapper(nullptr),
    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/767593-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<body>
+<div><span style="page-break-after: always;"></span><div style="position: fixed;"><span style="display: none;"></span></div>B</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/767593-2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<body>
+<div><span style="page-break-after: always;"></span><div style="position: fixed;"><span style="display: none;"></span><span style="display: none;"></span></div>B</div>
+</body>
+</html>
+
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -396,16 +396,18 @@ load 727601.html
 # that the reftest harness can't handle restarting crashed content
 # processes so we time out.)
 skip-if(Android||B2G||browserIsRemote) asserts(0-2) pref(dom.disable_open_during_load,false) load 735943.html # the assertion is bug 735966, for android bug 760271, for b2g bug 833371
 asserts(0-2) load 736389-1.xhtml # sometimes the above assertions are delayed and is reported on this test instead
 asserts-if(winWidget,0-2) load 736924-1.html # bug 738803
 load 749816-1.html
 load 763223-1.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.lineThreshold,100) load 763702.xhtml
+load 767593-1.html
+load 767593-2.html
 load 770381-1.html
 load 772306.html
 load 788360.html
 load 793848.html
 load 795646.html
 skip-if(1) load 802902.html # bug 901752
 load 806056-1.html
 load 806056-2.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -5262,30 +5262,36 @@ nsCSSFrameConstructor::AddFrameConstruct
   AddFrameConstructionItemsInternal(aState, aContent, aParentFrame,
                                     aContent->Tag(), aContent->GetNameSpaceID(),
                                     aSuppressWhiteSpaceOptimizations,
                                     styleContext,
                                     flags, nullptr,
                                     aItems);
 }
 
-/* static */ void
-nsCSSFrameConstructor::SetAsUndisplayedContent(FrameConstructionItemList& aList,
+void
+nsCSSFrameConstructor::SetAsUndisplayedContent(nsFrameConstructorState& aState,
+                                               FrameConstructionItemList& aList,
                                                nsIContent* aContent,
                                                nsStyleContext* aStyleContext,
                                                bool aIsGeneratedContent)
 {
   if (aStyleContext->GetPseudo()) {
     if (aIsGeneratedContent) {
       aContent->UnbindFromTree();
     }
     return;
   }
-
   NS_ASSERTION(!aIsGeneratedContent, "Should have had pseudo type");
+
+  if (aState.mCreatingExtraFrames) {
+    MOZ_ASSERT(GetUndisplayedContent(aContent),
+               "should have called SetUndisplayedContent earlier");
+    return;
+  }
   aList.AppendUndisplayedItem(aContent, aStyleContext);
 }
 
 void
 nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState& aState,
                                                          nsIContent* aContent,
                                                          nsContainerFrame* aParentFrame,
                                                          nsIAtom* aTag,
@@ -5341,17 +5347,17 @@ nsCSSFrameConstructor::AddFrameConstruct
     aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID);
   }
 
   bool isGeneratedContent = ((aFlags & ITEM_IS_GENERATED_CONTENT) != 0);
 
   // Pre-check for display "none" - if we find that, don't create
   // any frame at all
   if (NS_STYLE_DISPLAY_NONE == display->mDisplay) {
-    SetAsUndisplayedContent(aItems, aContent, styleContext, isGeneratedContent);
+    SetAsUndisplayedContent(aState, aItems, aContent, styleContext, isGeneratedContent);
     return;
   }
 
   bool isText = !aContent->IsElement();
 
   // never create frames for non-option/optgroup kids of <select> and
   // non-option kids of <optgroup> inside a <select>.
   // XXXbz it's not clear how this should best work with XBL.
@@ -5365,17 +5371,17 @@ nsCSSFrameConstructor::AddFrameConstruct
         !aContent->IsHTML(nsGkAtoms::option) &&
         // <optgroup> is OK in <select> but not in <optgroup>
         (!aContent->IsHTML(nsGkAtoms::optgroup) ||
          parentTag != nsGkAtoms::select) &&
         // Allow native anonymous content no matter what
         !aContent->IsRootOfNativeAnonymousSubtree()) {
       // No frame for aContent
       if (!isText) {
-        SetAsUndisplayedContent(aItems, aContent, styleContext,
+        SetAsUndisplayedContent(aState, aItems, aContent, styleContext,
                                 isGeneratedContent);
       }
       return;
     }
   }
 
   bool isPopup = false;
   // Try to find frame construction data for this content
@@ -5390,17 +5396,17 @@ nsCSSFrameConstructor::AddFrameConstruct
     Element* element = aContent->AsElement();
 
     // Don't create frames for non-SVG element children of SVG elements.
     if (aNameSpaceID != kNameSpaceID_SVG &&
         ((aParentFrame &&
           IsFrameForSVG(aParentFrame) &&
           !aParentFrame->IsFrameOfType(nsIFrame::eSVGForeignObject)) ||
          (aFlags & ITEM_IS_WITHIN_SVG_TEXT))) {
-      SetAsUndisplayedContent(aItems, element, styleContext,
+      SetAsUndisplayedContent(aState, aItems, element, styleContext,
                               isGeneratedContent);
       return;
     }
 
     data = FindHTMLData(element, aTag, aNameSpaceID, aParentFrame,
                         styleContext);
     if (!data) {
       data = FindXULTagData(element, aTag, aNameSpaceID, styleContext);
@@ -5423,44 +5429,44 @@ nsCSSFrameConstructor::AddFrameConstruct
     // And general display types
     if (!data) {
       data = FindDisplayData(display, element, aParentFrame, styleContext);
     }
 
     NS_ASSERTION(data, "Should have frame construction data now");
 
     if (data->mBits & FCDATA_SUPPRESS_FRAME) {
-      SetAsUndisplayedContent(aItems, element, styleContext, isGeneratedContent);
+      SetAsUndisplayedContent(aState, aItems, element, styleContext, isGeneratedContent);
       return;
     }
 
 #ifdef MOZ_XUL
     if ((data->mBits & FCDATA_IS_POPUP) &&
         (!aParentFrame || // Parent is inline
          aParentFrame->GetType() != nsGkAtoms::menuFrame)) {
       if (!aState.mPopupItems.containingBlock &&
           !aState.mHavePendingPopupgroup) {
-        SetAsUndisplayedContent(aItems, element, styleContext,
+        SetAsUndisplayedContent(aState, aItems, element, styleContext,
                                 isGeneratedContent);
         return;
       }
 
       isPopup = true;
     }
 #endif /* MOZ_XUL */
   }
 
   uint32_t bits = data->mBits;
 
   // Inside colgroups, suppress everything except columns.
   if (aParentFrame &&
       aParentFrame->GetType() == nsGkAtoms::tableColGroupFrame &&
       (!(bits & FCDATA_IS_TABLE_PART) ||
        display->mDisplay != NS_STYLE_DISPLAY_TABLE_COLUMN)) {
-    SetAsUndisplayedContent(aItems, aContent, styleContext, isGeneratedContent);
+    SetAsUndisplayedContent(aState, aItems, aContent, styleContext, isGeneratedContent);
     return;
   }
 
   bool canHavePageBreak =
     (aFlags & ITEM_ALLOW_PAGE_BREAK) &&
     aState.mPresContext->IsPaginated() &&
     !display->IsAbsolutelyPositionedStyle() &&
     !(bits & FCDATA_IS_TABLE_PART) &&
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -1763,20 +1763,21 @@ private:
 
   /**
    * Add the pair (aContent, aStyleContext) to the undisplayed items
    * in aList as needed.  This method enforces the invariant that all
    * style contexts in the undisplayed content map must be non-pseudo
    * contexts and also handles unbinding undisplayed generated content
    * as needed.
    */
-  static void SetAsUndisplayedContent(FrameConstructionItemList& aList,
-                                      nsIContent* aContent,
-                                      nsStyleContext* aStyleContext,
-                                      bool aIsGeneratedContent);
+  void SetAsUndisplayedContent(nsFrameConstructorState& aState,
+                               FrameConstructionItemList& aList,
+                               nsIContent* aContent,
+                               nsStyleContext* aStyleContext,
+                               bool aIsGeneratedContent);
   // Create touch caret frame.
   void ConstructAnonymousContentForCanvas(nsFrameConstructorState& aState,
                                           nsIFrame* aFrame,
                                           nsIContent* aDocElement);
 
 public:
 
   friend class nsFrameConstructorState;
--- a/media/webrtc/trunk/build/common.gypi
+++ b/media/webrtc/trunk/build/common.gypi
@@ -423,17 +423,17 @@
         # NSS usage.
         ['(OS=="linux" or OS=="solaris" or os_bsd==1) and use_openssl==0', {
           'use_nss%': 1,
         }, {
           'use_nss%': 0,
         }],
 
         # Flags to use X11 on non-Mac POSIX platforms
-        ['OS=="win" or OS=="mac" or OS=="ios" or OS=="android"', {
+        ['OS=="win" or OS=="mac" or OS=="ios" or OS=="android" or moz_widget_toolkit_gonk==1', {
           'use_glib%': 0,
           'use_x11%': 0,
         }, {
           'use_glib%': 1,
           'use_x11%': 1,
         }],
 
         # We always use skia text rendering in Aura on Windows, since GDI
--- a/media/webrtc/trunk/peerconnection.gyp
+++ b/media/webrtc/trunk/peerconnection.gyp
@@ -35,16 +35,23 @@
         'dependencies': [
           'webrtc/modules/modules.gyp:audio_device',
           'webrtc/modules/modules.gyp:video_capture_module',
 #          'webrtc/modules/modules.gyp:video_render_module',
 #          'webrtc/system_wrappers/source/system_wrappers.gyp:system_wrappers',
           'webrtc/video_engine/video_engine.gyp:video_engine_core',
           'webrtc/voice_engine/voice_engine.gyp:voice_engine',
         ],
+        'conditions': [
+          ['OS!="android"', {
+            'dependencies': [
+              'webrtc/modules/modules.gyp:desktop_capture',
+            ],
+          },
+         ]],
       }, ],
     ],
   }, ],
   'conditions': [
     ['build_with_mozilla==0', {
     'targets': [
     {
       'target_name': 'peerconnection_server',
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer.cc
@@ -0,0 +1,21 @@
+/*
+*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+*
+*  Use of this source code is governed by a BSD-style license
+*  that can be found in the LICENSE file in the root of the source
+*  tree. An additional intellectual property rights grant can be found
+*  in the file PATENTS.  All contributing project authors may
+*  be found in the AUTHORS file in the root of the source tree.
+*/
+
+#include "webrtc/modules/desktop_capture/app_capturer.h"
+#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
+
+namespace webrtc {
+
+// static
+AppCapturer* AppCapturer::Create() {
+  return Create(DesktopCaptureOptions::CreateDefault());
+}
+
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer.h
@@ -0,0 +1,50 @@
+/*
+*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+*
+*  Use of this source code is governed by a BSD-style license
+*  that can be found in the LICENSE file in the root of the source
+*  tree. An additional intellectual property rights grant can be found
+*  in the file PATENTS.  All contributing project authors may
+*  be found in the AUTHORS file in the root of the source tree.
+*/
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_APP_CAPTURER_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_APP_CAPTURER_H_
+
+#include <vector>
+#include <string>
+
+#include "webrtc/modules/desktop_capture/desktop_capture_types.h"
+#include "webrtc/modules/desktop_capture/desktop_capturer.h"
+#include "webrtc/system_wrappers/interface/constructor_magic.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class DesktopCaptureOptions;
+
+class AppCapturer : public DesktopCapturer {
+public:
+    typedef webrtc::ProcessId ProcessId;
+    struct App {
+        ProcessId id;
+        // Application Name in UTF-8 encoding.
+        std::string title;
+    };
+    typedef std::vector<App> AppList;
+
+    static AppCapturer* Create(const DesktopCaptureOptions& options);
+    static AppCapturer* Create();
+
+    virtual ~AppCapturer() {}
+
+    //AppCapturer Interfaces
+    virtual bool GetAppList(AppList* apps) = 0;
+    virtual bool SelectApp(ProcessId id) = 0;
+    virtual bool BringAppToFront() = 0;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_APP_CAPTURER_H_
+
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_mac.mm
@@ -0,0 +1,90 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/desktop_capture/window_capturer.h"
+#include "webrtc/modules/desktop_capture/app_capturer.h"
+
+#include <assert.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <Cocoa/Cocoa.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/system_wrappers/interface/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+class AppCapturerMac : public AppCapturer {
+ public:
+  AppCapturerMac();
+  virtual ~AppCapturerMac();
+
+  // AppCapturer interface.
+  virtual bool GetAppList(AppList* apps) OVERRIDE;
+  virtual bool SelectApp(ProcessId id) OVERRIDE;
+  virtual bool BringAppToFront() OVERRIDE;
+
+  // DesktopCapturer interface.
+  virtual void Start(Callback* callback) OVERRIDE;
+  virtual void Capture(const DesktopRegion& region) OVERRIDE;
+
+ private:
+  Callback* callback_;
+  ProcessId process_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppCapturerMac);
+};
+
+AppCapturerMac::AppCapturerMac()
+  : callback_(NULL),
+    process_id_(0) {
+}
+
+AppCapturerMac::~AppCapturerMac() {
+}
+
+// AppCapturer interface.
+bool AppCapturerMac::GetAppList(AppList* apps){
+  // Not implemented yet: See Bug 1036653
+  return true;
+}
+
+bool AppCapturerMac::SelectApp(ProcessId id){
+  // Not implemented yet: See Bug 1036653
+  return true;
+}
+
+bool AppCapturerMac::BringAppToFront() {
+  // Not implemented yet: See Bug 1036653
+   return true;
+}
+
+// DesktopCapturer interface.
+void AppCapturerMac::Start(Callback* callback) {
+  assert(!callback_);
+  assert(callback);
+
+  callback_ = callback;
+}
+
+void AppCapturerMac::Capture(const DesktopRegion& region) {
+  // Not implemented yet: See Bug 1036653
+}
+
+}  // namespace
+
+// static
+AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) {
+  return new AppCapturerMac();
+}
+
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc
@@ -0,0 +1,83 @@
+/*
+*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+*
+*  Use of this source code is governed by a BSD-style license
+*  that can be found in the LICENSE file in the root of the source
+*  tree. An additional intellectual property rights grant can be found
+*  in the file PATENTS.  All contributing project authors may
+*  be found in the AUTHORS file in the root of the source tree.
+*/
+#include "webrtc/modules/desktop_capture/window_capturer.h"
+#include "webrtc/modules/desktop_capture/app_capturer.h"
+
+#include <assert.h>
+
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+
+namespace webrtc {
+
+namespace {
+
+class AppCapturerNull : public AppCapturer {
+public:
+  AppCapturerNull();
+  virtual ~AppCapturerNull();
+
+  // AppCapturer interface.
+  virtual bool GetAppList(AppList* apps) OVERRIDE;
+  virtual bool SelectApp(ProcessId id) OVERRIDE;
+  virtual bool BringAppToFront()	OVERRIDE;
+
+  // DesktopCapturer interface.
+  virtual void Start(Callback* callback) OVERRIDE;
+  virtual void Capture(const DesktopRegion& region) OVERRIDE;
+
+private:
+  Callback* callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppCapturerNull);
+};
+
+AppCapturerNull::AppCapturerNull()
+  : callback_(NULL) {
+}
+
+AppCapturerNull::~AppCapturerNull() {
+}
+
+bool AppCapturerNull::GetAppList(AppList* apps) {
+  // Not implemented yet: See Bug 1036653
+  return false;
+}
+
+bool SelectApp(ProcessId id) {
+  // Not implemented yet: See Bug 1036653
+  return false;
+}
+
+bool BringAppToFront() {
+  // Not implemented yet: See Bug 1036653
+  return false;
+}
+
+// DesktopCapturer interface.
+void AppCapturerNull::Start(Callback* callback) {
+  assert(!callback_);
+  assert(callback);
+
+  callback_ = callback;
+}
+
+void AppCapturerNull::Capture(const DesktopRegion& region) {
+  // Not implemented yet: See Bug 1036653
+  callback_->OnCaptureCompleted(NULL);
+}
+
+}  // namespace
+
+// static
+AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) {
+  return new AppCapturerNull();
+}
+
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_unittest.cc
@@ -0,0 +1,89 @@
+/*
+*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+*
+*  Use of this source code is governed by a BSD-style license
+*  that can be found in the LICENSE file in the root of the source
+*  tree. An additional intellectual property rights grant can be found
+*  in the file PATENTS.  All contributing project authors may
+*  be found in the AUTHORS file in the root of the source tree.
+*/
+#include "webrtc/modules/desktop_capture/app_capturer.h"
+
+#include "gtest/gtest.h"
+#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/desktop_region.h"
+#include "webrtc/system_wrappers/interface/logging.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+class AppCapturerTest : public testing::Test,
+                        public DesktopCapturer::Callback {
+public:
+  void SetUp() OVERRIDE {
+    capturer_.reset(
+      AppCapturer::Create(DesktopCaptureOptions::CreateDefault())
+    );
+  }
+
+  void TearDown() OVERRIDE {
+  }
+
+  // DesktopCapturer::Callback interface
+  virtual SharedMemory* CreateSharedMemory(size_t size) OVERRIDE {
+    return NULL;
+  }
+
+  virtual void OnCaptureCompleted(DesktopFrame* frame) OVERRIDE {
+    frame_.reset(frame);
+  }
+
+protected:
+  scoped_ptr<AppCapturer> capturer_;
+  scoped_ptr<DesktopFrame> frame_;
+};
+
+// Verify that we can enumerate applications.
+TEST_F(AppCapturerTest, Enumerate) {
+  AppCapturer::AppList apps;
+  EXPECT_TRUE(capturer_->GetAppList(&apps));
+
+  // Verify that window titles are set.
+  for (AppCapturer::AppList::iterator it = apps.begin();
+       it != windows.end(); ++it) {
+    EXPECT_FALSE(it->title.empty());
+  }
+}
+
+// Verify we can capture a app.
+TEST_F(AppCapturerTest, Capture) {
+  AppCapturer::AppList apps;
+  capturer_->Start(this);
+  EXPECT_TRUE(capturer_->GetAppList(&apps));
+
+  // Verify that we can select and capture each app.
+  for (AppCapturer::AppList::iterator it = apps.begin();
+       it != apps.end(); ++it) {
+    frame_.reset();
+    if (capturer_->SelectApp(it->id)) {
+      capturer_->Capture(DesktopRegion());
+    }
+
+    // If we failed to capture a window make sure it no longer exists.
+    if (!frame_.get()) {
+      AppCapturer::AppList new_list;
+      EXPECT_TRUE(capturer_->GetAppList(&new_list));
+      for (AppCapturer::AppList::iterator new_list_it = apps.begin();
+           new_list_it != apps.end(); ++new_list_it) {
+        EXPECT_FALSE(it->id == new_list_it->id);
+      }
+      continue;
+    }
+
+    EXPECT_GT(frame_->size().width(), 0);
+    EXPECT_GT(frame_->size().height(), 0);
+  }
+}
+
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_win.cc
@@ -0,0 +1,103 @@
+/*
+*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+*
+*  Use of this source code is governed by a BSD-style license
+*  that can be found in the LICENSE file in the root of the source
+*  tree. An additional intellectual property rights grant can be found
+*  in the file PATENTS.  All contributing project authors may
+*  be found in the AUTHORS file in the root of the source tree.
+*/
+#include "webrtc/modules/desktop_capture/window_capturer.h"
+#include "webrtc/modules/desktop_capture/app_capturer.h"
+
+#include <assert.h>
+#include <windows.h>
+
+#include "webrtc/modules/desktop_capture/desktop_frame_win.h"
+#include "webrtc/system_wrappers/interface/logging.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+namespace {
+
+std::string Utf16ToUtf8(const WCHAR* str) {
+  int len_utf8 = WideCharToMultiByte(CP_UTF8, 0, str, -1,
+                                     NULL, 0, NULL, NULL);
+  if (len_utf8 <= 0)
+    return std::string();
+  std::string result(len_utf8, '\0');
+  int rv = WideCharToMultiByte(CP_UTF8, 0, str, -1,
+                               &*(result.begin()), len_utf8, NULL, NULL);
+  if (rv != len_utf8)
+    assert(false);
+
+  return result;
+}
+
+
+class AppCapturerWin : public AppCapturer {
+public:
+  AppCapturerWin();
+  virtual ~AppCapturerWin();
+
+  // AppCapturer interface.
+  virtual bool GetAppList(AppList* apps) OVERRIDE;
+  virtual bool SelectApp(ProcessId id) OVERRIDE;
+  virtual bool BringAppToFront() OVERRIDE;
+
+  // DesktopCapturer interface.
+  virtual void Start(Callback* callback) OVERRIDE;
+  virtual void Capture(const DesktopRegion& region) OVERRIDE;
+
+private:
+  Callback* callback_;
+
+  ProcessId processId_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppCapturerWin);
+};
+
+AppCapturerWin::AppCapturerWin()
+  : callback_(NULL),
+    processId_(NULL) {
+}
+
+AppCapturerWin::~AppCapturerWin() {
+}
+
+// AppCapturer interface.
+bool AppCapturerWin::GetAppList(AppList* apps){
+  // Not implemented yet: See Bug 1036653
+  return true;
+}
+
+bool AppCapturerWin::SelectApp(ProcessId id) {
+  // Not implemented yet: See Bug 1036653
+  return true;
+}
+
+bool AppCapturerWin::BringAppToFront() {
+  // Not implemented yet: See Bug 1036653
+  return true;
+}
+
+// DesktopCapturer interface.
+void AppCapturerWin::Start(Callback* callback) {
+  assert(!callback_);
+  assert(callback);
+
+  callback_ = callback;
+}
+void AppCapturerWin::Capture(const DesktopRegion& region) {
+  // Not implemented yet: See Bug 1036653
+}
+
+}  // namespace
+
+// static
+AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) {
+  return new AppCapturerWin();
+}
+
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc
@@ -0,0 +1,99 @@
+/*
+*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+*
+*  Use of this source code is governed by a BSD-style license
+*  that can be found in the LICENSE file in the root of the source
+*  tree. An additional intellectual property rights grant can be found
+*  in the file PATENTS.  All contributing project authors may
+*  be found in the AUTHORS file in the root of the source tree.
+*/
+#include "webrtc/modules/desktop_capture/window_capturer.h"
+#include "webrtc/modules/desktop_capture/app_capturer.h"
+
+#include <assert.h>
+#include <string.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/Xutil.h>
+
+#include <algorithm>
+
+#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
+#include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
+#include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
+#include "webrtc/system_wrappers/interface/logging.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/system_wrappers/interface/scoped_refptr.h"
+
+namespace webrtc {
+
+namespace {
+
+
+class AppCapturerLinux : public AppCapturer{
+public:
+  AppCapturerLinux(const DesktopCaptureOptions& options);
+  virtual ~AppCapturerLinux();
+
+  // AppCapturer interface.
+  virtual bool GetAppList(AppList* apps) OVERRIDE;
+  virtual bool SelectApp(ProcessId id) OVERRIDE;
+  virtual bool BringAppToFront() OVERRIDE;
+
+  // DesktopCapturer interface.
+  virtual void Start(Callback* callback) OVERRIDE;
+  virtual void Capture(const DesktopRegion& region) OVERRIDE;
+
+
+private:
+  Callback* callback_;
+  ProcessId selected_process_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppCapturerLinux);
+};
+
+AppCapturerLinux::AppCapturerLinux(const DesktopCaptureOptions& options)
+  : callback_(NULL),
+    selected_process_(0) {
+}
+
+AppCapturerLinux::~AppCapturerLinux() {
+}
+
+// AppCapturer interface.
+bool AppCapturerLinux::GetAppList(AppList* apps){
+  // Not implemented yet: See Bug 1036653
+  return true;
+}
+bool AppCapturerLinux::SelectApp(ProcessId id){
+  // Not implemented yet: See Bug 1036653
+  return true;
+}
+bool AppCapturerLinux::BringAppToFront(){
+  // Not implemented yet: See Bug 1036653
+  return true;
+}
+
+// DesktopCapturer interface.
+void AppCapturerLinux::Start(Callback* callback) {
+  assert(!callback_);
+  assert(callback);
+
+  callback_ = callback;
+}
+
+void AppCapturerLinux::Capture(const DesktopRegion& region) {
+  // Not implemented yet: See Bug 1036653
+}
+
+}  // namespace
+
+// static
+AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) {
+  return new AppCapturerLinux(options);
+}
+
+}  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture.gypi
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture.gypi
@@ -2,97 +2,94 @@
 #
 # Use of this source code is governed by a BSD-style license
 # that can be found in the LICENSE file in the root of the source
 # tree. An additional intellectual property rights grant can be found
 # in the file PATENTS.  All contributing project authors may
 # be found in the AUTHORS file in the root of the source tree.
 
 {
+  'variables': {
+    'multi_monitor_screenshare%' : 0,
+  },
   'targets': [
     {
       'target_name': 'desktop_capture',
       'type': 'static_library',
       'dependencies': [
         '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
       ],
       'sources': [
         "desktop_and_cursor_composer.cc",
         "desktop_and_cursor_composer.h",
         "desktop_capture_types.h",
         "desktop_capturer.h",
         "desktop_frame.cc",
         "desktop_frame.h",
-        "desktop_frame_win.cc",
-        "desktop_frame_win.h",
         "desktop_geometry.cc",
         "desktop_geometry.h",
         "desktop_capture_options.h",
         "desktop_capture_options.cc",
         "desktop_capturer.h",
         "desktop_region.cc",
         "desktop_region.h",
         "differ.cc",
         "differ.h",
         "differ_block.cc",
         "differ_block.h",
-        "mac/desktop_configuration.h",
-        "mac/desktop_configuration.mm",
-        "mac/desktop_configuration_monitor.h",
-        "mac/desktop_configuration_monitor.cc",
-        "mac/osx_version.h",
-        "mac/osx_version.cc",
-        "mac/scoped_pixel_buffer_object.cc",
-        "mac/scoped_pixel_buffer_object.h",
         "mouse_cursor.cc",
         "mouse_cursor.h",
         "mouse_cursor_monitor.h",
-        "mouse_cursor_monitor_mac.mm",
-        "mouse_cursor_monitor_win.cc",
-        "mouse_cursor_monitor_x11.cc",
         "mouse_cursor_shape.h",
         "screen_capture_frame_queue.cc",
         "screen_capture_frame_queue.h",
         "screen_capturer.cc",
         "screen_capturer.h",
         "screen_capturer_helper.cc",
         "screen_capturer_helper.h",
-        "screen_capturer_mac.mm",
-        "screen_capturer_win.cc",
-        "screen_capturer_x11.cc",
         "shared_desktop_frame.cc",
         "shared_desktop_frame.h",
         "shared_memory.cc",
         "shared_memory.h",
-        "win/cursor.cc",
-        "win/cursor.h",
-        "win/desktop.cc",
-        "win/desktop.h",
-        "win/scoped_gdi_object.h",
-        "win/scoped_thread_desktop.cc",
-        "win/scoped_thread_desktop.h",
         "window_capturer.cc",
         "window_capturer.h",
-        "window_capturer_mac.cc",
-        "window_capturer_win.cc",
-        "window_capturer_x11.cc",
-        "x11/shared_x_display.h",
-        "x11/shared_x_display.cc",
-        "x11/x_error_trap.cc",
-        "x11/x_error_trap.h",
-        "x11/x_server_pixel_buffer.cc",
-        "x11/x_server_pixel_buffer.h",
+        "desktop_device_info.h",
+        "desktop_device_info.cc",
+        "app_capturer.h",
+        "app_capturer.cc",
       ],
       'conditions': [
+        ['multi_monitor_screenshare != 0', {
+          'defines': [
+            'MULTI_MONITOR_SCREENSHARE'
+          ],
+        }],
         ['OS!="ios" and (target_arch=="ia32" or target_arch=="x64")', {
           'dependencies': [
             'desktop_capture_differ_sse2',
           ],
         }],
         ['use_x11 == 1', {
+          'defines':[
+            'USE_X11',
+          ],
+          'sources': [
+            "mouse_cursor_monitor_x11.cc",
+            "screen_capturer_x11.cc",
+            "window_capturer_x11.cc",
+            "x11/shared_x_display.h",
+            "x11/shared_x_display.cc",
+            "x11/x_error_trap.cc",
+            "x11/x_error_trap.h",
+            "x11/x_server_pixel_buffer.cc",
+            "x11/x_server_pixel_buffer.h",
+            "x11/desktop_device_info_x11.h",
+            "x11/desktop_device_info_x11.cc",
+            "app_capturer_x11.cc",
+          ],
           'link_settings': {
             'libraries': [
               '-lX11',
               '-lXcomposite',
               '-lXdamage',
               '-lXext',
               '-lXfixes',
               '-lXrender',
@@ -102,24 +99,59 @@
         ['OS!="win" and OS!="mac" and use_x11==0', {
           'sources': [
             "mouse_cursor_monitor_null.cc",
             "screen_capturer_null.cc",
             "window_capturer_null.cc",
           ],
         }],
         ['OS=="mac"', {
+          'sources': [
+            "mac/desktop_configuration.h",
+            "mac/desktop_configuration.mm",
+            "mac/desktop_configuration_monitor.h",
+            "mac/desktop_configuration_monitor.cc",
+            "mac/osx_version.h",
+            "mac/osx_version.cc",
+            "mac/scoped_pixel_buffer_object.cc",
+            "mac/scoped_pixel_buffer_object.h",
+            "mac/desktop_device_info_mac.h",
+            "mac/desktop_device_info_mac.mm",
+            "mouse_cursor_monitor_mac.mm",
+            "screen_capturer_mac.mm",
+            "window_capturer_mac.mm",
+            "app_capturer_mac.mm",
+          ],
           'link_settings': {
             'libraries': [
               '$(SDKROOT)/System/Library/Frameworks/AppKit.framework',
               '$(SDKROOT)/System/Library/Frameworks/IOKit.framework',
               '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
             ],
           },
         }],
+        ['OS=="win"', {
+           'sources': [
+             "desktop_frame_win.cc",
+             "desktop_frame_win.h",
+             "mouse_cursor_monitor_win.cc",
+             "screen_capturer_win.cc",
+             "win/cursor.cc",
+             "win/cursor.h",
+             "win/desktop.cc",
+             "win/desktop.h",
+             "win/scoped_gdi_object.h",
+             "win/scoped_thread_desktop.cc",
+             "win/scoped_thread_desktop.h",
+             "win/desktop_device_info_win.h",
+             "win/desktop_device_info_win.cc",
+             "window_capturer_win.cc",
+             "app_capturer_win.cc",
+           ],
+          }],
       ],
     },
   ],  # targets
   'conditions': [
     ['OS!="ios" and (target_arch=="ia32" or target_arch=="x64")', {
       'targets': [
         {
           # Have to be compiled as a separate target because it needs to be
@@ -127,18 +159,17 @@
           'target_name': 'desktop_capture_differ_sse2',
           'type': 'static_library',
           'sources': [
             "differ_block_sse2.cc",
             "differ_block_sse2.h",
           ],
           'conditions': [
             [ 'os_posix == 1 and OS != "mac"', {
-              'cflags': [
-                '-msse2',
-              ],
+              'cflags': [ '-msse2', ],
+              'cflags_mozilla': [ '-msse2', ],
             }],
           ],
         },
       ],  # targets
     }],
   ],
 }
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_types.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_types.h
@@ -32,12 +32,16 @@ const WindowId kNullWindowId = 0;
 //   - On Linux (with X11): TBD.
 typedef intptr_t ScreenId;
 
 // The screen id corresponds to all screen combined together.
 const ScreenId kFullDesktopScreenId = -1;
 
 const ScreenId kInvalidScreenId = -2;
 
+
+typedef intptr_t ProcessId;
+const ProcessId DesktopProcessId = 0;
+
 }  // namespace webrtc
 
 #endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_TYPES_H_
 
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_device_info.cc
@@ -0,0 +1,199 @@
+/* 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 "webrtc/modules/desktop_capture/desktop_device_info.h"
+
+#include <cstddef>
+#include <cstring>
+
+namespace webrtc {
+
+static inline void SetStringMember(char **member, const char *value) {
+  if (!value) {
+    return;
+  }
+
+  if (*member) {
+    delete [] *member;
+    *member = NULL;
+  }
+
+  size_t  nBufLen = strlen(value) + 1;
+  char *buffer = new char[nBufLen];
+  memcpy(buffer, value, nBufLen - 1);
+  buffer[nBufLen - 1] = '\0';
+  *member = buffer;
+}
+
+DesktopDisplayDevice::DesktopDisplayDevice() {
+  screenId_ = kInvalidScreenId;
+  deviceUniqueIdUTF8_ = NULL;
+  deviceNameUTF8_ = NULL;
+}
+
+DesktopDisplayDevice::~DesktopDisplayDevice() {
+  screenId_ = kInvalidScreenId;
+
+  if (deviceUniqueIdUTF8_){
+    delete [] deviceUniqueIdUTF8_;
+  }
+
+  if (deviceNameUTF8_){
+    delete [] deviceNameUTF8_;
+  }
+
+  deviceUniqueIdUTF8_ = NULL;
+  deviceNameUTF8_ = NULL;
+}
+
+void DesktopDisplayDevice::setScreenId(const ScreenId screenId) {
+  screenId_ = screenId;
+}
+
+void DesktopDisplayDevice::setDeviceName(const char *deviceNameUTF8) {
+  SetStringMember(&deviceNameUTF8_, deviceNameUTF8);
+}
+
+void DesktopDisplayDevice::setUniqueIdName(const char *deviceUniqueIdUTF8) {
+  SetStringMember(&deviceUniqueIdUTF8_, deviceUniqueIdUTF8);
+}
+
+ScreenId DesktopDisplayDevice::getScreenId() {
+  return screenId_;
+}
+
+const char *DesktopDisplayDevice::getDeviceName() {
+  return deviceNameUTF8_;
+}
+
+const char *DesktopDisplayDevice::getUniqueIdName() {
+  return deviceUniqueIdUTF8_;
+}
+
+DesktopDisplayDevice& DesktopDisplayDevice::operator= (DesktopDisplayDevice& other) {
+  screenId_ = other.getScreenId();
+  setUniqueIdName(other.getUniqueIdName());
+  setDeviceName(other.getDeviceName());
+
+  return *this;
+}
+
+
+DesktopApplication::DesktopApplication() {
+  processId_ =0;
+  processPathNameUTF8_= NULL;
+  applicationNameUTF8_= NULL;
+  processUniqueIdUTF8_= NULL;
+}
+
+DesktopApplication::~DesktopApplication() {
+}
+
+void DesktopApplication::setProcessId(const ProcessId processId) {
+  processId_ = processId;
+}
+
+void DesktopApplication::setProcessPathName(const char *appPathNameUTF8) {
+  SetStringMember(&processPathNameUTF8_, appPathNameUTF8);
+}
+
+void DesktopApplication::setUniqueIdName(const char *appUniqueIdUTF8) {
+  SetStringMember(&processPathNameUTF8_, appUniqueIdUTF8);
+}
+
+void DesktopApplication::setProcessAppName(const char *appNameUTF8) {
+  SetStringMember(&applicationNameUTF8_, appNameUTF8);
+}
+
+ProcessId DesktopApplication::getProcessId() {
+  return processId_;
+}
+
+const char *DesktopApplication::getProcessPathName() {
+  return processPathNameUTF8_;
+}
+
+const char *DesktopApplication::getUniqueIdName() {
+  return applicationNameUTF8_;
+}
+
+const char *DesktopApplication::getProcessAppName() {
+  return applicationNameUTF8_;
+}
+
+DesktopApplication& DesktopApplication::operator= (DesktopApplication& other) {
+  processId_ = other.getProcessId();
+  setProcessPathName(other.getProcessPathName());
+  setUniqueIdName(other.getUniqueIdName());
+  setProcessAppName(other.getProcessAppName());
+
+  return *this;
+}
+
+DesktopDeviceInfoImpl::DesktopDeviceInfoImpl() {
+}
+
+DesktopDeviceInfoImpl::~DesktopDeviceInfoImpl() {
+  std::map<intptr_t,DesktopDisplayDevice*>::iterator iterDevice;
+  for (iterDevice=desktop_display_list_.begin(); iterDevice!=desktop_display_list_.end(); iterDevice++){
+    DesktopDisplayDevice * pDesktopDisplayDevice = iterDevice->second;
+    if (pDesktopDisplayDevice) {
+      delete pDesktopDisplayDevice;
+    }
+    iterDevice->second = NULL;
+  }
+  desktop_display_list_.clear();
+
+  std::map<intptr_t,DesktopApplication*>::iterator iterApp;
+  for (iterApp=desktop_application_list_.begin(); iterApp!=desktop_application_list_.end(); iterApp++){
+    DesktopApplication * pDesktopApplication = iterApp->second;
+    if (pDesktopApplication) {
+      delete pDesktopApplication;
+    }
+    iterApp->second = NULL;
+  }
+  desktop_application_list_.clear();
+}
+
+int32_t DesktopDeviceInfoImpl::getDisplayDeviceCount() {
+  return desktop_display_list_.size();
+}
+
+int32_t DesktopDeviceInfoImpl::getDesktopDisplayDeviceInfo(int32_t nIndex,
+                                                           DesktopDisplayDevice & desktopDisplayDevice) {
+  if(nIndex < 0 || nIndex >= desktop_display_list_.size()) {
+    return -1;
+  }
+
+  std::map<intptr_t,DesktopDisplayDevice*>::iterator iter = desktop_display_list_.begin();
+  std::advance (iter, nIndex);
+  DesktopDisplayDevice * pDesktopDisplayDevice = iter->second;
+  if(pDesktopDisplayDevice) {
+    desktopDisplayDevice = (*pDesktopDisplayDevice);
+  }
+
+  return 0;
+}
+
+int32_t DesktopDeviceInfoImpl::getApplicationCount() {
+  return desktop_application_list_.size();
+}
+
+int32_t DesktopDeviceInfoImpl::getApplicationInfo(int32_t nIndex,
+                                                  DesktopApplication & desktopApplication) {
+  if(nIndex < 0 || nIndex >= desktop_application_list_.size()) {
+    return -1;
+  }
+
+  std::map<intptr_t,DesktopApplication*>::iterator iter = desktop_application_list_.begin();
+  std::advance (iter, nIndex);
+  DesktopApplication * pDesktopApplication = iter->second;
+  if (pDesktopApplication) {
+    desktopApplication = (*pDesktopApplication);
+  }
+
+  return 0;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_device_info.h
@@ -0,0 +1,95 @@
+/* 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 WEBRTC_MODULES_DESKTOP_CAPTURE_DEVICE_INFO_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_DEVICE_INFO_H_
+
+#include <map>
+#include "webrtc/modules/desktop_capture/desktop_capture_types.h"
+
+namespace webrtc {
+
+class DesktopDisplayDevice {
+public:
+  DesktopDisplayDevice();
+  ~DesktopDisplayDevice();
+
+  void setScreenId(const ScreenId screenId);
+  void setDeviceName(const char *deviceNameUTF8);
+  void setUniqueIdName(const char *deviceUniqueIdUTF8);
+
+  ScreenId getScreenId();
+  const char *getDeviceName();
+  const char *getUniqueIdName();
+
+  DesktopDisplayDevice& operator= (DesktopDisplayDevice& other);
+
+protected:
+  ScreenId screenId_;
+  char* deviceNameUTF8_;
+  char* deviceUniqueIdUTF8_;
+};
+
+typedef std::map<intptr_t,DesktopDisplayDevice*> DesktopDisplayDeviceList;
+
+class DesktopApplication {
+public:
+  DesktopApplication();
+  ~DesktopApplication();
+
+  void setProcessId(const ProcessId processId);
+  void setProcessPathName(const char *appPathNameUTF8);
+  void setUniqueIdName(const char *appUniqueIdUTF8);
+  void setProcessAppName(const char *appNameUTF8);
+
+  ProcessId getProcessId();
+  const char *getProcessPathName();
+  const char *getUniqueIdName();
+  const char *getProcessAppName();
+
+  DesktopApplication& operator= (DesktopApplication& other);
+
+protected:
+  ProcessId processId_;
+  char* processPathNameUTF8_;
+  char* applicationNameUTF8_;
+  char* processUniqueIdUTF8_;
+};
+
+typedef std::map<intptr_t,DesktopApplication*> DesktopApplicationList;
+
+class DesktopDeviceInfo {
+public:
+  virtual ~DesktopDeviceInfo() {};
+
+  virtual int32_t Init() = 0;
+  virtual int32_t getDisplayDeviceCount() = 0;
+  virtual int32_t getDesktopDisplayDeviceInfo(int32_t nIndex,
+                                              DesktopDisplayDevice & desktopDisplayDevice) = 0;
+  virtual int32_t getApplicationCount() = 0;
+  virtual int32_t getApplicationInfo(int32_t nIndex,
+                                     DesktopApplication & desktopApplication) = 0;
+};
+
+class DesktopDeviceInfoImpl : public DesktopDeviceInfo {
+public:
+  DesktopDeviceInfoImpl();
+  ~DesktopDeviceInfoImpl();
+
+  virtual int32_t getDisplayDeviceCount();
+  virtual int32_t getDesktopDisplayDeviceInfo(int32_t nIndex,
+                                              DesktopDisplayDevice & desktopDisplayDevice);
+  virtual int32_t getApplicationCount();
+  virtual int32_t getApplicationInfo(int32_t nIndex,
+                                     DesktopApplication & desktopApplication);
+
+  static DesktopDeviceInfo * Create();
+
+protected:
+  DesktopDisplayDeviceList desktop_display_list_;
+  DesktopApplicationList desktop_application_list_;
+};
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_device_info_mac.h
@@ -0,0 +1,24 @@
+/* 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 WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DEVICE_INFO_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DEVICE_INFO_H_
+
+#include "webrtc/typedefs.h"
+#include "webrtc/modules/desktop_capture/desktop_device_info.h"
+
+namespace webrtc {
+
+class DesktopDeviceInfoMac : public DesktopDeviceInfoImpl {
+public:
+  DesktopDeviceInfoMac();
+  ~DesktopDeviceInfoMac();
+
+  //DesktopDeviceInfo Interfaces
+  virtual int32_t Init();
+};
+
+}// namespace webrtc
+
+#endif //WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DEVICE_INFO_H_
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_device_info_mac.mm
@@ -0,0 +1,40 @@
+/* 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 "webrtc/modules/desktop_capture/mac/desktop_device_info_mac.h"
+#include <Cocoa/Cocoa.h>
+
+namespace webrtc {
+
+#define MULTI_MONITOR_NO_SUPPORT 1
+
+DesktopDeviceInfo * DesktopDeviceInfoImpl::Create() {
+  DesktopDeviceInfoMac * pDesktopDeviceInfo = new DesktopDeviceInfoMac();
+  if (pDesktopDeviceInfo && pDesktopDeviceInfo->Init() != 0){
+    delete pDesktopDeviceInfo;
+    pDesktopDeviceInfo = NULL;
+  }
+  return pDesktopDeviceInfo;
+}
+
+DesktopDeviceInfoMac::DesktopDeviceInfoMac() {
+}
+
+DesktopDeviceInfoMac::~DesktopDeviceInfoMac() {
+}
+
+int32_t DesktopDeviceInfoMac::Init() {
+#if !defined(MULTI_MONITOR_SCREENSHARE)
+  DesktopDisplayDevice *pDesktopDeviceInfo = new DesktopDisplayDevice;
+  if(pDesktopDeviceInfo) {
+    pDesktopDeviceInfo->setScreenId(0);
+    pDesktopDeviceInfo->setDeviceName("Primary Monitor");
+    pDesktopDeviceInfo->setUniqueIdName("\\screen\\monitor#1");
+    desktop_display_list_[pDesktopDeviceInfo->getScreenId()] = pDesktopDeviceInfo;
+  }
+#endif
+  return 0;
+}
+
+} //namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_win.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_win.cc
@@ -183,17 +183,18 @@ void ScreenCapturerWin::Capture(const De
     }
 
     // Calculate difference between the two last captured frames.
     DesktopRegion region;
     differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(),
                              &region);
     helper_.InvalidateRegion(region);
   } else {
-    // No previous frame is available. Invalidate the whole screen.
+    // No previous frame is available, or the screen is resized. Invalidate the
+    // whole screen.
     helper_.InvalidateScreen(current_frame->size());
   }
 
   helper_.set_size_most_recent(current_frame->size());
 
   // Emit the current frame.
   DesktopFrame* frame = queue_.current_frame()->Share();
   frame->set_dpi(DesktopVector(
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop_device_info_win.cc
@@ -0,0 +1,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/. */
+
+#include "webrtc/modules/desktop_capture/win/desktop_device_info_win.h"
+
+namespace webrtc{
+
+DesktopDeviceInfo * DesktopDeviceInfoImpl::Create() {
+  DesktopDeviceInfoWin * pDesktopDeviceInfo = new DesktopDeviceInfoWin();
+  if(pDesktopDeviceInfo && pDesktopDeviceInfo->Init() != 0){
+    delete pDesktopDeviceInfo;
+    pDesktopDeviceInfo = NULL;
+  }
+  return pDesktopDeviceInfo;
+}
+
+DesktopDeviceInfoWin::DesktopDeviceInfoWin() {
+}
+
+DesktopDeviceInfoWin::~DesktopDeviceInfoWin() {
+}
+
+int32_t DesktopDeviceInfoWin::Init() {
+#if !defined(MULTI_MONITOR_SCREENSHARE)
+  DesktopDisplayDevice *pDesktopDeviceInfo = new DesktopDisplayDevice;
+  if (pDesktopDeviceInfo) {
+    pDesktopDeviceInfo->setScreenId(0);
+    pDesktopDeviceInfo->setDeviceName("Primary Monitor");
+    pDesktopDeviceInfo->setUniqueIdName("\\screen\\monitor#1");
+
+    desktop_display_list_[pDesktopDeviceInfo->getScreenId()] = pDesktopDeviceInfo;
+  }
+#endif
+  return 0;
+}
+
+} //namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop_device_info_win.h
@@ -0,0 +1,24 @@
+/* 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 WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DEVICE_INFO_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DEVICE_INFO_H_
+
+#include "webrtc/typedefs.h"
+#include "webrtc/modules/desktop_capture/desktop_device_info.h"
+
+namespace webrtc {
+
+class DesktopDeviceInfoWin : public DesktopDeviceInfoImpl {
+public:
+  DesktopDeviceInfoWin();
+  ~DesktopDeviceInfoWin();
+
+  //DesktopDeviceInfo Interfaces
+  virtual int32_t Init();
+};
+
+}// namespace webrtc
+
+#endif //WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DEVICE_INFO_H_
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer.h
@@ -44,14 +44,22 @@ class WindowCapturer : public DesktopCap
   virtual ~WindowCapturer() {}
 
   // Get list of windows. Returns false in case of a failure.
   virtual bool GetWindowList(WindowList* windows) = 0;
 
   // Select window to be captured. Returns false in case of a failure (e.g. if
   // there is no window with the specified id).
   virtual bool SelectWindow(WindowId id) = 0;
+
+  // Bring the selected window to the front. Returns false in case of a
+  // failure or no window selected.
+  // TODO(jiayl): remove the default impl when FakeWindowCapturer is updated in
+  // Chromium.
+  virtual bool BringSelectedWindowToFront() {
+    return true;
+  }
 };
 
 }  // namespace webrtc
 
 #endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_CAPTURER_H_
 
deleted file mode 100755
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.cc
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "webrtc/modules/desktop_capture/window_capturer.h"
-
-#include <assert.h>
-#include <ApplicationServices/ApplicationServices.h>
-#include <CoreFoundation/CoreFoundation.h>
-
-#include "webrtc/modules/desktop_capture/desktop_frame.h"
-#include "webrtc/system_wrappers/interface/logging.h"
-
-namespace webrtc {
-
-namespace {
-
-bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) {
-  assert(string);
-  assert(str_utf8);
-  CFIndex length = CFStringGetLength(string);
-  size_t max_length_utf8 =
-      CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
-  str_utf8->resize(max_length_utf8);
-  CFIndex used_bytes;
-  int result = CFStringGetBytes(
-      string, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, false,
-      reinterpret_cast<UInt8*>(&*str_utf8->begin()), max_length_utf8,
-      &used_bytes);
-  if (result != length) {
-    str_utf8->clear();
-    return false;
-  }
-  str_utf8->resize(used_bytes);
-  return true;
-}
-
-class WindowCapturerMac : public WindowCapturer {
- public:
-  WindowCapturerMac();
-  virtual ~WindowCapturerMac();
-
-  // WindowCapturer interface.
-  virtual bool GetWindowList(WindowList* windows) OVERRIDE;
-  virtual bool SelectWindow(WindowId id) OVERRIDE;
-
-  // DesktopCapturer interface.
-  virtual void Start(Callback* callback) OVERRIDE;
-  virtual void Capture(const DesktopRegion& region) OVERRIDE;
-
- private:
-  Callback* callback_;
-  CGWindowID window_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
-};
-
-WindowCapturerMac::WindowCapturerMac()
-    : callback_(NULL),
-      window_id_(0) {
-}
-
-WindowCapturerMac::~WindowCapturerMac() {
-}
-
-bool WindowCapturerMac::GetWindowList(WindowList* windows) {
-  // Only get on screen, non-desktop windows.
-  CFArrayRef window_array = CGWindowListCopyWindowInfo(
-      kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
-      kCGNullWindowID);
-  if (!window_array)
-    return false;
-
-  // Check windows to make sure they have an id, title, and use window layer
-  // other than 0.
-  CFIndex count = CFArrayGetCount(window_array);
-  for (CFIndex i = 0; i < count; ++i) {
-    CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
-        CFArrayGetValueAtIndex(window_array, i));
-    CFStringRef window_title = reinterpret_cast<CFStringRef>(
-        CFDictionaryGetValue(window, kCGWindowName));
-    CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
-        CFDictionaryGetValue(window, kCGWindowNumber));
-    CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
-        CFDictionaryGetValue(window, kCGWindowLayer));
-    if (window_title && window_id && window_layer) {
-      // Skip windows with layer=0 (menu, dock).
-      int layer;
-      CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
-      if (layer != 0)
-        continue;
-
-      int id;
-      CFNumberGetValue(window_id, kCFNumberIntType, &id);
-      WindowCapturer::Window window;
-      window.id = id;
-      if (!CFStringRefToUtf8(window_title, &(window.title)) ||
-          window.title.empty()) {
-        continue;
-      }
-      windows->push_back(window);
-    }
-  }
-
-  CFRelease(window_array);
-  return true;
-}
-
-bool WindowCapturerMac::SelectWindow(WindowId id) {
-  // Request description for the specified window to make sure |id| is valid.
-  CGWindowID ids[1];
-  ids[0] = id;
-  CFArrayRef window_id_array =
-      CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
-  CFArrayRef window_array =
-      CGWindowListCreateDescriptionFromArray(window_id_array);
-  int results_count = window_array ? CFArrayGetCount(window_array) : 0;
-  CFRelease(window_id_array);
-  CFRelease(window_array);
-
-  if (results_count == 0) {
-    // Could not find the window. It might have been closed.
-    return false;
-  }
-
-  window_id_ = id;
-  return true;
-}
-
-void WindowCapturerMac::Start(Callback* callback) {
-  assert(!callback_);
-  assert(callback);
-
-  callback_ = callback;
-}
-
-void WindowCapturerMac::Capture(const DesktopRegion& region) {
-  CGImageRef window_image = CGWindowListCreateImage(
-      CGRectNull, kCGWindowListOptionIncludingWindow,
-      window_id_, kCGWindowImageBoundsIgnoreFraming);
-
-  if (!window_image) {
-    CFRelease(window_image);
-    callback_->OnCaptureCompleted(NULL);
-    return;
-  }
-
-  int bits_per_pixel = CGImageGetBitsPerPixel(window_image);
-  if (bits_per_pixel != 32) {
-    LOG(LS_ERROR) << "Unsupported window image depth: " << bits_per_pixel;
-    CFRelease(window_image);
-    callback_->OnCaptureCompleted(NULL);
-    return;
-  }
-
-  int width = CGImageGetWidth(window_image);
-  int height = CGImageGetHeight(window_image);
-  CGDataProviderRef provider = CGImageGetDataProvider(window_image);
-  CFDataRef cf_data = CGDataProviderCopyData(provider);
-  DesktopFrame* frame = new BasicDesktopFrame(
-      DesktopSize(width, height));
-
-  int src_stride = CGImageGetBytesPerRow(window_image);
-  const uint8_t* src_data = CFDataGetBytePtr(cf_data);
-  for (int y = 0; y < height; ++y) {
-    memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y,
-           DesktopFrame::kBytesPerPixel * width);
-  }
-
-  CFRelease(cf_data);
-  CFRelease(window_image);
-
-  callback_->OnCaptureCompleted(frame);
-}
-
-}  // namespace
-
-// static
-WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
-  return new WindowCapturerMac();
-}
-
-}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_mac.mm
@@ -0,0 +1,228 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/desktop_capture/window_capturer.h"
+
+#include <assert.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <Cocoa/Cocoa.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/system_wrappers/interface/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) {
+  assert(string);
+  assert(str_utf8);
+  CFIndex length = CFStringGetLength(string);
+  size_t max_length_utf8 =
+      CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
+  str_utf8->resize(max_length_utf8);
+  CFIndex used_bytes;
+  int result = CFStringGetBytes(
+      string, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, false,
+      reinterpret_cast<UInt8*>(&*str_utf8->begin()), max_length_utf8,
+      &used_bytes);
+  if (result != length) {
+    str_utf8->clear();
+    return false;
+  }
+  str_utf8->resize(used_bytes);
+  return true;
+}
+
+class WindowCapturerMac : public WindowCapturer {
+ public:
+  WindowCapturerMac();
+  virtual ~WindowCapturerMac();
+
+  // WindowCapturer interface.
+  virtual bool GetWindowList(WindowList* windows) OVERRIDE;
+  virtual bool SelectWindow(WindowId id) OVERRIDE;
+  virtual bool BringSelectedWindowToFront() OVERRIDE;
+
+  // DesktopCapturer interface.
+  virtual void Start(Callback* callback) OVERRIDE;
+  virtual void Capture(const DesktopRegion& region) OVERRIDE;
+
+ private:
+  Callback* callback_;
+  CGWindowID window_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
+};
+
+WindowCapturerMac::WindowCapturerMac()
+    : callback_(NULL),
+      window_id_(0) {
+}
+
+WindowCapturerMac::~WindowCapturerMac() {
+}
+
+bool WindowCapturerMac::GetWindowList(WindowList* windows) {
+  // Only get on screen, non-desktop windows.
+  CFArrayRef window_array = CGWindowListCopyWindowInfo(
+      kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
+      kCGNullWindowID);
+  if (!window_array)
+    return false;
+
+  // Check windows to make sure they have an id, title, and use window layer
+  // other than 0.
+  CFIndex count = CFArrayGetCount(window_array);
+  for (CFIndex i = 0; i < count; ++i) {
+    CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
+        CFArrayGetValueAtIndex(window_array, i));
+    CFStringRef window_title = reinterpret_cast<CFStringRef>(
+        CFDictionaryGetValue(window, kCGWindowName));
+    CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
+        CFDictionaryGetValue(window, kCGWindowNumber));
+    CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
+        CFDictionaryGetValue(window, kCGWindowLayer));
+    if (window_title && window_id && window_layer) {
+      // Skip windows with layer=0 (menu, dock).
+      int layer;
+      CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
+      if (layer != 0)
+        continue;
+
+      int id;
+      CFNumberGetValue(window_id, kCFNumberIntType, &id);
+      WindowCapturer::Window window;
+      window.id = id;
+      if (!CFStringRefToUtf8(window_title, &(window.title)) ||
+          window.title.empty()) {
+        continue;
+      }
+      windows->push_back(window);
+    }
+  }
+
+  CFRelease(window_array);
+  return true;
+}
+
+bool WindowCapturerMac::SelectWindow(WindowId id) {
+  // Request description for the specified window to make sure |id| is valid.
+  CGWindowID ids[1];
+  ids[0] = id;
+  CFArrayRef window_id_array =
+      CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
+  CFArrayRef window_array =
+      CGWindowListCreateDescriptionFromArray(window_id_array);
+  int results_count = window_array ? CFArrayGetCount(window_array) : 0;
+  CFRelease(window_id_array);
+  CFRelease(window_array);
+
+  if (results_count == 0) {
+    // Could not find the window. It might have been closed.
+    return false;
+  }
+
+  window_id_ = id;
+  return true;
+}
+
+bool WindowCapturerMac::BringSelectedWindowToFront() {
+  if (!window_id_)
+    return false;
+
+  CGWindowID ids[1];
+  ids[0] = window_id_;
+  CFArrayRef window_id_array =
+      CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
+
+  CFArrayRef window_array =
+      CGWindowListCreateDescriptionFromArray(window_id_array);
+  if (window_array == NULL || 0 == CFArrayGetCount(window_array)) {
+    // Could not find the window. It might have been closed.
+    LOG(LS_INFO) << "Window not found";
+    CFRelease(window_id_array);
+    return false;
+  }
+
+  CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
+      CFArrayGetValueAtIndex(window_array, 0));
+  CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
+      CFDictionaryGetValue(window, kCGWindowOwnerPID));
+
+  int pid;
+  CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
+
+  // TODO(jiayl): this will bring the process main window to the front. We
+  // should find a way to bring only the window to the front.
+  bool result =
+      [[NSRunningApplication runningApplicationWithProcessIdentifier: pid]
+          activateWithOptions: NSApplicationActivateIgnoringOtherApps];
+
+  CFRelease(window_id_array);
+  CFRelease(window_array);
+  return result;
+}
+
+void WindowCapturerMac::Start(Callback* callback) {
+  assert(!callback_);
+  assert(callback);
+
+  callback_ = callback;
+}
+
+void WindowCapturerMac::Capture(const DesktopRegion& region) {
+  CGImageRef window_image = CGWindowListCreateImage(
+      CGRectNull, kCGWindowListOptionIncludingWindow,
+      window_id_, kCGWindowImageBoundsIgnoreFraming);
+
+  if (!window_image) {
+    CFRelease(window_image);
+    callback_->OnCaptureCompleted(NULL);
+    return;
+  }
+
+  int bits_per_pixel = CGImageGetBitsPerPixel(window_image);
+  if (bits_per_pixel != 32) {
+    LOG(LS_ERROR) << "Unsupported window image depth: " << bits_per_pixel;
+    CFRelease(window_image);
+    callback_->OnCaptureCompleted(NULL);
+    return;
+  }
+
+  int width = CGImageGetWidth(window_image);
+  int height = CGImageGetHeight(window_image);
+  CGDataProviderRef provider = CGImageGetDataProvider(window_image);
+  CFDataRef cf_data = CGDataProviderCopyData(provider);
+  DesktopFrame* frame = new BasicDesktopFrame(
+      DesktopSize(width, height));
+
+  int src_stride = CGImageGetBytesPerRow(window_image);
+  const uint8_t* src_data = CFDataGetBytePtr(cf_data);
+  for (int y = 0; y < height; ++y) {
+    memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y,
+           DesktopFrame::kBytesPerPixel * width);
+  }
+
+  CFRelease(cf_data);
+  CFRelease(window_image);
+
+  callback_->OnCaptureCompleted(frame);
+}
+
+}  // namespace
+
+// static
+WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
+  return new WindowCapturerMac();
+}
+
+}  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc
@@ -21,16 +21,17 @@ namespace {
 class WindowCapturerNull : public WindowCapturer {
  public:
   WindowCapturerNull();
   virtual ~WindowCapturerNull();
 
   // WindowCapturer interface.
   virtual bool GetWindowList(WindowList* windows) OVERRIDE;
   virtual bool SelectWindow(WindowId id) OVERRIDE;
+  virtual bool BringSelectedWindowToFront() OVERRIDE;
 
   // DesktopCapturer interface.
   virtual void Start(Callback* callback) OVERRIDE;
   virtual void Capture(const DesktopRegion& region) OVERRIDE;
 
  private:
   Callback* callback_;
 
@@ -49,16 +50,21 @@ bool WindowCapturerNull::GetWindowList(W
   return false;
 }
 
 bool WindowCapturerNull::SelectWindow(WindowId id) {
   // Not implemented yet.
   return false;
 }
 
+bool WindowCapturerNull::BringSelectedWindowToFront() {
+  // Not implemented yet.
+  return false;
+}
+
 void WindowCapturerNull::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
 void WindowCapturerNull::Capture(const DesktopRegion& region) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_win.cc
@@ -84,16 +84,17 @@ BOOL CALLBACK WindowsEnumerationHandler(
 class WindowCapturerWin : public WindowCapturer {
  public:
   WindowCapturerWin();
   virtual ~WindowCapturerWin();
 
   // WindowCapturer interface.
   virtual bool GetWindowList(WindowList* windows) OVERRIDE;
   virtual bool SelectWindow(WindowId id) OVERRIDE;
+  virtual bool BringSelectedWindowToFront() OVERRIDE;
 
   // DesktopCapturer interface.
   virtual void Start(Callback* callback) OVERRIDE;
   virtual void Capture(const DesktopRegion& region) OVERRIDE;
 
  private:
   bool IsAeroEnabled();
 
@@ -152,16 +153,26 @@ bool WindowCapturerWin::SelectWindow(Win
   HWND window = reinterpret_cast<HWND>(id);
   if (!IsWindow(window) || !IsWindowVisible(window) || IsIconic(window))
     return false;
   window_ = window;
   previous_size_.set(0, 0);
   return true;
 }
 
+bool WindowCapturerWin::BringSelectedWindowToFront() {
+  if (!window_)
+    return false;
+
+  if (!IsWindow(window_) || !IsWindowVisible(window_) || IsIconic(window_))
+    return false;
+
+  return SetForegroundWindow(window_) != 0;
+}
+
 void WindowCapturerWin::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
 void WindowCapturerWin::Capture(const DesktopRegion& region) {
@@ -212,21 +223,21 @@ void WindowCapturerWin::Capture(const De
   //
   // If Aero is enabled, we prefer BitBlt() because it's faster and avoids
   // window flickering. Otherwise, we prefer PrintWindow() because BitBlt() may
   // render occluding windows on top of the desired window.
   //
   // When composition is enabled the DC returned by GetWindowDC() doesn't always
   // have window frame rendered correctly. Windows renders it only once and then
   // caches the result between captures. We hack it around by calling
-  // PrintWindow() whenever window size changes - it somehow affects what we
-  // get from BitBlt() on the subsequent captures.
+  // PrintWindow() whenever window size changes, including the first time of
+  // capturing - it somehow affects what we get from BitBlt() on the subsequent
+  // captures.
 
-  if (!IsAeroEnabled() ||
-      (!previous_size_.is_empty() && !previous_size_.equals(frame->size()))) {
+  if (!IsAeroEnabled() || !previous_size_.equals(frame->size())) {
     result = PrintWindow(window_, mem_dc, 0);
   }
 
   // Aero is enabled or PrintWindow() failed, use BitBlt.
   if (!result) {
     result = BitBlt(mem_dc, 0, 0, frame->size().width(), frame->size().height(),
                     window_dc, 0, 0, SRCCOPY);
   }
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc
@@ -10,16 +10,17 @@
 
 #include "webrtc/modules/desktop_capture/window_capturer.h"
 
 #include <string.h>
 #include <X11/Xatom.h>
 #include <X11/extensions/Xcomposite.h>
 #include <X11/extensions/Xrender.h>
 #include <X11/Xutil.h>
+
 #include <algorithm>
 #include <cassert>
 
 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
 #include "webrtc/modules/desktop_capture/desktop_frame.h"
 #include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
 #include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
@@ -79,29 +80,34 @@ class XWindowProperty {
  private:
   bool is_valid_;
   unsigned long size_;  // NOLINT: type required by XGetWindowProperty
   unsigned char* data_;
 
   DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
 };
 
-class WindowCapturerLinux : public WindowCapturer {
+class WindowCapturerLinux : public WindowCapturer,
+                            public SharedXDisplay::XEventHandler {
  public:
   WindowCapturerLinux(const DesktopCaptureOptions& options);
   virtual ~WindowCapturerLinux();
 
   // WindowCapturer interface.
   virtual bool GetWindowList(WindowList* windows) OVERRIDE;
   virtual bool SelectWindow(WindowId id) OVERRIDE;
+  virtual bool BringSelectedWindowToFront() OVERRIDE;
 
   // DesktopCapturer interface.
   virtual void Start(Callback* callback) OVERRIDE;
   virtual void Capture(const DesktopRegion& region) OVERRIDE;
 
+  // SharedXDisplay::XEventHandler interface.
+  virtual bool HandleXEvent(const XEvent& event) OVERRIDE;
+
  private:
   Display* display() { return x_display_->display(); }
 
   // Iterates through |window| hierarchy to find first visible window, i.e. one
   // that has WM_STATE property set to NormalState.
   // See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
   ::Window GetApplicationWindow(::Window window);
 
@@ -141,19 +147,23 @@ WindowCapturerLinux::WindowCapturerLinux
   if (XCompositeQueryExtension(display(), &event_base, &error_base) &&
       XCompositeQueryVersion(display(), &major_version, &minor_version) &&
       // XCompositeNameWindowPixmap() requires version 0.2
       (major_version > 0 || minor_version >= 2)) {
     has_composite_extension_ = true;
   } else {
     LOG(LS_INFO) << "Xcomposite extension not available or too old.";
   }
+
+  x_display_->AddEventHandler(ConfigureNotify, this);
 }
 
-WindowCapturerLinux::~WindowCapturerLinux() {}
+WindowCapturerLinux::~WindowCapturerLinux() {
+  x_display_->RemoveEventHandler(ConfigureNotify, this);
+}
 
 bool WindowCapturerLinux::GetWindowList(WindowList* windows) {
   WindowList result;
 
   XErrorTrap error_trap(display());
 
   int num_screens = XScreenCount(display());
   for (int screen = 0; screen < num_screens; ++screen) {
@@ -189,38 +199,92 @@ bool WindowCapturerLinux::GetWindowList(
 
   return true;
 }
 
 bool WindowCapturerLinux::SelectWindow(WindowId id) {
   if (!x_server_pixel_buffer_.Init(display(), id))
     return false;
 
+  // Tell the X server to send us window resizing events.
+  XSelectInput(display(), id, StructureNotifyMask);
+
   selected_window_ = id;
 
   // In addition to needing X11 server-side support for Xcomposite, it actually
   // needs to be turned on for the window. If the user has modern
   // hardware/drivers but isn't using a compositing window manager, that won't
   // be the case. Here we automatically turn it on.
 
   // Redirect drawing to an offscreen buffer (ie, turn on compositing). X11
   // remembers who has requested this and will turn it off for us when we exit.
   XCompositeRedirectWindow(display(), id, CompositeRedirectAutomatic);
 
   return true;
 }
 
+bool WindowCapturerLinux::BringSelectedWindowToFront() {
+  if (!selected_window_)
+    return false;
+
+  unsigned int num_children;
+  ::Window* children;
+  ::Window parent;
+  ::Window root;
+  // Find the root window to pass event to.
+  int status = XQueryTree(
+      display(), selected_window_, &root, &parent, &children, &num_children);
+  if (status == 0) {
+    LOG(LS_ERROR) << "Failed to query for the root window.";
+    return false;
+  }
+
+  if (children)
+    XFree(children);
+
+  XRaiseWindow(display(), selected_window_);
+
+  // Some window managers (e.g., metacity in GNOME) consider it illegal to
+  // raise a window without also giving it input focus with
+  // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough.
+  Atom atom = XInternAtom(display(), "_NET_ACTIVE_WINDOW", True);
+  if (atom != None) {
+    XEvent xev;
+    xev.xclient.type = ClientMessage;
+    xev.xclient.serial = 0;
+    xev.xclient.send_event = True;
+    xev.xclient.window = selected_window_;
+    xev.xclient.message_type = atom;
+
+    // The format member is set to 8, 16, or 32 and specifies whether the
+    // data should be viewed as a list of bytes, shorts, or longs.
+    xev.xclient.format = 32;
+
+    memset(xev.xclient.data.l, 0, sizeof(xev.xclient.data.l));
+
+    XSendEvent(display(),
+               root,
+               False,
+               SubstructureRedirectMask | SubstructureNotifyMask,
+               &xev);
+  }
+  XFlush(display());
+  return true;
+}
+
 void WindowCapturerLinux::Start(Callback* callback) {
   assert(!callback_);
   assert(callback);
 
   callback_ = callback;
 }
 
 void WindowCapturerLinux::Capture(const DesktopRegion& region) {
+  x_display_->ProcessPendingXEvents();
+
   if (!has_composite_extension_) {
     // Without the Xcomposite extension we capture when the whole window is
     // visible on screen and not covered by any other window. This is not
     // something we want so instead, just bail out.
     LOG(LS_INFO) << "No Xcomposite extension detected.";
     callback_->OnCaptureCompleted(NULL);
     return;
   }
@@ -230,16 +294,30 @@ void WindowCapturerLinux::Capture(const 
 
   x_server_pixel_buffer_.Synchronize();
   x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()),
                                      frame);
 
   callback_->OnCaptureCompleted(frame);
 }
 
+bool WindowCapturerLinux::HandleXEvent(const XEvent& event) {
+  if (event.type == ConfigureNotify) {
+    XConfigureEvent xce = event.xconfigure;
+    if (!DesktopSize(xce.width, xce.height).equals(
+            x_server_pixel_buffer_.window_size())) {
+      if (!x_server_pixel_buffer_.Init(display(), selected_window_)) {
+        LOG(LS_ERROR) << "Failed to initialize pixel buffer after resizing.";
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
 ::Window WindowCapturerLinux::GetApplicationWindow(::Window window) {
   // Get WM_STATE property of the window.
   XWindowProperty<uint32_t> window_state(display(), window, wm_state_atom_);
 
   // WM_STATE is considered to be set to WithdrawnState when it missing.
   int32_t state = window_state.is_valid() ?
       *window_state.data() : WithdrawnState;
 
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/desktop_device_info_x11.cc
@@ -0,0 +1,39 @@
+/* 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 "webrtc/modules/desktop_capture/x11/desktop_device_info_x11.h"
+#include "webrtc/modules/desktop_capture/window_capturer.h"
+
+namespace webrtc{
+
+DesktopDeviceInfo * DesktopDeviceInfoImpl::Create() {
+  DesktopDeviceInfoX11 * pDesktopDeviceInfo = new DesktopDeviceInfoX11();
+  if (pDesktopDeviceInfo && pDesktopDeviceInfo->Init() != 0){
+    delete pDesktopDeviceInfo;
+    pDesktopDeviceInfo = NULL;
+  }
+  return pDesktopDeviceInfo;
+}
+
+DesktopDeviceInfoX11::DesktopDeviceInfoX11() {
+}
+
+DesktopDeviceInfoX11::~DesktopDeviceInfoX11() {
+}
+
+int32_t DesktopDeviceInfoX11::Init() {
+#if !defined(MULTI_MONITOR_SCREENSHARE)
+  DesktopDisplayDevice *pDesktopDeviceInfo = new DesktopDisplayDevice;
+  if(pDesktopDeviceInfo){
+    pDesktopDeviceInfo->setScreenId(0);
+    pDesktopDeviceInfo->setDeviceName("Primary Monitor");
+    pDesktopDeviceInfo->setUniqueIdName("\\screen\\monitor#1");
+
+    desktop_display_list_[pDesktopDeviceInfo->getScreenId()] = pDesktopDeviceInfo;
+  }
+#endif
+  return 0;
+}
+
+} //namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/desktop_device_info_x11.h
@@ -0,0 +1,23 @@
+/* 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 WEBRTC_MODULES_DESKTOP_CAPTURE_X11_DEVICE_INFO_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_X11_DEVICE_INFO_H_
+
+#include "webrtc/typedefs.h"
+#include "webrtc/modules/desktop_capture/desktop_device_info.h"
+
+namespace webrtc {
+
+class DesktopDeviceInfoX11 : public DesktopDeviceInfoImpl {
+public:
+  DesktopDeviceInfoX11();
+  ~DesktopDeviceInfoX11();
+
+  //DesktopDeviceInfo Interfaces
+  virtual int32_t Init();
+};
+
+}// namespace webrtc
+#endif //WEBRTC_MODULES_DESKTOP_CAPTURE_X11_DEVICE_INFO_H_
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
@@ -0,0 +1,681 @@
+/*
+ *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/video_capture/video_capture_impl.h"
+
+#include <stdlib.h>
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/video_capture/video_capture_config.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/interface/ref_count.h"
+#include "webrtc/system_wrappers/interface/tick_util.h"
+#include "webrtc/system_wrappers/interface/trace.h"
+#include "webrtc/system_wrappers/interface/trace_event.h"
+#include "webrtc/video_engine/desktop_capture_impl.h"
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/desktop_device_info.h"
+#include "webrtc/modules/desktop_capture/app_capturer.h"
+#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
+
+namespace webrtc {
+
+ScreenDeviceInfoImpl::ScreenDeviceInfoImpl(const int32_t id) : _id(id) {
+}
+
+ScreenDeviceInfoImpl::~ScreenDeviceInfoImpl(void) {
+}
+
+int32_t ScreenDeviceInfoImpl::Init() {
+  desktop_device_info_.reset(DesktopDeviceInfoImpl::Create());
+  return 0;
+}
+
+uint32_t ScreenDeviceInfoImpl::NumberOfDevices() {
+  return desktop_device_info_->getDisplayDeviceCount();
+}
+
+int32_t ScreenDeviceInfoImpl::GetDeviceName(uint32_t deviceNumber,
+                                            char* deviceNameUTF8,
+                                            uint32_t deviceNameUTF8Length,
+                                            char* deviceUniqueIdUTF8,
+                                            uint32_t deviceUniqueIdUTF8Length,
+                                            char* productUniqueIdUTF8,
+                                            uint32_t productUniqueIdUTF8Length) {
+
+  DesktopDisplayDevice desktopDisplayDevice;
+
+  // always initialize output
+  if (deviceNameUTF8 && deviceNameUTF8Length > 0) {
+    memset(deviceNameUTF8, 0, deviceNameUTF8Length);
+  }
+
+  if (deviceUniqueIdUTF8 && deviceUniqueIdUTF8Length > 0) {
+    memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
+  }
+  if (productUniqueIdUTF8 && productUniqueIdUTF8Length > 0) {
+    memset(productUniqueIdUTF8, 0, productUniqueIdUTF8Length);
+  }
+
+  if (desktop_device_info_->getDesktopDisplayDeviceInfo(deviceNumber,
+                                                       desktopDisplayDevice) == 0) {
+    size_t len;
+
+    const char *deviceName = desktopDisplayDevice.getDeviceName();
+    len = deviceName ? strlen(deviceName) : 0;
+    if (len && deviceNameUTF8 && len <= deviceNameUTF8Length) {
+      memcpy(deviceNameUTF8,
+             deviceName,
+             len);
+    }
+
+    const char *deviceUniqueId = desktopDisplayDevice.getUniqueIdName();
+    len = deviceUniqueId ? strlen(deviceUniqueId) : 0;
+    if (len && deviceUniqueIdUTF8 && len <= deviceUniqueIdUTF8Length) {
+      memcpy(deviceUniqueIdUTF8,
+             deviceUniqueId,
+             len);
+    }
+  }
+
+  return 0;
+}
+
+int32_t ScreenDeviceInfoImpl::DisplayCaptureSettingsDialogBox(const char* deviceUniqueIdUTF8,
+                                                              const char* dialogTitleUTF8,
+                                                              void* parentWindow,
+                                                              uint32_t positionX,
+                                                              uint32_t positionY) {
+  // no device properties to change
+  return 0;
+}
+
+int32_t ScreenDeviceInfoImpl::NumberOfCapabilities(const char* deviceUniqueIdUTF8) {
+  return 0;
+}
+
+int32_t ScreenDeviceInfoImpl::GetCapability(const char* deviceUniqueIdUTF8,
+                                            const uint32_t deviceCapabilityNumber,
+                                            VideoCaptureCapability& capability) {
+  return 0;
+}
+
+int32_t ScreenDeviceInfoImpl::GetBestMatchedCapability(const char* deviceUniqueIdUTF8,
+                                                       const VideoCaptureCapability& requested,
+                                                       VideoCaptureCapability& resulting) {
+  return 0;
+}
+
+int32_t ScreenDeviceInfoImpl::GetOrientation(const char* deviceUniqueIdUTF8,
+                                             VideoCaptureRotation& orientation) {
+  return 0;
+}
+
+AppDeviceInfoImpl::AppDeviceInfoImpl(const int32_t id) {
+}
+
+AppDeviceInfoImpl::~AppDeviceInfoImpl(void) {
+}
+
+int32_t AppDeviceInfoImpl::Init() {
+  desktop_device_info_.reset(DesktopDeviceInfoImpl::Create());
+  return 0;
+}
+
+uint32_t AppDeviceInfoImpl::NumberOfDevices() {
+  return desktop_device_info_->getApplicationCount();
+}
+
+int32_t AppDeviceInfoImpl::GetDeviceName(uint32_t deviceNumber,
+                                         char* deviceNameUTF8,
+                                         uint32_t deviceNameUTF8Length,
+                                         char* deviceUniqueIdUTF8,
+                                         uint32_t deviceUniqueIdUTF8Length,
+                                         char* productUniqueIdUTF8,
+                                         uint32_t productUniqueIdUTF8Length) {
+
+  DesktopApplication desktopApplication;
+
+  // always initialize output
+  if (deviceNameUTF8Length && deviceNameUTF8Length > 0) {
+    memset(deviceNameUTF8, 0, deviceNameUTF8Length);
+  }
+  if (deviceUniqueIdUTF8 && deviceUniqueIdUTF8Length > 0) {
+    memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
+  }
+  if (productUniqueIdUTF8 && productUniqueIdUTF8Length > 0) {
+    memset(productUniqueIdUTF8, 0, productUniqueIdUTF8Length);
+  }
+
+  if (desktop_device_info_->getApplicationInfo(deviceNumber,desktopApplication) == 0) {
+    size_t len;
+
+    const char *deviceName = desktopApplication.getProcessAppName();
+    len = deviceName ? strlen(deviceName) : 0;
+    if (len && len <= deviceNameUTF8Length) {
+      memcpy(deviceNameUTF8, deviceName, len);
+    }
+
+    const char *deviceUniqueId = desktopApplication.getUniqueIdName();
+    len = deviceUniqueId ? strlen(deviceUniqueId) : 0;
+    if (len && deviceUniqueIdUTF8 && len <= deviceUniqueIdUTF8Length) {
+      memcpy(deviceUniqueIdUTF8,
+             deviceUniqueId,
+             len);
+    }
+  }
+  return 0;
+}
+
+int32_t AppDeviceInfoImpl::DisplayCaptureSettingsDialogBox(const char* deviceUniqueIdUTF8,
+                                                           const char* dialogTitleUTF8,
+                                                           void* parentWindow,
+                                                           uint32_t positionX,
+                                                           uint32_t positionY) {
+  return 0;
+}
+
+int32_t AppDeviceInfoImpl::NumberOfCapabilities(const char* deviceUniqueIdUTF8) {
+  return 0;
+}
+
+int32_t AppDeviceInfoImpl::GetCapability(
+                                         const char* deviceUniqueIdUTF8,
+                                         const uint32_t deviceCapabilityNumber,
+                                         VideoCaptureCapability& capability) {
+  return 0;
+}
+
+int32_t AppDeviceInfoImpl::GetBestMatchedCapability(
+                                                    const char* deviceUniqueIdUTF8,
+                                                    const VideoCaptureCapability& requested,
+                                                    VideoCaptureCapability& resulting) {
+  return 0;
+}
+
+int32_t AppDeviceInfoImpl::GetOrientation(const char* deviceUniqueIdUTF8,
+                                          VideoCaptureRotation& orientation) {
+  return 0;
+}
+
+VideoCaptureModule* DesktopCaptureImpl::Create(const int32_t id,
+                                               const char* uniqueId,
+                                               const bool bIsApp) {
+  RefCountImpl<DesktopCaptureImpl>* capture = new RefCountImpl<DesktopCaptureImpl>(id);
+
+  //create real screen capturer.
+  if (capture->Init(uniqueId, bIsApp) != 0) {
+    delete capture;
+    return NULL;
+  }
+
+  return capture;
+}
+
+VideoCaptureModule::DeviceInfo* DesktopCaptureImpl::CreateDeviceInfo(const int32_t id,
+                                                                     const bool bIsApp) {
+  if (bIsApp) {
+    AppDeviceInfoImpl * pAppDeviceInfoImpl = new AppDeviceInfoImpl(id);
+    if (!pAppDeviceInfoImpl || pAppDeviceInfoImpl->Init()) {
+      delete pAppDeviceInfoImpl;
+      pAppDeviceInfoImpl = NULL;
+    }
+    return pAppDeviceInfoImpl;
+  } else {
+    ScreenDeviceInfoImpl * pScreenDeviceInfoImpl = new ScreenDeviceInfoImpl(id);
+    if (!pScreenDeviceInfoImpl || pScreenDeviceInfoImpl->Init()) {
+      delete pScreenDeviceInfoImpl;
+      pScreenDeviceInfoImpl = NULL;
+    }
+    return pScreenDeviceInfoImpl;
+  }
+}
+
+const char* DesktopCaptureImpl::CurrentDeviceName() const {
+  return _deviceUniqueId;
+}
+
+int32_t DesktopCaptureImpl::ChangeUniqueId(const int32_t id) {
+  _id = id;
+  return 0;
+}
+
+int32_t DesktopCaptureImpl::Init(const char* uniqueId,
+                                 const bool bIsApp) {
+  if (bIsApp) {
+    AppCapturer *pAppCapturer = AppCapturer::Create();
+    if (!pAppCapturer) {
+      return -1;
+    }
+
+    // processid hard-coded until implemented.  See Bug 1036653
+    ProcessId processid = 0;
+    pAppCapturer->SelectApp(processid);
+
+    MouseCursorMonitor * pMouseCursorMonitor = MouseCursorMonitor::CreateForScreen(webrtc::DesktopCaptureOptions::CreateDefault(), webrtc::kFullDesktopScreenId);
+    desktop_capturer_cursor_composer_.reset(new DesktopAndCursorComposer(pAppCapturer, pMouseCursorMonitor));
+  } else {
+    ScreenCapturer *pScreenCapturer = ScreenCapturer::Create();
+    if (!pScreenCapturer) {
+      return -1;
+    }
+
+    ScreenId screenid = webrtc::kFullDesktopScreenId;
+    pScreenCapturer->SelectScreen(screenid);
+    pScreenCapturer->SetMouseShapeObserver(this);
+
+    MouseCursorMonitor * pMouseCursorMonitor = MouseCursorMonitor::CreateForScreen(webrtc::DesktopCaptureOptions::CreateDefault(), screenid);
+    desktop_capturer_cursor_composer_.reset(new DesktopAndCursorComposer(pScreenCapturer, pMouseCursorMonitor));
+  }
+  return 0;
+}
+
+// returns the number of milliseconds until the module want a worker thread to call Process
+int32_t DesktopCaptureImpl::TimeUntilNextProcess() {
+  CriticalSectionScoped cs(&_callBackCs);
+
+  int32_t timeToNormalProcess = kProcessInterval
+    - (int32_t)((TickTime::Now() - _lastProcessTime).Milliseconds());
+
+  return timeToNormalProcess;
+}
+
+// Process any pending tasks such as timeouts
+int32_t DesktopCaptureImpl::Process() {
+  CriticalSectionScoped cs(&_callBackCs);
+
+  const TickTime now = TickTime::Now();
+  _lastProcessTime = TickTime::Now();
+
+  // Handle No picture alarm
+  if (_lastProcessFrameCount.Ticks() == _incomingFrameTimes[0].Ticks() &&
+      _captureAlarm != Raised) {
+    if (_noPictureAlarmCallBack && _captureCallBack) {
+      _captureAlarm = Raised;
+      _captureCallBack->OnNoPictureAlarm(_id, _captureAlarm);
+    }
+  } else if (_lastProcessFrameCount.Ticks() != _incomingFrameTimes[0].Ticks() &&
+             _captureAlarm != Cleared) {
+    if (_noPictureAlarmCallBack && _captureCallBack) {
+      _captureAlarm = Cleared;
+      _captureCallBack->OnNoPictureAlarm(_id, _captureAlarm);
+    }
+  }
+
+  // Handle frame rate callback
+  if ((now - _lastFrameRateCallbackTime).Milliseconds()
+      > kFrameRateCallbackInterval) {
+    if (_frameRateCallBack && _captureCallBack) {
+      const uint32_t frameRate = CalculateFrameRate(now);
+      _captureCallBack->OnCaptureFrameRate(_id, frameRate);
+    }
+    _lastFrameRateCallbackTime = now; // Can be set by EnableFrameRateCallback
+
+  }
+
+  _lastProcessFrameCount = _incomingFrameTimes[0];
+
+  return 0;
+}
+
+DesktopCaptureImpl::DesktopCaptureImpl(const int32_t id)
+  : _id(id),
+    _deviceUniqueId(NULL),
+    _apiCs(*CriticalSectionWrapper::CreateCriticalSection()),
+    _captureDelay(0),
+    _requestedCapability(),
+    _callBackCs(*CriticalSectionWrapper::CreateCriticalSection()),
+    _lastProcessTime(TickTime::Now()),
+    _lastFrameRateCallbackTime(TickTime::Now()),
+    _frameRateCallBack(false),
+    _noPictureAlarmCallBack(false),
+    _captureAlarm(Cleared),
+    _setCaptureDelay(0),
+    _dataCallBack(NULL),
+    _captureCallBack(NULL),
+  _lastProcessFrameCount(TickTime::Now()),
+  _rotateFrame(kRotateNone),
+  last_capture_time_(TickTime::MillisecondTimestamp()),
+  delta_ntp_internal_ms_(
+                         Clock::GetRealTimeClock()->CurrentNtpInMilliseconds() -
+                         TickTime::MillisecondTimestamp()),
+  capturer_thread_(*ThreadWrapper::CreateThread(Run, this, kHighPriority, "ScreenCaptureThread")) {
+  _requestedCapability.width = kDefaultWidth;
+  _requestedCapability.height = kDefaultHeight;
+  _requestedCapability.maxFPS = 30;
+  _requestedCapability.rawType = kVideoI420;
+  _requestedCapability.codecType = kVideoCodecUnknown;
+  memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes));
+}
+
+DesktopCaptureImpl::~DesktopCaptureImpl() {
+  capturer_thread_.Stop();
+  delete &capturer_thread_;
+
+  DeRegisterCaptureDataCallback();
+  DeRegisterCaptureCallback();
+  delete &_callBackCs;
+  delete &_apiCs;
+
+  delete[] _deviceUniqueId;
+}
+
+void DesktopCaptureImpl::RegisterCaptureDataCallback(
+                                                     VideoCaptureDataCallback& dataCallBack)
+{
+  CriticalSectionScoped cs(&_apiCs);
+  CriticalSectionScoped cs2(&_callBackCs);
+  _dataCallBack = &dataCallBack;
+}
+
+void DesktopCaptureImpl::DeRegisterCaptureDataCallback()
+{
+  CriticalSectionScoped cs(&_apiCs);
+  CriticalSectionScoped cs2(&_callBackCs);
+  _dataCallBack = NULL;
+}
+
+void DesktopCaptureImpl::RegisterCaptureCallback(VideoCaptureFeedBack& callBack)
+{
+
+  CriticalSectionScoped cs(&_apiCs);
+  CriticalSectionScoped cs2(&_callBackCs);
+  _captureCallBack = &callBack;
+}
+
+void DesktopCaptureImpl::DeRegisterCaptureCallback()
+{
+
+  CriticalSectionScoped cs(&_apiCs);
+  CriticalSectionScoped cs2(&_callBackCs);
+  _captureCallBack = NULL;
+}
+
+void DesktopCaptureImpl::SetCaptureDelay(int32_t delayMS)
+{
+  CriticalSectionScoped cs(&_apiCs);
+  _captureDelay = delayMS;
+}
+
+int32_t DesktopCaptureImpl::CaptureDelay()
+{
+  CriticalSectionScoped cs(&_apiCs);
+  return _setCaptureDelay;
+}
+
+int32_t DesktopCaptureImpl::DeliverCapturedFrame(I420VideoFrame& captureFrame,
+                                                 int64_t capture_time) {
+  UpdateFrameCount();  // frame count used for local frame rate callback.
+
+  const bool callOnCaptureDelayChanged = _setCaptureDelay != _captureDelay;
+  // Capture delay changed
+  if (_setCaptureDelay != _captureDelay) {
+    _setCaptureDelay = _captureDelay;
+  }
+
+  // Set the capture time
+  if (capture_time != 0) {
+    captureFrame.set_render_time_ms(capture_time - delta_ntp_internal_ms_);
+  } else {
+    captureFrame.set_render_time_ms(TickTime::MillisecondTimestamp());
+  }
+
+  if (captureFrame.render_time_ms() == last_capture_time_) {
+    // We don't allow the same capture time for two frames, drop this one.
+    return -1;
+  }
+  last_capture_time_ = captureFrame.render_time_ms();
+
+  if (_dataCallBack) {
+    if (callOnCaptureDelayChanged) {
+      _dataCallBack->OnCaptureDelayChanged(_id, _captureDelay);
+    }
+    _dataCallBack->OnIncomingCapturedFrame(_id, captureFrame);
+  }
+
+  return 0;
+}
+
+// Copied from VideoCaptureImpl::IncomingFrame. See Bug 1038324
+int32_t DesktopCaptureImpl::IncomingFrame(uint8_t* videoFrame,
+                                          int32_t videoFrameLength,
+                                          const VideoCaptureCapability& frameInfo,
+                                          int64_t captureTime/*=0*/)
+{
+  WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideoCapture, _id,
+               "IncomingFrame width %d, height %d", (int) frameInfo.width,
+               (int) frameInfo.height);
+
+  TickTime startProcessTime = TickTime::Now();
+
+  CriticalSectionScoped cs(&_callBackCs);
+
+  const int32_t width = frameInfo.width;
+  const int32_t height = frameInfo.height;
+
+  TRACE_EVENT1("webrtc", "VC::IncomingFrame", "capture_time", captureTime);
+
+  if (frameInfo.codecType == kVideoCodecUnknown) {
+    // Not encoded, convert to I420.
+    const VideoType commonVideoType =
+      RawVideoTypeToCommonVideoVideoType(frameInfo.rawType);
+
+    if (frameInfo.rawType != kVideoMJPEG &&
+        CalcBufferSize(commonVideoType, width,
+                       abs(height)) != videoFrameLength) {
+      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
+                   "Wrong incoming frame length.");
+      return -1;
+    }
+
+    int stride_y = width;
+    int stride_uv = (width + 1) / 2;
+    int target_width = width;
+    int target_height = height;
+    // Rotating resolution when for 90/270 degree rotations.
+    if (_rotateFrame == kRotate90 || _rotateFrame == kRotate270)  {
+      target_width = abs(height);
+      target_height = width;
+    }
+
+    // Setting absolute height (in case it was negative).
+    // In Windows, the image starts bottom left, instead of top left.
+    // Setting a negative source height, inverts the image (within LibYuv).
+    int ret = _captureFrame.CreateEmptyFrame(target_width,
+                                             abs(target_height),
+                                             stride_y,
+                                             stride_uv, stride_uv);
+    if (ret < 0) {
+      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
+                   "Failed to allocate I420 frame.");
+      return -1;
+    }
+    const int conversionResult = ConvertToI420(commonVideoType,
+                                               videoFrame,
+                                               0, 0,  // No cropping
+                                               width, height,
+                                               videoFrameLength,
+                                               _rotateFrame,
+                                               &_captureFrame);
+    if (conversionResult < 0) {
+      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
+                   "Failed to convert capture frame from type %d to I420",
+                   frameInfo.rawType);
+      return -1;
+    }
+    DeliverCapturedFrame(_captureFrame, captureTime);
+  } else {
+    assert(false);
+    return -1;
+  }
+
+  const uint32_t processTime =
+    (uint32_t)(TickTime::Now() - startProcessTime).Milliseconds();
+
+  if (processTime > 10) {
+    WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
+                 "Too long processing time of Incoming frame: %ums",
+                 (unsigned int) processTime);
+  }
+
+  return 0;
+}
+
+int32_t DesktopCaptureImpl::IncomingI420VideoFrame(I420VideoFrame* video_frame, int64_t captureTime) {
+
+  CriticalSectionScoped cs(&_callBackCs);
+  int stride_y = video_frame->stride(kYPlane);
+  int stride_u = video_frame->stride(kUPlane);
+  int stride_v = video_frame->stride(kVPlane);
+  int size_y = video_frame->height() * stride_y;
+  int size_u = stride_u * ((video_frame->height() + 1) / 2);
+  int size_v =  stride_v * ((video_frame->height() + 1) / 2);
+  int ret = _captureFrame.CreateFrame(size_y, video_frame->buffer(kYPlane),
+                                      size_u, video_frame->buffer(kUPlane),
+                                      size_v, video_frame->buffer(kVPlane),
+                                      video_frame->width(), video_frame->height(),
+                                      stride_y, stride_u, stride_v);
+
+  if (ret < 0) {
+    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
+                 "Failed to create I420VideoFrame");
+    return -1;
+  }
+
+  DeliverCapturedFrame(_captureFrame, captureTime);
+
+  return 0;
+}
+
+int32_t DesktopCaptureImpl::SetCaptureRotation(VideoCaptureRotation rotation) {
+  CriticalSectionScoped cs(&_apiCs);
+  CriticalSectionScoped cs2(&_callBackCs);
+  switch (rotation) {
+  case kCameraRotate0:
+    _rotateFrame = kRotateNone;
+    break;
+  case kCameraRotate90:
+    _rotateFrame = kRotate90;
+    break;
+  case kCameraRotate180:
+    _rotateFrame = kRotate180;
+    break;
+  case kCameraRotate270:
+    _rotateFrame = kRotate270;
+    break;
+  }
+  return 0;
+}
+
+void DesktopCaptureImpl::EnableFrameRateCallback(const bool enable) {
+  CriticalSectionScoped cs(&_apiCs);
+  CriticalSectionScoped cs2(&_callBackCs);
+  _frameRateCallBack = enable;
+  if (enable) {
+    _lastFrameRateCallbackTime = TickTime::Now();
+  }
+}
+
+void DesktopCaptureImpl::EnableNoPictureAlarm(const bool enable) {
+  CriticalSectionScoped cs(&_apiCs);
+  CriticalSectionScoped cs2(&_callBackCs);
+  _noPictureAlarmCallBack = enable;
+}
+
+void DesktopCaptureImpl::UpdateFrameCount() {
+  if (_incomingFrameTimes[0].MicrosecondTimestamp() == 0) {
+    // first no shift
+  } else {
+    // shift
+    for (int i = (kFrameRateCountHistorySize - 2); i >= 0; i--) {
+      _incomingFrameTimes[i + 1] = _incomingFrameTimes[i];
+    }
+  }
+  _incomingFrameTimes[0] = TickTime::Now();
+}
+
+uint32_t DesktopCaptureImpl::CalculateFrameRate(const TickTime& now) {
+  int32_t num = 0;
+  int32_t nrOfFrames = 0;
+  for (num = 1; num < (kFrameRateCountHistorySize - 1); num++) {
+    // don't use data older than 2sec
+    if (_incomingFrameTimes[num].Ticks() <= 0
+        || (now - _incomingFrameTimes[num]).Milliseconds() > kFrameRateHistoryWindowMs) {
+      break;
+    } else {
+      nrOfFrames++;
+    }
+  }
+  if (num > 1) {
+    int64_t diff = (now - _incomingFrameTimes[num - 1]).Milliseconds();
+    if (diff > 0) {
+      // round to nearest value rather than return minimumid_
+      return uint32_t((nrOfFrames * 1000.0f / diff) + 0.5f);
+    }
+  }
+
+  return nrOfFrames;
+}
+
+int32_t DesktopCaptureImpl::StartCapture(const VideoCaptureCapability& capability) {
+  _requestedCapability = capability;
+  desktop_capturer_cursor_composer_->Start(this);
+  unsigned int t_id =0;
+  capturer_thread_.Start(t_id);
+  return 0;
+}
+
+int32_t DesktopCaptureImpl::StopCapture() {
+  return -1;
+}
+
+bool DesktopCaptureImpl::CaptureStarted() {