merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 21 Apr 2017 10:57:02 +0200
changeset 402379 950b641e2b9c5109ee8df6b2d39f09529d0f3c38
parent 402346 7b43acb948540e6687bab6d508dcf6add5823e03 (current diff)
parent 402378 d7a79e8ceb5813c8bcf4d4d5864b8a093a006c9f (diff)
child 402380 dd530a59750adcaa0d48fa4f69b0cdb52715852a
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/base/nsDOMWindowUtils.cpp
dom/media/tests/mochitest/mochitest.ini
gfx/thebes/gfxPrefs.h
layout/style/res/forms.css
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/ZoomedView.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/moz.build
mobile/android/base/resources/drawable/toolbar_grey_round.xml
mobile/android/base/resources/layout/zoomed_view.xml
mobile/android/base/resources/values/dimens.xml
mobile/android/base/strings.xml.in
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java
testing/crashtest/autophone-crashtest-webrtc.list
testing/mochitest/manifests/autophone-media.ini
testing/mochitest/manifests/autophone-webrtc.ini
toolkit/library/gtest/rust/Cargo.toml
toolkit/library/gtest/rust/moz.build
toolkit/library/rust/Cargo.toml
toolkit/library/rust/moz.build
toolkit/library/rust/shared/Cargo.toml
widget/nsBaseWidget.h
widget/nsIWidget.h
--- a/browser/extensions/e10srollout/bootstrap.js
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -122,16 +122,17 @@ function defineCohort() {
 
   let inMultiExperiment = false;
   if (userOptedOut.e10s || userOptedOut.multi) {
     // If we detected that the user opted out either for multi or e10s, then
     // the proper prefs must already be set.
     setCohort("optedOut");
   } else if (userOptedIn.e10s) {
     setCohort("optedIn");
+    inMultiExperiment = true;
   } else if (temporaryDisqualification != "") {
     // Users who are disqualified by the backend (from multiprocessBlockPolicy)
     // can be put into either the test or control groups, because e10s will
     // still be denied by the backend, which is useful so that the E10S_STATUS
     // telemetry probe can be correctly set.
 
     // For these volatile disqualification reasons, however, we must not try
     // to activate e10s because the backend doesn't know about it. E10S_STATUS
@@ -180,17 +181,17 @@ function defineCohort() {
     2: .5,
     4: .75,
     8: 1
   };
 
   let multiUserSample = getUserSample(true);
   for (let sampleName of Object.getOwnPropertyNames(BUCKETS)) {
     if (multiUserSample < BUCKETS[sampleName]) {
-      setCohort(`${cohortPrefix}multiBucket${sampleName}`);
+      setCohort(`multiBucket${sampleName}`);
       Preferences.set(PREF_E10S_PROCESSCOUNT + ".web", sampleName);
       break;
     }
   }
 }
 
 function shutdown(data, reason) {
 }
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.8.229
+Current extension version is: 1.8.243
--- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
@@ -85,17 +85,17 @@ var PdfjsChromeUtils = {
       this._mmg = Cc["@mozilla.org/globalmessagemanager;1"].
         getService(Ci.nsIMessageListenerManager);
       this._mmg.addMessageListener("PDFJS:Parent:displayWarning", this);
 
       this._mmg.addMessageListener("PDFJS:Parent:addEventListener", this);
       this._mmg.addMessageListener("PDFJS:Parent:removeEventListener", this);
       this._mmg.addMessageListener("PDFJS:Parent:updateControlState", this);
 
-      // observer to handle shutdown
+      // Observer to handle shutdown.
       Services.obs.addObserver(this, "quit-application");
     }
   },
 
   uninit() {
     if (this._ppmm) {
       this._ppmm.removeMessageListener("PDFJS:Parent:clearUserPref", this);
       this._ppmm.removeMessageListener("PDFJS:Parent:setIntPref", this);
--- a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm
@@ -40,16 +40,17 @@ var PdfjsContentUtils = {
 
   init() {
     // child *process* mm, or when loaded into the parent for in-content
     // support the psuedo child process mm 'child PPMM'.
     if (!this._mm) {
       this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].
         getService(Ci.nsISyncMessageSender);
       this._mm.addMessageListener("PDFJS:Child:refreshSettings", this);
+
       Services.obs.addObserver(this, "quit-application");
     }
   },
 
   uninit() {
     if (this._mm) {
       this._mm.removeMessageListener("PDFJS:Child:refreshSettings", this);
       Services.obs.removeObserver(this, "quit-application");
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -956,22 +956,22 @@ var StatTimer = function StatTimerClosur
     }
   };
   return StatTimer;
 }();
 var createBlob = function createBlob(data, contentType) {
   if (typeof Blob !== 'undefined') {
     return new Blob([data], { type: contentType });
   }
-  warn('The "Blob" constructor is not supported.');
+  throw new Error('The "Blob" constructor is not supported.');
 };
 var createObjectURL = function createObjectURLClosure() {
   var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
-  return function createObjectURL(data, contentType, forceDataSchema) {
-    if (!forceDataSchema && typeof URL !== 'undefined' && URL.createObjectURL) {
+  return function createObjectURL(data, contentType, forceDataSchema = false) {
+    if (!forceDataSchema) {
       var blob = createBlob(data, contentType);
       return URL.createObjectURL(blob);
     }
     var buffer = 'data:' + contentType + ';base64,';
     for (var i = 0, ii = data.length; i < ii; i += 3) {
       var b1 = data[i] & 0xFF;
       var b2 = data[i + 1] & 0xFF;
       var b3 = data[i + 2] & 0xFF;
@@ -3386,18 +3386,18 @@ var _UnsupportedManager = function Unsup
       for (var i = 0, ii = listeners.length; i < ii; i++) {
         listeners[i](featureId);
       }
     }
   };
 }();
 var version, build;
 {
-  exports.version = version = '1.8.229';
-  exports.build = build = '5ad3611c';
+  exports.version = version = '1.8.243';
+  exports.build = build = '96cb599e';
 }
 exports.getDocument = getDocument;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports._UnsupportedManager = _UnsupportedManager;
 exports.version = version;
@@ -4389,18 +4389,18 @@ var _text_layer = __w_pdfjs_require__(5)
 var _svg = __w_pdfjs_require__(4);
 
 var isWorker = typeof window === 'undefined';
 if (!_util.globalScope.PDFJS) {
   _util.globalScope.PDFJS = {};
 }
 var PDFJS = _util.globalScope.PDFJS;
 {
-  PDFJS.version = '1.8.229';
-  PDFJS.build = '5ad3611c';
+  PDFJS.version = '1.8.243';
+  PDFJS.build = '96cb599e';
 }
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
   (0, _util.setVerbosityLevel)(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
   get: function () {
@@ -6704,18 +6704,18 @@ exports.TilingPattern = TilingPattern;
 
 /***/ }),
 /* 13 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.8.229';
-var pdfjsBuild = '5ad3611c';
+var pdfjsVersion = '1.8.243';
+var pdfjsBuild = '96cb599e';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(8);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(5);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(2);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(4);
 exports.PDFJS = pdfjsDisplayGlobal.PDFJS;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -956,22 +956,22 @@ var StatTimer = function StatTimerClosur
     }
   };
   return StatTimer;
 }();
 var createBlob = function createBlob(data, contentType) {
   if (typeof Blob !== 'undefined') {
     return new Blob([data], { type: contentType });
   }
-  warn('The "Blob" constructor is not supported.');
+  throw new Error('The "Blob" constructor is not supported.');
 };
 var createObjectURL = function createObjectURLClosure() {
   var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
-  return function createObjectURL(data, contentType, forceDataSchema) {
-    if (!forceDataSchema && typeof URL !== 'undefined' && URL.createObjectURL) {
+  return function createObjectURL(data, contentType, forceDataSchema = false) {
+    if (!forceDataSchema) {
       var blob = createBlob(data, contentType);
       return URL.createObjectURL(blob);
     }
     var buffer = 'data:' + contentType + ';base64,';
     for (var i = 0, ii = data.length; i < ii; i += 3) {
       var b1 = data[i] & 0xFF;
       var b2 = data[i + 1] & 0xFF;
       var b3 = data[i + 2] & 0xFF;
@@ -36560,18 +36560,18 @@ exports.Type1Parser = Type1Parser;
 
 /***/ }),
 /* 35 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.8.229';
-var pdfjsBuild = '5ad3611c';
+var pdfjsVersion = '1.8.243';
+var pdfjsBuild = '96cb599e';
 var pdfjsCoreWorker = __w_pdfjs_require__(17);
 ;
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 36 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -310,19 +310,28 @@ function getVisibleElements(scrollEl, vi
     first: first,
     last: last,
     views: visible
   };
 }
 function noContextMenuHandler(e) {
   e.preventDefault();
 }
-function getPDFFileNameFromURL(url, defaultFilename) {
-  if (typeof defaultFilename === 'undefined') {
-    defaultFilename = 'document.pdf';
+function isDataSchema(url) {
+  var i = 0,
+      ii = url.length;
+  while (i < ii && url[i].trim() === '') {
+    i++;
+  }
+  return url.substr(i, 5).toLowerCase() === 'data:';
+}
+function getPDFFileNameFromURL(url, defaultFilename = 'document.pdf') {
+  if (isDataSchema(url)) {
+    console.warn('getPDFFileNameFromURL: ' + 'ignoring "data:" URL for performance reasons.');
+    return defaultFilename;
   }
   var reURI = /^(?:(?:[^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
   var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
   var splitURI = reURI.exec(url);
   var suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]);
   if (suggestedFilename) {
     suggestedFilename = suggestedFilename[0];
     if (suggestedFilename.indexOf('%') !== -1) {
@@ -3864,21 +3873,24 @@ var PDFAttachmentViewer = function PDFAt
     },
     _dispatchEvent: function PDFAttachmentViewer_dispatchEvent(attachmentsCount) {
       this.eventBus.dispatch('attachmentsloaded', {
         source: this,
         attachmentsCount: attachmentsCount
       });
       this._renderedCapability.resolve();
     },
-    _bindPdfLink: function PDFAttachmentViewer_bindPdfLink(button, content, filename) {
+    _bindPdfLink(button, content, filename) {
+      if (_pdfjs.PDFJS.disableCreateObjectURL) {
+        throw new Error('bindPdfLink: ' + 'Unsupported "PDFJS.disableCreateObjectURL" value.');
+      }
       var blobUrl;
       button.onclick = function () {
         if (!blobUrl) {
-          blobUrl = (0, _pdfjs.createObjectURL)(content, 'application/pdf', _pdfjs.PDFJS.disableCreateObjectURL);
+          blobUrl = (0, _pdfjs.createObjectURL)(content, 'application/pdf');
         }
         var viewerUrl;
         viewerUrl = blobUrl + '?' + encodeURIComponent(filename);
         window.open(viewerUrl);
         return false;
       };
     },
     _bindLink: function PDFAttachmentViewer_bindLink(button, content, filename) {
@@ -3906,17 +3918,17 @@ var PDFAttachmentViewer = function PDFAt
       attachmentsCount = names.length;
       for (var i = 0; i < attachmentsCount; i++) {
         var item = attachments[names[i]];
         var filename = (0, _pdfjs.removeNullCharacters)((0, _pdfjs.getFilenameFromUrl)(item.filename));
         var div = document.createElement('div');
         div.className = 'attachmentsItem';
         var button = document.createElement('button');
         button.textContent = filename;
-        if (/\.pdf$/i.test(filename)) {
+        if (/\.pdf$/i.test(filename) && !_pdfjs.PDFJS.disableCreateObjectURL) {
           this._bindPdfLink(button, item.content, filename);
         } else {
           this._bindLink(button, item.content, filename);
         }
         div.appendChild(button);
         this.container.appendChild(div);
       }
       this._dispatchEvent(attachmentsCount);
@@ -7625,17 +7637,17 @@ exports.ViewHistory = ViewHistory;
 
 var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
 ;
 var pdfjsWebApp;
 {
   pdfjsWebApp = __webpack_require__(4);
 }
 {
-  window.FirefoxCom = __webpack_require__(10).FirefoxCom;
+  __webpack_require__(10);
   __webpack_require__(9);
 }
 ;
 ;
 ;
 function getViewerConfiguration() {
   return {
     appContainer: document.body,
--- a/build/moz.configure/warnings.configure
+++ b/build/moz.configure/warnings.configure
@@ -56,16 +56,19 @@ check_and_add_gcc_warning('-Wclass-varar
 check_and_add_gcc_warning('-Wloop-analysis')
 
 # catches C++ version forward-compat issues
 check_and_add_gcc_warning('-Wc++11-compat-pedantic', cxx_compiler)
 check_and_add_gcc_warning('-Wc++14-compat', cxx_compiler)
 check_and_add_gcc_warning('-Wc++14-compat-pedantic', cxx_compiler)
 check_and_add_gcc_warning('-Wc++1z-compat', cxx_compiler)
 
+# catches possible misuse of the comma operator
+check_and_add_gcc_warning('-Wcomma', cxx_compiler)
+
 # catches unintentional switch case fallthroughs
 check_and_add_gcc_warning('-Wimplicit-fallthrough', cxx_compiler)
 
 # catches expressions used as a null pointer constant
 # XXX: at the time of writing, the version of clang used on the OS X test
 # machines has a bug that causes it to reject some valid files if both
 # -Wnon-literal-null-conversion and -Wsometimes-uninitialized are
 # specified. We work around this by instead using
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -4147,31 +4147,16 @@ nsDOMWindowUtils::HasRuleProcessorUsedBy
     return NS_ERROR_FAILURE;
   }
 
   return presShell->HasRuleProcessorUsedByMultipleStyleSets(aSheetType,
                                                             aRetVal);
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::SetNextPaintSyncId(int32_t aSyncId)
-{
-  if (nsIWidget* widget = GetWidget()) {
-    RefPtr<LayerManager> lm = widget->GetLayerManager();
-    if (lm && lm->AsClientLayerManager()) {
-      lm->AsClientLayerManager()->SetNextPaintSyncId(aSyncId);
-      return NS_OK;
-    }
-  }
-
-  NS_WARNING("Paint sync id could not be set on the ClientLayerManager");
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDOMWindowUtils::RespectDisplayPortSuppression(bool aEnabled)
 {
   nsCOMPtr<nsIPresShell> shell(GetPresShell());
   APZCCallbackHelper::RespectDisplayPortSuppression(aEnabled, shell);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1951,18 +1951,16 @@ interface nsIDOMWindowUtils : nsISupport
    * counters and an image node with an SVG source will flush the SVG
    * document's counters.  Normally, use counters are flushed to telemetry
    * upon document destruction, but as document destruction is somewhat
    * non-deterministic, we have this method here for more determinism when
    * running tests.
    */
   void forceUseCounterFlush(in nsIDOMNode aNode);
 
-  void setNextPaintSyncId(in long aSyncId);
-
   /**
    * Enable or disable displayport suppression. This is intended to be used by
    * testing code, to provide more deterministic behaviour over the displayport
    * suppression during tests. Note that this updates a flag, so whatever value
    * was last provided is what will be used.
    */
   void respectDisplayPortSuppression(in boolean aEnabled);
 
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -67,17 +67,17 @@ skip-if = ((os == "win" && os_version ==
 [test_DurationUpdated.html]
 [test_DurationUpdated_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_EndedEvent.html]
 [test_EndOfStream.html]
 [test_EndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_Eviction_mp4.html]
-skip-if = (os == "win" && os_version == "5.1") # Not supported on xp.
+skip-if = (os == "win" && os_version == "5.1") || (android_version == '15') # Not supported on xp. Android(Bug 1358271)
 [test_FrameSelection.html]
 [test_FrameSelection_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_HaveMetadataUnbufferedSeek.html]
 [test_HaveMetadataUnbufferedSeek_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_LiveSeekable.html]
 [test_LoadedDataFired_mp4.html]
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -26,31 +26,31 @@ support-files =
 [test_a_noOp.html]
 [test_dataChannel_basicAudio.html]
 skip-if = (android_version == '18') # Bug 962984 for debug, bug 963244 for opt
 [test_dataChannel_basicAudioVideo.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_dataChannel_basicAudioVideoNoBundle.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_dataChannel_basicAudioVideoCombined.html]
-skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = toolkit == 'android'  # Bug 1189784
 [test_dataChannel_basicDataOnly.html]
 [test_dataChannel_basicVideo.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_dataChannel_bug1013809.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_dataChannel_noOffer.html]
 [test_enumerateDevices.html]
 [test_enumerateDevices_iframe.html]
 skip-if = true # needed by test_enumerateDevices.html on builders
 [test_ondevicechange.html]
 skip-if = os == 'android'
 [test_getUserMedia_active_autoplay.html]
 [test_getUserMedia_audioCapture.html]
-skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = toolkit == 'android' # android(Bug 1189784, timeouts on 4.3 emulator), android(Bug 1264333)
 [test_getUserMedia_addTrackRemoveTrack.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_getUserMedia_addtrack_removetrack_events.html]
 [test_getUserMedia_basicAudio.html]
 [test_getUserMedia_basicVideo.html]
 [test_getUserMedia_basicVideo_playAfterLoadedmetadata.html]
 [test_getUserMedia_basicScreenshare.html]
 skip-if = toolkit == 'android' # no screenshare on android
@@ -106,133 +106,134 @@ skip-if = true # need pyopenssl on build
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioPcmaPcmuOnly.html]
 skip-if = android_version == '18'
 [test_peerConnection_basicAudioDynamicPtMissingRtpmap.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioVideo.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioVideoCombined.html]
-skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = toolkit == 'android'  # Bug 1189784
 [test_peerConnection_basicAudioVideoNoBundle.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioVideoNoBundleNoRtcpMux.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioVideoNoRtcpMux.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicVideo.html]
 skip-if = (android_version == '18' && debug) # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicScreenshare.html]
 # frequent timeouts/crashes on e10s (bug 1048455)
 skip-if = toolkit == 'android' # no screenshare on android
 [test_peerConnection_basicWindowshare.html]
 # frequent timeouts/crashes on e10s (bug 1048455)
 skip-if = toolkit == 'android' # no screenshare on android
 [test_peerConnection_basicH264Video.html]
-skip-if = os == 'android' # bug 1043403
+skip-if = toolkit == 'android' # Bug 1043403, Bug 1355786, Bug 1149374
 [test_peerConnection_bug822674.html]
 [test_peerConnection_bug825703.html]
 [test_peerConnection_bug827843.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_bug834153.html]
 [test_peerConnection_bug1013809.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_bug1042791.html]
-skip-if = os == 'android' # bug 1043403
+skip-if = toolkit == 'android' # Bug 1043403, Bug 1355786, Bug 1149374
 [test_peerConnection_bug1064223.html]
 [test_peerConnection_capturedVideo.html]
 tags=capturestream
-skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = toolkit == 'android' # android(Bug 1189784, timeouts on 4.3 emulator), Bug 1264340
 [test_peerConnection_captureStream_canvas_2d.html]
 skip-if = android_version == '18' # android(Bug 1319019, timeouts on 4.3 emulator)
 [test_peerConnection_captureStream_canvas_2d_noSSRC.html]
 skip-if = android_version == '18' # android(Bug 1319019, timeouts on 4.3 emulator)
 [test_peerConnection_multiple_captureStream_canvas_2d.html]
 skip-if = (android_version == '18' && debug) # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_captureStream_canvas_webgl.html]
+skip-if = android_version <= '17' # bug 1346630
 # [test_peerConnection_certificates.html] # bug 1180968
 [test_peerConnection_close.html]
 [test_peerConnection_closeDuringIce.html]
 [test_peerConnection_constructedStream.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_errorCallbacks.html]
 [test_peerConnection_iceFailure.html]
-skip-if = os == 'linux' || os == 'mac' || os == 'win' || android_version == '18' # (Bug 1180388 for win, mac and linux), android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = os == 'linux' || os == 'mac' || os == 'win' || toolkit == 'android' # (Bug 1180388 for win, mac and linux), android(Bug 1189784, timeouts on 4.3 emulator), Bug 1180388
 [test_peerConnection_insertDTMF.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_forwarding_basicAudioVideoCombined.html]
-skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = toolkit == 'android'  # Bug 1189784
 [test_peerConnection_noTrickleAnswer.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_noTrickleOffer.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_noTrickleOfferAnswer.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_offerRequiresReceiveAudio.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_offerRequiresReceiveVideo.html]
 [test_peerConnection_offerRequiresReceiveVideoAudio.html]
-skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = toolkit == 'android' # android(Bug 1189784)
 [test_peerConnection_promiseSendOnly.html]
 skip-if = (android_version == '18') # android(Bug 1189784, 1318809 timeouts on 4.3 emulator)
 [test_peerConnection_renderAfterRenegotiation.html]
 skip-if = (android_version == '18') # android(Bug 1189784, 1326005 timeouts on 4.3 emulator)
 [test_peerConnection_restartIce.html]
-skip-if = android_version
+skip-if = toolkit == 'android'
 [test_peerConnection_restartIceNoBundle.html]
-skip-if = android_version
+skip-if = toolkit == 'android'
 [test_peerConnection_restartIceNoBundleNoRtcpMux.html]
-skip-if = android_version
+skip-if = toolkit == 'android'
 [test_peerConnection_restartIceNoRtcpMux.html]
-skip-if = android_version
+skip-if = toolkit == 'android'
 [test_peerConnection_restartIceLocalRollback.html]
-skip-if = android_version
+skip-if = toolkit == 'android'
 [test_peerConnection_restartIceLocalAndRemoteRollback.html]
-skip-if = android_version
+skip-if = toolkit == 'android'
 [test_peerConnection_scaleResolution.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 # disable test_peerConnection_simulcastOffer.html for Bug 1351590
 #[test_peerConnection_simulcastOffer.html]
 #skip-if = android_version # no simulcast support on android
 # disable test_peerConnection_simulcastAnswer.html for Bug 1351531
 #[test_peerConnection_simulcastAnswer.html]
 #skip-if = android_version # no simulcast support on android
 #[test_peerConnection_relayOnly.html]
 [test_peerConnection_callbacks.html]
-skip-if = (android_version == '18' && debug) # android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = toolkit == 'android' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_replaceTrack.html]
-skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = toolkit == 'android'  # Bug 1189784
 [test_peerConnection_syncSetDescription.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_setLocalAnswerInHaveLocalOffer.html]
 [test_peerConnection_setLocalAnswerInStable.html]
 [test_peerConnection_setLocalOfferInHaveRemoteOffer.html]
 [test_peerConnection_setParameters.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html]
 [test_peerConnection_setRemoteAnswerInStable.html]
 [test_peerConnection_setRemoteOfferInHaveLocalOffer.html]
 [test_peerConnection_throwInCallbacks.html]
 [test_peerConnection_toJSON.html]
 [test_peerConnection_trackDisabling_clones.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_trackDisabling.html]
-skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = toolkit == 'android' # android(Bug 1189784, timeouts on 4.3 emulator), Bug 1265878
 [test_peerConnection_twoAudioStreams.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_twoAudioTracksInOneStream.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_twoAudioVideoStreams.html]
 skip-if = (os == 'linux' && debug && e10s) || android_version == '18' # Bug 1171255 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_twoAudioVideoStreamsCombined.html]
-skip-if = (os == 'linux' && debug && e10s) || android_version == '18' # Bug 1127828 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = (os == 'linux' && debug && e10s) || toolkit == 'android' # Bug 1127828 for Linux debug e10s, android(Bug 1189784)
 [test_peerConnection_twoVideoStreams.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_twoVideoTracksInOneStream.html]
-skip-if = os == "android" # android(Bug 1189784, timeouts on 4.3 emulator)
+skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_addAudioTrackToExistingVideoStream.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_addSecondAudioStream.html]
 skip-if = (android_version == '18') # emulator is too slow to finish a renegotiation test in under 5 minutes
 [test_peerConnection_answererAddSecondAudioStream.html]
 skip-if = (android_version == '18') # emulator is too slow to finish a renegotiation test in under 5 minutes
 [test_peerConnection_removeAudioTrack.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
--- a/dom/media/webaudio/blink/HRTFDatabaseLoader.cpp
+++ b/dom/media/webaudio/blink/HRTFDatabaseLoader.cpp
@@ -24,16 +24,17 @@
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "HRTFDatabaseLoader.h"
 #include "HRTFDatabase.h"
 #include "GeckoProfiler.h"
+#include "nsThreadUtils.h"
 
 using namespace mozilla;
 
 namespace WebCore {
 
 // Singleton
 nsTHashtable<HRTFDatabaseLoader::LoaderByRateEntry>*
     HRTFDatabaseLoader::s_loaderMap = nullptr;
@@ -148,17 +149,17 @@ void HRTFDatabaseLoader::MainThreadRelea
         delete this;
     }
 }
 
 // Asynchronously load the database in this thread.
 static void databaseLoaderEntry(void* threadData)
 {
     AutoProfilerRegister registerThread("HRTFDatabaseLdr");
-    PR_SetCurrentThreadName("HRTFDatabaseLdr");
+    NS_SetCurrentThreadName("HRTFDatabaseLdr");
 
     HRTFDatabaseLoader* loader = reinterpret_cast<HRTFDatabaseLoader*>(threadData);
     MOZ_ASSERT(loader);
     loader->load();
 }
 
 void HRTFDatabaseLoader::load()
 {
--- a/dom/storage/StorageDBThread.cpp
+++ b/dom/storage/StorageDBThread.cpp
@@ -336,17 +336,17 @@ StorageDBThread::SetDefaultPriority()
     PR_SetThreadPriority(mThread, PR_PRIORITY_LOW);
   }
 }
 
 void
 StorageDBThread::ThreadFunc(void* aArg)
 {
   AutoProfilerRegister registerThread("localStorage DB");
-  PR_SetCurrentThreadName("localStorage DB");
+  NS_SetCurrentThreadName("localStorage DB");
   mozilla::IOInterposer::RegisterCurrentThread();
 
   StorageDBThread* thread = static_cast<StorageDBThread*>(aArg);
   thread->ThreadFunc();
   mozilla::IOInterposer::UnregisterCurrentThread();
 }
 
 void
--- a/dom/webidl/CSSGroupingRule.webidl
+++ b/dom/webidl/CSSGroupingRule.webidl
@@ -6,12 +6,12 @@
  * The origin of this IDL file is
  * https://drafts.csswg.org/cssom/#cssgroupingrule
  */
 
 // https://drafts.csswg.org/cssom/#cssgroupingrule
 interface CSSGroupingRule : CSSRule {
   [SameObject] readonly attribute CSSRuleList cssRules;
   [Throws]
-  unsigned long insertRule(DOMString rule, unsigned long index);
+  unsigned long insertRule(DOMString rule, optional unsigned long index = 0);
   [Throws]
   void deleteRule(unsigned long index);
 };
--- a/dom/webidl/CSSStyleSheet.webidl
+++ b/dom/webidl/CSSStyleSheet.webidl
@@ -16,12 +16,12 @@ enum CSSStyleSheetParsingMode {
 interface CSSStyleSheet : StyleSheet {
   [Pure]
   readonly attribute CSSRule? ownerRule;
   [Throws, NeedsSubjectPrincipal]
   readonly attribute CSSRuleList cssRules;
   [ChromeOnly, BinaryName="parsingModeDOM"]
   readonly attribute CSSStyleSheetParsingMode parsingMode;
   [Throws, NeedsSubjectPrincipal]
-  unsigned long insertRule(DOMString rule, unsigned long index);
+  unsigned long insertRule(DOMString rule, optional unsigned long index = 0);
   [Throws, NeedsSubjectPrincipal]
   void deleteRule(unsigned long index);
 };
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -2820,17 +2820,17 @@ NS_IMPL_ISUPPORTS_INHERITED0(WorkerThrea
 
 NS_IMETHODIMP
 WorkerThreadPrimaryRunnable::Run()
 {
   using mozilla::ipc::BackgroundChild;
 
   char stackBaseGuess;
 
-  PR_SetCurrentThreadName("DOM Worker");
+  NS_SetCurrentThreadName("DOM Worker");
 
   nsAutoCString threadName;
   threadName.AssignLiteral("DOM Worker '");
   threadName.Append(NS_LossyConvertUTF16toASCII(mWorkerPrivate->ScriptURL()));
   threadName.Append('\'');
 
   profiler_register_thread(threadName.get(), &stackBaseGuess);
 
--- a/gfx/ipc/CompositorSession.h
+++ b/gfx/ipc/CompositorSession.h
@@ -5,16 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef _include_mozilla_gfx_ipc_CompositorSession_h_
 #define _include_mozilla_gfx_ipc_CompositorSession_h_
 
 #include "base/basictypes.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "nsISupportsImpl.h"
+#if defined(MOZ_WIDGET_ANDROID)
+#include "mozilla/layers/UiCompositorControllerChild.h"
+#endif // defined(MOZ_WIDGET_ANDROID)
 
 class nsIWidget;
 
 namespace mozilla {
 namespace widget {
 class CompositorWidget;
 class CompositorWidgetDelegate;
 } // namespace widget
@@ -67,27 +70,40 @@ public:
     return mCompositorWidgetDelegate;
   }
 
   // Return the id of the root layer tree.
   uint64_t RootLayerTreeId() const {
     return mRootLayerTreeId;
   }
 
+#if defined(MOZ_WIDGET_ANDROID)
+  // Set the UiCompositorControllerChild after Session creation so the Session constructor
+  // doesn't get mucked up for other platforms.
+  void SetUiCompositorControllerChild(RefPtr<UiCompositorControllerChild> aUiController) {
+    mUiCompositorControllerChild = aUiController;
+  }
+
+  RefPtr<UiCompositorControllerChild> GetUiCompositorControllerChild() {
+    return mUiCompositorControllerChild;
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
 protected:
   CompositorSession(CompositorWidgetDelegate* aDelegate,
                     CompositorBridgeChild* aChild,
                     const uint64_t& aRootLayerTreeId);
   virtual ~CompositorSession();
 
 protected:
   CompositorWidgetDelegate* mCompositorWidgetDelegate;
   RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
   uint64_t mRootLayerTreeId;
-
+#if defined(MOZ_WIDGET_ANDROID)
+  RefPtr<UiCompositorControllerChild> mUiCompositorControllerChild;
+#endif // defined(MOZ_WIDGET_ANDROID)
 private:
   DISALLOW_COPY_AND_ASSIGN(CompositorSession);
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // _include_mozilla_gfx_ipc_CompositorSession_h_
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -229,19 +229,19 @@ GPUParent::RecvInitImageBridge(Endpoint<
 mozilla::ipc::IPCResult
 GPUParent::RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint)
 {
   VRManagerParent::CreateForGPUProcess(Move(aEndpoint));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-GPUParent::RecvInitUiCompositorController(Endpoint<PUiCompositorControllerParent>&& aEndpoint)
+GPUParent::RecvInitUiCompositorController(const uint64_t& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint)
 {
-  UiCompositorControllerParent::Start(Move(aEndpoint));
+  UiCompositorControllerParent::Start(aRootLayerTreeId, Move(aEndpoint));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 GPUParent::RecvUpdatePref(const GfxPrefSetting& setting)
 {
   gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()];
   pref->SetCachedValue(setting.value());
--- a/gfx/ipc/GPUParent.h
+++ b/gfx/ipc/GPUParent.h
@@ -32,17 +32,17 @@ public:
 
   mozilla::ipc::IPCResult RecvInit(nsTArray<GfxPrefSetting>&& prefs,
                                    nsTArray<GfxVarUpdate>&& vars,
                                    const DevicePrefs& devicePrefs,
                                    nsTArray<LayerTreeIdMapping>&& mappings) override;
   mozilla::ipc::IPCResult RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) override;
   mozilla::ipc::IPCResult RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
-  mozilla::ipc::IPCResult RecvInitUiCompositorController(Endpoint<PUiCompositorControllerParent>&& aEndpoint) override;
+  mozilla::ipc::IPCResult RecvInitUiCompositorController(const uint64_t& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint) override;
   mozilla::ipc::IPCResult RecvUpdatePref(const GfxPrefSetting& pref) override;
   mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref) override;
   mozilla::ipc::IPCResult RecvNewWidgetCompositor(
       Endpoint<PCompositorBridgeParent>&& aEndpoint,
       const CSSToLayoutDeviceScale& aScale,
       const TimeDuration& aVsyncRate,
       const CompositorOptions& aOptions,
       const bool& aUseExternalSurface,
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -249,48 +249,43 @@ GPUProcessManager::EnsureVRManager()
     return;
   }
 
   mGPUChild->SendInitVRManager(Move(parentPipe));
   VRManagerChild::InitWithGPUProcess(Move(childPipe));
 }
 
 #if defined(MOZ_WIDGET_ANDROID)
-void
-GPUProcessManager::EnsureUiCompositorController()
+already_AddRefed<UiCompositorControllerChild>
+GPUProcessManager::CreateUiCompositorController(nsBaseWidget* aWidget, const uint64_t aId)
 {
-  if (UiCompositorControllerChild::IsInitialized()) {
-    return;
-  }
-
-  RefPtr<nsThread> uiThread;
-
-  uiThread = GetAndroidUiThread();
-
-  MOZ_ASSERT(uiThread);
+  RefPtr<UiCompositorControllerChild> result;
 
   if (!EnsureGPUReady()) {
-    UiCompositorControllerChild::InitSameProcess(uiThread);
-    return;
-  }
+    result = UiCompositorControllerChild::CreateForSameProcess(aId);
+  } else {
+    ipc::Endpoint<PUiCompositorControllerParent> parentPipe;
+    ipc::Endpoint<PUiCompositorControllerChild> childPipe;
+    nsresult rv = PUiCompositorController::CreateEndpoints(
+      mGPUChild->OtherPid(),
+      base::GetCurrentProcId(),
+      &parentPipe,
+      &childPipe);
+    if (NS_FAILED(rv)) {
+      DisableGPUProcess("Failed to create PUiCompositorController endpoints");
+      return nullptr;
+    }
 
-  ipc::Endpoint<PUiCompositorControllerParent> parentPipe;
-  ipc::Endpoint<PUiCompositorControllerChild> childPipe;
-  nsresult rv = PUiCompositorController::CreateEndpoints(
-    mGPUChild->OtherPid(),
-    base::GetCurrentProcId(),
-    &parentPipe,
-    &childPipe);
-  if (NS_FAILED(rv)) {
-    DisableGPUProcess("Failed to create PUiCompositorController endpoints");
-    return;
+    mGPUChild->SendInitUiCompositorController(aId, Move(parentPipe));
+    result = UiCompositorControllerChild::CreateForGPUProcess(mProcessToken, Move(childPipe));
   }
-
-  mGPUChild->SendInitUiCompositorController(Move(parentPipe));
-  UiCompositorControllerChild::InitWithGPUProcess(uiThread, mProcessToken, Move(childPipe));
+  if (result) {
+    result->SetBaseWidget(aWidget);
+  }
+  return result.forget();
 }
 #endif // defined(MOZ_WIDGET_ANDROID)
 
 void
 GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
@@ -545,19 +540,16 @@ GPUProcessManager::DestroyProcess()
   mProcess->Shutdown();
   mProcessToken = 0;
   mProcess = nullptr;
   mGPUChild = nullptr;
   if (mVsyncBridge) {
     mVsyncBridge->Close();
     mVsyncBridge = nullptr;
   }
-#if defined(MOZ_WIDGET_ANDROID)
-  UiCompositorControllerChild::Shutdown();
-#endif // defined(MOZ_WIDGET_ANDROID)
 
 #ifdef MOZ_CRASHREPORTER
   CrashReporter::AnnotateCrashReport(
     NS_LITERAL_CSTRING("GPUProcessStatus"),
     NS_LITERAL_CSTRING("Destroyed"));
 #endif
 }
 
@@ -568,46 +560,55 @@ GPUProcessManager::CreateTopLevelComposi
                                             const CompositorOptions& aOptions,
                                             bool aUseExternalSurfaceSize,
                                             const gfx::IntSize& aSurfaceSize)
 {
   uint64_t layerTreeId = AllocateLayerTreeId();
 
   EnsureImageBridgeChild();
   EnsureVRManager();
-#if defined(MOZ_WIDGET_ANDROID)
-  EnsureUiCompositorController();
-#endif // defined(MOZ_WIDGET_ANDROID)
+
+  RefPtr<CompositorSession> session;
 
   if (EnsureGPUReady()) {
-    RefPtr<CompositorSession> session = CreateRemoteSession(
+    session = CreateRemoteSession(
       aWidget,
       aLayerManager,
       layerTreeId,
       aScale,
       aOptions,
       aUseExternalSurfaceSize,
       aSurfaceSize);
-    if (session) {
-      return session;
+    if (!session) {
+      // We couldn't create a remote compositor, so abort the process.
+      DisableGPUProcess("Failed to create remote compositor");
     }
-
-    // We couldn't create a remote compositor, so abort the process.
-    DisableGPUProcess("Failed to create remote compositor");
   }
 
-  return InProcessCompositorSession::Create(
-    aWidget,
-    aLayerManager,
-    layerTreeId,
-    aScale,
-    aOptions,
-    aUseExternalSurfaceSize,
-    aSurfaceSize,
-    AllocateNamespace());
+  if (!session) {
+    session = InProcessCompositorSession::Create(
+      aWidget,
+      aLayerManager,
+      layerTreeId,
+      aScale,
+      aOptions,
+      aUseExternalSurfaceSize,
+      aSurfaceSize,
+      AllocateNamespace());
+  }
+
+#if defined(MOZ_WIDGET_ANDROID)
+  if (session) {
+    // Nothing to do if controller gets a nullptr
+    RefPtr<UiCompositorControllerChild> controller = CreateUiCompositorController(aWidget, session->RootLayerTreeId());
+    session->SetUiCompositorControllerChild(controller);
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  return session;
 }
 
 RefPtr<CompositorSession>
 GPUProcessManager::CreateRemoteSession(nsBaseWidget* aWidget,
                                        LayerManager* aLayerManager,
                                        const uint64_t& aRootLayerTreeId,
                                        CSSToLayoutDeviceScale aScale,
                                        const CompositorOptions& aOptions,
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -26,16 +26,17 @@ class MemoryReportingProcess;
 namespace layers {
 class IAPZCTreeManager;
 class CompositorOptions;
 class CompositorSession;
 class CompositorUpdateObserver;
 class PCompositorBridgeChild;
 class PImageBridgeChild;
 class RemoteCompositorSession;
+class UiCompositorControllerChild;
 } // namespace layers
 namespace widget {
 class CompositorWidget;
 } // namespace widget
 namespace dom {
 class ContentParent;
 class TabParent;
 class PVideoDecoderManagerChild;
@@ -61,16 +62,17 @@ class GPUProcessManager final : public G
   typedef layers::CompositorOptions CompositorOptions;
   typedef layers::CompositorSession CompositorSession;
   typedef layers::CompositorUpdateObserver CompositorUpdateObserver;
   typedef layers::IAPZCTreeManager IAPZCTreeManager;
   typedef layers::LayerManager LayerManager;
   typedef layers::PCompositorBridgeChild PCompositorBridgeChild;
   typedef layers::PImageBridgeChild PImageBridgeChild;
   typedef layers::RemoteCompositorSession RemoteCompositorSession;
+  typedef layers::UiCompositorControllerChild UiCompositorControllerChild;
 
 public:
   static void Initialize();
   static void Shutdown();
   static GPUProcessManager* Get();
 
   ~GPUProcessManager();
 
@@ -208,17 +210,20 @@ private:
 
   void HandleProcessLost();
 
   void EnsureVsyncIOThread();
   void ShutdownVsyncIOThread();
 
   void EnsureImageBridgeChild();
   void EnsureVRManager();
-  void EnsureUiCompositorController();
+
+#if defined(MOZ_WIDGET_ANDROID)
+  already_AddRefed<UiCompositorControllerChild> CreateUiCompositorController(nsBaseWidget* aWidget, const uint64_t aId);
+#endif // defined(MOZ_WIDGET_ANDROID)
 
   RefPtr<CompositorSession> CreateRemoteSession(
     nsBaseWidget* aWidget,
     LayerManager* aLayerManager,
     const uint64_t& aRootLayerTreeId,
     CSSToLayoutDeviceScale aScale,
     const CompositorOptions& aOptions,
     bool aUseExternalSurfaceSize,
--- a/gfx/ipc/InProcessCompositorSession.cpp
+++ b/gfx/ipc/InProcessCompositorSession.cpp
@@ -75,12 +75,18 @@ InProcessCompositorSession::Shutdown()
   // Destroy will synchronously wait for the parent to acknowledge shutdown,
   // at which point CBP will defer a Release on the compositor thread. We
   // can safely release our reference now, and let the destructor run on either
   // thread.
   mCompositorBridgeChild->Destroy();
   mCompositorBridgeChild = nullptr;
   mCompositorBridgeParent = nullptr;
   mCompositorWidget = nullptr;
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mUiCompositorControllerChild) {
+    mUiCompositorControllerChild->Destroy();
+    mUiCompositorControllerChild = nullptr;
+  }
+#endif //defined(MOZ_WIDGET_ANDROID)
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -53,17 +53,17 @@ parent:
   async Init(GfxPrefSetting[] prefs,
              GfxVarUpdate[] vars,
              DevicePrefs devicePrefs,
              LayerTreeIdMapping[] mapping);
 
   async InitVsyncBridge(Endpoint<PVsyncBridgeParent> endpoint);
   async InitImageBridge(Endpoint<PImageBridgeParent> endpoint);
   async InitVRManager(Endpoint<PVRManagerParent> endpoint);
-  async InitUiCompositorController(Endpoint<PUiCompositorControllerParent> endpoint);
+  async InitUiCompositorController(uint64_t rootLayerTreeId, Endpoint<PUiCompositorControllerParent> endpoint);
 
   // Called to update a gfx preference or variable.
   async UpdatePref(GfxPrefSetting pref);
   async UpdateVar(GfxVarUpdate var);
 
   // Create a new top-level compositor.
   async NewWidgetCompositor(Endpoint<PCompositorBridgeParent> endpoint,
                             CSSToLayoutDeviceScale scale,
--- a/gfx/ipc/RemoteCompositorSession.cpp
+++ b/gfx/ipc/RemoteCompositorSession.cpp
@@ -5,16 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "RemoteCompositorSession.h"
 #include "mozilla/VsyncDispatcher.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/APZCTreeManagerChild.h"
 #include "mozilla/Unused.h"
 #include "nsBaseWidget.h"
+#if defined(MOZ_WIDGET_ANDROID)
+#include "mozilla/layers/UiCompositorControllerChild.h"
+#endif // defined(MOZ_WIDGET_ANDROID)
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
 using namespace widget;
 
 RemoteCompositorSession::RemoteCompositorSession(nsBaseWidget* aWidget,
@@ -31,16 +34,19 @@ RemoteCompositorSession::RemoteComposito
     mAPZ->SetCompositorSession(this);
   }
 }
 
 RemoteCompositorSession::~RemoteCompositorSession()
 {
   // This should have been shutdown first.
   MOZ_ASSERT(!mCompositorBridgeChild);
+#if defined(MOZ_WIDGET_ANDROID)
+  MOZ_ASSERT(!mUiCompositorControllerChild);
+#endif //defined(MOZ_WIDGET_ANDROID)
 }
 
 void
 RemoteCompositorSession::NotifyDeviceReset(uint64_t aSeqNo)
 {
   MOZ_ASSERT(mWidget);
   mWidget->OnRenderingDeviceReset(aSeqNo);
 }
@@ -102,13 +108,19 @@ RemoteCompositorSession::Shutdown()
   mContentController = nullptr;
   if (mAPZ) {
     mAPZ->SetCompositorSession(nullptr);
   }
   mCompositorBridgeChild->Destroy();
   mCompositorBridgeChild = nullptr;
   mCompositorWidgetDelegate = nullptr;
   mWidget = nullptr;
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mUiCompositorControllerChild) {
+    mUiCompositorControllerChild->Destroy();
+    mUiCompositorControllerChild = nullptr;
+  }
+#endif //defined(MOZ_WIDGET_ANDROID)
   GPUProcessManager::Get()->UnregisterSession(this);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/ipc/VsyncBridgeParent.cpp
+++ b/gfx/ipc/VsyncBridgeParent.cpp
@@ -20,16 +20,17 @@ VsyncBridgeParent::Start(Endpoint<PVsync
 
   return parent;
 }
 
 VsyncBridgeParent::VsyncBridgeParent()
  : mOpen(false)
 {
   MOZ_COUNT_CTOR(VsyncBridgeParent);
+  mCompositorThreadRef = CompositorThreadHolder::GetSingleton();
 }
 
 VsyncBridgeParent::~VsyncBridgeParent()
 {
   MOZ_COUNT_DTOR(VsyncBridgeParent);
 }
 
 void
@@ -70,16 +71,17 @@ VsyncBridgeParent::ShutdownImpl()
     mOpen = false;
   }
 }
 
 void
 VsyncBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   mOpen = false;
+  mCompositorThreadRef = nullptr;
 }
 
 void
 VsyncBridgeParent::DeallocPVsyncBridgeParent()
 {
   Release();
 }
 
--- a/gfx/ipc/VsyncBridgeParent.h
+++ b/gfx/ipc/VsyncBridgeParent.h
@@ -5,16 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef include_gfx_ipc_VsyncBridgeParent_h
 #define include_gfx_ipc_VsyncBridgeParent_h
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/gfx/PVsyncBridgeParent.h"
 
 namespace mozilla {
+namespace layers {
+class CompositorThreadHolder;
+} // namespace layers
+
 namespace gfx {
 
 class VsyncBridgeParent final : public PVsyncBridgeParent
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeParent)
 
   static RefPtr<VsyncBridgeParent> Start(Endpoint<PVsyncBridgeParent>&& aEndpoint);
@@ -29,14 +33,15 @@ private:
   VsyncBridgeParent();
   ~VsyncBridgeParent();
 
   void Open(Endpoint<PVsyncBridgeParent>&& aEndpoint);
   void ShutdownImpl();
 
 private:
   bool mOpen;
+  RefPtr<layers::CompositorThreadHolder> mCompositorThreadRef;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // include_gfx_ipc_VsyncBridgeParent_h
--- a/gfx/layers/LayerAttributes.h
+++ b/gfx/layers/LayerAttributes.h
@@ -39,23 +39,16 @@ public:
   {
   }
 
   //
   // Setters.
   // All set methods return true if values changed, false otherwise.
   //
 
-  bool SetLayerBounds(const gfx::IntRect& aLayerBounds) {
-    if (mLayerBounds.IsEqualEdges(aLayerBounds)) {
-      return false;
-    }
-    mLayerBounds = aLayerBounds;
-    return true;
-  }
   bool SetPostScale(float aXScale, float aYScale) {
     if (mPostXScale == aXScale && mPostYScale == aYScale) {
       return false;
     }
     mPostXScale = aXScale;
     mPostYScale = aYScale;
     return true;
   }
@@ -190,19 +183,16 @@ public:
     }
     return true;
   }
 
   //
   // Getters.
   //
 
-  const gfx::IntRect& LayerBounds() const {
-    return mLayerBounds;
-  }
   float PostXScale() const {
     return mPostXScale;
   }
   float PostYScale() const {
     return mPostYScale;
   }
   uint32_t ContentFlags() const {
     return mContentFlags;
@@ -260,35 +250,33 @@ public:
   const LayerRect& StickyScrollRangeOuter() const {
     return mStickyPositionData->mOuter;
   }
   const LayerRect& StickyScrollRangeInner() const {
     return mStickyPositionData->mInner;
   }
 
   bool operator ==(const SimpleLayerAttributes& aOther) const {
-    return mLayerBounds == aOther.mLayerBounds &&
-           mTransform == aOther.mTransform &&
+    return mTransform == aOther.mTransform &&
            mTransformIsPerspective == aOther.mTransformIsPerspective &&
            mScrolledClip == aOther.mScrolledClip &&
            mPostXScale == aOther.mPostXScale &&
            mPostYScale == aOther.mPostYScale &&
            mContentFlags == aOther.mContentFlags &&
            mOpacity == aOther.mOpacity &&
            mIsFixedPosition == aOther.mIsFixedPosition &&
            mScrollbarTargetContainerId == aOther.mScrollbarTargetContainerId &&
            mScrollbarDirection == aOther.mScrollbarDirection &&
            mScrollbarThumbRatio == aOther.mScrollbarThumbRatio &&
            mIsScrollbarContainer == aOther.mIsScrollbarContainer &&
            mMixBlendMode == aOther.mMixBlendMode &&
            mForceIsolatedGroup == aOther.mForceIsolatedGroup;
   }
 
 private:
-  gfx::IntRect mLayerBounds;
   gfx::Matrix4x4 mTransform;
   bool mTransformIsPerspective;
   Maybe<LayerClip> mScrolledClip;
   float mPostXScale;
   float mPostYScale;
   uint32_t mContentFlags;
   float mOpacity;
   bool mIsFixedPosition;
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -571,18 +571,17 @@ Layer::CalculateScissorRect(const Render
   } else {
     currentClip = aCurrentScissorRect;
   }
 
   if (!clipLayer->GetLocalClipRect()) {
     return currentClip;
   }
 
-  if (GetLocalVisibleRegion().IsEmpty() &&
-      !(AsHostLayer() && AsHostLayer()->NeedToDrawCheckerboarding())) {
+  if (GetLocalVisibleRegion().IsEmpty()) {
     // When our visible region is empty, our parent may not have created the
     // intermediate surface that we would require for correct clipping; however,
     // this does not matter since we are invisible.
     // Make sure we still compute a clip rect if we want to draw checkboarding
     // for this layer, since we want to do this even if the layer is invisible.
     return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
   }
 
@@ -1834,19 +1833,16 @@ Layer::PrintInfo(std::stringstream& aStr
     AppendToString(aStream, GetBaseTransform(), " [transform=", "]");
   }
   if (!GetEffectiveTransform().IsIdentity()) {
     AppendToString(aStream, GetEffectiveTransform(), " [effective-transform=", "]");
   }
   if (GetTransformIsPerspective()) {
     aStream << " [perspective]";
   }
-  if (!GetLayerBounds().IsEmpty()) {
-    AppendToString(aStream, GetLayerBounds(), " [bounds=", "]");
-  }
   if (!mVisibleRegion.IsEmpty()) {
     AppendToString(aStream, mVisibleRegion.ToUnknownRegion(), " [visible=", "]");
   } else {
     aStream << " [not visible]";
   }
   if (!mEventRegions.IsEmpty()) {
     AppendToString(aStream, mEventRegions, " ", "");
   }
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -894,32 +894,16 @@ public:
     if (mSimpleAttrs.SetContentFlags(aFlags)) {
       MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ContentFlags", this));
       MutatedSimple();
     }
   }
 
   /**
    * CONSTRUCTION PHASE ONLY
-   * The union of the bounds of all the display item that got flattened
-   * into this layer. This is intended to be an approximation to the
-   * size of the layer if the nearest scrollable ancestor had an infinitely
-   * large displayport. Computing this more exactly is too expensive,
-   * but this approximation is sufficient for what we need to use it for.
-   */
-  virtual void SetLayerBounds(const gfx::IntRect& aLayerBounds)
-  {
-    if (mSimpleAttrs.SetLayerBounds(aLayerBounds)) {
-      MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) LayerBounds", this));
-      MutatedSimple();
-    }
-  }
-
-  /**
-   * CONSTRUCTION PHASE ONLY
    * Tell this layer which region will be visible. The visible region
    * is a region which contains all the contents of the layer that can
    * actually affect the rendering of the window. It can exclude areas
    * that are covered by opaque contents of other layers, and it can
    * exclude areas where this layer simply contains no content at all.
    * (This can be an overapproximation to the "true" visible region.)
    *
    * There is no general guarantee that drawing outside the bounds of the
@@ -1334,17 +1318,16 @@ public:
 
   // These getters can be used anytime.
   float GetOpacity() { return mSimpleAttrs.Opacity(); }
   gfx::CompositionOp GetMixBlendMode() const { return mSimpleAttrs.MixBlendMode(); }
   const Maybe<ParentLayerIntRect>& GetClipRect() const { return mClipRect; }
   const Maybe<LayerClip>& GetScrolledClip() const { return mSimpleAttrs.ScrolledClip(); }
   Maybe<ParentLayerIntRect> GetScrolledClipRect() const;
   uint32_t GetContentFlags() { return mSimpleAttrs.ContentFlags(); }
-  const gfx::IntRect& GetLayerBounds() const { return mSimpleAttrs.LayerBounds(); }
   const LayerIntRegion& GetVisibleRegion() const { return mVisibleRegion; }
   const ScrollMetadata& GetScrollMetadata(uint32_t aIndex) const;
   const FrameMetrics& GetFrameMetrics(uint32_t aIndex) const;
   uint32_t GetScrollMetadataCount() const { return mScrollMetadata.Length(); }
   const nsTArray<ScrollMetadata>& GetAllScrollMetadata() { return mScrollMetadata; }
   bool HasScrollableFrameMetrics() const;
   bool IsScrollInfoLayer() const;
   const EventRegions& GetEventRegions() const { return mEventRegions; }
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -156,17 +156,16 @@ public:
    * Notify content that the repaint requests have been flushed.
    */
   virtual void NotifyFlushComplete() = 0;
 
   virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) = 0;
 
   virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) {}
   virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) {}
-  virtual void SetScrollingRootContent(bool isRootContent) {}
 
   GeckoContentController() {}
 
   /**
    * Needs to be called on the main thread.
    */
   virtual void Destroy() {}
 
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -174,16 +174,19 @@ APZCTreeManager::APZCTreeManager()
       mApzcTreeLog("apzctree")
 {
   RefPtr<APZCTreeManager> self(this);
   NS_DispatchToMainThread(NS_NewRunnableFunction([self] {
     self->mFlushObserver = new CheckerboardFlushObserver(self);
   }));
   AsyncPanZoomController::InitializeGlobalState();
   mApzcTreeLog.ConditionOnPrefFunction(gfxPrefs::APZPrintTree);
+#if defined(MOZ_WIDGET_ANDROID)
+  mToolbarAnimator = new AndroidDynamicToolbarAnimator();
+#endif // (MOZ_WIDGET_ANDROID)
 }
 
 APZCTreeManager::~APZCTreeManager()
 {
 }
 
 /*static*/ void
 APZCTreeManager::InitializeGlobalState()
@@ -716,16 +719,26 @@ APZCTreeManager::FlushApzRepaints(uint64
 
 nsEventStatus
 APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
                                    ScrollableLayerGuid* aOutTargetGuid,
                                    uint64_t* aOutInputBlockId)
 {
   APZThreadUtils::AssertOnControllerThread();
 
+#if defined(MOZ_WIDGET_ANDROID)
+  MOZ_ASSERT(mToolbarAnimator);
+  nsEventStatus isConsumed = mToolbarAnimator->ReceiveInputEvent(aEvent);
+  // Check if the mToolbarAnimator consumed the event.
+  if (isConsumed == nsEventStatus_eConsumeNoDefault) {
+    APZCTM_LOG("Dynamic toolbar consumed event");
+    return isConsumed;
+  }
+#endif // (MOZ_WIDGET_ANDROID)
+
   // Initialize aOutInputBlockId to a sane value, and then later we overwrite
   // it if the input event goes into a block.
   if (aOutInputBlockId) {
     *aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
   }
   nsEventStatus result = nsEventStatus_eIgnore;
   HitTestResult hitResult = HitNothing;
   switch (aEvent.mInputType) {
@@ -2117,10 +2130,25 @@ APZCTreeManager::CommonAncestor(AsyncPan
       break;
     }
     aApzc1 = aApzc1->GetParent();
     aApzc2 = aApzc2->GetParent();
   }
   return ancestor.forget();
 }
 
+#if defined(MOZ_WIDGET_ANDROID)
+void
+APZCTreeManager::InitializeDynamicToolbarAnimator(const int64_t& aRootLayerTreeId)
+{
+  MOZ_ASSERT(mToolbarAnimator);
+  mToolbarAnimator->Initialize(aRootLayerTreeId);
+}
+
+AndroidDynamicToolbarAnimator*
+APZCTreeManager::GetAndroidDynamicToolbarAnimator()
+{
+  return mToolbarAnimator;
+}
+#endif // defined(MOZ_WIDGET_ANDROID)
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -14,16 +14,20 @@
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/layers/TouchCounter.h"// for TouchCounter
 #include "mozilla/layers/IAPZCTreeManager.h" // for IAPZCTreeManager
 #include "mozilla/Mutex.h"              // for Mutex
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/TimeStamp.h"          // for mozilla::TimeStamp
 #include "nsCOMPtr.h"                   // for already_AddRefed
 
+#if defined(MOZ_WIDGET_ANDROID)
+#include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
+#endif // defined(MOZ_WIDGET_ANDROID)
+
 
 namespace mozilla {
 class MultiTouchInput;
 
 namespace layers {
 
 class Layer;
 class AsyncPanZoomController;
@@ -520,14 +524,23 @@ private:
    * pref). */
   gfx::TreeLog mApzcTreeLog;
 
   class CheckerboardFlushObserver;
   friend class CheckerboardFlushObserver;
   RefPtr<CheckerboardFlushObserver> mFlushObserver;
 
   static float sDPI;
+
+#if defined(MOZ_WIDGET_ANDROID)
+public:
+  void InitializeDynamicToolbarAnimator(const int64_t& aRootLayerTreeId);
+  AndroidDynamicToolbarAnimator* GetAndroidDynamicToolbarAnimator();
+
+private:
+  RefPtr<AndroidDynamicToolbarAnimator> mToolbarAnimator;
+#endif // defined(MOZ_WIDGET_ANDROID)
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_PanZoomController_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/AndroidDynamicToolbarAnimator.cpp
@@ -0,0 +1,882 @@
+/* -*- 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 "mozilla/layers/AndroidDynamicToolbarAnimator.h"
+
+#include <cmath>
+#include "FrameMetrics.h"
+#include "gfxPrefs.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/UiCompositorControllerMessageTypes.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Move.h"
+#include "mozilla/Unused.h"
+
+namespace {
+
+// Internal flags and constants
+static const float   ANIMATION_DURATION  = 0.15f; // How many seconds the complete animation should span
+static const int32_t MOVE_TOOLBAR_DOWN =  1;      // Multiplier to move the toolbar down
+static const int32_t MOVE_TOOLBAR_UP   = -1;      // Multiplier to move the toolbar up
+static const float   SHRINK_FACTOR     = 0.95f;   // Amount to shrink the either the full content for small pages or the amount left
+                                                  // See: IsEnoughPageToHideToolbar()
+} // namespace
+
+namespace mozilla {
+namespace layers {
+
+AndroidDynamicToolbarAnimator::AndroidDynamicToolbarAnimator()
+  : mRootLayerTreeId(0)
+  // Read/Write Compositor Thread, Read only Controller thread
+  , mToolbarState(eToolbarVisible)
+  , mPinnedFlags(0)
+  // Controller thread only
+  , mControllerScrollingRootContent(false)
+  , mControllerDragThresholdReached(false)
+  , mControllerCancelTouchTracking(false)
+  , mControllerDragChangedDirection(false)
+  , mControllerStartTouch(0)
+  , mControllerPreviousTouch(0)
+  , mControllerTotalDistance(0)
+  , mControllerMaxToolbarHeight(0)
+  , mControllerToolbarHeight(0)
+  , mControllerSurfaceHeight(0)
+  , mControllerCompositionHeight(0)
+  , mControllerLastDragDirection(0)
+  , mControllerLastEventTimeStamp(0)
+  , mControllerState(eNothingPending)
+  // Compositor thread only
+  , mCompositorShutdown(false)
+  , mCompositorAnimationDeferred(false)
+  , mCompositorLayersUpdateEnabled(false)
+  , mCompositorAnimationStyle(eAnimate)
+  , mCompositorMaxToolbarHeight(0)
+  , mCompositorToolbarHeight(0)
+  , mCompositorSurfaceHeight(0)
+  , mCompositorAnimationDirection(0)
+  , mCompositorAnimationStartHeight(0)
+{}
+
+void
+AndroidDynamicToolbarAnimator::Initialize(uint64_t aRootLayerTreeId)
+{
+  mRootLayerTreeId = aRootLayerTreeId;
+  RefPtr<UiCompositorControllerParent> uiController = UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
+  MOZ_ASSERT(uiController);
+  uiController->RegisterAndroidDynamicToolbarAnimator(this);
+}
+
+static bool
+GetTouchY(MultiTouchInput& multiTouch, ScreenIntCoord* value)
+{
+  MOZ_ASSERT(value);
+  if (multiTouch.mTouches.Length() == 1) {
+    *value = multiTouch.mTouches[0].mScreenPoint.y;
+    return true;
+  }
+
+  return false;
+}
+
+nsEventStatus
+AndroidDynamicToolbarAnimator::ReceiveInputEvent(InputData& aEvent)
+{
+  MOZ_ASSERT(APZThreadUtils::IsControllerThread());
+
+  // Only process and adjust touch events. Wheel events (aka scroll events) are adjusted in the NativePanZoomController
+  if (aEvent.mInputType != MULTITOUCH_INPUT) {
+    return nsEventStatus_eIgnore;
+  }
+
+  MultiTouchInput& multiTouch = aEvent.AsMultiTouchInput();
+  ScreenIntCoord currentTouch = 0;
+
+  if (mPinnedFlags || !GetTouchY(multiTouch, &currentTouch)) {
+    TranslateTouchEvent(multiTouch);
+    return nsEventStatus_eIgnore;
+  }
+
+  // Only the return value from ProcessTouchDelta should
+  // change status to nsEventStatus_eConsumeNoDefault
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  const StaticToolbarState currentToolbarState = mToolbarState;
+  switch (multiTouch.mType) {
+  case MultiTouchInput::MULTITOUCH_START:
+    mControllerCancelTouchTracking = false;
+    mControllerStartTouch = mControllerPreviousTouch = currentTouch;
+    if (currentToolbarState == eToolbarAnimating) {
+      StopCompositorAnimation();
+    }
+    break;
+  case MultiTouchInput::MULTITOUCH_MOVE: {
+    if ((mControllerState != eAnimationStartPending) &&
+        (mControllerState != eAnimationStopPending) &&
+        (currentToolbarState != eToolbarAnimating) &&
+        !mControllerCancelTouchTracking) {
+
+      ScreenIntCoord delta = currentTouch - mControllerPreviousTouch;
+      mControllerPreviousTouch = currentTouch;
+      mControllerTotalDistance += delta;
+      if (delta != 0) {
+        ScreenIntCoord direction = (delta > 0 ? MOVE_TOOLBAR_DOWN : MOVE_TOOLBAR_UP);
+        if (mControllerLastDragDirection && (direction != mControllerLastDragDirection)) {
+          mControllerDragChangedDirection = true;
+        }
+        mControllerLastDragDirection = direction;
+      }
+      if (IsEnoughPageToHideToolbar(delta)) {
+        // NOTE: gfxPrefs::ToolbarScrollThreshold() returns a percentage as an intt32_t. So multiply it by 0.01f to convert.
+        const uint32_t dragThreshold = Abs(std::lround(0.01f * gfxPrefs::ToolbarScrollThreshold() * mControllerCompositionHeight));
+        if ((Abs(mControllerTotalDistance.value) > dragThreshold) && (delta != 0)) {
+          mControllerDragThresholdReached = true;
+          status = ProcessTouchDelta(currentToolbarState, delta, multiTouch.mTime);
+        }
+      }
+      mControllerLastEventTimeStamp = multiTouch.mTime;
+    }
+    break;
+  }
+  case MultiTouchInput::MULTITOUCH_END:
+  case MultiTouchInput::MULTITOUCH_CANCEL:
+    HandleTouchEnd(currentToolbarState, currentTouch);
+    break;
+  default:
+    break;
+  }
+
+  TranslateTouchEvent(multiTouch);
+
+  return status;
+}
+
+void
+AndroidDynamicToolbarAnimator::SetMaxToolbarHeight(ScreenIntCoord aHeight)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  UpdateControllerToolbarHeight(aHeight, aHeight);
+  mCompositorMaxToolbarHeight = aHeight;
+  UpdateCompositorToolbarHeight(aHeight);
+}
+
+void
+AndroidDynamicToolbarAnimator::SetPinned(bool aPinned, int32_t aReason)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  MOZ_ASSERT(aReason < 32);
+  uint32_t bit = 0x01 << aReason;
+  uint32_t current = mPinnedFlags;
+  if (aPinned) {
+    mPinnedFlags = current | bit;
+  } else {
+    mPinnedFlags = current & (~bit);
+  }
+}
+
+ScreenIntCoord
+AndroidDynamicToolbarAnimator::GetMaxToolbarHeight() const
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  return mCompositorMaxToolbarHeight;
+}
+
+ScreenIntCoord
+AndroidDynamicToolbarAnimator::GetCurrentToolbarHeight() const
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  return mCompositorToolbarHeight;
+}
+
+ScreenIntCoord
+AndroidDynamicToolbarAnimator::GetCurrentSurfaceHeight() const
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  return mCompositorSurfaceHeight;
+}
+
+ScreenIntCoord
+AndroidDynamicToolbarAnimator::GetCompositionHeight() const
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  return mCompositorCompositionSize.height;
+}
+
+bool
+AndroidDynamicToolbarAnimator::SetCompositionSize(ScreenIntSize aSize)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  if (mCompositorCompositionSize == aSize) {
+    return false;
+  }
+
+  ScreenIntCoord prevHeight = mCompositorCompositionSize.height;
+  mCompositorCompositionSize = aSize;
+
+  if (prevHeight != aSize.height) {
+    UpdateControllerCompositionHeight(aSize.height);
+    UpdateFixedLayerMargins();
+  }
+
+  return true;
+}
+
+void
+AndroidDynamicToolbarAnimator::SetScrollingRootContent()
+{
+  MOZ_ASSERT(APZThreadUtils::IsControllerThread());
+  mControllerScrollingRootContent = true;
+}
+
+void
+AndroidDynamicToolbarAnimator::ToolbarAnimatorMessageFromUI(int32_t aMessage)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  switch(aMessage) {
+  case STATIC_TOOLBAR_NEEDS_UPDATE:
+    break;
+  case STATIC_TOOLBAR_READY:
+    break;
+  case TOOLBAR_HIDDEN:
+    // If the toolbar is animating, then it is already unlocked.
+    if (mToolbarState != eToolbarAnimating) {
+      mToolbarState = eToolbarUnlocked;
+      if (mCompositorAnimationDeferred) {
+        StartCompositorAnimation(mCompositorAnimationDirection, mCompositorAnimationStyle, mCompositorToolbarHeight);
+      }
+    } else {
+      // The compositor is already animating the toolbar so no need to defer.
+      mCompositorAnimationDeferred = false;
+    }
+    break;
+  case TOOLBAR_VISIBLE:
+    mToolbarState = eToolbarVisible;
+    break;
+  case TOOLBAR_SHOW:
+    break;
+  case FIRST_PAINT:
+    break;
+  case REQUEST_SHOW_TOOLBAR_IMMEDIATELY:
+    NotifyControllerPendingAnimation(MOVE_TOOLBAR_DOWN, eImmediate);
+    break;
+  case REQUEST_SHOW_TOOLBAR_ANIMATED:
+    NotifyControllerPendingAnimation(MOVE_TOOLBAR_DOWN, eAnimate);
+    break;
+  case REQUEST_HIDE_TOOLBAR_IMMEDIATELY:
+    NotifyControllerPendingAnimation(MOVE_TOOLBAR_UP, eImmediate);
+    break;
+  case REQUEST_HIDE_TOOLBAR_ANIMATED:
+    NotifyControllerPendingAnimation(MOVE_TOOLBAR_UP, eAnimate);
+    break;
+  default:
+    break;
+  }
+}
+
+bool
+AndroidDynamicToolbarAnimator::UpdateAnimation(const TimeStamp& aCurrentFrame)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  if (mToolbarState != eToolbarAnimating) {
+    return false;
+  }
+
+  bool continueAnimating = true;
+
+  if (mCompositorAnimationStyle == eImmediate) {
+    if (mCompositorAnimationDirection == MOVE_TOOLBAR_DOWN) {
+      mCompositorToolbarHeight = mCompositorMaxToolbarHeight;
+    } else if (mCompositorAnimationDirection == MOVE_TOOLBAR_UP) {
+      mCompositorToolbarHeight = 0;
+    }
+  } else if (mCompositorAnimationStyle == eAnimate) {
+    const float rate = ((float)mCompositorMaxToolbarHeight) / ANIMATION_DURATION;
+    float deltaTime = (aCurrentFrame - mCompositorAnimationStartTimeStamp).ToSeconds();
+    // This animation was started in the future!
+    if (deltaTime < 0.0f) {
+      deltaTime = 0.0f;
+    }
+    mCompositorToolbarHeight = mCompositorAnimationStartHeight + ((int32_t)(rate * deltaTime) * mCompositorAnimationDirection);
+  }
+
+  if ((mCompositorAnimationDirection == MOVE_TOOLBAR_DOWN) && (mCompositorToolbarHeight >= mCompositorMaxToolbarHeight)) {
+    continueAnimating = false;
+    mToolbarState = eToolbarVisible;
+    PostMessage(TOOLBAR_SHOW);
+    mCompositorToolbarHeight = mCompositorMaxToolbarHeight;
+  } else if ((mCompositorAnimationDirection == MOVE_TOOLBAR_UP) && (mCompositorToolbarHeight <= 0)) {
+    continueAnimating = false;
+    mToolbarState = eToolbarUnlocked;
+    mCompositorToolbarHeight = 0;
+  }
+
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
+  if (parent) {
+    AsyncCompositionManager* manager = parent->GetCompositionManager(nullptr);
+    if (manager) {
+      manager->SetFixedLayerMarginsBottom(GetFixedLayerMarginsBottom());
+    }
+  }
+
+  if (!continueAnimating) {
+    NotifyControllerAnimationStopped(mCompositorToolbarHeight);
+  }
+
+  return continueAnimating;
+}
+
+void
+AndroidDynamicToolbarAnimator::FirstPaint()
+{
+  PostMessage(FIRST_PAINT);
+}
+
+void
+AndroidDynamicToolbarAnimator::UpdateRootFrameMetrics(const FrameMetrics& aMetrics)
+{
+  CSSToScreenScale scale = ViewTargetAs<ScreenPixel>(aMetrics.GetZoom().ToScaleFactor(),
+                                                     PixelCastJustification::ScreenIsParentLayerForRoot);
+  ScreenPoint scrollOffset = aMetrics.GetScrollOffset() * scale;
+  CSSRect cssPageRect = aMetrics.GetScrollableRect();
+
+  UpdateFrameMetrics(scrollOffset, scale, cssPageRect);
+}
+
+// Layers updates are need by Robocop test which enables them
+void
+AndroidDynamicToolbarAnimator::EnableLayersUpdateNotifications(bool aEnable)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  mCompositorLayersUpdateEnabled = aEnable;
+}
+
+void
+AndroidDynamicToolbarAnimator::NotifyLayersUpdated()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  if (mCompositorLayersUpdateEnabled) {
+    PostMessage(LAYERS_UPDATED);
+  }
+}
+
+void
+AndroidDynamicToolbarAnimator::AdoptToolbarPixels(mozilla::ipc::Shmem&& aMem, const ScreenIntSize& aSize)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  mCompositorToolbarPixels = Some(Move(aMem));
+  mCompositorToolbarPixelsSize = aSize;
+}
+
+Effect*
+AndroidDynamicToolbarAnimator::GetToolbarEffect(CompositorOGL* gl)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  // if the compositor has shutdown, do not create any new rendering objects.
+  if (mCompositorShutdown) {
+    return nullptr;
+  }
+
+  if (mCompositorToolbarPixels) {
+    RefPtr<DataSourceSurface> surface = Factory::CreateWrappingDataSourceSurface(
+        mCompositorToolbarPixels.ref().get<uint8_t>(),
+        mCompositorToolbarPixelsSize.width * 4,
+        IntSize(mCompositorToolbarPixelsSize.width, mCompositorToolbarPixelsSize.height),
+        gfx::SurfaceFormat::B8G8R8A8);
+
+    if (!mCompositorToolbarTexture) {
+      mCompositorToolbarTexture = gl->CreateDataTextureSource();
+      mCompositorToolbarEffect = nullptr;
+    }
+
+    if (!mCompositorToolbarTexture->Update(surface)) {
+      // Upload failed!
+      mCompositorToolbarTexture = nullptr;
+    }
+
+    RefPtr<UiCompositorControllerParent> uiController = UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
+    uiController->DeallocShmem(mCompositorToolbarPixels.ref());
+    mCompositorToolbarPixels.reset();
+    // Send notification that texture is ready after the current composition has completed.
+    if (mCompositorToolbarTexture) {
+      CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod(this, &AndroidDynamicToolbarAnimator::PostToolbarReady));
+    }
+  }
+
+  if (mCompositorToolbarTexture) {
+    if (!mCompositorToolbarEffect) {
+      mCompositorToolbarEffect = new EffectRGB(mCompositorToolbarTexture, true, SamplingFilter::LINEAR);
+    }
+
+    float ratioVisible = (float)mCompositorToolbarHeight / (float)mCompositorMaxToolbarHeight;
+    mCompositorToolbarEffect->mTextureCoords.y = 1.0f - ratioVisible;
+    mCompositorToolbarEffect->mTextureCoords.height = ratioVisible;
+  }
+
+  return mCompositorToolbarEffect.get();
+}
+
+void
+AndroidDynamicToolbarAnimator::Shutdown()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  mCompositorShutdown = true;
+  mCompositorToolbarEffect = nullptr;
+  mCompositorToolbarTexture = nullptr;
+  if (mCompositorToolbarPixels) {
+    RefPtr<UiCompositorControllerParent> uiController = UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
+    uiController->DeallocShmem(mCompositorToolbarPixels.ref());
+    mCompositorToolbarPixels.reset();
+  }
+}
+
+nsEventStatus
+AndroidDynamicToolbarAnimator::ProcessTouchDelta(StaticToolbarState aCurrentToolbarState, ScreenIntCoord aDelta, uint32_t aTimeStamp)
+{
+  MOZ_ASSERT(APZThreadUtils::IsControllerThread());
+  nsEventStatus status = nsEventStatus_eIgnore;
+
+  const bool tryingToHideToolbar = aDelta < 0;
+
+  if (tryingToHideToolbar && !mControllerScrollingRootContent) {
+    // This prevent the toolbar from hiding if a subframe is being scrolled up.
+    // The toolbar will always become visible regardless what is being scrolled down.
+    return status;
+  }
+
+  if (aCurrentToolbarState == eToolbarVisible) {
+    if (tryingToHideToolbar && (mControllerState != eUnlockPending)) {
+      PostMessage(STATIC_TOOLBAR_NEEDS_UPDATE);
+      mControllerState = eUnlockPending;
+    }
+    return status;
+  }
+
+  if (aCurrentToolbarState != eToolbarUnlocked) {
+    return status;
+  }
+
+  if ((mControllerState != eUnlockPending) && (mControllerState != eNothingPending)) {
+    return status;
+  }
+
+  mControllerState = eNothingPending;
+  if ((tryingToHideToolbar && (mControllerToolbarHeight > 0)) ||
+      (!tryingToHideToolbar && (mControllerToolbarHeight < mControllerMaxToolbarHeight))) {
+    ScreenIntCoord deltaRemainder = 0;
+    mControllerToolbarHeight += aDelta;
+    if (tryingToHideToolbar && (mControllerToolbarHeight <= 0 )) {
+      deltaRemainder = mControllerToolbarHeight;
+      mControllerToolbarHeight = 0;
+    } else if (!tryingToHideToolbar && (mControllerToolbarHeight >= mControllerMaxToolbarHeight)) {
+      deltaRemainder = mControllerToolbarHeight - mControllerMaxToolbarHeight;
+      mControllerToolbarHeight = mControllerMaxToolbarHeight;
+      PostMessage(TOOLBAR_SHOW);
+      mControllerState = eShowPending;
+    }
+
+    UpdateCompositorToolbarHeight(mControllerToolbarHeight);
+    RequestComposite();
+    // If there was no delta left over, the event was completely consumed.
+    if (deltaRemainder == 0) {
+      status = nsEventStatus_eConsumeNoDefault;
+    }
+
+    uint32_t timeDelta = aTimeStamp - mControllerLastEventTimeStamp;
+    if (mControllerLastEventTimeStamp && timeDelta && aDelta) {
+      float speed = -(float)aDelta / (float)timeDelta;
+      CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
+      if (parent) {
+        parent->GetAPZCTreeManager()->ProcessTouchVelocity(aTimeStamp, speed);
+      }
+    }
+  }
+
+  return status;
+}
+
+void
+AndroidDynamicToolbarAnimator::HandleTouchEnd(StaticToolbarState aCurrentToolbarState, ScreenIntCoord aCurrentTouch)
+{
+  MOZ_ASSERT(APZThreadUtils::IsControllerThread());
+  int32_t direction = mControllerLastDragDirection;
+  mControllerLastDragDirection = 0;
+  bool isRoot = mControllerScrollingRootContent;
+  mControllerScrollingRootContent = false;
+  bool dragChangedDirection = mControllerDragChangedDirection;
+  mControllerDragChangedDirection = false;
+
+  // If the drag direction changed and the toolbar is partially visible, hide in the direction with the least distance to travel.
+  if (dragChangedDirection &&
+      (mControllerToolbarHeight != mControllerMaxToolbarHeight) &&
+      (mControllerToolbarHeight != 0)) {
+    direction = ((float)mControllerToolbarHeight / (float)mControllerMaxToolbarHeight) < 0.5f ? MOVE_TOOLBAR_UP : MOVE_TOOLBAR_DOWN;
+  }
+
+  // If the last touch didn't have a drag direction, use start of touch to find direction
+  if (!direction) {
+    direction = ((aCurrentTouch - mControllerStartTouch) > 0 ? MOVE_TOOLBAR_DOWN : MOVE_TOOLBAR_UP);
+    // If there still isn't a direction, default to show just to be safe
+    if (!direction) {
+      direction = MOVE_TOOLBAR_DOWN;
+    }
+  }
+  bool dragThresholdReached = mControllerDragThresholdReached;
+  mControllerStartTouch = 0;
+  mControllerPreviousTouch = 0;
+  mControllerTotalDistance = 0;
+  mControllerDragThresholdReached = false;
+  mControllerLastEventTimeStamp = 0;
+
+  // Received a UI thread request to show or hide the snapshot during a touch.
+  // This overrides the touch event so just return
+  if (mControllerCancelTouchTracking) {
+    mControllerCancelTouchTracking = false;
+    return;
+  }
+
+  // Don't animate up if not scrolling root content. Even though ShowToolbarIfNotVisible checks if
+  // snapshot toolbar is completely visible before showing, we don't want to enter this if block
+  // if the snapshot toolbar isn't completely visible to avoid early return.
+  if (!isRoot &&
+      ((direction == MOVE_TOOLBAR_UP) && (mControllerToolbarHeight == mControllerMaxToolbarHeight))) {
+    ShowToolbarIfNotVisible(aCurrentToolbarState);
+    return;
+  }
+
+  // The page is either too small or too close to the end to animate
+  if (!IsEnoughPageToHideToolbar(direction)) {
+    if (mControllerToolbarHeight == mControllerMaxToolbarHeight) {
+      ShowToolbarIfNotVisible(aCurrentToolbarState);
+      return;
+    } else if (mControllerToolbarHeight != 0) {
+      // The snapshot is partially visible but there is not enough page
+      // to hide the snapshot so make it visible by moving it down
+      direction = MOVE_TOOLBAR_DOWN;
+    }
+  }
+
+  // This makes sure the snapshot is not left partially visible at the end of a touch.
+  if ((aCurrentToolbarState != eToolbarAnimating) && dragThresholdReached) {
+    if (((direction == MOVE_TOOLBAR_DOWN) && (mControllerToolbarHeight != mControllerMaxToolbarHeight)) ||
+        ((direction == MOVE_TOOLBAR_UP) && (mControllerToolbarHeight != 0))) {
+      StartCompositorAnimation(direction, eAnimate, mControllerToolbarHeight);
+    }
+  } else {
+    ShowToolbarIfNotVisible(aCurrentToolbarState);
+  }
+}
+
+void
+AndroidDynamicToolbarAnimator::PostMessage(int32_t aMessage) {
+  RefPtr<UiCompositorControllerParent> uiController = UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
+  MOZ_ASSERT(uiController);
+  // ToolbarAnimatorMessageFromCompositor may be called from any thread.
+  uiController->ToolbarAnimatorMessageFromCompositor(aMessage);
+}
+
+void
+AndroidDynamicToolbarAnimator::UpdateCompositorToolbarHeight(ScreenIntCoord aHeight)
+{
+  if (!CompositorThreadHolder::IsInCompositorThread()) {
+    CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod<ScreenIntCoord>(this, &AndroidDynamicToolbarAnimator::UpdateCompositorToolbarHeight, aHeight));
+    return;
+  }
+
+  mCompositorToolbarHeight = aHeight;
+  UpdateFixedLayerMargins();
+}
+
+void
+AndroidDynamicToolbarAnimator::UpdateControllerToolbarHeight(ScreenIntCoord aHeight, ScreenIntCoord aMaxHeight)
+{
+  if (!APZThreadUtils::IsControllerThread()) {
+    APZThreadUtils::RunOnControllerThread(NewRunnableMethod<ScreenIntCoord, ScreenIntCoord>(this, &AndroidDynamicToolbarAnimator::UpdateControllerToolbarHeight, aHeight, aMaxHeight));
+    return;
+  }
+
+  mControllerToolbarHeight = aHeight;
+  if (aMaxHeight >= 0) {
+    mControllerMaxToolbarHeight = aMaxHeight;
+  }
+}
+
+void
+AndroidDynamicToolbarAnimator::UpdateControllerSurfaceHeight(ScreenIntCoord aHeight)
+{
+  if (!APZThreadUtils::IsControllerThread()) {
+    APZThreadUtils::RunOnControllerThread(NewRunnableMethod<ScreenIntCoord>(this, &AndroidDynamicToolbarAnimator::UpdateControllerSurfaceHeight, aHeight));
+    return;
+  }
+
+  mControllerSurfaceHeight = aHeight;
+}
+
+void
+AndroidDynamicToolbarAnimator::UpdateControllerCompositionHeight(ScreenIntCoord aHeight)
+{
+  if (!APZThreadUtils::IsControllerThread()) {
+    APZThreadUtils::RunOnControllerThread(NewRunnableMethod<ScreenIntCoord>(this, &AndroidDynamicToolbarAnimator::UpdateControllerCompositionHeight, aHeight));
+    return;
+  }
+
+  mControllerCompositionHeight = aHeight;
+}
+
+// Ensures the margin for the fixed layers match the position of the toolbar
+void
+AndroidDynamicToolbarAnimator::UpdateFixedLayerMargins()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
+  if (parent) {
+    ScreenIntCoord surfaceHeight = parent->GetEGLSurfaceSize().height;
+    if (surfaceHeight != mCompositorSurfaceHeight) {
+      mCompositorSurfaceHeight = surfaceHeight;
+      UpdateControllerSurfaceHeight(mCompositorSurfaceHeight);
+    }
+    AsyncCompositionManager* manager = parent->GetCompositionManager(nullptr);
+    if (manager) {
+      manager->SetFixedLayerMarginsBottom(GetFixedLayerMarginsBottom());
+    }
+  }
+}
+
+void
+AndroidDynamicToolbarAnimator::NotifyControllerPendingAnimation(int32_t aDirection, AnimationStyle aAnimationStyle)
+{
+  if (!APZThreadUtils::IsControllerThread()) {
+    APZThreadUtils::RunOnControllerThread(NewRunnableMethod<int32_t, AnimationStyle>(this, &AndroidDynamicToolbarAnimator::NotifyControllerPendingAnimation, aDirection, aAnimationStyle));
+    return;
+  }
+
+  mControllerCancelTouchTracking = true;
+
+  // If the toolbar is already where it needs to be, just abort the request.
+  if (((mControllerToolbarHeight == mControllerMaxToolbarHeight) && (aDirection == MOVE_TOOLBAR_DOWN)) ||
+      ((mControllerToolbarHeight == 0) && (aDirection == MOVE_TOOLBAR_UP))) {
+    // We received a show request but the real toolbar is hidden, so tell it to show now.
+    if ((aDirection == MOVE_TOOLBAR_DOWN) && (mToolbarState == eToolbarUnlocked)) {
+      PostMessage(TOOLBAR_SHOW);
+    }
+    return;
+  }
+
+  // NOTE: StartCompositorAnimation will set mControllerState to eAnimationStartPending
+  StartCompositorAnimation(aDirection, aAnimationStyle, mControllerToolbarHeight);
+  MOZ_ASSERT(mControllerState == eAnimationStartPending);
+}
+
+void
+AndroidDynamicToolbarAnimator::StartCompositorAnimation(int32_t aDirection, AnimationStyle aAnimationStyle, ScreenIntCoord aHeight)
+{
+  if (!CompositorThreadHolder::IsInCompositorThread()) {
+    mControllerState = eAnimationStartPending;
+    CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod<int32_t, AnimationStyle, ScreenIntCoord>(
+      this, &AndroidDynamicToolbarAnimator::StartCompositorAnimation, aDirection, aAnimationStyle, aHeight));
+    return;
+  }
+
+  MOZ_ASSERT(aDirection == MOVE_TOOLBAR_UP || aDirection == MOVE_TOOLBAR_DOWN);
+
+  const StaticToolbarState currentToolbarState = mToolbarState;
+  mCompositorAnimationDirection = aDirection;
+  mCompositorAnimationStartHeight = mCompositorToolbarHeight = aHeight;
+  mCompositorAnimationStyle = aAnimationStyle;
+  // If the snapshot is not unlocked, request the UI thread update the snapshot
+  // and defer animation until it has been unlocked
+  if (currentToolbarState != eToolbarUnlocked) {
+    mCompositorAnimationDeferred = true;
+    PostMessage(STATIC_TOOLBAR_NEEDS_UPDATE);
+  } else {
+    // Toolbar is unlocked so animation may begin immediately
+    mCompositorAnimationDeferred = false;
+    mToolbarState = eToolbarAnimating;
+    // Let the controller know we starting an animation so it may clear the AnimationStartPending flag.
+    NotifyControllerAnimationStarted();
+    // Kick the compositor to start the animation
+    CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
+    if (parent) {
+      mCompositorAnimationStartTimeStamp = parent->GetAPZCTreeManager()->GetFrameTime();
+    }
+    RequestComposite();
+  }
+}
+
+void
+AndroidDynamicToolbarAnimator::NotifyControllerAnimationStarted()
+{
+  if (!APZThreadUtils::IsControllerThread()) {
+    APZThreadUtils::RunOnControllerThread(NewRunnableMethod(this, &AndroidDynamicToolbarAnimator::NotifyControllerAnimationStarted));
+    return;
+  }
+
+  // It is possible there was a stop request after the start request so only set to NothingPending
+  // if start is what were are still waiting for.
+  if (mControllerState == eAnimationStartPending) {
+    mControllerState = eNothingPending;
+  }
+}
+
+void
+AndroidDynamicToolbarAnimator::StopCompositorAnimation()
+{
+  if (!CompositorThreadHolder::IsInCompositorThread()) {
+    mControllerState = eAnimationStopPending;
+    CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod(this, &AndroidDynamicToolbarAnimator::StopCompositorAnimation));
+    return;
+  }
+
+  mToolbarState = eToolbarUnlocked;
+  NotifyControllerAnimationStopped(mCompositorToolbarHeight);
+}
+
+void
+AndroidDynamicToolbarAnimator::NotifyControllerAnimationStopped(ScreenIntCoord aHeight)
+{
+  if (!APZThreadUtils::IsControllerThread()) {
+    APZThreadUtils::RunOnControllerThread(NewRunnableMethod<ScreenIntCoord>(this, &AndroidDynamicToolbarAnimator::NotifyControllerAnimationStopped, aHeight));
+    return;
+  }
+
+  if (mControllerState == eAnimationStopPending) {
+    mControllerState = eNothingPending;
+  }
+
+  mControllerToolbarHeight = aHeight;
+}
+
+void
+AndroidDynamicToolbarAnimator::RequestComposite()
+{
+  if (!CompositorThreadHolder::IsInCompositorThread()) {
+    CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod(this, &AndroidDynamicToolbarAnimator::RequestComposite));
+    return;
+  }
+
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
+  if (parent) {
+    AsyncCompositionManager* manager = parent->GetCompositionManager(nullptr);
+    if (manager) {
+      manager->SetFixedLayerMarginsBottom(GetFixedLayerMarginsBottom());
+      parent->Invalidate();
+      parent->ScheduleComposition();
+    }
+  }
+}
+
+void
+AndroidDynamicToolbarAnimator::PostToolbarReady()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  RequestComposite();
+  // Notify the UI thread the static toolbar is being rendered so the real
+  // toolbar needs to be hidden. Once the TOOLBAR_HIDDEN message is
+  // received, a pending animation may start or the toolbar snapshot may be
+  // translated.
+  PostMessage(STATIC_TOOLBAR_READY);
+  if (mToolbarState != eToolbarAnimating) {
+    mToolbarState = eToolbarUpdated;
+  } else {
+    // The compositor is already animating the toolbar so no need to defer.
+    mCompositorAnimationDeferred = false;
+  }
+}
+
+void
+AndroidDynamicToolbarAnimator::UpdateFrameMetrics(ScreenPoint aScrollOffset,
+                                                  CSSToScreenScale aScale,
+                                                  CSSRect aCssPageRect)
+{
+  if (!APZThreadUtils::IsControllerThread()) {
+    APZThreadUtils::RunOnControllerThread(NewRunnableMethod<ScreenPoint, CSSToScreenScale, CSSRect>(this, &AndroidDynamicToolbarAnimator::UpdateFrameMetrics, aScrollOffset, aScale, aCssPageRect));
+    return;
+  }
+
+  if (mControllerFrameMetrics.Update(aScrollOffset, aScale, aCssPageRect)) {
+    RefPtr<UiCompositorControllerParent> uiController = UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
+    MOZ_ASSERT(uiController);
+    CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod<ScreenPoint, CSSToScreenScale, CSSRect>(
+                                               uiController, &UiCompositorControllerParent::SendRootFrameMetrics,
+                                               aScrollOffset, aScale, aCssPageRect));
+  }
+}
+
+bool
+AndroidDynamicToolbarAnimator::IsEnoughPageToHideToolbar(ScreenIntCoord delta)
+{
+  MOZ_ASSERT(APZThreadUtils::IsControllerThread());
+  // The toolbar will only hide if dragging up so ignore positive deltas from dragging down.
+  if (delta >= 0) {
+    return true;
+  }
+
+  // if the page is 1) too small or 2) too close to the bottom, then the toolbar can not be hidden
+  if (((float)mControllerSurfaceHeight >= (mControllerFrameMetrics.mPageRect.YMost() * SHRINK_FACTOR)) ||
+      ((float)mControllerSurfaceHeight >= ((mControllerFrameMetrics.mPageRect.YMost() - mControllerFrameMetrics.mScrollOffset.y) * SHRINK_FACTOR))) {
+    return false;
+  }
+
+  return true;
+}
+
+void
+AndroidDynamicToolbarAnimator::ShowToolbarIfNotVisible(StaticToolbarState aCurrentToolbarState)
+{
+  MOZ_ASSERT(APZThreadUtils::IsControllerThread());
+  if ((mControllerToolbarHeight == mControllerMaxToolbarHeight) &&
+      (aCurrentToolbarState != eToolbarVisible) &&
+      (mControllerState != eShowPending)) {
+    PostMessage(TOOLBAR_SHOW);
+  }
+}
+
+bool
+AndroidDynamicToolbarAnimator::FrameMetricsState::Update(const ScreenPoint& aScrollOffset,
+                                                         const CSSToScreenScale& aScale,
+                                                         const CSSRect& aCssPageRect)
+{
+  if (!FuzzyEqualsMultiplicative(aScrollOffset.x, mScrollOffset.x) ||
+      !FuzzyEqualsMultiplicative(aScrollOffset.y, mScrollOffset.y) ||
+      !FuzzyEqualsMultiplicative(aScale.scale, mScale.scale) ||
+      !FuzzyEqualsMultiplicative(aCssPageRect.width, mCssPageRect.width) ||
+      !FuzzyEqualsMultiplicative(aCssPageRect.height, mCssPageRect.height) ||
+      !FuzzyEqualsMultiplicative(aCssPageRect.x, mCssPageRect.x) ||
+      !FuzzyEqualsMultiplicative(aCssPageRect.y, mCssPageRect.y)) {
+    mScrollOffset = aScrollOffset;
+    mScale = aScale;
+    mCssPageRect = aCssPageRect;
+    mPageRect = mCssPageRect * mScale;
+    return true;
+  }
+
+  return false;
+}
+
+void
+AndroidDynamicToolbarAnimator::TranslateTouchEvent(MultiTouchInput& aTouchEvent)
+{
+  MOZ_ASSERT(APZThreadUtils::IsControllerThread());
+  if (mControllerToolbarHeight > 0) {
+    aTouchEvent.Translate(ScreenPoint(0.0f, -(float)mControllerToolbarHeight));
+  }
+}
+
+ScreenIntCoord
+AndroidDynamicToolbarAnimator::GetFixedLayerMarginsBottom()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  return mCompositorToolbarHeight - (mCompositorSurfaceHeight - mCompositorCompositionSize.height);
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/AndroidDynamicToolbarAnimator.h
@@ -0,0 +1,212 @@
+/* -*- 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_AndroidDynamicToolbarAnimator_h_
+#define mozilla_layers_AndroidDynamicToolbarAnimator_h_
+
+#include "InputData.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/layers/Effects.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/TimeStamp.h"
+#include "nsISupports.h"
+
+namespace mozilla {
+namespace layers {
+
+struct FrameMetrics;
+class CompositorOGL;
+
+/*
+ * The AndroidDynamicToolbarAnimator is responsible for calculating the position
+ * and drawing the static snapshot of the toolbar. The animator lives in both
+ * compositor thread and controller thread. It intercepts input events in the
+ * controller thread and determines if the intercepted touch events will cause
+ * the toolbar to move or be animated. Once the proper conditions have been met,
+ * the animator requests that the UI thread send a static snapshot of the current
+ * state of the toolbar. Once the animator has received the snapshot and
+ * converted it into an OGL texture, the animator notifies the UI thread it is
+ * ready. The UI thread will then hide the real toolbar and notify the animator
+ * that it is unlocked and may begin translating the snapshot. The
+ * animator is responsible for rendering the snapshot until it receives a message
+ * to show the toolbar or touch events cause the snapshot to be completely visible.
+ * When the snapshot is made completely visible the animator locks the static
+ * toolbar and sends a message to the UI thread to show the real toolbar and the
+ * whole process may start again. The toolbar height is in screen pixels. The
+ * toolbar height will be at max height when completely visible and at 0 when
+ * completely hidden. The toolbar is only locked when it is completely visible.
+ * The animator must ask for an update of the toolbar snapshot and that the real
+ * toolbar be hidden in order to unlock the static snapshot and begin translating it.
+ *
+ * See Bug 1335895 for more details.
+ */
+
+class AndroidDynamicToolbarAnimator {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AndroidDynamicToolbarAnimator);
+  AndroidDynamicToolbarAnimator();
+  void Initialize(uint64_t aRootLayerTreeId);
+  // Used to intercept events to determine if the event affects the toolbar.
+  // May apply translation to touch events if the toolbar is visible.
+  // Returns nsEventStatus_eIgnore when the event is not consumed and
+  // nsEventStatus_eConsumeNoDefault when the event was used to translate the
+  // toolbar.
+  nsEventStatus ReceiveInputEvent(InputData& aEvent);
+  void SetMaxToolbarHeight(ScreenIntCoord aHeight);
+  // When a pinned reason is set to true, the animator will prevent
+  // touch events from altering the height of the toolbar. All pinned
+  // reasons must be cleared before touch events will affect the toolbar.
+  // Animation requests from the UI thread are still honored even if any
+  // pin reason is set. This allows the UI thread to pin the toolbar for
+  // full screen and then request the animator hide the toolbar.
+  void SetPinned(bool aPinned, int32_t aReason);
+  // returns maximum number of Y device pixels the toolbar will cover when fully visible.
+  ScreenIntCoord GetMaxToolbarHeight() const;
+  // returns the current number of Y device pixels the toolbar is currently showing.
+  ScreenIntCoord GetCurrentToolbarHeight() const;
+  // returns the height in device pixels of the current Android surface used to display content and the toolbar.
+  // This will only change when the surface provided by the system actually changes size such as when
+  // the device is rotated or the virtual keyboard is made visible.
+  ScreenIntCoord GetCurrentSurfaceHeight() const;
+  // This is the height in device pixels of the root document's content. While the toolbar is being hidden or
+  // shown, the content may extend beyond the bottom of the surface until the toolbar is completely
+  // visible or hidden.
+  ScreenIntCoord GetCompositionHeight() const;
+  // Returns true if the composition size has changed from the last time it was set.
+  bool SetCompositionSize(ScreenIntSize aSize);
+  // Called to signal that root content is being scrolled. This prevents sub scroll frames from
+  // affecting the toolbar when being scrolled up. The idea is a scrolling down will always
+  // show the toolbar while scrolling up will only hide the toolbar if it is the root content
+  // being scrolled.
+  void SetScrollingRootContent();
+  void ToolbarAnimatorMessageFromUI(int32_t aMessage);
+  // Returns true if the animation will continue and false if it has completed.
+  bool UpdateAnimation(const TimeStamp& aCurrentFrame);
+  // Called to signify the first paint has occurred.
+  void FirstPaint();
+  // Called whenever the root document's FrameMetrics have reached a steady state.
+  void UpdateRootFrameMetrics(const FrameMetrics& aMetrics);
+  // When aEnable is set to true, it informs the animator that the UI thread expects to
+  // be notified when the layer tree  has been updated. Enabled currently by robocop tests.
+  void EnableLayersUpdateNotifications(bool aEnable);
+  // Called when a layer has been updated so the UI thread may be notified if necessary.
+  void NotifyLayersUpdated();
+  // Adopts the Shmem containing the toolbar snapshot sent from the UI thread.
+  // The AndroidDynamicToolbarAnimator is responsible for deallocating the Shmem when
+  // it is done being used.
+  void AdoptToolbarPixels(mozilla::ipc::Shmem&& aMem, const ScreenIntSize& aSize);
+  // Returns the Effect object used by the compositor to render the toolbar snapshot.
+  Effect* GetToolbarEffect(CompositorOGL* gl);
+  void Shutdown();
+
+protected:
+  enum StaticToolbarState {
+    eToolbarVisible,
+    eToolbarUpdated,
+    eToolbarUnlocked,
+    eToolbarAnimating
+  };
+  enum ControllerThreadState {
+    eNothingPending,
+    eShowPending,
+    eUnlockPending,
+    eAnimationStartPending,
+    eAnimationStopPending
+  };
+  enum AnimationStyle {
+    eImmediate,
+    eAnimate
+  };
+
+  ~AndroidDynamicToolbarAnimator(){}
+  nsEventStatus ProcessTouchDelta(StaticToolbarState aCurrentToolbarState, ScreenIntCoord aDelta, uint32_t aTimeStamp);
+  // Called when a touch ends
+  void HandleTouchEnd(StaticToolbarState aCurrentToolbarState, ScreenIntCoord aCurrentTouch);
+  // Sends a message to the UI thread. May be called from any thread
+  void PostMessage(int32_t aMessage);
+  void UpdateCompositorToolbarHeight(ScreenIntCoord aHeight);
+  void UpdateControllerToolbarHeight(ScreenIntCoord aHeight, ScreenIntCoord aMaxHeight = -1);
+  void UpdateControllerSurfaceHeight(ScreenIntCoord aHeight);
+  void UpdateControllerCompositionHeight(ScreenIntCoord aHeight);
+  void UpdateFixedLayerMargins();
+  void NotifyControllerPendingAnimation(int32_t aDirection, AnimationStyle aStyle);
+  void StartCompositorAnimation(int32_t aDirection, AnimationStyle aStyle, ScreenIntCoord aHeight);
+  void NotifyControllerAnimationStarted();
+  void StopCompositorAnimation();
+  void NotifyControllerAnimationStopped(ScreenIntCoord aHeight);
+  void RequestComposite();
+  void PostToolbarReady();
+  void UpdateFrameMetrics(ScreenPoint aScrollOffset,
+                          CSSToScreenScale aScale,
+                          CSSRect aCssPageRect);
+  bool IsEnoughPageToHideToolbar(ScreenIntCoord delta);
+  void ShowToolbarIfNotVisible(StaticToolbarState aCurrentToolbarState);
+  void TranslateTouchEvent(MultiTouchInput& aTouchEvent);
+  ScreenIntCoord GetFixedLayerMarginsBottom();
+
+  // Read only Compositor and Controller threads after Initialize()
+  uint64_t mRootLayerTreeId;
+
+  // Read/Write Compositor Thread, Read only Controller thread
+  Atomic<StaticToolbarState> mToolbarState; // Current toolbar state.
+  Atomic<uint32_t> mPinnedFlags;            // The toolbar should not be moved or animated unless no flags are set
+
+  // Controller thread only
+  bool    mControllerScrollingRootContent;     // Set to true when the root content is being scrolled
+  bool    mControllerDragThresholdReached;     // Set to true when the drag threshold has been passed in a single drag
+  bool    mControllerCancelTouchTracking;      // Set to true when the UI thread requests the toolbar be made visible
+  bool    mControllerDragChangedDirection;     // Set to true if the drag ever goes in more than one direction
+  ScreenIntCoord mControllerStartTouch;        // The Y position where the touch started
+  ScreenIntCoord mControllerPreviousTouch;     // The previous Y position of the touch
+  ScreenIntCoord mControllerTotalDistance;     // Total distance travel during the current touch
+  ScreenIntCoord mControllerMaxToolbarHeight;  // Max height of the toolbar
+  ScreenIntCoord mControllerToolbarHeight;     // Current height of the toolbar
+  ScreenIntCoord mControllerSurfaceHeight;     // Current height of the render surface
+  ScreenIntCoord mControllerCompositionHeight; // Current height of the visible page
+  int32_t mControllerLastDragDirection;        // Direction of movement of the previous touch move event
+  uint32_t mControllerLastEventTimeStamp;      // Time stamp for the previous touch event received
+  ControllerThreadState mControllerState;      // Contains the expected pending state of the mToolbarState
+
+  // Contains the values from the last steady state root content FrameMetrics
+  struct FrameMetricsState {
+    ScreenPoint mScrollOffset;
+    CSSToScreenScale mScale;
+    CSSRect mCssPageRect;
+    ScreenRect mPageRect;
+
+    // Returns true if any of the values have changed.
+    bool Update(const ScreenPoint& aScrollOffset,
+                const CSSToScreenScale& aScale,
+                const CSSRect& aCssPageRect);
+  };
+
+  // Controller thread only
+  FrameMetricsState mControllerFrameMetrics;
+
+  // Compositor thread only
+  bool    mCompositorShutdown;
+  bool    mCompositorAnimationDeferred;           // An animation has been deferred until the toolbar is unlocked
+  bool    mCompositorLayersUpdateEnabled;         // Flag set to true when the UI thread is expecting to be notified when a layer has been updated
+  AnimationStyle mCompositorAnimationStyle;       // Set to true when the snap should be immediately hidden or shown in the animation update
+  ScreenIntCoord mCompositorMaxToolbarHeight;     // Should contain the same value as mControllerMaxToolbarHeight
+  ScreenIntCoord mCompositorToolbarHeight;        // This value is only updated by the compositor thread when the mToolbarState == ToolbarAnimating
+  ScreenIntCoord mCompositorSurfaceHeight;        // Current height of the render surface
+  ScreenIntSize  mCompositorCompositionSize;      // Current size of the visible page
+  int32_t mCompositorAnimationDirection;          // Direction the snapshot should be animated
+  ScreenIntCoord mCompositorAnimationStartHeight; // The height of the snapshot at the start of an animation
+  ScreenIntSize mCompositorToolbarPixelsSize;     // Size of the received toolbar pixels
+  Maybe<mozilla::ipc::Shmem> mCompositorToolbarPixels; // Shared memory contain the updated snapshot pixels used to create the OGL texture
+  RefPtr<DataTextureSource> mCompositorToolbarTexture; // The OGL texture used to render the snapshot in the compositor
+  RefPtr<EffectRGB> mCompositorToolbarEffect;          // Effect used to render the snapshot in the compositor
+  TimeStamp mCompositorAnimationStartTimeStamp;        // Time stamp when the current animation started
+};
+
+} // namespace layers
+} // namespace mozilla
+#endif // mozilla_layers_AndroidDynamicToolbarAnimator_h_
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -72,16 +72,17 @@
 #include "nsThreadUtils.h"              // for NS_IsMainThread
 #include "nsViewportInfo.h"             // for kViewportMinScale, kViewportMaxScale
 #include "prsystem.h"                   // for PR_GetPhysicalMemorySize
 #include "SharedMemoryBasic.h"          // for SharedMemoryBasic
 #include "ScrollSnap.h"                 // for ScrollSnapUtils
 #include "WheelScrollAnimation.h"
 #if defined(MOZ_WIDGET_ANDROID)
 #include "AndroidAPZ.h"
+#include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
 #endif // defined(MOZ_WIDGET_ANDROID)
 
 #define ENABLE_APZC_LOGGING 0
 // #define ENABLE_APZC_LOGGING 1
 
 #if ENABLE_APZC_LOGGING
 #  define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
 #  define APZC_LOG_FM(fm, prefix, ...) \
@@ -1186,22 +1187,16 @@ nsEventStatus AsyncPanZoomController::On
       break;
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
   APZC_LOG("%p got a touch-end in state %d\n", this, mState);
-
-  RefPtr<GeckoContentController> controller = GetGeckoContentController();
-  if (controller) {
-    controller->SetScrollingRootContent(false);
-  }
-
   OnTouchEndOrCancel();
 
   // In case no touch behavior triggered previously we can avoid sending
   // scroll events or requesting content repaint. This condition is added
   // to make tests consistent - in case touch-action is NONE (and therefore
   // no pans/zooms can be performed) we expected neither scroll or repaint
   // events.
   if (mState != NOTHING) {
@@ -2432,22 +2427,25 @@ bool AsyncPanZoomController::AttemptScro
 
     if (xChanged || yChanged) {
       ScheduleComposite();
     }
 
     if (!IsZero(adjustedDisplacement)) {
       ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom());
       if (CancelableBlockState* block = GetCurrentInputBlock()) {
-        if (block->AsTouchBlock() && (block->GetScrolledApzc() != this)) {
-          RefPtr<GeckoContentController> controller = GetGeckoContentController();
-          if (controller) {
-            controller->SetScrollingRootContent(IsRootContent());
+#if defined(MOZ_WIDGET_ANDROID)
+        if (block->AsTouchBlock() && (block->GetScrolledApzc() != this) && IsRootContent()) {
+          if (APZCTreeManager* manager = GetApzcTreeManager()) {
+            AndroidDynamicToolbarAnimator* animator = manager->GetAndroidDynamicToolbarAnimator();
+            MOZ_ASSERT(animator);
+            animator->SetScrollingRootContent();
           }
         }
+#endif
         block->SetScrolledApzc(this);
       }
       ScheduleCompositeAndMaybeRepaint();
       UpdateSharedCompositorFrameMetrics();
     }
 
     // Adjust the start point to reflect the consumed portion of the scroll.
     aStartPoint = aEndPoint + overscroll;
@@ -3449,16 +3447,31 @@ void AsyncPanZoomController::NotifyLayer
   }
 
   bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll()
        && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
 
   // TODO if we're in a drag and scrollOffsetUpdated is set then we want to
   // ignore it
 
+#if defined(MOZ_WIDGET_ANDROID)
+  if (aLayerMetrics.IsRootContent()) {
+    if (APZCTreeManager* manager = GetApzcTreeManager()) {
+      AndroidDynamicToolbarAnimator* animator = manager->GetAndroidDynamicToolbarAnimator();
+      MOZ_ASSERT(animator);
+      CSSToScreenScale scale = ViewTargetAs<ScreenPixel>(aLayerMetrics.GetZoom().ToScaleFactor(),
+                                                         PixelCastJustification::ScreenIsParentLayerForRoot);
+      ScreenIntSize size = ScreenIntSize::Round(aLayerMetrics.GetRootCompositionSize() * scale);
+      if (animator->SetCompositionSize(size)) {
+        animator->UpdateRootFrameMetrics(aLayerMetrics);
+      }
+    }
+  }
+#endif
+
   if ((aIsFirstPaint && aThisLayerTreeUpdated) || isDefault) {
     // Initialize our internal state to something sane when the content
     // that was just painted is something we knew nothing about previously
     CancelAnimation();
 
     mScrollMetadata = aScrollMetadata;
     mExpectedGeckoMetrics = aLayerMetrics;
     ShareCompositorFrameMetrics();
@@ -3821,16 +3834,27 @@ void AsyncPanZoomController::DispatchSta
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
       // Let the compositor know about scroll state changes so it can manage
       // windowed plugins.
       if (gfxPrefs::HidePluginsForScroll() && mCompositorController) {
         mCompositorController->ScheduleHideAllPluginWindows();
       }
 #endif
     } else if (IsTransformingState(aOldState) && !IsTransformingState(aNewState)) {
+#if defined(MOZ_WIDGET_ANDROID)
+      // The Android UI thread only shows overlay UI elements when the content is not being
+      // panned or zoomed and it is in a steady state. So the FrameMetrics only need to be
+      // updated when the transform ends.
+      if (APZCTreeManager* manager = GetApzcTreeManager()) {
+        AndroidDynamicToolbarAnimator* animator = manager->GetAndroidDynamicToolbarAnimator();
+        MOZ_ASSERT(animator);
+        animator->UpdateRootFrameMetrics(mFrameMetrics);
+      }
+#endif
+
       controller->NotifyAPZStateChange(
           GetGuid(), APZStateChange::eTransformEnd);
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
       if (gfxPrefs::HidePluginsForScroll() && mCompositorController) {
         mCompositorController->ScheduleShowAllPluginWindows();
       }
 #endif
     }
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -864,22 +864,16 @@ ClientLayerManager::GetBackendName(nsASt
 
 bool
 ClientLayerManager::AsyncPanZoomEnabled() const
 {
   return mWidget && mWidget->AsyncPanZoomEnabled();
 }
 
 void
-ClientLayerManager::SetNextPaintSyncId(int32_t aSyncId)
-{
-  mForwarder->SetPaintSyncId(aSyncId);
-}
-
-void
 ClientLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
 {
   mForwarder->SetLayerObserverEpoch(aLayerObserverEpoch);
 }
 
 void
 ClientLayerManager::AddDidCompositeObserver(DidCompositeObserver* aObserver)
 {
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -233,18 +233,16 @@ public:
   virtual void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) override;
 
   virtual uint64_t GetLastTransactionId() override { return mLatestTransactionId; }
 
   float RequestProperty(const nsAString& aProperty) override;
 
   bool AsyncPanZoomEnabled() const override;
 
-  void SetNextPaintSyncId(int32_t aSyncId);
-
   virtual void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch) override;
 
   virtual void AddDidCompositeObserver(DidCompositeObserver* aObserver) override;
   virtual void RemoveDidCompositeObserver(DidCompositeObserver* aObserver) override;
 
   virtual already_AddRefed<PersistentBufferProvider>
   CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
 
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -164,17 +164,17 @@ ClientTiledPaintedLayer::BeginPaint()
   const FrameMetrics& scrollMetrics = scrollAncestor.Metrics();
   const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics();
 
   // Calculate the transform required to convert ParentLayer space of our
   // display port ancestor to the Layer space of this layer.
   ParentLayerToLayerMatrix4x4 transformDisplayPortToLayer =
     GetTransformToAncestorsParentLayer(this, displayPortAncestor).Inverse();
 
-  LayerRect layerBounds = ViewAs<LayerPixel>(Rect(GetLayerBounds()));
+  LayerRect layerBounds(GetVisibleRegion().GetBounds());
 
   // Compute the critical display port that applies to this layer in the
   // LayoutDevice space of this layer, but only if there is no OMT animation
   // on this layer. If there is an OMT animation then we need to draw the whole
   // visible region of this layer as determined by layout, because we don't know
   // what parts of it might move into view in the compositor.
   mPaintData.mHasTransformAnimation = hasTransformAnimation;
   if (!mPaintData.mHasTransformAnimation &&
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1213,17 +1213,17 @@ ClientMultiTiledLayerBuffer::ComputeProg
       return aIsRepeated;
     }
   }
 
   Maybe<LayerRect> transformedCompositionBounds =
     GetCompositorSideCompositionBounds(scrollAncestor,
                                        aPaintData->mTransformToCompBounds,
                                        viewTransform,
-                                       ViewAs<LayerPixel>(Rect(mPaintedLayer.GetLayerBounds())));
+                                       LayerRect(mPaintedLayer.GetVisibleRegion().GetBounds()));
 
   if (!transformedCompositionBounds) {
     aPaintData->mPaintFinished = true;
     return false;
   }
 
   TILING_LOG("TILING %p: Progressive update transformed compositor bounds %s\n", &mPaintedLayer, Stringify(*transformedCompositionBounds).c_str());
 
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -34,16 +34,17 @@
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
 #include "nsTArrayForwardDeclare.h"     // for InfallibleTArray
 #include "UnitTransforms.h"             // for TransformTo
 #include "gfxPrefs.h"
 #if defined(MOZ_WIDGET_ANDROID)
 # include <android/log.h>
+# include "mozilla/layers/UiCompositorControllerParent.h"
 # include "mozilla/widget/AndroidCompositorWidget.h"
 #endif
 #include "GeckoProfiler.h"
 #include "FrameUniformityData.h"
 #include "TreeTraversal.h"              // for ForEachNode, BreadthFirstSearch
 #include "VsyncSource.h"
 
 struct nsCSSValueSharedList;
@@ -67,17 +68,16 @@ ContentMightReflowOnOrientationChange(co
   return rect.width != rect.height;
 }
 
   AsyncCompositionManager::AsyncCompositionManager(CompositorBridgeParent* aParent,
                                                    HostLayerManager* aManager)
   : mLayerManager(aManager)
   , mIsFirstPaint(true)
   , mLayersUpdated(false)
-  , mPaintSyncId(0)
   , mReadyForCompose(true)
   , mCompositorBridge(aParent)
 {
 }
 
 AsyncCompositionManager::~AsyncCompositionManager()
 {
 }
@@ -849,16 +849,17 @@ AsyncCompositionManager::ApplyAsyncConte
       {
         Maybe<ParentLayerIntRect> clipDeferredFromChildren = stackDeferredClips.top();
         stackDeferredClips.pop();
         LayerToParentLayerMatrix4x4 oldTransform = layer->GetTransformTyped() *
             AsyncTransformMatrix();
 
         AsyncTransformComponentMatrix combinedAsyncTransform;
         bool hasAsyncTransform = false;
+        // Only set on the root layer for Android.
         ScreenMargin fixedLayerMargins;
 
         // Each layer has multiple clips:
         //  - Its local clip, which is fixed to the layer contents, i.e. it moves
         //    with those async transforms which the layer contents move with.
         //  - Its scrolled clip, which moves with all async transforms.
         //  - For each ScrollMetadata on the layer, a scroll clip. This includes
         //    the composition bounds and any other clips induced by layout. This
@@ -938,40 +939,35 @@ AsyncCompositionManager::ApplyAsyncConte
           // should not need the root content metrics at all. See bug 1201529 comment
           // 6 for details.
           if (!(*aOutFoundRoot)) {
             *aOutFoundRoot = metrics.IsRootContent() ||       /* RCD */
                   (layer->GetParent() == nullptr &&          /* rootmost metrics */
                    i + 1 >= layer->GetScrollMetadataCount());
             if (*aOutFoundRoot) {
               mRootScrollableId = metrics.GetScrollId();
-              CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
+              Compositor* compositor = mLayerManager->GetCompositor();
               if (mIsFirstPaint) {
-                LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
-                mContentRect = metrics.GetScrollableRect();
-                SetFirstPaintViewport(scrollOffsetLayerPixels,
-                                      geckoZoom,
-                                      mContentRect);
-              } else {
-                ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(
-                    AsyncPanZoomController::RESPECT_FORCE_DISABLE);
-                // Compute the painted displayport in document-relative CSS pixels.
-                CSSRect displayPort(metrics.GetCriticalDisplayPort().IsEmpty() ?
-                    metrics.GetDisplayPort() :
-                    metrics.GetCriticalDisplayPort());
-                displayPort += metrics.GetScrollOffset();
-                SyncFrameMetrics(scrollOffset,
-                    geckoZoom * asyncTransformWithoutOverscroll.mScale,
-                    metrics.GetScrollableRect(), displayPort, geckoZoom, mLayersUpdated,
-                    mPaintSyncId, fixedLayerMargins);
-                mFixedLayerMargins = fixedLayerMargins;
+                if (CompositorBridgeParent* bridge = compositor->GetCompositorBridgeParent()) {
+                  AndroidDynamicToolbarAnimator* animator = bridge->GetAPZCTreeManager()->GetAndroidDynamicToolbarAnimator();
+                  MOZ_ASSERT(animator);
+                  animator->UpdateRootFrameMetrics(metrics);
+                  animator->FirstPaint();
+                }
+              }
+              if (mLayersUpdated) {
+                if (CompositorBridgeParent* bridge = compositor->GetCompositorBridgeParent()) {
+                  AndroidDynamicToolbarAnimator* animator = bridge->GetAPZCTreeManager()->GetAndroidDynamicToolbarAnimator();
+                  MOZ_ASSERT(animator);
+                  animator->NotifyLayersUpdated();
+                }
                 mLayersUpdated = false;
-                mPaintSyncId = 0;
               }
               mIsFirstPaint = false;
+              fixedLayerMargins = mFixedLayerMargins;
             }
           }
 #else
           *aOutFoundRoot = false;
           // Non-Android platforms still care about this flag being cleared after
           // the first call to TransformShadowTree().
           mIsFirstPaint = false;
 #endif
@@ -1370,16 +1366,34 @@ AsyncCompositionManager::TransformShadow
     animationProcess, layerAreaAnimated, aVsyncRate);
 
   if (!wantNextFrame) {
     // Clean up the CompositorAnimationStorage because
     // there are no active animations running
     storage->Clear();
   }
 
+  // Advance animations to the next expected vsync timestamp, if we can
+  // get it.
+  TimeStamp nextFrame = aCurrentFrame;
+
+  MOZ_ASSERT(aVsyncRate != TimeDuration::Forever());
+  if (aVsyncRate != TimeDuration::Forever()) {
+    nextFrame += aVsyncRate;
+  }
+
+#if defined(MOZ_WIDGET_ANDROID)
+  Compositor* compositor = mLayerManager->GetCompositor();
+  if (CompositorBridgeParent* bridge = compositor->GetCompositorBridgeParent()) {
+    AndroidDynamicToolbarAnimator* animator = bridge->GetAPZCTreeManager()->GetAndroidDynamicToolbarAnimator();
+    MOZ_ASSERT(animator);
+    wantNextFrame |= animator->UpdateAnimation(nextFrame);
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
   // Reset the previous time stamp if we don't already have any running
   // animations to avoid using the time which is far behind for newly
   // started animations.
   mPreviousFrameTimeStamp = wantNextFrame ? aCurrentFrame : TimeStamp();
 
   if (!(aSkip & TransformsToSkip::APZ)) {
     // FIXME/bug 775437: unify this interface with the ~native-fennec
     // derived code
@@ -1397,25 +1411,16 @@ AsyncCompositionManager::TransformShadow
 #if defined(MOZ_WIDGET_ANDROID)
       MOZ_ASSERT(foundRoot);
       if (foundRoot && mFixedLayerMargins != ScreenMargin()) {
         MoveScrollbarForLayerMargin(root, mRootScrollableId, mFixedLayerMargins);
       }
 #endif
     }
 
-    // Advance APZ animations to the next expected vsync timestamp, if we can
-    // get it.
-    TimeStamp nextFrame = aCurrentFrame;
-
-    MOZ_ASSERT(aVsyncRate != TimeDuration::Forever());
-    if (aVsyncRate != TimeDuration::Forever()) {
-      nextFrame += aVsyncRate;
-    }
-
     bool apzAnimating = SampleAPZAnimations(LayerMetricsWrapper(root), nextFrame);
     mAnimationMetricsTracker.UpdateApzAnimationInProgress(apzAnimating, aVsyncRate);
     wantNextFrame |= apzAnimating;
   }
 
   HostLayer* rootComposite = root->AsHostLayer();
 
   gfx::Matrix4x4 trans = rootComposite->GetShadowBaseTransform();
@@ -1424,47 +1429,18 @@ AsyncCompositionManager::TransformShadow
 
   if (gfxPrefs::CollectScrollTransforms()) {
     RecordShadowTransforms(root);
   }
 
   return wantNextFrame;
 }
 
-void
-AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset,
-                                               const CSSToLayerScale& aZoom,
-                                               const CSSRect& aCssPageRect)
-{
-#ifdef MOZ_WIDGET_ANDROID
-  widget::AndroidCompositorWidget* widget =
-      mLayerManager->GetCompositor()->GetWidget()->AsAndroid();
-  if (!widget) {
-    return;
-  }
-  widget->SetFirstPaintViewport(aOffset, aZoom, aCssPageRect);
-#endif
-}
-
+#if defined(MOZ_WIDGET_ANDROID)
 void
-AsyncCompositionManager::SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
-                                          const CSSToParentLayerScale& aZoom,
-                                          const CSSRect& aCssPageRect,
-                                          const CSSRect& aDisplayPort,
-                                          const CSSToLayerScale& aPaintedResolution,
-                                          bool aLayersUpdated,
-                                          int32_t aPaintSyncId,
-                                          ScreenMargin& aFixedLayerMargins)
+AsyncCompositionManager::SetFixedLayerMarginsBottom(ScreenIntCoord aBottom)
 {
-#ifdef MOZ_WIDGET_ANDROID
-  widget::AndroidCompositorWidget* widget =
-      mLayerManager->GetCompositor()->GetWidget()->AsAndroid();
-  if (!widget) {
-    return;
-  }
-  widget->SyncFrameMetrics(
-      aScrollOffset, aZoom, aCssPageRect, aDisplayPort, aPaintedResolution,
-      aLayersUpdated, aPaintSyncId, aFixedLayerMargins);
-#endif
+  mFixedLayerMargins.bottom = aBottom;
 }
+#endif // defined(MOZ_WIDGET_ANDROID)
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -73,41 +73,37 @@ public:
 
   explicit AsyncCompositionManager(CompositorBridgeParent* aParent, HostLayerManager* aManager);
 
   /**
    * This forces the is-first-paint flag to true. This is intended to
    * be called by the widget code when it loses its viewport information
    * (or for whatever reason wants to refresh the viewport information).
    * The information refresh happens because the compositor will call
-   * SetFirstPaintViewport on the next frame of composition.
+   * AndroidDynamicToolbarAnimator::FirstPaint() on the next frame of composition.
    */
   void ForceIsFirstPaint() { mIsFirstPaint = true; }
 
   // Sample transforms for layer trees.  Return true to request
   // another animation frame.
   enum class TransformsToSkip : uint8_t { NoneOfThem = 0, APZ = 1 };
   bool TransformShadowTree(TimeStamp aCurrentFrame,
                            TimeDuration aVsyncRate,
                            TransformsToSkip aSkip = TransformsToSkip::NoneOfThem);
 
   // Calculates the correct rotation and applies the transform to
   // our layer manager
   void ComputeRotation();
 
   // Call after updating our layer tree.
-  void Updated(bool isFirstPaint, const TargetConfig& aTargetConfig,
-               int32_t aPaintSyncId)
+  void Updated(bool isFirstPaint, const TargetConfig& aTargetConfig)
   {
     mIsFirstPaint |= isFirstPaint;
     mLayersUpdated = true;
     mTargetConfig = aTargetConfig;
-    if (aPaintSyncId) {
-      mPaintSyncId = aPaintSyncId;
-    }
   }
 
   bool RequiresReorientation(mozilla::dom::ScreenOrientationInternal aOrientation) const
   {
     return mTargetConfig.orientation() != aOrientation;
   }
 
   // True if the underlying layer tree is ready to be composited.
@@ -144,28 +140,16 @@ private:
   bool ApplyAsyncContentTransformToTree(Layer* aLayer,
                                         bool* aOutFoundRoot);
   /**
    * Update the shadow transform for aLayer assuming that is a scrollbar,
    * so that it stays in sync with the content that is being scrolled by APZ.
    */
   void ApplyAsyncTransformToScrollbar(Layer* aLayer);
 
-  void SetFirstPaintViewport(const LayerIntPoint& aOffset,
-                             const CSSToLayerScale& aZoom,
-                             const CSSRect& aCssPageRect);
-  void SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
-                        const CSSToParentLayerScale& aZoom,
-                        const CSSRect& aCssPageRect,
-                        const CSSRect& aDisplayPort,
-                        const CSSToLayerScale& aPaintedResolution,
-                        bool aLayersUpdated,
-                        int32_t aPaintSyncId,
-                        ScreenMargin& aFixedLayerMargins);
-
   /**
    * Adds a translation to the transform of any fixed position (whose parent
    * layer is not fixed) or sticky position layer descendant of
    * |aTransformedSubtreeRoot|. The translation is chosen so that the layer's
    * anchor point relative to |aTransformedSubtreeRoot|'s parent layer is the same
    * as it was when |aTransformedSubtreeRoot|'s GetLocalTransform() was
    * |aPreviousTransformForRoot|. |aCurrentTransformForRoot| is
    * |aTransformedSubtreeRoot|'s current GetLocalTransform() modulo any
@@ -226,29 +210,30 @@ private:
   // front-end (e.g. Java on Android) about this so that it take the new page
   // size and zoom into account when providing us with the next view transform.
   bool mIsFirstPaint;
 
   // This flag is set during a layers update, so that the first composition
   // after a layers update has it set. It is cleared after that first composition.
   bool mLayersUpdated;
 
-  int32_t mPaintSyncId;
-
   bool mReadyForCompose;
 
   gfx::Matrix mWorldTransform;
   LayerTransformRecorder mLayerTransformRecorder;
 
   TimeStamp mPreviousFrameTimeStamp;
   AnimationMetricsTracker mAnimationMetricsTracker;
 
   CompositorBridgeParent* mCompositorBridge;
 
 #ifdef MOZ_WIDGET_ANDROID
+public:
+  void SetFixedLayerMarginsBottom(ScreenIntCoord aBottom);
+private:
   // The following two fields are only needed on Fennec with C++ APZ, because
   // then we need to reposition the gecko scrollbar to deal with the
   // dynamic toolbar shifting content around.
   FrameMetrics::ViewID mRootScrollableId;
   ScreenMargin mFixedLayerMargins;
 #endif
 };
 
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -207,18 +207,17 @@ ContainerPrepare(ContainerT* aContainer,
 
     if (layerToRender->GetLayer()->IsBackfaceHidden()) {
       continue;
     }
 
     // We don't want to skip container layers because otherwise their mPrepared
     // may be null which is not allowed.
     if (!layerToRender->GetLayer()->AsContainerLayer()) {
-      if (!layerToRender->GetLayer()->IsVisible() &&
-          !layerToRender->NeedToDrawCheckerboarding(nullptr)) {
+      if (!layerToRender->GetLayer()->IsVisible()) {
         CULLING_LOG("Sublayer %p has no effective visible region\n", layerToRender->GetLayer());
         continue;
       }
 
       if (clipRect.IsEmpty()) {
         CULLING_LOG("Sublayer %p has an empty world clip rect\n", layerToRender->GetLayer());
         continue;
       }
@@ -418,34 +417,16 @@ RenderLayers(ContainerT* aContainer, Lay
       for (const auto& metadata : layer->GetAllScrollMetadata()) {
         if (metadata.IsApzForceDisabled()) {
           aManager->DisabledApzWarning();
           break;
         }
       }
     }
 
-    Color color;
-    if (layerToRender->NeedToDrawCheckerboarding(&color)) {
-      if (gfxPrefs::APZHighlightCheckerboardedAreas()) {
-        color = Color(255 / 255.f, 188 / 255.f, 217 / 255.f, 1.f); // "Cotton Candy"
-      }
-      // Ideally we would want to intersect the checkerboard region from the APZ with the layer bounds
-      // and only fill in that area. However the layer bounds takes into account the base translation
-      // for the painted layer whereas the checkerboard region does not. One does not simply
-      // intersect areas in different coordinate spaces. So we do this a little more permissively
-      // and only fill in the background when we know there is checkerboard, which in theory
-      // should only occur transiently.
-      EffectChain effectChain(layer);
-      effectChain.mPrimaryEffect = new EffectSolidColor(color);
-      aManager->GetCompositor()->DrawGeometry(gfx::Rect(layer->GetLayerBounds()), clipRect,
-                                              effectChain, layer->GetEffectiveOpacity(),
-                                              layer->GetEffectiveTransform(), Nothing());
-    }
-
     if (layerToRender->HasLayerBeenComposited()) {
       // Composer2D will compose this layer so skip GPU composition
       // this time. The flag will be reset for the next composition phase
       // at the beginning of LayerManagerComposite::Rener().
       gfx::IntRect clearRect = layerToRender->GetClearRect();
       if (!clearRect.IsEmpty()) {
         // Clear layer's visible rect on FrameBuffer with transparent pixels
         gfx::Rect fbRect(clearRect.x, clearRect.y, clearRect.width, clearRect.height);
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -48,24 +48,25 @@
 #include "nsAppRunner.h"
 #include "mozilla/RefPtr.h"                   // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_WARNING, NS_RUNTIMEABORT, etc
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsRegion.h"                   // for nsIntRegion, etc
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID)
 #include <android/log.h>
 #include <android/native_window.h>
-#endif
-#if defined(MOZ_WIDGET_ANDROID)
+#include "mozilla/widget/AndroidCompositorWidget.h"
 #include "opengl/CompositorOGL.h"
+#include "GLConsts.h"
 #include "GLContextEGL.h"
 #include "GLContextProvider.h"
+#include "mozilla/Unused.h"
 #include "mozilla/widget/AndroidCompositorWidget.h"
 #include "ScopedGLHelpers.h"
 #endif
 #include "GeckoProfiler.h"
 #include "TextRenderer.h"               // for TextRenderer
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "TreeTraversal.h"              // for ForEachNode
 
@@ -142,16 +143,19 @@ HostLayerManager::RecordUpdateTime(float
  */
 LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor)
 : mUnusedApzTransformWarning(false)
 , mDisabledApzWarning(false)
 , mCompositor(aCompositor)
 , mInTransaction(false)
 , mIsCompositorReady(false)
 , mGeometryChanged(true)
+#if defined(MOZ_WIDGET_ANDROID)
+, mScreenPixelsTarget(nullptr)
+#endif // defined(MOZ_WIDGET_ANDROID)
 {
   mTextRenderer = new TextRenderer();
   mDiagnostics = MakeUnique<Diagnostics>();
   MOZ_ASSERT(aCompositor);
 
 #ifdef USE_SKIA
   mPaintCounter = nullptr;
 #endif
@@ -833,16 +837,35 @@ ClearLayerFlags(Layer* aLayer) {
       [] (Layer* layer)
       {
         if (layer->AsHostLayer()) {
           static_cast<LayerComposite*>(layer->AsHostLayer())->SetLayerComposited(false);
         }
       });
 }
 
+#if defined(MOZ_WIDGET_ANDROID)
+class ScopedCompositorRenderOffset {
+public:
+  ScopedCompositorRenderOffset(CompositorOGL* aCompositor, const ScreenPoint& aOffset) :
+    mCompositor(aCompositor),
+    mOriginalOffset(mCompositor->GetScreenRenderOffset())
+  {
+    mCompositor->SetScreenRenderOffset(aOffset);
+  }
+  ~ScopedCompositorRenderOffset()
+  {
+    mCompositor->SetScreenRenderOffset(mOriginalOffset);
+  }
+private:
+  CompositorOGL* const mCompositor;
+  const ScreenPoint mOriginalOffset;
+};
+#endif // defined(MOZ_WIDGET_ANDROID)
+
 void
 LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion)
 {
   PROFILER_LABEL("LayerManagerComposite", "Render",
     js::ProfileEntry::Category::GRAPHICS);
 
   if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) {
     NS_WARNING("Call on destroyed layer manager");
@@ -918,16 +941,21 @@ LayerManagerComposite::Render(const nsIn
     clipRect = *mRoot->GetClipRect();
     IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
     mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds);
   } else {
     gfx::IntRect rect;
     mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, aOpaqueRegion, &rect, &actualBounds);
     clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height);
   }
+#if defined(MOZ_WIDGET_ANDROID)
+  int32_t toolbarHeight = RenderToolbar();
+  // This doesn't affect the projection matrix after BeginFrame has been called.
+  ScopedCompositorRenderOffset scopedOffset(mCompositor->AsCompositorOGL(), ScreenPoint(0, toolbarHeight));
+#endif
 
   if (actualBounds.IsEmpty()) {
     mCompositor->GetWidget()->PostRender(&widgetContext);
     return;
   }
 
   // Allow widget to render a custom background.
   mCompositor->GetWidget()->DrawWindowUnderlay(
@@ -972,16 +1000,20 @@ LayerManagerComposite::Render(const nsIn
   }
 
   // Allow widget to render a custom foreground.
   mCompositor->GetWidget()->DrawWindowOverlay(
     &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds));
 
   mCompositor->NormalDrawingDone();
 
+#if defined(MOZ_WIDGET_ANDROID)
+  HandlePixelsTarget();
+#endif // defined(MOZ_WIDGET_ANDROID)
+
   // Debugging
   RenderDebugOverlay(actualBounds);
 
   {
     PROFILER_LABEL("LayerManagerComposite", "EndFrame",
       js::ProfileEntry::Category::GRAPHICS);
 
     mCompositor->EndFrame();
@@ -1026,33 +1058,16 @@ public:
   {
     mCompositor->SetDestinationSurfaceSize(mOriginalSize);
   }
 private:
   CompositorOGL* const mCompositor;
   const gfx::IntSize mOriginalSize;
 };
 
-class ScopedCompositorRenderOffset {
-public:
-  ScopedCompositorRenderOffset(CompositorOGL* aCompositor, const ScreenPoint& aOffset) :
-    mCompositor(aCompositor),
-    mOriginalOffset(mCompositor->GetScreenRenderOffset())
-  {
-    mCompositor->SetScreenRenderOffset(aOffset);
-  }
-  ~ScopedCompositorRenderOffset()
-  {
-    mCompositor->SetScreenRenderOffset(mOriginalOffset);
-  }
-private:
-  CompositorOGL* const mCompositor;
-  const ScreenPoint mOriginalOffset;
-};
-
 class ScopedContextSurfaceOverride {
 public:
   ScopedContextSurfaceOverride(GLContextEGL* aContext, void* aSurface) :
     mContext(aContext)
   {
     MOZ_ASSERT(aSurface);
     mContext->SetEGLSurfaceOverride(aSurface);
     mContext->MakeCurrent(true);
@@ -1160,16 +1175,75 @@ LayerManagerComposite::RenderToPresentat
 
   const IntRect clipRect = IntRect::Truncate(0, 0, actualWidth, actualHeight);
 
   RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect));
   RootLayer()->RenderLayer(clipRect, Nothing());
 
   mCompositor->EndFrame();
 }
+
+int32_t
+LayerManagerComposite::RenderToolbar()
+{
+  int32_t toolbarHeight = 0;
+
+  // If GetTargetContext returns null we are drawing to the screen so draw the toolbar offset if present.
+  if (mCompositor->GetTargetContext() != nullptr) {
+    return toolbarHeight;
+  }
+
+  if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) {
+    AndroidDynamicToolbarAnimator* animator = bridge->GetAPZCTreeManager()->GetAndroidDynamicToolbarAnimator();
+    MOZ_ASSERT(animator);
+    toolbarHeight = animator->GetCurrentToolbarHeight();
+    if (toolbarHeight == 0) {
+      return toolbarHeight;
+    }
+
+    EffectChain effects;
+    effects.mPrimaryEffect = animator->GetToolbarEffect(mCompositor->AsCompositorOGL());
+    if (!effects.mPrimaryEffect) {
+      // No toolbar texture so just draw a red square
+      effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 0, 0));
+    }
+    mCompositor->DrawQuad(gfx::Rect(0, 0, mRenderBounds.width, toolbarHeight),
+                          IntRect(0, 0, mRenderBounds.width, toolbarHeight), effects, 1.0, gfx::Matrix4x4());
+
+    // Move the content down the surface by the toolbar's height so they don't overlap
+    gfx::Matrix4x4 mat = mCompositor->AsCompositorOGL()->GetProjMatrix();
+    mat.PreTranslate(0.0f, float(toolbarHeight), 0.0f);
+    mCompositor->AsCompositorOGL()->SetProjMatrix(mat);
+  }
+
+  return toolbarHeight;
+}
+
+// Used by robocop tests to get a snapshot of the frame buffer.
+void
+LayerManagerComposite::HandlePixelsTarget()
+{
+  if (!mScreenPixelsTarget) {
+    return;
+  }
+
+  int32_t bufferWidth = mRenderBounds.width;
+  int32_t bufferHeight = mRenderBounds.height;
+  ipc::Shmem mem;
+  if (!mScreenPixelsTarget->AllocPixelBuffer(bufferWidth * bufferHeight * sizeof(uint32_t), &mem)) {
+    // Failed to alloc shmem, Just bail out.
+    return;
+  }
+  CompositorOGL* compositor = mCompositor->AsCompositorOGL();
+  GLContext* gl = compositor->gl();
+  MOZ_ASSERT(gl);
+  gl->fReadPixels(0, 0, bufferWidth, bufferHeight, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, mem.get<uint8_t>());
+  Unused << mScreenPixelsTarget->SendScreenPixels(mem, ScreenIntSize(bufferWidth, bufferHeight));
+  mScreenPixelsTarget = nullptr;
+}
 #endif
 
 class TextLayerComposite : public TextLayer,
                            public LayerComposite
 {
 public:
   explicit TextLayerComposite(LayerManagerComposite *aManager)
     : TextLayer(aManager, nullptr)
@@ -1475,45 +1549,16 @@ HostLayer::GetShadowTransform() {
 }
 
 bool
 LayerComposite::HasStaleCompositor() const
 {
   return mCompositeManager->GetCompositor() != mCompositor;
 }
 
-static bool
-LayerHasCheckerboardingAPZC(Layer* aLayer, Color* aOutColor)
-{
-  bool answer = false;
-  for (LayerMetricsWrapper i(aLayer, LayerMetricsWrapper::StartAt::BOTTOM); i; i = i.GetParent()) {
-    if (!i.Metrics().IsScrollable()) {
-      continue;
-    }
-    if (i.GetApzc() && i.GetApzc()->IsCurrentlyCheckerboarding()) {
-      if (aOutColor) {
-        *aOutColor = i.Metadata().GetBackgroundColor();
-      }
-      answer = true;
-      break;
-    }
-    break;
-  }
-  return answer;
-}
-
-bool
-LayerComposite::NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor)
-{
-  return GetLayer()->Manager()->AsyncPanZoomEnabled() &&
-         (GetLayer()->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
-         GetLayer()->IsOpaqueForVisibility() &&
-         LayerHasCheckerboardingAPZC(GetLayer(), aOutCheckerboardingColor);
-}
-
 #ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
 
 /*static*/ bool
 LayerManagerComposite::SupportsDirectTexturing()
 {
   return false;
 }
 
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -58,16 +58,17 @@ class ImageLayer;
 class ImageLayerComposite;
 class LayerComposite;
 class RefLayerComposite;
 class PaintedLayerComposite;
 class TextRenderer;
 class CompositingRenderTarget;
 struct FPSState;
 class PaintCounter;
+class UiCompositorControllerParent;
 
 static const int kVisualWarningDuration = 150; // ms
 
 // An implementation of LayerManager that acts as a pair with ClientLayerManager
 // and is mirrored across IPDL. This gets managed/updated by LayerTransactionParent.
 class HostLayerManager : public LayerManager
 {
 public:
@@ -208,16 +209,23 @@ protected:
 
   // Render time for the current composition.
   TimeStamp mCompositionTime;
 
   // When nonnull, during rendering, some compositable indicated that it will
   // change its rendering at this time. In order not to miss it, we composite
   // on every vsync until this time occurs (this is the latest such time).
   TimeStamp mCompositeUntilTime;
+#if defined(MOZ_WIDGET_ANDROID)
+public:
+  // Used by UiCompositorControllerParent to set itself as the target for the
+  // contents of the frame buffer after a composite.
+  // Implemented in LayerManagerComposite
+  virtual void RequestScreenPixels(UiCompositorControllerParent* aController) {}
+#endif // defined(MOZ_WIDGET_ANDROID)
 };
 
 // A layer manager implementation that uses the Compositor API
 // to render layers.
 class LayerManagerComposite final : public HostLayerManager
 {
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::IntSize IntSize;
@@ -424,16 +432,20 @@ private:
   void UpdateAndRender();
 
   /**
    * Render the current layer tree to the active target.
    */
   void Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion);
 #if defined(MOZ_WIDGET_ANDROID)
   void RenderToPresentationSurface();
+  // Returns the height of the toolbar in screen pixels.
+  int32_t RenderToolbar();
+  // Used by robocop tests to get a snapshot of the frame buffer.
+  void HandlePixelsTarget();
 #endif
 
   /**
    * We need to know our invalid region before we're ready to render.
    */
   void InvalidateDebugOverlay(nsIntRegion& aInvalidRegion, const gfx::IntRect& aBounds);
 
   /**
@@ -477,16 +489,25 @@ private:
 
 #ifdef USE_SKIA
   /**
    * Render paint and composite times above the frame.
    */
   void DrawPaintTimes(Compositor* aCompositor);
   RefPtr<PaintCounter> mPaintCounter;
 #endif
+#if defined(MOZ_WIDGET_ANDROID)
+public:
+  virtual void RequestScreenPixels(UiCompositorControllerParent* aController)
+  {
+    mScreenPixelsTarget = aController;
+  }
+private:
+  UiCompositorControllerParent* mScreenPixelsTarget;
+#endif // defined(MOZ_WIDGET_ANDROID)
 };
 
 /**
  * Compositor layers are for use with OMTC on the compositor thread only. There
  * must be corresponding Client layers on the content thread. For composite
  * layers, the layer manager only maintains the layer tree.
  */
 class HostLayer
@@ -563,22 +584,16 @@ public:
   float GetShadowOpacity() { return mShadowOpacity; }
   const Maybe<ParentLayerIntRect>& GetShadowClipRect() { return mShadowClipRect; }
   const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
   const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; }
   gfx::Matrix4x4 GetShadowTransform();
   bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
   bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
 
-  /**
-   * Return true if a checkerboarding background color needs to be drawn
-   * for this layer.
-   */
-  virtual bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr) { return false; }
-
 protected:
   HostLayerManager* mCompositorManager;
 
   gfx::Matrix4x4 mShadowTransform;
   LayerIntRegion mShadowVisibleRegion;
   Maybe<ParentLayerIntRect> mShadowClipRect;
   float mShadowOpacity;
   bool mShadowTransformSetByAnimation;
@@ -668,18 +683,16 @@ public:
 
   /**
    * Return the part of the visible region that has been fully rendered.
    * While progressive drawing is in progress this region will be
    * a subset of the shadow visible region.
    */
   virtual nsIntRegion GetFullyRenderedRegion();
 
-  virtual bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr);
-
 protected:
   LayerManagerComposite* mCompositeManager;
 
   RefPtr<Compositor> mCompositor;
   bool mDestroyed;
   bool mLayerComposited;
   gfx::IntRect mClearRect;
 };
--- a/gfx/layers/ipc/APZChild.cpp
+++ b/gfx/layers/ipc/APZChild.cpp
@@ -48,23 +48,16 @@ APZChild::RecvUpdateOverscrollVelocity(c
 mozilla::ipc::IPCResult
 APZChild::RecvUpdateOverscrollOffset(const float& aX, const float& aY, const bool& aIsRootContent)
 {
   mController->UpdateOverscrollOffset(aX, aY, aIsRootContent);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-APZChild::RecvSetScrollingRootContent(const bool& aIsRootContent)
-{
-  mController->SetScrollingRootContent(aIsRootContent);
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
 APZChild::RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId,
                                         const nsString& aEvent)
 {
   mController->NotifyMozMouseScrollEvent(aScrollId, aEvent);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
--- a/gfx/layers/ipc/APZChild.h
+++ b/gfx/layers/ipc/APZChild.h
@@ -26,18 +26,16 @@ public:
   ~APZChild();
 
   mozilla::ipc::IPCResult RecvRequestContentRepaint(const FrameMetrics& frame) override;
 
   mozilla::ipc::IPCResult RecvUpdateOverscrollVelocity(const float& aX, const float& aY, const bool& aIsRootContent) override;
 
   mozilla::ipc::IPCResult RecvUpdateOverscrollOffset(const float& aX, const float& aY, const bool& aIsRootContent) override;
 
-  mozilla::ipc::IPCResult RecvSetScrollingRootContent(const bool& aIsRootContent) override;
-
   mozilla::ipc::IPCResult RecvNotifyMozMouseScrollEvent(const ViewID& aScrollId,
                                                         const nsString& aEvent) override;
 
   mozilla::ipc::IPCResult RecvNotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                                    const APZStateChange& aChange,
                                                    const int& aArg) override;
 
   mozilla::ipc::IPCResult RecvNotifyFlushComplete() override;
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1192,17 +1192,17 @@ CompositorBridgeParent::ShadowLayersUpda
   // change, dimension change would be done at the stage, update the size here is free of
   // race condition.
   mLayerManager->UpdateRenderBounds(targetConfig.naturalBounds());
   mLayerManager->SetRegionToClear(targetConfig.clearRegion());
   if (mLayerManager->GetCompositor()) {
     mLayerManager->GetCompositor()->SetScreenRotation(targetConfig.rotation());
   }
 
-  mCompositionManager->Updated(aInfo.isFirstPaint(), targetConfig, aInfo.paintSyncId());
+  mCompositionManager->Updated(aInfo.isFirstPaint(), targetConfig);
   Layer* root = aLayerTree->GetRoot();
   mLayerManager->SetRoot(root);
 
   if (mApzcTreeManager && !aInfo.isRepeatTransaction() && aHitTestUpdate) {
     AutoResolveRefLayers resolve(mCompositionManager);
 
     mApzcTreeManager->UpdateHitTestingTree(
       mRootLayerTreeID, root, aInfo.isFirstPaint(),
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -35,16 +35,17 @@
 #include "mozilla/layers/LayersMessages.h"  // for TargetConfig
 #include "mozilla/layers/MetricsSharingController.h"
 #include "mozilla/layers/PCompositorBridgeParent.h"
 #include "mozilla/layers/APZTestData.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "nsISupportsImpl.h"
 #include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
 
 class MessageLoop;
 class nsIWidget;
 
 namespace mozilla {
 
 class CancelableRunnable;
 
@@ -379,16 +380,17 @@ public:
     // this is needed in case a device reset occurs in between allocating a
     // RefLayer id on the parent, and allocating a PLayerTransaction on the
     // child.
     Maybe<uint64_t> mPendingCompositorUpdate;
 
     CompositorController* GetCompositorController() const;
     MetricsSharingController* CrossProcessSharingController() const;
     MetricsSharingController* InProcessSharingController() const;
+    RefPtr<UiCompositorControllerParent> mUiControllerParent;
   };
 
   /**
    * Lookup the indirect shadow tree for |aId| and return it if it
    * exists.  Otherwise null is returned.  This must only be called on
    * the compositor thread.
    */
   static LayerTreeState* GetIndirectShadowTree(uint64_t aId);
@@ -461,16 +463,27 @@ public:
   PWebRenderBridgeParent* AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
                                                       const LayoutDeviceIntSize& aSize,
                                                       TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                       uint32_t* aIdNamespace) override;
   bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
   static void SetWebRenderProfilerEnabled(bool aEnabled);
 
   static CompositorBridgeParent* GetCompositorBridgeParentFromLayersId(const uint64_t& aLayersId);
+
+#if defined(MOZ_WIDGET_ANDROID)
+  gfx::IntSize GetEGLSurfaceSize() {
+    return mEGLSurfaceSize;
+  }
+
+  uint64_t GetRootLayerTreeId() {
+    return mRootLayerTreeID;
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
 private:
 
   void Initialize();
 
   /**
    * Called during destruction in order to release resources as early as possible.
    */
   void StopAndClearResources();
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -553,13 +553,12 @@ struct TransactionInfo
   uint64_t id;
   TargetConfig targetConfig;
   PluginWindowData[] plugins;
   bool isFirstPaint;
   bool scheduleComposite;
   uint32_t paintSequenceNumber;
   bool isRepeatTransaction;
   TimeStamp transactionStart;
-  int32_t paintSyncId;
 };
 
 } // namespace
 } // namespace
--- a/gfx/layers/ipc/PAPZ.ipdl
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -51,18 +51,16 @@ parent:
 child:
 
   async RequestContentRepaint(FrameMetrics frame);
 
   async UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent);
 
   async UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent);
 
-  async SetScrollingRootContent(bool aIsRootContent);
-
   async NotifyMozMouseScrollEvent(ViewID aScrollId, nsString aEvent);
 
   async NotifyAPZStateChange(ScrollableLayerGuid aGuid, APZStateChange aChange, int aArg);
 
   async NotifyFlushComplete();
 
   async NotifyAsyncScrollbarDragRejected(ViewID aScrollId);
 
--- a/gfx/layers/ipc/PUiCompositorController.ipdl
+++ b/gfx/layers/ipc/PUiCompositorController.ipdl
@@ -1,29 +1,46 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* 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/. */
 
+using CSSRect from "Units.h";
+using CSSToScreenScale from "Units.h";
+using ScreenIntSize from "Units.h";
+using ScreenPoint from "Units.h";
+
 namespace mozilla {
 namespace layers {
 
 /**
  * The PUiCompositorController protocol is used to pause and resume the
  * compositor from the UI thread. Primarily used on Android to coordinate registering and
  * releasing the surface with the compositor.
  */
 sync protocol PUiCompositorController
 {
 
 parent:
   // Pause/resume the compositor. These are intended to be used on mobile, when
   // the compositor needs to pause/resume in lockstep with the application.
-  sync Pause(uint64_t layersId);
-  sync Resume(uint64_t layersId);
-  sync ResumeAndResize(uint64_t layersId, int32_t height, int32_t width);
-  async InvalidateAndRender(uint64_t layersId);
+  sync Pause();
+  sync Resume();
+  sync ResumeAndResize(int32_t aWidth, int32_t aHeight);
+
+  async InvalidateAndRender();
+  async MaxToolbarHeight(int32_t aHeight);
+  async Pinned(bool aPinned, int32_t aReason);
+  async ToolbarAnimatorMessageFromUI(int32_t aMessage);
+  async DefaultClearColor(uint32_t aColor);
+  async RequestScreenPixels();
+  async EnableLayerUpdateNotifications(bool aEnable);
+  async ToolbarPixelsToCompositor(Shmem aMem, ScreenIntSize aSize);
+child:
+  async ToolbarAnimatorMessageFromCompositor(int32_t aMessage);
+  async RootFrameMetrics(ScreenPoint aScrollOffset, CSSToScreenScale aZoom, CSSRect aPage);
+  async ScreenPixels(Shmem aMem, ScreenIntSize aSize);
 };
 
 } // layers
 } // mozilla
--- a/gfx/layers/ipc/RemoteContentController.cpp
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -198,30 +198,16 @@ RemoteContentController::UpdateOverscrol
     return;
   }
   if (mCanSend) {
     Unused << SendUpdateOverscrollOffset(aX, aY, aIsRootContent);
   }
 }
 
 void
-RemoteContentController::SetScrollingRootContent(bool aIsRootContent)
-{
-  if (MessageLoop::current() != mCompositorThread) {
-    mCompositorThread->PostTask(NewRunnableMethod<bool>(this,
-                                             &RemoteContentController::SetScrollingRootContent,
-                                             aIsRootContent));
-    return;
-  }
-  if (mCanSend) {
-    Unused << SendSetScrollingRootContent(aIsRootContent);
-  }
-}
-
-void
 RemoteContentController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
                                                    const nsString& aEvent)
 {
   if (MessageLoop::current() != mCompositorThread) {
     // We have to send messages from the compositor thread
     mCompositorThread->PostTask(NewRunnableMethod<FrameMetrics::ViewID,
                                         nsString>(this,
                                                   &RemoteContentController::NotifyMozMouseScrollEvent,
--- a/gfx/layers/ipc/RemoteContentController.h
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -62,18 +62,16 @@ public:
   virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                     APZStateChange aChange,
                                     int aArg) override;
 
   virtual void UpdateOverscrollVelocity(float aX, float aY, bool aIsRootContent) override;
 
   virtual void UpdateOverscrollOffset(float aX, float aY, bool aIsRootContent) override;
 
-  virtual void SetScrollingRootContent(bool aIsRootContent) override;
-
   virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
                                          const nsString& aEvent) override;
 
   virtual void NotifyFlushComplete() override;
 
   virtual void NotifyAsyncScrollbarDragRejected(const FrameMetrics::ViewID& aScrollId) override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -186,17 +186,16 @@ KnowsCompositor::~KnowsCompositor()
 {}
 
 ShadowLayerForwarder::ShadowLayerForwarder(ClientLayerManager* aClientLayerManager)
  : mClientLayerManager(aClientLayerManager)
  , mMessageLoop(MessageLoop::current())
  , mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
  , mIsFirstPaint(false)
  , mWindowOverlayChanged(false)
- , mPaintSyncId(0)
  , mNextLayerHandle(1)
 {
   mTxn = new Transaction();
   if (TabGroup* tabGroup = mClientLayerManager->GetTabGroup()) {
     mEventTarget = tabGroup->EventTargetFor(TaskCategory::Other);
   }
   MOZ_ASSERT(mEventTarget || !XRE_IsContentProcess());
   mActiveResourceTracker = MakeUnique<ActiveResourceTracker>(
@@ -731,17 +730,16 @@ ShadowLayerForwarder::EndTransaction(con
   info.fwdTransactionId() = GetFwdTransactionId();
   info.id() = aId;
   info.plugins() = mPluginWindowData;
   info.isFirstPaint() = mIsFirstPaint;
   info.scheduleComposite() = aScheduleComposite;
   info.paintSequenceNumber() = aPaintSequenceNumber;
   info.isRepeatTransaction() = aIsRepeatTransaction;
   info.transactionStart() = aTransactionStart;
-  info.paintSyncId() = mPaintSyncId;
 
   TargetConfig targetConfig(mTxn->mTargetBounds,
                             mTxn->mTargetRotation,
                             mTxn->mTargetOrientation,
                             aRegionToClear);
   info.targetConfig() = targetConfig;
 
   if (!GetTextureForwarder()->IsSameProcess()) {
@@ -772,17 +770,16 @@ ShadowLayerForwarder::EndTransaction(con
 
   if (startTime) {
     mPaintTiming.sendMs() = (TimeStamp::Now() - startTime.value()).ToMilliseconds();
     mShadowManager->SendRecordPaintTimes(mPaintTiming);
   }
 
   *aSent = true;
   mIsFirstPaint = false;
-  mPaintSyncId = 0;
   MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
   return true;
 }
 
 RefPtr<CompositableClient>
 ShadowLayerForwarder::FindCompositable(const CompositableHandle& aHandle)
 {
   CompositableClient* client = nullptr;
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -362,18 +362,16 @@ public:
    */
   LayerHandle ConstructShadowFor(ShadowableLayer* aLayer);
 
   /**
    * Flag the next paint as the first for a document.
    */
   void SetIsFirstPaint() { mIsFirstPaint = true; }
 
-  void SetPaintSyncId(int32_t aSyncId) { mPaintSyncId = aSyncId; }
-
   void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch);
 
   static void PlatformSyncBeforeUpdate();
 
   virtual bool AllocSurfaceDescriptor(const gfx::IntSize& aSize,
                                       gfxContentType aContent,
                                       SurfaceDescriptor* aBuffer) override;
 
@@ -437,17 +435,16 @@ protected:
 private:
 
   ClientLayerManager* mClientLayerManager;
   Transaction* mTxn;
   MessageLoop* mMessageLoop;
   DiagnosticTypes mDiagnosticTypes;
   bool mIsFirstPaint;
   bool mWindowOverlayChanged;
-  int32_t mPaintSyncId;
   InfallibleTArray<PluginWindowData> mPluginWindowData;
   UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
   uint64_t mNextLayerHandle;
   nsDataHashtable<nsUint64HashKey, CompositableClient*> mCompositables;
   PaintTiming mPaintTiming;
   /**
    * ShadowLayerForwarder might dispatch tasks to main while puppet widget and
    * tabChild don't exist anymore; therefore we hold the event target since its
--- a/gfx/layers/ipc/UiCompositorControllerChild.cpp
+++ b/gfx/layers/ipc/UiCompositorControllerChild.cpp
@@ -1,242 +1,362 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "UiCompositorControllerChild.h"
-#include "UiCompositorControllerParent.h"
+#include "mozilla/layers/UiCompositorControllerChild.h"
+
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/UiCompositorControllerMessageTypes.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/StaticPtr.h"
+#include "nsBaseWidget.h"
 #include "nsThreadUtils.h"
-#include "mozilla/gfx/GPUProcessManager.h"
+
+#if defined(MOZ_WIDGET_ANDROID)
+#include "mozilla/widget/AndroidUiThread.h"
+
+static RefPtr<nsThread>
+GetUiThread()
+{
+  return mozilla::GetAndroidUiThread();
+}
+#else
+static RefPtr<nsThread>
+GetUiThread()
+{
+  MOZ_CRASH("Platform does not support UiCompositorController");
+  return nullptr;
+}
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+static bool
+IsOnUiThread()
+{
+  return NS_GetCurrentThread() == GetUiThread();
+}
 
 namespace mozilla {
 namespace layers {
-using namespace gfx;
 
-static bool sInitialized = false;
-static StaticRefPtr<UiCompositorControllerChild> sChild;
-static StaticRefPtr<UiCompositorControllerParent> sParent;
-
-namespace {
-
-struct SurfaceResizeCache {
-  int32_t mSurfaceWidth;
-  int32_t mSurfaceHeight;
-
-  SurfaceResizeCache(int32_t aWidth, int32_t aHeight) :
-    mSurfaceWidth(aWidth),
-    mSurfaceHeight(aHeight) {}
-
-  SurfaceResizeCache(const SurfaceResizeCache& value)
-  {
-    *this = value;
-  }
 
-  SurfaceResizeCache& operator=(const SurfaceResizeCache& value)
-  {
-    mSurfaceWidth = value.mSurfaceWidth;
-    mSurfaceHeight = value.mSurfaceHeight;
-    return *this;
-  }
-
-  SurfaceResizeCache() :
-    mSurfaceWidth(0),
-    mSurfaceHeight(0) {}
-};
-
-static std::map<int64_t, SurfaceResizeCache> sResizeCache;
-
-static void
-DoCachedResize()
+// public:
+/* static */ RefPtr<UiCompositorControllerChild>
+UiCompositorControllerChild::CreateForSameProcess(const int64_t& aRootLayerTreeId)
 {
-  MOZ_ASSERT(sChild);
-  MOZ_ASSERT(sChild->IsOnUiThread());
-
-  for (auto& cache : sResizeCache) {
-    sChild->SendResumeAndResize(cache.first, cache.second.mSurfaceWidth, cache.second.mSurfaceHeight);
-  }
-
-  sResizeCache.clear();
+  RefPtr<UiCompositorControllerChild> child = new UiCompositorControllerChild(0);
+  child->mParent = new UiCompositorControllerParent(aRootLayerTreeId);
+  GetUiThread()->Dispatch(NewRunnableMethod(child, &UiCompositorControllerChild::OpenForSameProcess), nsIThread::DISPATCH_NORMAL);
+  return child;
 }
 
-} // namespace
-
-UiCompositorControllerChild::UiCompositorControllerChild(RefPtr<nsThread> aThread, const uint64_t& aProcessToken)
- : mUiThread(aThread),
-   mProcessToken(aProcessToken)
-{
-}
-
-UiCompositorControllerChild::~UiCompositorControllerChild()
-{
-}
-
-/* static */ UiCompositorControllerChild*
-UiCompositorControllerChild::Get()
-{
-  return sChild;
-}
-
-/* static */ bool
-UiCompositorControllerChild::IsInitialized()
-{
-  return sInitialized;
-}
-
-/* static */ void
-UiCompositorControllerChild::Shutdown()
+/* static */ RefPtr<UiCompositorControllerChild>
+UiCompositorControllerChild::CreateForGPUProcess(const uint64_t& aProcessToken,
+                                                 Endpoint<PUiCompositorControllerChild>&& aEndpoint)
 {
-  RefPtr<UiCompositorControllerChild> child = sChild;
-  if (child) {
-    child->Close();
-    sInitialized = false;
-  }
-}
-
-/* static */ void
-UiCompositorControllerChild::InitSameProcess(RefPtr<nsThread> aThread)
-{
-  MOZ_ASSERT(!sChild);
-  MOZ_ASSERT(!sParent);
-  MOZ_ASSERT(aThread);
-  MOZ_ASSERT(!sInitialized);
-
-  sInitialized = true;
-  RefPtr<UiCompositorControllerChild> child = new UiCompositorControllerChild(aThread, 0);
-  sParent = new UiCompositorControllerParent();
-  aThread->Dispatch(NewRunnableMethod(child, &UiCompositorControllerChild::OpenForSameProcess), nsIThread::DISPATCH_NORMAL);
-}
-
-/* static */ void
-UiCompositorControllerChild::InitWithGPUProcess(RefPtr<nsThread> aThread,
-                                                const uint64_t& aProcessToken,
-                                                Endpoint<PUiCompositorControllerChild>&& aEndpoint)
-{
-  MOZ_ASSERT(!sChild);
-  MOZ_ASSERT(!sParent);
-  MOZ_ASSERT(aThread);
-  MOZ_ASSERT(!sInitialized);
-
-  sInitialized = true;
-  RefPtr<UiCompositorControllerChild> child = new UiCompositorControllerChild(aThread, aProcessToken);
+  RefPtr<UiCompositorControllerChild> child = new UiCompositorControllerChild(aProcessToken);
 
   RefPtr<nsIRunnable> task = NewRunnableMethod<Endpoint<PUiCompositorControllerChild>&&>(
     child, &UiCompositorControllerChild::OpenForGPUProcess, Move(aEndpoint));
 
-  aThread->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL);
+  GetUiThread()->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL);
+  return child;
+}
+
+bool
+UiCompositorControllerChild::Pause()
+{
+  if (!mIsOpen) {
+    return false;
+  }
+  return SendPause();
+}
+
+bool
+UiCompositorControllerChild::Resume()
+{
+  if (!mIsOpen) {
+    return false;
+  }
+  return SendResume();
 }
 
-/* static */ void
-UiCompositorControllerChild::CacheSurfaceResize(int64_t aId, int32_t aWidth, int32_t aHeight)
+bool
+UiCompositorControllerChild::ResumeAndResize(const int32_t& aWidth, const int32_t& aHeight)
+{
+  if (!mIsOpen) {
+    mResize = Some(gfx::IntSize(aWidth, aHeight));
+    // Since we are caching these values, pretend the call succeeded.
+    return true;
+  }
+  return SendResumeAndResize(aWidth, aHeight);
+}
+
+bool
+UiCompositorControllerChild::InvalidateAndRender()
 {
-  // This should only be called if the sChild has not been set yet.
-  // It should also only be called from the UI thread but since the sChild hasn't been set
-  // yet, there isn't a good way to verify this.
-  MOZ_ASSERT(!sChild);
-  sResizeCache[aId] = SurfaceResizeCache{aWidth, aHeight};
+  if (!mIsOpen) {
+    return false;
+  }
+  return SendInvalidateAndRender();
+}
+
+bool
+UiCompositorControllerChild::SetMaxToolbarHeight(const int32_t& aHeight)
+{
+  if (!mIsOpen) {
+    mMaxToolbarHeight = Some(aHeight);
+    // Since we are caching this value, pretend the call succeeded.
+    return true;
+  }
+  return SendMaxToolbarHeight(aHeight);
 }
 
-void
-UiCompositorControllerChild::OpenForSameProcess()
+bool
+UiCompositorControllerChild::SetPinned(const bool& aPinned, const int32_t& aReason)
+{
+  if (!mIsOpen) {
+    return false;
+  }
+  return SendPinned(aPinned, aReason);
+}
+
+bool
+UiCompositorControllerChild::ToolbarAnimatorMessageFromUI(const int32_t& aMessage)
 {
-  MOZ_ASSERT(sParent);
-  MOZ_ASSERT(!sChild);
-  MOZ_ASSERT(IsOnUiThread());
+  if (!mIsOpen) {
+    return false;
+  }
+
+  return SendToolbarAnimatorMessageFromUI(aMessage);
+}
 
-  if (!Open(sParent->GetIPCChannel(),
-           mozilla::layers::CompositorThreadHolder::Loop(),
-           mozilla::ipc::ChildSide)) {
-    sParent = nullptr;
-    return;
+bool
+UiCompositorControllerChild::SetDefaultClearColor(const uint32_t& aColor)
+{
+  if (!mIsOpen) {
+    mDefaultClearColor = Some(aColor);
+    // Since we are caching this value, pretend the call succeeded.
+    return true;
   }
 
-  AddRef();
-  sChild = this;
-  DoCachedResize();
+  return SendDefaultClearColor(aColor);
+}
+
+bool
+UiCompositorControllerChild::RequestScreenPixels()
+{
+  if (!mIsOpen) {
+    return false;
+  }
+
+  return SendRequestScreenPixels();
+}
+
+bool
+UiCompositorControllerChild::EnableLayerUpdateNotifications(const bool& aEnable)
+{
+  if (!mIsOpen) {
+    mLayerUpdateEnabled = Some(aEnable);
+    // Since we are caching this value, pretend the call succeeded.
+    return true;
+  }
+
+  return SendEnableLayerUpdateNotifications(aEnable);
+}
+
+bool
+UiCompositorControllerChild::ToolbarPixelsToCompositor(Shmem& aMem, const ScreenIntSize& aSize)
+{
+  if (!mIsOpen) {
+    return false;
+  }
+
+  return SendToolbarPixelsToCompositor(aMem, aSize);
 }
 
 void
-UiCompositorControllerChild::OpenForGPUProcess(Endpoint<PUiCompositorControllerChild>&& aEndpoint)
+UiCompositorControllerChild::Destroy()
 {
-  MOZ_ASSERT(!sChild);
-  MOZ_ASSERT(IsOnUiThread());
-
-  if (!aEndpoint.Bind(this)) {
-    // The GPU Process Manager might be gone if we receive ActorDestroy very
-    // late in shutdown.
-    if (GPUProcessManager* gpm = GPUProcessManager::Get()) {
-      gpm->NotifyRemoteActorDestroyed(mProcessToken);
-    }
+  if (!IsOnUiThread()) {
+    GetUiThread()->Dispatch(NewRunnableMethod(this, &UiCompositorControllerChild::Destroy), nsIThread::DISPATCH_NORMAL);
     return;
   }
 
-  AddRef();
-  sChild = this;
-  DoCachedResize();
+  if (mIsOpen) {
+    // Close the underlying IPC channel.
+    PUiCompositorControllerChild::Close();
+    mIsOpen = false;
+  }
 }
 
 void
-UiCompositorControllerChild::Close()
+UiCompositorControllerChild::SetBaseWidget(nsBaseWidget* aWidget)
 {
-  if (!IsOnUiThread()) {
-    mUiThread->Dispatch(NewRunnableMethod(this, &UiCompositorControllerChild::Close), nsIThread::DISPATCH_NORMAL);
-    return;
-  }
-
-  // We clear mProcessToken when the channel is closed.
-  if (!mProcessToken) {
-    return;
-  }
-
-  // Clear the process token so we don't notify the GPUProcessManager. It already
-  // knows we're closed since it manually called Close, and in fact the GPM could
-  // have already been destroyed during shutdown.
-  mProcessToken = 0;
-  if (this == sChild) {
-    sChild = nullptr;
-  }
-
-  // Close the underlying IPC channel.
-  PUiCompositorControllerChild::Close();
+  mWidget = aWidget;
 }
 
+bool
+UiCompositorControllerChild::AllocPixelBuffer(const int32_t aSize, Shmem* aMem)
+{
+  MOZ_ASSERT(aSize > 0);
+  return AllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, aMem);
+}
+
+bool
+UiCompositorControllerChild::DeallocPixelBuffer(Shmem& aMem)
+{
+  return DeallocShmem(aMem);
+}
+
+// protected:
 void
 UiCompositorControllerChild::ActorDestroy(ActorDestroyReason aWhy)
 {
+  mIsOpen = false;
+  mParent = nullptr;
+
   if (mProcessToken) {
-    GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
+    gfx::GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken);
     mProcessToken = 0;
-    sParent = nullptr;
   }
 }
 
 void
 UiCompositorControllerChild::DeallocPUiCompositorControllerChild()
 {
+  if (mParent) {
+    mParent = nullptr;
+  }
   Release();
-  sInitialized = false;
 }
 
 void
 UiCompositorControllerChild::ProcessingError(Result aCode, const char* aReason)
 {
   MOZ_RELEASE_ASSERT(aCode == MsgDropped, "Processing error in UiCompositorControllerChild");
 }
 
 void
 UiCompositorControllerChild::HandleFatalError(const char* aName, const char* aMsg) const
 {
   dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
 }
 
-bool
-UiCompositorControllerChild::IsOnUiThread() const
+mozilla::ipc::IPCResult
+UiCompositorControllerChild::RecvToolbarAnimatorMessageFromCompositor(const int32_t& aMessage)
+{
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mWidget) {
+    mWidget->RecvToolbarAnimatorMessageFromCompositor(aMessage);
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerChild::RecvRootFrameMetrics(const ScreenPoint& aScrollOffset, const CSSToScreenScale& aZoom, const CSSRect& aPage)
+{
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mWidget) {
+    mWidget->UpdateRootFrameMetrics(aScrollOffset, aZoom, aPage);
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerChild::RecvScreenPixels(ipc::Shmem&& aMem, const ScreenIntSize& aSize)
+{
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mWidget) {
+    mWidget->RecvScreenPixels(Move(aMem), aSize);
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  return IPC_OK();
+}
+
+// private:
+UiCompositorControllerChild::UiCompositorControllerChild(const uint64_t& aProcessToken)
+ : mIsOpen(false)
+ , mProcessToken(aProcessToken)
+ , mWidget(nullptr)
+{
+}
+
+UiCompositorControllerChild::~UiCompositorControllerChild()
+{
+}
+
+void
+UiCompositorControllerChild::OpenForSameProcess()
 {
-  return NS_GetCurrentThread() == mUiThread;
+  MOZ_ASSERT(IsOnUiThread());
+
+  mIsOpen = Open(mParent->GetIPCChannel(),
+                 mozilla::layers::CompositorThreadHolder::Loop(),
+                 mozilla::ipc::ChildSide);
+
+  if (!mIsOpen) {
+    mParent = nullptr;
+    return;
+  }
+
+  mParent->InitializeForSameProcess();
+  AddRef();
+  SendCachedValues();
+  // Let Ui thread know the connection is open;
+  RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN);
+}
+
+void
+UiCompositorControllerChild::OpenForGPUProcess(Endpoint<PUiCompositorControllerChild>&& aEndpoint)
+{
+  MOZ_ASSERT(IsOnUiThread());
+
+  mIsOpen = aEndpoint.Bind(this);
+
+  if (!mIsOpen) {
+    // The GPU Process Manager might be gone if we receive ActorDestroy very
+    // late in shutdown.
+    if (gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get()) {
+      gpm->NotifyRemoteActorDestroyed(mProcessToken);
+    }
+    return;
+  }
+
+  AddRef();
+  SendCachedValues();
+  // Let Ui thread know the connection is open;
+  RecvToolbarAnimatorMessageFromCompositor(COMPOSITOR_CONTROLLER_OPEN);
+}
+
+void
+UiCompositorControllerChild::SendCachedValues()
+{
+  MOZ_ASSERT(mIsOpen);
+  if (mResize) {
+    SendResumeAndResize(mResize.ref().width, mResize.ref().height);
+    mResize.reset();
+  }
+  if (mMaxToolbarHeight) {
+    SendMaxToolbarHeight(mMaxToolbarHeight.ref());
+    mMaxToolbarHeight.reset();
+  }
+  if (mDefaultClearColor) {
+    SendDefaultClearColor(mDefaultClearColor.ref());
+    mDefaultClearColor.reset();
+  }
+  if (mLayerUpdateEnabled) {
+    SendEnableLayerUpdateNotifications(mLayerUpdateEnabled.ref());
+    mLayerUpdateEnabled.reset();
+  }
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/UiCompositorControllerChild.h
+++ b/gfx/layers/ipc/UiCompositorControllerChild.h
@@ -2,51 +2,78 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef include_gfx_ipc_UiCompositorControllerChild_h
 #define include_gfx_ipc_UiCompositorControllerChild_h
 
 #include "mozilla/layers/PUiCompositorControllerChild.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
 #include "mozilla/RefPtr.h"
-#include <nsThread.h>
+#include "nsThread.h"
+
+class nsBaseWidget;
 
 namespace mozilla {
 namespace layers {
 
-class UiCompositorControllerChild final : public PUiCompositorControllerChild
+class UiCompositorControllerChild final : protected PUiCompositorControllerChild
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerChild)
 
-  static bool IsInitialized();
-  static void Shutdown();
-  static UiCompositorControllerChild* Get();
-  static void InitSameProcess(RefPtr<nsThread> aThread);
-  static void InitWithGPUProcess(RefPtr<nsThread> aThread,
-                                 const uint64_t& aProcessToken,
-                                 Endpoint<PUiCompositorControllerChild>&& aEndpoint);
+  static RefPtr<UiCompositorControllerChild> CreateForSameProcess(const int64_t& aRootLayerTreeId);
+  static RefPtr<UiCompositorControllerChild> CreateForGPUProcess(const uint64_t& aProcessToken,
+                                                                 Endpoint<PUiCompositorControllerChild>&& aEndpoint);
 
-  static void CacheSurfaceResize(int64_t aId, int32_t aWidth, int32_t aHeight);
-  void Close();
+  bool Pause();
+  bool Resume();
+  bool ResumeAndResize(const int32_t& aHeight, const int32_t& aWidth);
+  bool InvalidateAndRender();
+  bool SetMaxToolbarHeight(const int32_t& aHeight);
+  bool SetPinned(const bool& aPinned, const int32_t& aReason);
+  bool ToolbarAnimatorMessageFromUI(const int32_t& aMessage);
+  bool SetDefaultClearColor(const uint32_t& aColor);
+  bool RequestScreenPixels();
+  bool EnableLayerUpdateNotifications(const bool& aEnable);
+  bool ToolbarPixelsToCompositor(Shmem& aMem, const ScreenIntSize& aSize);
 
+  void Destroy();
+
+  void SetBaseWidget(nsBaseWidget* aWidget);
+  bool AllocPixelBuffer(const int32_t aSize, Shmem* aMem);
+  bool DeallocPixelBuffer(Shmem& aMem);
+
+protected:
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPUiCompositorControllerChild() override;
   void ProcessingError(Result aCode, const char* aReason) override;
-
   virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
-
-  bool IsOnUiThread() const;
+  mozilla::ipc::IPCResult RecvToolbarAnimatorMessageFromCompositor(const int32_t& aMessage) override;
+  mozilla::ipc::IPCResult RecvRootFrameMetrics(const ScreenPoint& aScrollOffset, const CSSToScreenScale& aZoom, const CSSRect& aPage) override;
+  mozilla::ipc::IPCResult RecvScreenPixels(ipc::Shmem&& aMem, const ScreenIntSize& aSize) override;
 private:
-  UiCompositorControllerChild(RefPtr<nsThread> aThread, const uint64_t& aProcessToken);
+  explicit UiCompositorControllerChild(const uint64_t& aProcessToken);
   ~UiCompositorControllerChild();
   void OpenForSameProcess();
   void OpenForGPUProcess(Endpoint<PUiCompositorControllerChild>&& aEndpoint);
+  void SendCachedValues();
 
-  RefPtr<nsThread> mUiThread;
+  bool mIsOpen;
   uint64_t mProcessToken;
+  Maybe<gfx::IntSize> mResize;
+  Maybe<int32_t> mMaxToolbarHeight;
+  Maybe<uint32_t> mDefaultClearColor;
+  Maybe<bool> mLayerUpdateEnabled;
+  nsBaseWidget* mWidget;
+  // Should only be set when compositor is in process.
+  RefPtr<UiCompositorControllerParent> mParent;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // include_gfx_ipc_UiCompositorControllerChild_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/UiCompositorControllerMessageTypes.h
@@ -0,0 +1,36 @@
+/* -*- 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 include_gfx_ipc_UiCompositorControllerMessageTypes_h
+#define include_gfx_ipc_UiCompositorControllerMessageTypes_h
+
+namespace mozilla {
+namespace layers {
+
+//
+// NOTE: These values are also defined in mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
+//       and must be kept in sync. Any new message added here must also be added there to the AnimatorMessageType enum.
+//
+
+enum UiCompositorControllerMessageTypes {
+  STATIC_TOOLBAR_NEEDS_UPDATE      = 0,  // Sent from compositor when the static toolbar wants to hide.
+  STATIC_TOOLBAR_READY             = 1,  // Sent from compositor when the static toolbar image has been updated and is ready to animate.
+  TOOLBAR_HIDDEN                   = 2,  // Sent to compositor when the real toolbar has been hidden.
+  TOOLBAR_VISIBLE                  = 3,  // Sent to compositor when the real toolbar has been made  visible
+  TOOLBAR_SHOW                     = 4,  // Sent from compositor when the real toolbar should be shown
+  FIRST_PAINT                      = 5,  // Sent from compositor after first paint
+  REQUEST_SHOW_TOOLBAR_IMMEDIATELY = 6,  // Sent to the compositor when the snapshot should be shown immediately
+  REQUEST_SHOW_TOOLBAR_ANIMATED    = 7,  // Sent to the compositor when the snapshot should be shown with an animation
+  REQUEST_HIDE_TOOLBAR_IMMEDIATELY = 8,  // Sent to the compositor when the snapshot should be hidden immediately
+  REQUEST_HIDE_TOOLBAR_ANIMATED    = 9,  // Sent to the compositor when the snapshot should be hidden with an animation
+  LAYERS_UPDATED                   = 10, // Sent from the compositor when any layer has been updated
+  COMPOSITOR_CONTROLLER_OPEN       = 20  // Compositor controller IPC is open
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // include_gfx_ipc_UiCompositorControllerMessageTypes_h
--- a/gfx/layers/ipc/UiCompositorControllerParent.cpp
+++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp
@@ -1,118 +1,299 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "UiCompositorControllerParent.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/Move.h"
+#include "mozilla/Unused.h"
 
 namespace mozilla {
 namespace layers {
 
-RefPtr<UiCompositorControllerParent>
-UiCompositorControllerParent::Start(Endpoint<PUiCompositorControllerParent>&& aEndpoint)
+typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
+
+/* static */ RefPtr<UiCompositorControllerParent>
+UiCompositorControllerParent::GetFromRootLayerTreeId(const uint64_t& aRootLayerTreeId)
 {
-  RefPtr<UiCompositorControllerParent> parent = new UiCompositorControllerParent();
+  LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aRootLayerTreeId);
+  if (state) {
+    return state->mUiControllerParent;
+  }
+
+  return nullptr;
+}
+
+/* static */ RefPtr<UiCompositorControllerParent>
+UiCompositorControllerParent::Start(const uint64_t& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint)
+{
+  RefPtr<UiCompositorControllerParent> parent = new UiCompositorControllerParent(aRootLayerTreeId);
 
   RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PUiCompositorControllerParent>&&>(
     parent, &UiCompositorControllerParent::Open, Move(aEndpoint));
   CompositorThreadHolder::Loop()->PostTask(task.forget());
 
   return parent;
 }
 
-UiCompositorControllerParent::UiCompositorControllerParent()
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvPause()
+{
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
+  if (parent) {
+    parent->PauseComposition();
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvResume()
+{
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
+  if (parent) {
+    parent->ResumeComposition();
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvResumeAndResize(const int32_t& aWidth,
+                                                  const int32_t& aHeight)
+{
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
+  if (parent) {
+    parent->ResumeCompositionAndResize(aWidth, aHeight);
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvInvalidateAndRender()
+{
+  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
+  if (parent) {
+    parent->Invalidate();
+    parent->ScheduleComposition();
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvMaxToolbarHeight(const int32_t& aHeight)
+{
+  mMaxToolbarHeight = aHeight;
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mAnimator) {
+    mAnimator->SetMaxToolbarHeight(mMaxToolbarHeight);
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvPinned(const bool& aPinned, const int32_t& aReason)
+{
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mAnimator) {
+    mAnimator->SetPinned(aPinned, aReason);
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvToolbarAnimatorMessageFromUI(const int32_t& aMessage)
+{
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mAnimator) {
+    mAnimator->ToolbarAnimatorMessageFromUI(aMessage);
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvDefaultClearColor(const uint32_t& aColor)
+{
+  LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+
+  if (state && state->mLayerManager) {
+    Compositor* compositor = state->mLayerManager->GetCompositor();
+    if (compositor) {
+      // Android Color is ARGB which is apparently unusual.
+      compositor->SetDefaultClearColor(gfx::Color::UnusualFromARGB(aColor));
+    }
+  }
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvRequestScreenPixels()
+{
+#if defined(MOZ_WIDGET_ANDROID)
+  LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+
+  if (state && state->mLayerManager && state->mParent) {
+    state->mLayerManager->RequestScreenPixels(this);
+    state->mParent->Invalidate();
+    state->mParent->ScheduleComposition();
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvEnableLayerUpdateNotifications(const bool& aEnable)
+{
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mAnimator) {
+    mAnimator->EnableLayersUpdateNotifications(aEnable);
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UiCompositorControllerParent::RecvToolbarPixelsToCompositor(Shmem&& aMem, const ScreenIntSize& aSize)
+{
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mAnimator) {
+    // By adopting the Shmem, the animator is responsible for deallocating.
+    mAnimator->AdoptToolbarPixels(Move(aMem), aSize);
+  } else {
+    DeallocShmem(aMem);
+  }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  return IPC_OK();
+}
+
+void
+UiCompositorControllerParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+}
+
+void
+UiCompositorControllerParent::DeallocPUiCompositorControllerParent()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  Shutdown();
+  Release(); // For AddRef in Initialize()
+}
+
+#if defined(MOZ_WIDGET_ANDROID)
+void
+UiCompositorControllerParent::RegisterAndroidDynamicToolbarAnimator(AndroidDynamicToolbarAnimator* aAnimator)
+{
+  MOZ_ASSERT(!mAnimator);
+  mAnimator = aAnimator;
+  if (mAnimator) {
+    mAnimator->SetMaxToolbarHeight(mMaxToolbarHeight);
+  }
+}
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+void
+UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor(int32_t aMessage)
+{
+  // This function can be call from ether compositor or controller thread.
+  if (!CompositorThreadHolder::IsInCompositorThread()) {
+    CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod<int32_t>(this, &UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor, aMessage));
+    return;
+  }
+
+  Unused << SendToolbarAnimatorMessageFromCompositor(aMessage);
+}
+
+bool
+UiCompositorControllerParent::AllocPixelBuffer(const int32_t aSize, ipc::Shmem* aMem)
+{
+  MOZ_ASSERT(aSize > 0);
+  return AllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, aMem);
+}
+
+UiCompositorControllerParent::UiCompositorControllerParent(const uint64_t& aRootLayerTreeId)
+  : mRootLayerTreeId(aRootLayerTreeId)
+  , mMaxToolbarHeight(0)
 {
   MOZ_COUNT_CTOR(UiCompositorControllerParent);
 }
 
 UiCompositorControllerParent::~UiCompositorControllerParent()
 {
   MOZ_COUNT_DTOR(UiCompositorControllerParent);
 }
 
 void
+UiCompositorControllerParent::InitializeForSameProcess()
+{
+  // This function is called by UiCompositorControllerChild in the main thread.
+  // So dispatch to the compositor thread to Initialize.
+  if (!CompositorThreadHolder::IsInCompositorThread()) {
+    CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod(this, &UiCompositorControllerParent::InitializeForSameProcess));
+    return;
+  }
+
+  Initialize();
+}
+
+void
+UiCompositorControllerParent::InitializeForOutOfProcess()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  Initialize();
+}
+
+void
+UiCompositorControllerParent::Initialize()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  AddRef();
+  LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+  MOZ_ASSERT(state);
+  MOZ_ASSERT(state->mParent);
+  state->mUiControllerParent = this;
+#if defined(MOZ_WIDGET_ANDROID)
+  state->mParent->GetAPZCTreeManager()->InitializeDynamicToolbarAnimator(mRootLayerTreeId);
+#endif
+}
+
+void
 UiCompositorControllerParent::Open(Endpoint<PUiCompositorControllerParent>&& aEndpoint)
 {
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (!aEndpoint.Bind(this)) {
     // We can't recover from this.
     MOZ_CRASH("Failed to bind UiCompositorControllerParent to endpoint");
   }
-  AddRef();
-}
-
-mozilla::ipc::IPCResult
-UiCompositorControllerParent::RecvPause(const uint64_t& aLayersId)
-{
-  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(aLayersId);
-  if (parent) {
-    parent->PauseComposition();
-  }
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-UiCompositorControllerParent::RecvResume(const uint64_t& aLayersId)
-{
-  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(aLayersId);
-  if (parent) {
-    parent->ResumeComposition();
-  }
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-UiCompositorControllerParent::RecvResumeAndResize(const uint64_t& aLayersId,
-                                                  const int32_t& aHeight,
-                                                  const int32_t& aWidth)
-{
-  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(aLayersId);
-  if (parent) {
-    parent->ResumeCompositionAndResize(aHeight, aWidth);
-  }
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
-UiCompositorControllerParent::RecvInvalidateAndRender(const uint64_t& aLayersId)
-{
-  CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(aLayersId);
-  if (parent) {
-    parent->Invalidate();
-    parent->ScheduleComposition();
-  }
-  return IPC_OK();
+  InitializeForOutOfProcess();
 }
 
 void
 UiCompositorControllerParent::Shutdown()
 {
-  MessageLoop* ccloop = CompositorThreadHolder::Loop();
-  if (MessageLoop::current() != ccloop) {
-    ccloop->PostTask(NewRunnableMethod(this, &UiCompositorControllerParent::ShutdownImpl));
-    return;
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mAnimator) {
+    mAnimator->Shutdown();
   }
-
-  ShutdownImpl();
-}
-
-void
-UiCompositorControllerParent::ShutdownImpl()
-{
-  Close();
-}
-
-void
-UiCompositorControllerParent::ActorDestroy(ActorDestroyReason aWhy)
-{
-}
-
-void
-UiCompositorControllerParent::DeallocPUiCompositorControllerParent()
-{
-  Release();
+#endif // defined(MOZ_WIDGET_ANDROID)
+  LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
+  if (state) {
+    state->mUiControllerParent = nullptr;
+  }
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/UiCompositorControllerParent.h
+++ b/gfx/layers/ipc/UiCompositorControllerParent.h
@@ -1,47 +1,73 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef include_gfx_ipc_UiCompositorControllerParent_h
 #define include_gfx_ipc_UiCompositorControllerParent_h
 
+#include "mozilla/layers/PUiCompositorControllerParent.h"
+#if defined(MOZ_WIDGET_ANDROID)
+#include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
+#endif // defined(MOZ_WIDGET_ANDROID)
+#include "mozilla/ipc/Shmem.h"
 #include "mozilla/RefPtr.h"
-#include "mozilla/layers/PUiCompositorControllerParent.h"
 
 namespace mozilla {
 namespace layers {
 
 class UiCompositorControllerParent final : public PUiCompositorControllerParent
 {
+// UiCompositorControllerChild needs to call the private constructor when running in process.
+friend class UiCompositorControllerChild;
 public:
-  UiCompositorControllerParent();
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerParent)
 
-  static RefPtr<UiCompositorControllerParent> Start(Endpoint<PUiCompositorControllerParent>&& aEndpoint);
+  static RefPtr<UiCompositorControllerParent> GetFromRootLayerTreeId(const uint64_t& aRootLayerTreeId);
+  static RefPtr<UiCompositorControllerParent> Start(const uint64_t& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint);
 
-  mozilla::ipc::IPCResult RecvPause(const uint64_t& aLayersId) override;
-  mozilla::ipc::IPCResult RecvResume(const uint64_t& aLayersId) override;
-  mozilla::ipc::IPCResult RecvResumeAndResize(const uint64_t& aLayersId,
-                                              const int32_t& aHeight,
+  // PUiCompositorControllerParent functions
+  mozilla::ipc::IPCResult RecvPause() override;
+  mozilla::ipc::IPCResult RecvResume() override;
+  mozilla::ipc::IPCResult RecvResumeAndResize(const int32_t& aHeight,
                                               const int32_t& aWidth) override;
-  mozilla::ipc::IPCResult RecvInvalidateAndRender(const uint64_t& aLayersId) override;
-
+  mozilla::ipc::IPCResult RecvInvalidateAndRender() override;
+  mozilla::ipc::IPCResult RecvMaxToolbarHeight(const int32_t& aHeight) override;
+  mozilla::ipc::IPCResult RecvPinned(const bool& aPinned, const int32_t& aReason) override;
+  mozilla::ipc::IPCResult RecvToolbarAnimatorMessageFromUI(const int32_t& aMessage) override;
+  mozilla::ipc::IPCResult RecvDefaultClearColor(const uint32_t& aColor) override;
+  mozilla::ipc::IPCResult RecvRequestScreenPixels() override;
+  mozilla::ipc::IPCResult RecvEnableLayerUpdateNotifications(const bool& aEnable) override;
+  mozilla::ipc::IPCResult RecvToolbarPixelsToCompositor(Shmem&& aMem, const ScreenIntSize& aSize) override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPUiCompositorControllerParent() override;
 
-  void Shutdown();
+  // Class specific functions
+#if defined(MOZ_WIDGET_ANDROID)
+  void RegisterAndroidDynamicToolbarAnimator(AndroidDynamicToolbarAnimator* aAnimator);
+#endif // MOZ_WIDGET_ANDROID
+  void ToolbarAnimatorMessageFromCompositor(int32_t aMessage);
+  bool AllocPixelBuffer(const int32_t aSize, Shmem* aMem);
 
 private:
+  explicit UiCompositorControllerParent(const uint64_t& aRootLayerTreeId);
   ~UiCompositorControllerParent();
-
+  void InitializeForSameProcess();
+  void InitializeForOutOfProcess();
+  void Initialize();
   void Open(Endpoint<PUiCompositorControllerParent>&& aEndpoint);
-  void ShutdownImpl();
+  void Shutdown();
+
+  uint64_t mRootLayerTreeId;
 
-private:
+#if defined(MOZ_WIDGET_ANDROID)
+  RefPtr<AndroidDynamicToolbarAnimator> mAnimator;
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+  int32_t mMaxToolbarHeight;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // include_gfx_ipc_UiCompositorControllerParent_h
--- a/gfx/layers/ipc/VideoBridgeParent.cpp
+++ b/gfx/layers/ipc/VideoBridgeParent.cpp
@@ -1,31 +1,32 @@
 /* vim: set ts=2 sw=2 et tw=80: */
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "VideoBridgeParent.h"
+#include "CompositorThread.h"
 #include "mozilla/layers/TextureHost.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
 
-
 static VideoBridgeParent* sVideoBridgeSingleton;
 
 VideoBridgeParent::VideoBridgeParent()
   : mClosed(false)
 {
   mSelfRef = this;
   sVideoBridgeSingleton = this;
+  mCompositorThreadRef = CompositorThreadHolder::GetSingleton();
 }
 
 VideoBridgeParent::~VideoBridgeParent()
 {
   sVideoBridgeSingleton = nullptr;
 }
 
 /* static */ VideoBridgeParent*
@@ -45,16 +46,17 @@ VideoBridgeParent::ActorDestroy(ActorDes
 {
   // Can't alloc/dealloc shmems from now on.
   mClosed = true;
 }
 
 void
 VideoBridgeParent::DeallocPVideoBridgeParent()
 {
+  mCompositorThreadRef = nullptr;
   mSelfRef = nullptr;
 }
 
 PTextureParent*
 VideoBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                        const LayersBackend& aLayersBackend,
                                        const TextureFlags& aFlags,
                                        const uint64_t& aSerial)
--- a/gfx/layers/ipc/VideoBridgeParent.h
+++ b/gfx/layers/ipc/VideoBridgeParent.h
@@ -7,16 +7,18 @@
 #define gfx_layers_ipc_VideoBridgeParent_h_
 
 #include "mozilla/layers/PVideoBridgeParent.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
 
 namespace mozilla {
 namespace layers {
 
+class CompositorThreadHolder;
+
 class VideoBridgeParent final : public PVideoBridgeParent,
                                 public HostIPCAllocator,
                                 public ShmemAllocator
 {
 public:
   VideoBridgeParent();
   ~VideoBridgeParent();
 
@@ -56,16 +58,17 @@ public:
   void DeallocShmem(ipc::Shmem& aShmem) override;
 
 private:
   void DeallocPVideoBridgeParent() override;
 
   // This keeps us alive until ActorDestroy(), at which point we do a
   // deferred destruction of ourselves.
   RefPtr<VideoBridgeParent> mSelfRef;
+  RefPtr<CompositorThreadHolder> mCompositorThreadRef;
 
   std::map<uint64_t, PTextureParent*> mTextureMap;
 
   bool mClosed;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -173,16 +173,17 @@ EXPORTS.mozilla.layers += [
     'ipc/LayerTreeOwnerTracker.h',
     'ipc/RemoteContentController.h',
     'ipc/ShadowLayers.h',
     'ipc/SharedPlanarYCbCrImage.h',
     'ipc/SharedRGBImage.h',
     'ipc/SynchronousTask.h',
     'ipc/TextureForwarder.h',
     'ipc/UiCompositorControllerChild.h',
+    'ipc/UiCompositorControllerMessageTypes.h',
     'ipc/UiCompositorControllerParent.h',
     'ipc/VideoBridgeChild.h',
     'ipc/VideoBridgeParent.h',
     'LayerAttributes.h',
     'LayerMetricsWrapper.h',
     'LayersTypes.h',
     'opengl/CompositingRenderTargetOGL.h',
     'opengl/CompositorOGL.h',
@@ -240,16 +241,20 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
         'ipc/ShadowLayerUtilsMac.cpp',
         'MacIOSurfaceHelpers.cpp',
         'MacIOSurfaceImage.cpp',
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     UNIFIED_SOURCES += [
         'apz/src/AndroidAPZ.cpp',
+        'apz/src/AndroidDynamicToolbarAnimator.cpp',
+    ]
+    EXPORTS.mozilla.layers += [
+        'apz/src/AndroidDynamicToolbarAnimator.h',
     ]
 
 UNIFIED_SOURCES += [
     'AnimationHelper.cpp',
     'apz/public/IAPZCTreeManager.cpp',
     'apz/src/APZCTreeManager.cpp',
     'apz/src/AsyncPanZoomController.cpp',
     'apz/src/Axis.cpp',
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -17,20 +17,16 @@
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/layers/WebRenderCompositableHolder.h"
 #include "mozilla/layers/WebRenderTextureHost.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/widget/CompositorWidget.h"
 
-#if defined(MOZ_WIDGET_ANDROID)
-# include "mozilla/widget/AndroidCompositorWidget.h"
-#endif
-
 bool is_in_main_thread()
 {
   return NS_IsMainThread();
 }
 
 bool is_in_compositor_thread()
 {
   return mozilla::layers::CompositorThreadHolder::IsInCompositorThread();
@@ -138,20 +134,21 @@ WebRenderBridgeParent::RecvCreate(const 
     return IPC_OK();
   }
 
   MOZ_ASSERT(mApi);
 
 #ifdef MOZ_WIDGET_ANDROID
   // XXX temporary hack.
   // XXX Remove it when APZ is supported.
-  widget::AndroidCompositorWidget* widget = mWidget->AsAndroid();
-  if (widget) {
-    widget->SetFirstPaintViewport(LayerIntPoint(0, 0), CSSToLayerScale(), CSSRect(0, 0, aSize.width, aSize.height));
-  }
+  // XXX Broken by Dynamic Toolbar v3. See: Bug 1335895
+//  RefPtr<UiCompositorControllerParent> uiController = UiCompositorControllerParent::GetFromRootLayerTreeId(/* Root Layer Tree ID */);
+//  if (uiController) {
+//    uiController->ToolbarAnimatorMessageFromCompositor(/*FIRST_PAINT*/ 5);
+//  }
 #endif
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvShutdown()
 {
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -85,17 +85,16 @@
 #include "plstr.h"
 #include "nsCRT.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "mozilla/gfx/Logging.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "TexturePoolOGL.h"
-#include "mozilla/layers/UiCompositorControllerChild.h"
 #endif
 
 #ifdef USE_SKIA
 # ifdef __GNUC__
 #  pragma GCC diagnostic push
 #  pragma GCC diagnostic ignored "-Wshadow"
 # endif
 # include "skia/include/core/SkGraphics.h"
@@ -985,19 +984,16 @@ gfxPlatform::ShutdownLayersIPC()
         if (gfxPrefs::ChildProcessShutdown()) {
           layers::CompositorBridgeChild::ShutDown();
           layers::ImageBridgeChild::ShutDown();
         }
     } else if (XRE_IsParentProcess()) {
         gfx::VRManagerChild::ShutDown();
         layers::CompositorBridgeChild::ShutDown();
         layers::ImageBridgeChild::ShutDown();
-#if defined(MOZ_WIDGET_ANDROID)
-        layers::UiCompositorControllerChild::Shutdown();
-#endif // defined(MOZ_WIDGET_ANDROID)
         // This has to happen after shutting down the child protocols.
         layers::CompositorThreadHolder::Shutdown();
         if (gfxVars::UseWebRender()) {
             wr::RenderThread::ShutDown();
         }
     } else {
       // TODO: There are other kind of processes and we should make sure gfx
       // stuff is either not created there or shut down properly.
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -336,16 +336,17 @@ private:
   DECL_GFX_PREF(Live, "apz.x_skate_size_multiplier",           APZXSkateSizeMultiplier, float, 1.5f);
   DECL_GFX_PREF(Live, "apz.x_stationary_size_multiplier",      APZXStationarySizeMultiplier, float, 3.0f);
   DECL_GFX_PREF(Live, "apz.y_skate_highmem_adjust",            APZYSkateHighMemAdjust, float, 0.0f);
   DECL_GFX_PREF(Live, "apz.y_skate_size_multiplier",           APZYSkateSizeMultiplier, float, 2.5f);
   DECL_GFX_PREF(Live, "apz.y_stationary_size_multiplier",      APZYStationarySizeMultiplier, float, 3.5f);
   DECL_GFX_PREF(Live, "apz.zoom_animation_duration_ms",        APZZoomAnimationDuration, int32_t, 250);
   DECL_GFX_PREF(Live, "apz.scale_repaint_delay_ms",            APZScaleRepaintDelay, int32_t, 500);
 
+  DECL_GFX_PREF(Live, "browser.ui.scroll-toolbar-threshold",   ToolbarScrollThreshold, int32_t, 10);
   DECL_GFX_PREF(Live, "browser.ui.zoom.force-user-scalable",   ForceUserScalable, bool, false);
   DECL_GFX_PREF(Live, "browser.viewport.desktopWidth",         DesktopViewportWidth, int32_t, 980);
 
   DECL_GFX_PREF(Live, "dom.ipc.plugins.asyncdrawing.enabled",  PluginAsyncDrawingEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.meta-viewport.enabled",             MetaViewportEnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.enabled",                        VREnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.autoactivate.enabled",           VRAutoActivateEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.controller_trigger_threshold",   VRControllerTriggerThreshold, float, 0.1f);
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -778,17 +778,20 @@ MapObject::delete_(JSContext* cx, unsign
 }
 
 bool
 MapObject::iterator(JSContext* cx, IteratorKind kind,
                     HandleObject obj, MutableHandleValue iter)
 {
     ValueMap& map = extract(obj);
     Rooted<JSObject*> iterobj(cx, MapIteratorObject::create(cx, obj, &map, kind));
-    return iterobj && (iter.setObject(*iterobj), true);
+    if (!iterobj)
+        return false;
+    iter.setObject(*iterobj);
+    return true;
 }
 
 bool
 MapObject::iterator_impl(JSContext* cx, const CallArgs& args, IteratorKind kind)
 {
     RootedObject obj(cx, &args.thisv().toObject());
     return iterator(cx, kind, obj, args.rval());
 }
@@ -1363,17 +1366,20 @@ SetObject::delete_(JSContext* cx, unsign
 
 bool
 SetObject::iterator(JSContext *cx, IteratorKind kind,
                     HandleObject obj, MutableHandleValue iter)
 {
     MOZ_ASSERT(SetObject::is(obj));
     ValueSet &set = extract(obj);
     Rooted<JSObject*> iterobj(cx, SetIteratorObject::create(cx, obj, &set, kind));
-    return iterobj && (iter.setObject(*iterobj), true);
+    if (!iterobj)
+        return false;
+    iter.setObject(*iterobj);
+    return true;
 }
 
 bool
 SetObject::iterator_impl(JSContext *cx, const CallArgs& args, IteratorKind kind)
 {
     Rooted<SetObject*> setobj(cx, &args.thisv().toObject().as<SetObject>());
     ValueSet& set = *setobj->getData();
     Rooted<JSObject*> iterobj(cx, SetIteratorObject::create(cx, setobj, &set, kind));
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1266,25 +1266,29 @@ InterpretDollar(JSLinearString* matched,
         /* $n, $nn */
         unsigned num = JS7_UNDEC(c);
         if (num > captures.length()) {
             // The result is implementation-defined, do not substitute.
             return false;
         }
 
         const CharT* currentChar = currentDollar + 2;
-        if (currentChar < replacementEnd && (c = *currentChar, JS7_ISDEC(c))) {
-            unsigned tmpNum = 10 * num + JS7_UNDEC(c);
-            // If num > captures.length(), the result is implementation-defined.
-            // Consume next character only if num <= captures.length().
-            if (tmpNum <= captures.length()) {
-                currentChar++;
-                num = tmpNum;
+        if (currentChar < replacementEnd) {
+            c = *currentChar;
+            if (JS7_ISDEC(c)) {
+                unsigned tmpNum = 10 * num + JS7_UNDEC(c);
+                // If num > captures.length(), the result is implementation-defined.
+                // Consume next character only if num <= captures.length().
+                if (tmpNum <= captures.length()) {
+                    currentChar++;
+                    num = tmpNum;
+                }
             }
         }
+
         if (num == 0) {
             // The result is implementation-defined.
             // Do not substitute.
             return false;
         }
 
         *skip = currentChar - currentDollar;
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -543,17 +543,17 @@ FunctionBox::initWithEnclosingParseConte
         thisBinding_ = sc->thisBinding();
     } else {
         allowNewTarget_ = true;
         allowSuperProperty_ = fun->allowSuperProperty();
 
         if (kind == ClassConstructor || kind == DerivedClassConstructor) {
             auto stmt = enclosing->findInnermostStatement<ParseContext::ClassStatement>();
             MOZ_ASSERT(stmt);
-            stmt->setConstructorBox(this);
+            stmt->constructorBox = this;
 
             if (kind == DerivedClassConstructor) {
                 setDerivedClassConstructor();
                 allowSuperCall_ = true;
                 needsThisTDZChecks_ = true;
             }
         }
 
@@ -570,26 +570,16 @@ FunctionBox::initWithEnclosingParseConte
             return stmt->kind() == StatementKind::With;
         };
 
         inWith_ = enclosing->findInnermostStatement(isWith);
     }
 }
 
 void
-FunctionBox::resetForAbortedSyntaxParse(ParseContext* enclosing, FunctionSyntaxKind kind)
-{
-    if (kind == ClassConstructor || kind == DerivedClassConstructor) {
-        auto stmt = enclosing->findInnermostStatement<ParseContext::ClassStatement>();
-        MOZ_ASSERT(stmt);
-        stmt->clearConstructorBoxForAbortedSyntaxParse(this);
-    }
-}
-
-void
 FunctionBox::initWithEnclosingScope(Scope* enclosingScope)
 {
     if (!function()->isArrow()) {
         allowNewTarget_ = true;
         allowSuperProperty_ = function()->allowSuperProperty();
 
         if (isDerivedClassConstructor()) {
             setDerivedClassConstructor();
@@ -3450,17 +3440,16 @@ Parser<FullParseHandler>::trySyntaxParse
                                    inheritedDirectives, newDirectives))
         {
             if (parser->hadAbortedSyntaxParse()) {
                 // Try again with a full parse. UsedNameTracker needs to be
                 // rewound to just before we tried the syntax parse for
                 // correctness.
                 parser->clearAbortedSyntaxParse();
                 usedNames.rewind(token);
-                funbox->resetForAbortedSyntaxParse(pc, kind);
                 MOZ_ASSERT_IF(!parser->context->helperThread(),
                               !parser->context->isExceptionPending());
                 break;
             }
             return false;
         }
 
         // Advance this parser over tokens processed by the syntax parser.
@@ -7163,17 +7152,17 @@ Parser<ParseHandler>::classDefinition(Yi
             propType = PropertyType::SetterNoExpressionClosure;
 
         bool isConstructor = !isStatic && propAtom == context->names().constructor;
         if (isConstructor) {
             if (propType != PropertyType::Method) {
                 errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
                 return null();
             }
-            if (classStmt.constructorBox()) {
+            if (classStmt.constructorBox) {
                 errorAt(nameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor");
                 return null();
             }
             propType = hasHeritage ? PropertyType::DerivedConstructor : PropertyType::Constructor;
         } else if (isStatic && propAtom == context->names().prototype) {
             errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
             return null();
         }
@@ -7210,17 +7199,17 @@ Parser<ParseHandler>::classDefinition(Yi
         JSOp op = JSOpFromPropertyType(propType);
         if (!handler.addClassMethodDefinition(classMethods, propName, fn, op, isStatic))
             return null();
     }
 
     // Amend the toStringEnd offset for the constructor now that we've
     // finished parsing the class.
     uint32_t classEndOffset = pos().end;
-    if (FunctionBox* ctorbox = classStmt.constructorBox()) {
+    if (FunctionBox* ctorbox = classStmt.constructorBox) {
         if (ctorbox->function()->isInterpretedLazy())
             ctorbox->function()->lazyScript()->setToStringEnd(classEndOffset);
         ctorbox->toStringEnd = classEndOffset;
     }
 
     Node nameNode = null();
     Node methodsOrBlock = classMethods;
     if (name) {
@@ -9931,17 +9920,16 @@ Parser<ParseHandler>::primaryExpr(YieldH
             return generatorComprehension(begin);
         }
 
         // Pass |possibleError| to support destructuring in arrow parameters.
         Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
         if (!expr)
             return null();
         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
-        handler.setEndPosition(expr, pos().end);
         return handler.parenthesize(expr);
       }
 
       case TOK_TEMPLATE_HEAD:
         return templateLiteral(yieldHandling);
 
       case TOK_NO_SUBS_TEMPLATE:
         return noSubstitutionUntaggedTemplate();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -82,39 +82,24 @@ class ParseContext : public Nestable<Par
             label_(pc->sc_->context, label)
         { }
 
         HandleAtom label() const {
             return label_;
         }
     };
 
-    class ClassStatement : public Statement
+    struct ClassStatement : public Statement
     {
-        FunctionBox* constructorBox_;
+        FunctionBox* constructorBox;
 
-      public:
         explicit ClassStatement(ParseContext* pc)
           : Statement(pc, StatementKind::Class),
-            constructorBox_(nullptr)
+            constructorBox(nullptr)
         { }
-
-        void clearConstructorBoxForAbortedSyntaxParse(FunctionBox* funbox) {
-            MOZ_ASSERT(constructorBox_ == funbox);
-            constructorBox_ = nullptr;
-        }
-
-        void setConstructorBox(FunctionBox* funbox) {
-            MOZ_ASSERT(!constructorBox_);
-            constructorBox_ = funbox;
-        }
-
-        FunctionBox* constructorBox() const {
-            return constructorBox_;
-        }
     };
 
     // The intra-function scope stack.
     //
     // Tracks declared and used names within a scope.
     class Scope : public Nestable<Scope>
     {
         // Names declared in this scope. Corresponds to the union of
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -498,17 +498,16 @@ class FunctionBox : public ObjectBox, pu
     MutableHandle<VarScope::Data*> extraVarScopeBindings() {
         MOZ_ASSERT(context->keepAtoms);
         return MutableHandle<VarScope::Data*>::fromMarkedLocation(&extraVarScopeBindings_);
     }
 
     void initFromLazyFunction();
     void initStandaloneFunction(Scope* enclosingScope);
     void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind);
-    void resetForAbortedSyntaxParse(ParseContext* enclosing, FunctionSyntaxKind kind);
 
     ObjectBox* toObjectBox() override { return this; }
     JSFunction* function() const { return &object->as<JSFunction>(); }
 
     Scope* compilationEnclosingScope() const override {
         // This method is used to distinguish the outermost SharedContext. If
         // a FunctionBox is the outermost SharedContext, it must be a lazy
         // function.
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/class/bug1357506.js
@@ -0,0 +1,8 @@
+// Test that constructors that abort due to asm.js do not assert due to the
+// parser keeping track of the FunctionBox corresponding to the constructor.
+
+class a {
+  constructor() {
+    "use asm";
+  }
+}
--- a/js/src/jit/x64/BaseAssembler-x64.h
+++ b/js/src/jit/x64/BaseAssembler-x64.h
@@ -616,17 +616,17 @@ class BaseAssemblerX64 : public BaseAsse
         }
 
         spew("movq       %p, %s", addr, GPReg64Name(dst));
         m_formatter.oneByteOp64(OP_MOV_GvEv, addr, dst);
     }
 
     void leaq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
-        spew("leaq       " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst)),
+        spew("leaq       " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst));
         m_formatter.oneByteOp64(OP_LEA, offset, base, index, scale, dst);
     }
 
     void movq_i32m(int32_t imm, int32_t offset, RegisterID base)
     {
         spew("movq       $%d, " MEM_ob, imm, ADDR_ob(offset, base));
         m_formatter.oneByteOp64(OP_GROUP11_EvIz, offset, base, GROUP11_MOV);
         m_formatter.immediate32(imm);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2409,17 +2409,17 @@ js::array_unshift(JSContext* cx, unsigne
             } while (false);
 
             // Steps 4.b-c.
             if (!optimized) {
                 uint32_t last = length;
                 double upperIndex = double(last) + args.length();
                 RootedValue value(cx);
                 do {
-                    --last, --upperIndex;
+                    --last; --upperIndex;
                     if (!CheckForInterrupt(cx))
                         return false;
                     bool hole;
                     if (!HasAndGetElement(cx, obj, last, &hole, &value))
                         return false;
                     if (hole) {
                         if (!DeletePropertyOrThrow(cx, obj, upperIndex))
                             return false;
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -112,17 +112,17 @@ JSCompartment::~JSCompartment()
 
     js_delete(jitCompartment_);
     js_delete(watchpointMap);
     js_delete(scriptCountsMap);
     js_delete(debugScriptMap);
     js_delete(debugEnvs);
     js_delete(objectMetadataTable);
     js_delete(lazyArrayBuffers);
-    js_delete(nonSyntacticLexicalEnvironments_),
+    js_delete(nonSyntacticLexicalEnvironments_);
     js_free(enumerators);
 
 #ifdef DEBUG
     // Avoid assertion destroying the unboxed layouts list if the embedding
     // leaked GC things.
     if (!rt->gc.shutdownCollectedEverything())
         unboxedLayouts.clear();
 #endif
@@ -1445,9 +1445,8 @@ AutoSetNewObjectMetadata::~AutoSetNewObj
         // in order.
         cx_->compartment()->objectMetadataState = prevState_;
 
         obj = SetNewObjectMetadata(cx_, obj);
     } else {
         cx_->compartment()->objectMetadataState = prevState_;
     }
 }
-
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3704,17 +3704,17 @@ js_strlen(const char16_t* s)
 int32_t
 js_strcmp(const char16_t* lhs, const char16_t* rhs)
 {
     while (true) {
         if (*lhs != *rhs)
             return int32_t(*lhs) - int32_t(*rhs);
         if (*lhs == 0)
             return 0;
-        ++lhs, ++rhs;
+        ++lhs; ++rhs;
     }
 }
 
 int32_t
 js_fputs(const char16_t* s, FILE* f)
 {
     while (*s != 0) {
         if (fputwc(wchar_t(*s), f) == WEOF)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2896,17 +2896,17 @@ struct DisassembleOptionParser {
             if (JS_FlatStringEqualsAscii(flatStr, "-l"))
                 lines = true;
             else if (JS_FlatStringEqualsAscii(flatStr, "-r"))
                 recursive = true;
             else if (JS_FlatStringEqualsAscii(flatStr, "-S"))
                 sourceNotes = false;
             else
                 break;
-            argv++, argc--;
+            argv++; argc--;
         }
         return true;
     }
 };
 
 } /* anonymous namespace */
 
 static bool
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/parenExprToString.js
@@ -0,0 +1,8 @@
+// Test that parenthesized class expressions don't get their toString offsets
+// messed up.
+
+assertEq((class {}).toString(), "class {}");
+assertEq(((class {})).toString(), "class {}");
+
+if (typeof reportCompare === "function")
+    reportCompare(0, 0, "OK");
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -12,16 +12,17 @@
 #include "xpcprivate.h"
 #include "xpcpublic.h"
 #include "XPCWrapper.h"
 #include "XPCJSMemoryReporter.h"
 #include "WrapperFactory.h"
 #include "mozJSComponentLoader.h"
 #include "nsAutoPtr.h"
 #include "nsNetUtil.h"
+#include "nsThreadUtils.h"
 
 #include "nsIMemoryInfoDumper.h"
 #include "nsIMemoryReporter.h"
 #include "nsIObserverService.h"
 #include "nsIDebug2.h"
 #include "nsIDocShell.h"
 #include "nsIRunnable.h"
 #include "amIAddonManager.h"
@@ -374,17 +375,17 @@ AutoLockWatchdog::~AutoLockWatchdog()
 {
     PR_Unlock(mWatchdog->GetLock());
 }
 
 static void
 WatchdogMain(void* arg)
 {
     mozilla::AutoProfilerRegister registerThread("JS Watchdog");
-    PR_SetCurrentThreadName("JS Watchdog");
+    NS_SetCurrentThreadName("JS Watchdog");
 
     Watchdog* self = static_cast<Watchdog*>(arg);
     WatchdogManager* manager = self->Manager();
 
     // Lock lasts until we return
     AutoLockWatchdog lock(self);
 
     MOZ_ASSERT(self->Initialized());
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -308,17 +308,17 @@ DefinePropertyIfFound(XPCCallContext& cc
             JSAutoByteString name;
             RefPtr<XPCNativeInterface> iface2;
             XPCWrappedNativeTearOff* to;
             RootedObject jso(ccx);
             nsresult rv = NS_OK;
 
             if (JSID_IS_STRING(id) &&
                 name.encodeLatin1(ccx, JSID_TO_STRING(id)) &&
-                (iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr()), iface2) &&
+                (iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr())) &&
                 nullptr != (to = wrapperToReflectInterfaceNames->
                            FindTearOff(iface2, true, &rv)) &&
                 nullptr != (jso = to->GetJSObject()))
 
             {
                 AutoResolveName arn(ccx, id);
                 if (resolved)
                     *resolved = true;
--- a/layout/base/PositionedEventTargeting.cpp
+++ b/layout/base/PositionedEventTargeting.cpp
@@ -122,23 +122,21 @@ GetPrefsFor(EventClassID aEventClassID)
           "ui.mouse.radius.inputSource.touchOnly", true);
     } else {
       prefs->mTouchOnly = false;
     }
 
     nsPrintfCString repositionPref("ui.%s.radius.reposition", prefBranch);
     Preferences::AddBoolVarCache(&prefs->mRepositionEventCoords, repositionPref.get(), false);
 
-    Preferences::AddBoolVarCache(&prefs->mTouchClusterDetectionEnabled, "ui.zoomedview.enabled", false);
-
-    Preferences::AddBoolVarCache(&prefs->mSimplifiedClusterDetection, "ui.zoomedview.simplified", false);
-
-    Preferences::AddUintVarCache(&prefs->mLimitReadableSize, "ui.zoomedview.limitReadableSize", 8);
-
-    Preferences::AddUintVarCache(&prefs->mKeepLimitSizeForCluster, "ui.zoomedview.keepLimitSize", 16);
+    // These values were formerly set by ui.zoomedview preferences.
+    prefs->mTouchClusterDetectionEnabled = false;
+    prefs->mSimplifiedClusterDetection = false;
+    prefs->mLimitReadableSize = 8;
+    prefs->mKeepLimitSizeForCluster = 16;
   }
 
   return prefs;
 }
 
 static bool
 HasMouseListener(nsIContent* aContent)
 {
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -5345,30 +5345,39 @@ PresShell::AddCanvasBackgroundColorItem(
   nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor);
   if (NS_GET_A(bgcolor) == 0)
     return;
 
   // To make layers work better, we want to avoid having a big non-scrolled
   // color background behind a scrolled transparent background. Instead,
   // we'll try to move the color background into the scrolled content
   // by making nsDisplayCanvasBackground paint it.
+  bool addedScrollingBackgroundColor = false;
   if (!aFrame->GetParent()) {
     nsIScrollableFrame* sf =
       aFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
     if (sf) {
       nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
       if (canvasFrame && canvasFrame->IsVisibleForPainting(&aBuilder)) {
-        if (AddCanvasBackgroundColor(aList, canvasFrame, bgcolor, mHasCSSBackgroundColor))
-          return;
-      }
-    }
-  }
-
-  aList.AppendNewToBottom(
-    new (&aBuilder) nsDisplaySolidColor(&aBuilder, aFrame, aBounds, bgcolor));
+        addedScrollingBackgroundColor =
+          AddCanvasBackgroundColor(aList, canvasFrame, bgcolor, mHasCSSBackgroundColor);
+      }
+    }
+  }
+
+  if (!addedScrollingBackgroundColor ||
+      (nsLayoutUtils::UsesAsyncScrolling(aFrame) && NS_GET_A(bgcolor) == 255)) {
+    // With async scrolling, we'd like to have two instances of the background
+    // color: one that scrolls with the content (for the reasons stated above),
+    // and one underneath which does not scroll with the content, but which can
+    // be shown during checkerboarding and overscroll.
+    // We can only do that if the color is opaque.
+    aList.AppendNewToBottom(
+      new (&aBuilder) nsDisplaySolidColor(&aBuilder, aFrame, aBounds, bgcolor));
+  }
 }
 
 static bool IsTransparentContainerElement(nsPresContext* aPresContext)
 {
   nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
   if (!docShell) {
     return false;
   }
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -647,20 +647,16 @@ public:
   /*
    * Updates mCommonClipCount by checking for rounded rect clips in common
    * between the clip on a new item (aCurrentClip) and the common clips
    * on items already in the layer (the first mCommonClipCount rounded rects
    * in mItemClip).
    */
   void UpdateCommonClipCount(const DisplayItemClip& aCurrentClip);
   /**
-   * The union of all the bounds of the display items in this layer.
-   */
-  nsIntRect mBounds;
-  /**
    * The region of visible content above the layer and below the
    * next PaintedLayerData currently in the stack, if any.
    * This is a conservative approximation: it contains the true region.
    */
   nsIntRegion mVisibleAboveRegion;
   /**
    * All the display items that have been assigned to this painted layer.
    * These items get added by Accumulate().
@@ -1315,16 +1311,17 @@ protected:
   /**
    * Computes the snapped opaque area of aItem. Sets aList's opaque flag
    * if it covers the entire list bounds. Sets *aHideAllLayersBelow to true
    * this item covers the entire viewport so that all layers below are
    * permanently invisible.
    */
   nsIntRegion ComputeOpaqueRect(nsDisplayItem* aItem,
                                 AnimatedGeometryRoot* aAnimatedGeometryRoot,
+                                const ActiveScrolledRoot* aASR,
                                 const DisplayItemClip& aClip,
                                 nsDisplayList* aList,
                                 bool* aHideAllLayersBelow,
                                 bool* aOpaqueForAnimatedGeometryRootParent);
 
   /**
    * Return a PaintedLayerData object that is initialized for a layer that
    * aItem will be assigned to.
@@ -3262,20 +3259,16 @@ void ContainerState::FinishPaintedLayerD
     newLayerEntry->mVisibleRegion = data->mVisibleRegion;
     newLayerEntry->mOpaqueRegion = data->mOpaqueRegion;
     newLayerEntry->mHideAllLayersBelow = data->mHideAllLayersBelow;
     newLayerEntry->mOpaqueForAnimatedGeometryRootParent = data->mOpaqueForAnimatedGeometryRootParent;
   } else {
     SetOuterVisibleRegionForLayer(layer, data->mVisibleRegion);
   }
 
-  nsIntRect layerBounds = data->mBounds;
-  layerBounds.MoveBy(-GetTranslationForPaintedLayer(data->mLayer));
-  layer->SetLayerBounds(layerBounds);
-
 #ifdef MOZ_DUMP_PAINTING
   if (!data->mLog.IsEmpty()) {
     if (PaintedLayerData* containingPld = mLayerBuilder->GetContainingPaintedLayerData()) {
       containingPld->mLayer->AddExtraDumpInfo(nsCString(data->mLog));
     } else {
       layer->AddExtraDumpInfo(nsCString(data->mLog));
     }
   }
@@ -3462,20 +3455,16 @@ PaintedLayerData::Accumulate(ContainerSt
                             nsDisplayItem* aItem,
                             const nsIntRegion& aClippedOpaqueRegion,
                             const nsIntRect& aVisibleRect,
                             const DisplayItemClip& aClip,
                             LayerState aLayerState)
 {
   FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating dp=%s(%p), f=%p against pld=%p\n", aItem->Name(), aItem, aItem->Frame(), this);
 
-  bool snap;
-  nsRect itemBounds = aItem->GetBounds(aState->mBuilder, &snap);
-  mBounds = mBounds.Union(aState->ScaleToOutsidePixels(itemBounds, snap));
-
   if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) {
     mForceTransparentSurface = true;
   }
   if (aState->mParameters.mDisableSubpixelAntialiasingInDescendants) {
     // Disable component alpha.
     // Note that the transform (if any) on the PaintedLayer is always an integer translation so
     // we don't have to factor that in here.
     aItem->DisableComponentAlpha();
@@ -3787,17 +3776,17 @@ ContainerState::GetDisplayPortForAnimate
 {
   if (mLastDisplayPortAGR == aAnimatedGeometryRoot) {
     return mLastDisplayPortRect;
   }
 
   mLastDisplayPortAGR = aAnimatedGeometryRoot;
 
   nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(*aAnimatedGeometryRoot);
-  if (sf == nullptr) {
+  if (sf == nullptr || nsLayoutUtils::UsesAsyncScrolling(*aAnimatedGeometryRoot)) {
     mLastDisplayPortRect = nsRect();
     return mLastDisplayPortRect;
   }
 
   bool usingDisplayport =
     nsLayoutUtils::GetDisplayPort((*aAnimatedGeometryRoot)->GetContent(), &mLastDisplayPortRect,
                                   RelativeTo::ScrollFrame);
   if (!usingDisplayport) {
@@ -3808,16 +3797,17 @@ ContainerState::GetDisplayPortForAnimate
   nsIFrame* scrollFrame = do_QueryFrame(sf);
   mLastDisplayPortRect += scrollFrame->GetOffsetToCrossDoc(mContainerReferenceFrame);
   return mLastDisplayPortRect;
 }
 
 nsIntRegion
 ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem,
                                   AnimatedGeometryRoot* aAnimatedGeometryRoot,
+                                  const ActiveScrolledRoot* aASR,
                                   const DisplayItemClip& aClip,
                                   nsDisplayList* aList,
                                   bool* aHideAllLayersBelow,
                                   bool* aOpaqueForAnimatedGeometryRootParent)
 {
   bool snapOpaque;
   nsRegion opaque = aItem->GetOpaqueRegion(mBuilder, &snapOpaque);
   if (opaque.IsEmpty()) {
@@ -3826,16 +3816,17 @@ ContainerState::ComputeOpaqueRect(nsDisp
 
   nsIntRegion opaquePixels;
   nsRegion opaqueClipped;
   for (auto iter = opaque.RectIter(); !iter.Done(); iter.Next()) {
     opaqueClipped.Or(opaqueClipped,
                      aClip.ApproximateIntersectInward(iter.Get()));
   }
   if (aAnimatedGeometryRoot == mContainerAnimatedGeometryRoot &&
+      aASR == mContainerASR &&
       opaqueClipped.Contains(mContainerBounds)) {
     *aHideAllLayersBelow = true;
     aList->SetIsOpaque();
   }
   // Add opaque areas to the "exclude glass" region. Only do this when our
   // container layer is going to be the rootmost layer, otherwise transforms
   // etc will mess us up (and opaque contributions from other containers are
   // not needed).
@@ -4410,17 +4401,17 @@ ContainerState::ProcessDisplayItems(nsDi
           // to avoid failure caused by singular transforms.
           newLayerEntry->mUntransformedVisibleRegion = true;
           newLayerEntry->mVisibleRegion =
             item->GetVisibleRectForChildren().ToOutsidePixels(mAppUnitsPerDevPixel);
         } else {
           newLayerEntry->mVisibleRegion = itemVisibleRegion;
         }
         newLayerEntry->mOpaqueRegion = ComputeOpaqueRect(item,
-          animatedGeometryRoot, itemClip, aList,
+          animatedGeometryRoot, itemASR, itemClip, aList,
           &newLayerEntry->mHideAllLayersBelow,
           &newLayerEntry->mOpaqueForAnimatedGeometryRootParent);
       } else {
         bool useChildrenVisible =
           itemType == nsDisplayItem::TYPE_TRANSFORM &&
           (item->Frame()->IsPreserve3DLeaf() ||
            item->Frame()->HasPerspective());
         const nsIntRegion &visible = useChildrenVisible ?
@@ -4466,17 +4457,17 @@ ContainerState::ProcessDisplayItems(nsDi
         paintedLayerData->AccumulateEventRegions(this, eventRegions);
       } else {
         // check to see if the new item has rounded rect clips in common with
         // other items in the layer
         if (mManager->IsWidgetLayerManager()) {
           paintedLayerData->UpdateCommonClipCount(itemClip);
         }
         nsIntRegion opaquePixels = ComputeOpaqueRect(item,
-            animatedGeometryRoot, itemClip, aList,
+            animatedGeometryRoot, itemASR, itemClip, aList,
             &paintedLayerData->mHideAllLayersBelow,
             &paintedLayerData->mOpaqueForAnimatedGeometryRootParent);
         MOZ_ASSERT(nsIntRegion(itemDrawRect).Contains(opaquePixels));
         opaquePixels.AndWith(itemVisibleRect);
         paintedLayerData->Accumulate(this, item, opaquePixels,
             itemVisibleRect, itemClip, layerState);
 
         if (!paintedLayerData->mLayer) {
@@ -4947,26 +4938,29 @@ ContainerState::CollectOldLayers()
                    "Could not recycle mask layer, unsupported layer type.");
       mRecycledMaskImageLayers.Put(MaskLayerKey(layer, Some(i)), static_cast<ImageLayer*>(maskLayer));
     }
   }
 }
 
 struct OpaqueRegionEntry {
   AnimatedGeometryRoot* mAnimatedGeometryRoot;
+  const ActiveScrolledRoot* mASR;
   nsIntRegion mOpaqueRegion;
 };
 
 static OpaqueRegionEntry*
 FindOpaqueRegionEntry(nsTArray<OpaqueRegionEntry>& aEntries,
-                      AnimatedGeometryRoot* aAnimatedGeometryRoot)
+                      AnimatedGeometryRoot* aAnimatedGeometryRoot,
+                      const ActiveScrolledRoot* aASR)
 {
   for (uint32_t i = 0; i < aEntries.Length(); ++i) {
     OpaqueRegionEntry* d = &aEntries[i];
-    if (d->mAnimatedGeometryRoot == aAnimatedGeometryRoot) {
+    if (d->mAnimatedGeometryRoot == aAnimatedGeometryRoot &&
+        d->mASR == aASR) {
       return d;
     }
   }
   return nullptr;
 }
 
 const ActiveScrolledRoot*
 FindDirectChildASR(const ActiveScrolledRoot* aParent, const ActiveScrolledRoot* aDescendant)
@@ -5186,17 +5180,17 @@ ContainerState::PostprocessRetainedLayer
   int32_t opaqueRegionForContainer = -1;
 
   for (int32_t i = mNewChildLayers.Length() - 1; i >= 0; --i) {
     NewLayerEntry* e = &mNewChildLayers.ElementAt(i);
     if (!e->mLayer) {
       continue;
     }
 
-    OpaqueRegionEntry* data = FindOpaqueRegionEntry(opaqueRegions, e->mAnimatedGeometryRoot);
+    OpaqueRegionEntry* data = FindOpaqueRegionEntry(opaqueRegions, e->mAnimatedGeometryRoot, e->mASR);
 
     SetupScrollingMetadata(e);
 
     if (hideAll) {
       e->mVisibleRegion.SetEmpty();
     } else if (!e->mLayer->IsScrollbarContainer()) {
       Maybe<ParentLayerIntRect> clipRect = GetStationaryClipInContainer(e->mLayer);
       if (clipRect && opaqueRegionForContainer >= 0 &&
@@ -5209,29 +5203,33 @@ ContainerState::PostprocessRetainedLayer
 
     SetOuterVisibleRegionForLayer(e->mLayer,
                                   e->mVisibleRegion,
                                   e->mLayerContentsVisibleRect.width >= 0 ? &e->mLayerContentsVisibleRect : nullptr,
                                   e->mUntransformedVisibleRegion);
 
     if (!e->mOpaqueRegion.IsEmpty()) {
       AnimatedGeometryRoot* animatedGeometryRootToCover = e->mAnimatedGeometryRoot;
+      const ActiveScrolledRoot* asrToCover = e->mASR;
       if (e->mOpaqueForAnimatedGeometryRootParent &&
           e->mAnimatedGeometryRoot->mParentAGR == mContainerAnimatedGeometryRoot) {
         animatedGeometryRootToCover = mContainerAnimatedGeometryRoot;
-        data = FindOpaqueRegionEntry(opaqueRegions, animatedGeometryRootToCover);
+        asrToCover = mContainerASR;
+        data = FindOpaqueRegionEntry(opaqueRegions, animatedGeometryRootToCover, asrToCover);
       }
 
       if (!data) {
-        if (animatedGeometryRootToCover == mContainerAnimatedGeometryRoot) {
+        if (animatedGeometryRootToCover == mContainerAnimatedGeometryRoot &&
+            asrToCover == mContainerASR) {
           NS_ASSERTION(opaqueRegionForContainer == -1, "Already found it?");
           opaqueRegionForContainer = opaqueRegions.Length();
         }
         data = opaqueRegions.AppendElement();
         data->mAnimatedGeometryRoot = animatedGeometryRootToCover;
+        data->mASR = asrToCover;
       }
 
       nsIntRegion clippedOpaque = e->mOpaqueRegion;
       Maybe<ParentLayerIntRect> clipRect = e->mLayer->GetCombinedClipRect();
       if (clipRect) {
         clippedOpaque.AndWith(clipRect->ToUnknownRect());
       }
       if (e->mLayer->GetScrolledClip()) {
--- a/layout/reftests/async-scrolling/checkerboard-2-ref.html
+++ b/layout/reftests/async-scrolling/checkerboard-2-ref.html
@@ -1,6 +1,7 @@
 <!DOCTYPE HTML>
 <html>
 <body style="background-color: green; overflow:hidden">
+  <div style="position:fixed; left: 0px; top: 0px; width: 100px; height: 500px; background-color: purple; z-index: -1"></div>
   <div style="position:absolute; left: 0px; top: 0px; background-color: yellow; width: 100px; height: 200px"></div>
   <div style="position:fixed; left: 10px; top: 10px; width: 10px; height: 10px; background-color: blue"></div>
 </body>
--- a/layout/reftests/async-scrolling/checkerboard-3-ref.html
+++ b/layout/reftests/async-scrolling/checkerboard-3-ref.html
@@ -1,5 +1,6 @@
 <!DOCTYPE HTML>
 <html>
 <body style="background-color: green; overflow:hidden">
+  <div style="position:fixed; left: 0px; top: 0px; width: 100px; height: 500px; background-color: purple; z-index: -1"></div>
   <div style="position:fixed; left: 10px; top: 10px; width: 10px; height: 10px; background-color: blue"></div>
 </body>
--- a/layout/reftests/native-theme/progress-overflow-small.html
+++ b/layout/reftests/native-theme/progress-overflow-small.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <html>
   <body>
-    <div style="-moz-appearance: progressbar; width:180px; height:6px;">
-      <div style="-moz-appearance: progresschunk; width:50%; height:100%;">
+    <div style="-moz-appearance: progressbar; appearance: auto; width:180px; height:6px;">
+      <div style="-moz-appearance: progresschunk; appearance: auto; width:50%; height:100%;">
       </div>
     </div>
   </body>
 </html>
--- a/layout/reftests/native-theme/reftest.list
+++ b/layout/reftests/native-theme/reftest.list
@@ -73,12 +73,12 @@ skip-if(!winWidget) == scroll-thumb-mini
 
 == checkbox-dynamic-1.html checkbox-dynamic-1-ref.html
 
 # These tests have been written to test the overflow of the window widget
 # (bug 568825) but we can't test it on Windows and Cocoa because they have
 # animated progress bars.
 # Nothing shows up on Android, presumably because that appearance type is
 # not implemented.
-pref(layout.css.moz-appearance.enabled,true) skip-if(cocoaWidget) skip-if(winWidget) == progress-overflow.html progress-overflow-ref.html
-pref(layout.css.moz-appearance.enabled,true) fails-if(Android) skip-if(cocoaWidget) skip-if(winWidget) != progress-overflow-small.html progress-nobar.html
+pref(layout.css.moz-appearance.enabled,true) pref(layout.css.appearance.enabled,true) skip-if(cocoaWidget) skip-if(winWidget) == progress-overflow.html progress-overflow-ref.html
+pref(layout.css.moz-appearance.enabled,true) pref(layout.css.appearance.enabled,true) fails-if(Android) skip-if(cocoaWidget) skip-if(winWidget) != progress-overflow-small.html progress-nobar.html
 
 == 676387-1.xul 676387-1-ref.xul
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -737,18 +737,18 @@ const KTableEntry nsCSSProps::kAnimation
 
 const KTableEntry nsCSSProps::kAnimationPlayStateKTable[] = {
   { eCSSKeyword_running, NS_STYLE_ANIMATION_PLAY_STATE_RUNNING },
   { eCSSKeyword_paused, NS_STYLE_ANIMATION_PLAY_STATE_PAUSED },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kAppearanceKTable[] = {
+  { eCSSKeyword_none,    NS_THEME_NONE },
   { eCSSKeyword_auto,    NS_THEME_AUTO },
-  { eCSSKeyword_none,    NS_THEME_NONE },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kMozAppearanceKTable[] = {
   { eCSSKeyword_none,                   NS_THEME_NONE },
   { eCSSKeyword_button,                 NS_THEME_BUTTON },
   { eCSSKeyword_radio,                  NS_THEME_RADIO },
   { eCSSKeyword_checkbox,               NS_THEME_CHECKBOX },
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -6194,22 +6194,22 @@ nsRuleNode::ComputeDisplayData(void* aSt
 
   // -moz-appearance: enum, inherit, initial
   SetValue(*aRuleData->ValueForMozAppearance(),
            display->mMozAppearance, conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentDisplay->mMozAppearance,
            NS_THEME_NONE);
 
-  // appearance: auto | none
+  // appearance: none | auto
   SetValue(*aRuleData->ValueForAppearance(),
            display->mAppearance, conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentDisplay->mAppearance,
-           NS_THEME_AUTO);
+           NS_THEME_NONE);
 
   // binding: url, none, inherit
   const nsCSSValue* bindingValue = aRuleData->ValueForBinding();
   if (eCSSUnit_URL == bindingValue->GetUnit()) {
     mozilla::css::URLValue* url = bindingValue->GetURLStructValue();
     NS_ASSERTION(url, "What's going on here?");
     display->mBinding.Set(url);
   }
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3304,17 +3304,17 @@ StyleAnimation::operator==(const StyleAn
 // --------------------
 // nsStyleDisplay
 //
 nsStyleDisplay::nsStyleDisplay(const nsPresContext* aContext)
   : mDisplay(StyleDisplay::Inline)
   , mOriginalDisplay(StyleDisplay::Inline)
   , mContain(NS_STYLE_CONTAIN_NONE)
   , mMozAppearance(NS_THEME_NONE)
-  , mAppearance(NS_THEME_AUTO)
+  , mAppearance(NS_THEME_NONE)
   , mPosition(NS_STYLE_POSITION_STATIC)
   , mFloat(StyleFloat::None)
   , mOriginalFloat(StyleFloat::None)
   , mBreakType(StyleClear::None)
   , mBreakInside(NS_STYLE_PAGE_BREAK_AUTO)
   , mBreakBefore(false)
   , mBreakAfter(false)
   , mOverflowX(NS_STYLE_OVERFLOW_VISIBLE)
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -83,16 +83,17 @@ label {
 
 /* default inputs, text inputs, and selects */
 
 /* Note: Values in nsNativeTheme IsWidgetStyled function
    need to match textfield background/border values here */
 
 input {
   -moz-appearance: textfield;
+  appearance: auto;
   /* The sum of border and padding on block-start and block-end
      must be the same here, for buttons, and for <select> (including its
      internal padding magic) */
   padding: 1px;
   border: 2px inset ThreeDLightShadow;
   background-color: -moz-Field;
   color: -moz-FieldText;
   font: -moz-field;
@@ -146,16 +147,17 @@ textarea {
   text-transform: none;
   word-spacing: normal;
   letter-spacing: normal;
   vertical-align: text-bottom;
   cursor: text;
   resize: both;
   -moz-binding: url("chrome://global/content/platformHTMLBindings.xml#textAreas");
   -moz-appearance: textfield-multiline;
+  appearance: auto;
   text-indent: 0;
   -moz-user-select: text;
   text-shadow: none;
   white-space: pre-wrap;
   word-wrap: break-word;
   overflow-clip-box: content-box;
 }
 
@@ -254,16 +256,17 @@ select {
   line-height: normal !important;
   white-space: nowrap !important;
   word-wrap: normal !important;
   text-align: start;
   cursor: default;
   box-sizing: border-box;
   -moz-user-select: none;
   -moz-appearance: menulist;
+  appearance: auto;
   border-width: 2px;
   border-style: inset;
   text-indent: 0;
   overflow: -moz-hidden-unscrollable;
   text-shadow: none;
   /* No text-decoration reaching inside, by default */
   display: inline-block;
   page-break-inside: avoid;
@@ -280,36 +283,39 @@ select[size][multiple] {
   background-color: -moz-Field;
   color: -moz-FieldText;
   vertical-align: text-bottom;
   padding-block-start: 1px;
   padding-block-end: 1px;
   padding-inline-start: 0;
   padding-inline-end: 0;
   -moz-appearance: listbox;
+  appearance: auto;
 }
 
 select[size="0"],
 select[size="1"] {
   /* Except this is not a listbox */
   background-color: -moz-Combobox;
   color: -moz-ComboboxText;
   vertical-align: baseline;
   padding: 0;
   -moz-appearance: menulist;
+  appearance: auto;
 }
 
 select > button {
   inline-size: 12px;
   white-space: nowrap;
   position: static !important;
   background-image: url("arrow.gif") !important;
   background-repeat: no-repeat !important;
   background-position: center !important;
   -moz-appearance: menulist-button;
+  appearance: auto;
 
   /* Make sure to size correctly if the combobox has a non-auto height. */
   block-size: 100% ! important;
   box-sizing: border-box ! important;
 
   /*
     Make sure to align properly with the display frame.  Note that we
     want the baseline of the combobox to match the baseline of the
@@ -552,26 +558,28 @@ input[type="file"]:dir(rtl) > xul|label 
   padding-inline-start: 0px;
   padding-inline-end: 5px;
 }
 
 /* radio buttons */
 input[type="radio"] {
   display: inline-block;
   -moz-appearance: radio;
+  appearance: auto;
   margin-block-start: 3px;
   margin-block-end: 0px;
   margin-inline-start: 5px;
   margin-inline-end: 3px;
 }
 
 /* check boxes */
 input[type="checkbox"] {
   display: inline-block;
   -moz-appearance: checkbox;
+  appearance: auto;
   margin-block-start: 3px;
   margin-block-end: 3px;
   margin-inline-start: 4px;
   margin-inline-end: 3px;
 }
 
 /* common features of radio buttons and check boxes */
 
@@ -701,16 +709,17 @@ input[type="search"] {
 /* Non text-related properties for buttons: these ones are shared with
    input[type="color"] */
 button,
 input[type="color"]:-moz-system-metric(color-picker-available),
 input[type="reset"],
 input[type="button"],
 input[type="submit"] {
   -moz-appearance: button;
+  appearance: auto;
   /* The sum of border and padding on block-start and block-end
      must be the same here, for text inputs, and for <select>.
      Note -moz-focus-inner padding does not affect button size. */
   padding-block-start: 0px;
   padding-inline-end: 8px;
   padding-block-end: 0px;
   padding-inline-start: 8px;
   border: 2px outset ThreeDLightShadow;
@@ -925,16 +934,17 @@ output:-moz-ui-invalid {
     -moz-user-input: none !important;
   }
 
   input[type="file"] { height: 2em; }
 }
 
 progress {
   -moz-appearance: progressbar;
+  appearance: auto;
   display: inline-block;
   vertical-align: -0.2em;
 
   /* Default style in case of there is -moz-appearance: none; */
   border: 2px solid;
   /* #e6e6e6 is a light gray. */
   -moz-border-top-colors: ThreeDShadow #e6e6e6;
   -moz-border-right-colors: ThreeDHighlight #e6e6e6;
@@ -947,39 +957,42 @@ progress {
   /* Prevent styling that would change the type of frame we construct. */
   display: inline-block ! important;
   float: none ! important;
   position: static ! important;
   overflow: visible ! important;
   box-sizing: border-box ! important;
 
   -moz-appearance: progresschunk;
+  appearance: auto;
   height: 100%;
   width: 100%;
 
   /* Default style in case of there is -moz-appearance: none; */
   background-color: #0064b4; /* blue */
 }
 
 meter {
   -moz-appearance: meterbar;
+  appearance: auto;
   display: inline-block;
   vertical-align: -0.2em;
 
   background: linear-gradient(#e6e6e6, #e6e6e6, #eeeeee 20%, #cccccc 45%, #cccccc 55%);
 }
 
 ::-moz-meter-bar {
   /* Block styles that would change the type of frame we construct. */
   display: inline-block ! important;
   float: none ! important;
   position: static ! important;
   overflow: visible ! important;
 
   -moz-appearance: meterchunk;
+  appearance: auto;
   height: 100%;
   width: 100%;
 }
 
 :-moz-meter-optimum::-moz-meter-bar {
   /* green. */
   background: linear-gradient(#ad7, #ad7, #cea 20%, #7a3 45%, #7a3 55%);
 }
@@ -989,16 +1002,17 @@ meter {
 }
 :-moz-meter-sub-sub-optimum::-moz-meter-bar {
   /* red. */
   background: linear-gradient(#f77, #f77, #fcc 20%, #d44 45%, #d44 55%);
 }
 
 input[type=range] {
   -moz-appearance: range;
+  appearance: auto;
   display: inline-block;
   inline-size: 12em;
   block-size: 1.3em;
   margin-inline-start: 0.7em;
   margin-inline-end: 0.7em;
   margin-block-start: 0;
   margin-block-end: 0;
   /* Override some rules that apply on all input types: */
@@ -1106,16 +1120,17 @@ input[type=range]::-moz-range-progress {
  * logic to position it). Specifically the 'margin', 'top' and 'left'
  * properties are ignored.
  */
 input[type=range]::-moz-range-thumb {
   /* Native theming is atomic for range. Set -moz-appearance on the range
    * to get rid of it. The thumb's -moz-appearance is fixed.
    */
   -moz-appearance: range-thumb !important;
+  appearance: auto !important;
   /* Prevent styling that would change the type of frame we construct. */
   display: inline-block !important;
   float: none !important;
   position: static !important;
   width: 1em;
   height: 1em;
   border: 0.1em solid #999;
   border-radius: 0.5em;
@@ -1178,16 +1193,17 @@ input[type=number]::-moz-number-spin-box
   max-height: 1em;
   align-self: center;
   justify-content: center;
 }
 
 input[type=number]::-moz-number-spin-up {
   writing-mode: horizontal-tb;
   -moz-appearance: spinner-upbutton;
+  appearance: auto;
   display: block; /* bug 926670 */
   flex: none;
   cursor: default;
   /* Style for when native theming is off: */
   background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="6" height="5"><path d="M1,4 L3,0 5,4" fill="dimgrey"/></svg>');
   background-repeat: no-repeat;
   background-position: center bottom;
   border: 1px solid darkgray;
@@ -1196,16 +1212,17 @@ input[type=number]::-moz-number-spin-up 
      as we probably don't want to turn the spinner sideways in vertical writing mode */
   border-top-left-radius: 4px;
   border-top-right-radius: 4px;
 }
 
 input[type=number]::-moz-number-spin-down {
   writing-mode: horizontal-tb;
   -moz-appearance: spinner-downbutton;
+  appearance: auto;
   display: block; /* bug 926670 */
   flex: none;
   cursor: default;
   /* Style for when native theming is off: */
   background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="6" height="5"><path d="M1,1 L3,5 5,1" fill="dimgrey"/></svg>');
   background-repeat: no-repeat;
   background-position: center top;
   border: 1px solid darkgray;
--- a/layout/style/res/number-control.css
+++ b/layout/style/res/number-control.css
@@ -4,15 +4,16 @@
 
 /* This file exists purely because we need the styling for input[type=number]
  * to apply only if the pref dom.forms.number is true. Once bug 677302 is
  * fixed this rule can move back to forms.css.
  */
 
 input[type="number"] {
   -moz-appearance: number-input;
+  appearance: auto;
   /* Has to revert some properties applied by the generic input rule. */
   -moz-binding: none;
   inline-size: 20ch; /* It'd be nice if this matched the default inline-size
                         of <input type=text>, but that's not easy to achieve
                         due to platform differences. */
 }
 
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -7778,18 +7778,18 @@ if (IsCSSPropertyPrefEnabled("layout.css
   gCSSProperties["display"].other_values.push("flow-root");
 }
 
 if (IsCSSPropertyPrefEnabled("layout.css.appearance.enabled")) {
   gCSSProperties["appearance"] = {
     domProp: "appearance",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
-    initial_values: [ "auto" ],
-    other_values: [ "none" ],
+    initial_values: [ "none" ],
+    other_values: [ "auto" ],
     invalid_values: [ "button" ]
   };
   gCSSProperties["-webkit-appearance"] = {
     domProp: "webkitAppearance",
     inherited: false,
     type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
     alias_for: "appearance",
     subproperties: [ "appearance" ],
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/AUTHORS
@@ -0,0 +1,1 @@
+Dan Glastonbury <dglastonbury@mozilla.com>
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "cubeb-pulse"
+version = "0.0.1"
+authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
+description = "Cubeb backed for PulseAudio written in Rust"
+
+[features]
+pulse-dlopen = ["pulse-ffi/dlopen"]
+
+[dependencies]
+cubeb-ffi = { path = "cubeb-ffi" }
+pulse-ffi = { path = "pulse-ffi" }
+semver = "^0.6"
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/LICENSE
@@ -0,0 +1,13 @@
+Copyright © 2011 Mozilla Foundation
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/README.md
@@ -0,0 +1,5 @@
+# cubeb-pulse-rs
+
+Implementation of PulseAudio backend for Cubeb written in Rust.
+
+[![Travis Build Status](https://travis-ci.org/djg/cubeb-pulse-rs.svg?branch=master)](https://travis-ci.org/djg/cubeb-pulse-rs)
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/README_MOZILLA
@@ -0,0 +1,8 @@
+The source from this directory was copied from the cubeb-pulse-rs
+git repository using the update.sh script.  The only changes
+made were those applied by update.sh and the addition of
+Makefile.in build files for the Mozilla build system.
+
+The cubeb-pulse-rs git repository is: https://github.com/djg/cubeb-pulse-rs.git
+
+The git commit ID used was faa1dcf3a061144c1f7edee76f23691eabd1f436 (2017-04-20 11:23:09 +1000)
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "cubeb-ffi"
+version = "0.0.1"
+authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
+description = "FFI bindings for implementing cubeb backends"
+
+[dependencies]
+bitflags = "^0.7.0"
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/errors.rs
@@ -0,0 +1,6 @@
+pub const OK: i32 = 0;
+pub const ERROR: i32 = -1;
+pub const ERROR_INVALID_FORMAT: i32 = -2;
+pub const ERROR_INVALID_PARAMETER: i32 = -3;
+pub const ERROR_NOT_SUPPORTED: i32 = -4;
+pub const ERROR_DEVICE_UNAVAILABLE: i32 = -5;
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/ffi.rs
@@ -0,0 +1,520 @@
+// Copyright © 2017 Mozilla Foundation
+//
+// This program is made available under an ISC-style license.  See the
+// accompanying file LICENSE for details.
+
+use std::default::Default;
+use std::os::raw::{c_char, c_long, c_void};
+use std::ptr;
+
+pub enum Context {}
+pub enum Stream {}
+
+// TODO endian check
+#[repr(C)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct SampleFormat(i32);
+
+// These need to match cubeb_sample_format
+pub const SAMPLE_S16LE: SampleFormat = SampleFormat(0);
+pub const SAMPLE_S16BE: SampleFormat = SampleFormat(1);
+pub const SAMPLE_FLOAT32LE: SampleFormat = SampleFormat(2);
+pub const SAMPLE_FLOAT32BE: SampleFormat = SampleFormat(3);
+
+#[cfg(target_endian = "little")]
+pub const SAMPLE_S16NE: SampleFormat = SAMPLE_S16LE;
+#[cfg(target_endian = "little")]
+pub const SAMPLE_FLOAT32NE: SampleFormat = SAMPLE_FLOAT32LE;
+#[cfg(target_endian = "big")]
+pub const SAMPLE_S16NE: SampleFormat = SAMPLE_S16BE;
+#[cfg(target_endian = "big")]
+pub const SAMPLE_FLOAT32NE: SampleFormat = SAMPLE_FLOAT32BE;
+
+pub type DeviceId = *const c_void;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct ChannelLayout(i32);
+
+// These need to match cubeb_channel_layout
+pub const LAYOUT_UNDEFINED: ChannelLayout = ChannelLayout(0);
+pub const LAYOUT_DUAL_MONO: ChannelLayout = ChannelLayout(1);
+pub const LAYOUT_DUAL_MONO_LFE: ChannelLayout = ChannelLayout(2);
+pub const LAYOUT_MONO: ChannelLayout = ChannelLayout(3);
+pub const LAYOUT_MONO_LFE: ChannelLayout = ChannelLayout(4);
+pub const LAYOUT_STEREO: ChannelLayout = ChannelLayout(5);
+pub const LAYOUT_STEREO_LFE: ChannelLayout = ChannelLayout(6);
+pub const LAYOUT_3F: ChannelLayout = ChannelLayout(7);
+pub const LAYOUT_3F_LFE: ChannelLayout = ChannelLayout(8);
+pub const LAYOUT_2F1: ChannelLayout = ChannelLayout(9);
+pub const LAYOUT_2F1_LFE: ChannelLayout = ChannelLayout(10);
+pub const LAYOUT_3F1: ChannelLayout = ChannelLayout(11);
+pub const LAYOUT_3F1_LFE: ChannelLayout = ChannelLayout(12);
+pub const LAYOUT_2F2: ChannelLayout = ChannelLayout(13);
+pub const LAYOUT_2F2_LFE: ChannelLayout = ChannelLayout(14);
+pub const LAYOUT_3F2: ChannelLayout = ChannelLayout(15);
+pub const LAYOUT_3F2_LFE: ChannelLayout = ChannelLayout(16);
+pub const LAYOUT_3F3R_LFE: ChannelLayout = ChannelLayout(17);
+pub const LAYOUT_3F4_LFE: ChannelLayout = ChannelLayout(18);
+pub const LAYOUT_MAX: ChannelLayout = ChannelLayout(19);
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct StreamParams {
+    pub format: SampleFormat,
+    pub rate: u32,
+    pub channels: u32,
+    pub layout: ChannelLayout,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct Device {
+    pub output_name: *mut c_char,
+    pub input_name: *mut c_char,
+}
+
+impl Default for Device {
+    fn default() -> Self {
+        Device {
+            output_name: ptr::null_mut(),
+            input_name: ptr::null_mut(),
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct State(i32);
+
+// These need to match cubeb_state
+pub const STATE_STARTED: State = State(0);
+pub const STATE_STOPPED: State = State(1);
+pub const STATE_DRAINED: State = State(2);
+pub const STATE_ERROR: State = State(3);
+
+pub const OK: i32 = 0;
+pub const ERROR: i32 = -1;
+pub const ERROR_INVALID_FORMAT: i32 = -2;
+pub const ERROR_INVALID_PARAMETER: i32 = -3;
+pub const ERROR_NOT_SUPPORTED: i32 = -4;
+pub const ERROR_DEVICE_UNAVAILABLE: i32 = -5;
+
+// These need to match cubeb_device_type
+bitflags! {
+    #[repr(C)]
+    pub flags DeviceType : u32 {
+        const DEVICE_TYPE_UNKNOWN = 0b00,
+        const DEVICE_TYPE_INPUT = 0b01,
+        const DEVICE_TYPE_OUTPUT = 0b10,
+        const DEVICE_TYPE_ALL = 0b11,
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum DeviceState {
+    Disabled = 0,
+    Unplugged = 1,
+    Enabled = 2,
+}
+
+// These need to match cubeb_device_fmt
+bitflags! {
+    #[repr(C)]
+    pub flags DeviceFmt: u32 {
+        const DEVICE_FMT_S16LE = 0x0010,
+        const DEVICE_FMT_S16BE = 0x0020,
+        const DEVICE_FMT_F32LE = 0x1000,
+        const DEVICE_FMT_F32BE = 0x2000,
+        const DEVICE_FMT_S16_MASK = DEVICE_FMT_S16LE.bits | DEVICE_FMT_S16BE.bits,
+        const DEVICE_FMT_F32_MASK = DEVICE_FMT_F32LE.bits | DEVICE_FMT_F32BE.bits,
+        const DEVICE_FMT_ALL = DEVICE_FMT_S16_MASK.bits | DEVICE_FMT_F32_MASK.bits,
+    }
+}
+
+// Ideally these would be defined as part of `flags DeviceFmt` but
+// that doesn't work with current bitflags crate.
+#[cfg(target_endian = "little")]
+pub const CUBEB_FMT_S16NE: DeviceFmt = DEVICE_FMT_S16LE;
+#[cfg(target_endian = "little")]
+pub const CUBEB_FMT_F32NE: DeviceFmt = DEVICE_FMT_F32LE;
+#[cfg(target_endian = "big")]
+pub const CUBEB_FMT_S16NE: DeviceFmt = DEVICE_FMT_S16BE;
+#[cfg(target_endian = "big")]
+pub const CUBEB_FMT_F32NE: DeviceFmt = DEVICE_FMT_F32BE;
+
+// These need to match cubeb_device_pref
+bitflags! {
+    #[repr(C)]
+    pub flags DevicePref : u32 {
+        const DEVICE_PREF_MULTIMEDIA = 0x1,
+        const DEVICE_PREF_VOICE = 0x2,
+        const DEVICE_PREF_NOTIFICATION = 0x4,
+        const DEVICE_PREF_ALL = 0xF
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct DeviceInfo {
+    pub devid: DeviceId,
+    pub device_id: *const c_char,
+    pub friendly_name: *const c_char,
+    pub group_id: *const c_char,
+    pub vendor_name: *const c_char,
+    pub devtype: DeviceType,
+    pub state: DeviceState,
+    pub preferred: DevicePref,
+    pub format: DeviceFmt,
+    pub default_format: DeviceFmt,
+    pub max_channels: u32,
+    pub default_rate: u32,
+    pub max_rate: u32,
+    pub min_rate: u32,
+    pub latency_lo: u32,
+    pub latency_hi: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct DeviceCollection {
+    /// Device count in collection.
+    pub count: u32,
+    /// Array of pointers to device info.
+    pub device: [*const DeviceInfo; 0],
+}
+
+pub type DataCallback = Option<unsafe extern "C" fn(stream: *mut Stream,
+                                                    user_ptr: *mut c_void,
+                                                    input_buffer: *const c_void,
+                                                    output_buffer: *mut c_void,
+                                                    nframes: c_long)
+                                                    -> c_long>;
+pub type StateCallback = Option<unsafe extern "C" fn(stream: *mut Stream, user_ptr: *mut c_void, state: State)>;
+pub type DeviceChangedCallback = Option<unsafe extern "C" fn(user_ptr: *mut c_void)>;
+pub type DeviceCollectionChangedCallback = Option<unsafe extern "C" fn(context: *mut Context, user_ptr: *mut c_void)>;
+
+pub type StreamInitFn = Option<unsafe extern "C" fn(context: *mut Context,
+                                                    stream: *mut *mut Stream,
+                                                    stream_name: *const c_char,
+                                                    input_device: DeviceId,
+                                                    input_stream_params: *mut StreamParams,
+                                                    output_device: DeviceId,
+                                                    output_stream_params: *mut StreamParams,
+                                                    latency: u32,
+                                                    data_callback: DataCallback,
+                                                    state_callback: StateCallback,
+                                                    user_ptr: *mut c_void)
+                                                    -> i32>;
+
+pub type RegisterDeviceCollectionChangedFn = Option<unsafe extern "C" fn(context: *mut Context,
+                                                                         devtype: DeviceType,
+                                                                         callback: DeviceCollectionChangedCallback,
+                                                                         user_ptr: *mut c_void)
+                                                                         -> i32>;
+
+#[repr(C)]
+pub struct Ops {
+    pub init: Option<unsafe extern "C" fn(context: *mut *mut Context, context_name: *const c_char) -> i32>,
+    pub get_backend_id: Option<unsafe extern "C" fn(context: *mut Context) -> *const c_char>,
+    pub get_max_channel_count: Option<unsafe extern "C" fn(context: *mut Context, max_channels: *mut u32) -> i32>,
+    pub get_min_latency: Option<unsafe extern "C" fn(context: *mut Context,
+                                                     params: StreamParams,
+                                                     latency_ms: *mut u32)
+                                                     -> i32>,
+    pub get_preferred_sample_rate: Option<unsafe extern "C" fn(context: *mut Context, rate: *mut u32) -> i32>,
+    pub get_preferred_channel_layout:
+        Option<unsafe extern "C" fn(context: *mut Context, layout: *mut ChannelLayout) -> i32>,
+    pub enumerate_devices: Option<unsafe extern "C" fn(context: *mut Context,
+                                                       devtype: DeviceType,
+                                                       collection: *mut *mut DeviceCollection)
+                                                       -> i32>,
+    pub destroy: Option<unsafe extern "C" fn(context: *mut Context)>,
+    pub stream_init: StreamInitFn,
+    pub stream_destroy: Option<unsafe extern "C" fn(stream: *mut Stream)>,
+    pub stream_start: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
+    pub stream_stop: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
+    pub stream_get_position: Option<unsafe extern "C" fn(stream: *mut Stream, position: *mut u64) -> i32>,
+    pub stream_get_latency: Option<unsafe extern "C" fn(stream: *mut Stream, latency: *mut u32) -> i32>,
+    pub stream_set_volume: Option<unsafe extern "C" fn(stream: *mut Stream, volumes: f32) -> i32>,
+    pub stream_set_panning: Option<unsafe extern "C" fn(stream: *mut Stream, panning: f32) -> i32>,
+    pub stream_get_current_device: Option<unsafe extern "C" fn(stream: *mut Stream, device: *mut *const Device) -> i32>,
+    pub stream_device_destroy: Option<unsafe extern "C" fn(stream: *mut Stream, device: *mut Device) -> i32>,
+    pub stream_register_device_changed_callback:
+        Option<unsafe extern "C" fn(stream: *mut Stream,
+                                    device_changed_callback: DeviceChangedCallback)
+                                    -> i32>,
+    pub register_device_collection_changed: RegisterDeviceCollectionChangedFn,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct LayoutMap {
+    pub name: *const c_char,
+    pub channels: u32,
+    pub layout: ChannelLayout,
+}
+
+// cubeb_mixer.h
+#[repr(C)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Channel(i32);
+impl Into<i32> for Channel {
+    fn into(self) -> i32 {
+        self.0
+    }
+}
+
+// These need to match cubeb_channel
+pub const CHANNEL_INVALID: Channel = Channel(-1);
+pub const CHANNEL_MONO: Channel = Channel(0);
+pub const CHANNEL_LEFT: Channel = Channel(1);
+pub const CHANNEL_RIGHT: Channel = Channel(2);
+pub const CHANNEL_CENTER: Channel = Channel(3);
+pub const CHANNEL_LS: Channel = Channel(4);
+pub const CHANNEL_RS: Channel = Channel(5);
+pub const CHANNEL_RLS: Channel = Channel(6);
+pub const CHANNEL_RCENTER: Channel = Channel(7);
+pub const CHANNEL_RRS: Channel = Channel(8);
+pub const CHANNEL_LFE: Channel = Channel(9);
+pub const CHANNEL_MAX: Channel = Channel(10);
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct ChannelMap {
+    pub channels: u32,
+    pub map: [Channel; 10],
+}
+impl ::std::default::Default for ChannelMap {
+    fn default() -> Self {
+        ChannelMap {
+            channels: 0,
+            map: [CHANNEL_INVALID,
+                  CHANNEL_INVALID,
+                  CHANNEL_INVALID,
+                  CHANNEL_INVALID,
+                  CHANNEL_INVALID,
+                  CHANNEL_INVALID,
+                  CHANNEL_INVALID,
+                  CHANNEL_INVALID,
+                  CHANNEL_INVALID,
+                  CHANNEL_INVALID],
+        }
+    }
+}
+
+extern "C" {
+    pub fn cubeb_channel_map_to_layout(channel_map: *const ChannelMap) -> ChannelLayout;
+    pub fn cubeb_should_upmix(stream: *const StreamParams, mixer: *const StreamParams) -> bool;
+    pub fn cubeb_should_downmix(stream: *const StreamParams, mixer: *const StreamParams) -> bool;
+    pub fn cubeb_downmix_float(input: *const f32,
+                               inframes: c_long,
+                               output: *mut f32,
+                               in_channels: u32,
+                               out_channels: u32,
+                               in_layout: ChannelLayout,
+                               out_layout: ChannelLayout);
+    pub fn cubeb_upmix_float(input: *const f32,
+                             inframes: c_long,
+                             output: *mut f32,
+                             in_channels: u32,
+                             out_channels: u32);
+}
+
+#[test]
+fn bindgen_test_layout_stream_params() {
+    assert_eq!(::std::mem::size_of::<StreamParams>(),
+               16usize,
+               concat!("Size of: ", stringify!(StreamParams)));
+    assert_eq!(::std::mem::align_of::<StreamParams>(),
+               4usize,
+               concat!("Alignment of ", stringify!(StreamParams)));
+    assert_eq!(unsafe { &(*(0 as *const StreamParams)).format as *const _ as usize },
+               0usize,
+               concat!("Alignment of field: ",
+                       stringify!(StreamParams),
+                       "::",
+                       stringify!(format)));
+    assert_eq!(unsafe { &(*(0 as *const StreamParams)).rate as *const _ as usize },
+               4usize,
+               concat!("Alignment of field: ",
+                       stringify!(StreamParams),
+                       "::",
+                       stringify!(rate)));
+    assert_eq!(unsafe { &(*(0 as *const StreamParams)).channels as *const _ as usize },
+               8usize,
+               concat!("Alignment of field: ",
+                       stringify!(StreamParams),
+                       "::",
+                       stringify!(channels)));
+    assert_eq!(unsafe { &(*(0 as *const StreamParams)).layout as *const _ as usize },
+               12usize,
+               concat!("Alignment of field: ",
+                       stringify!(StreamParams),
+                       "::",
+                       stringify!(layout)));
+}
+
+#[test]
+fn bindgen_test_layout_cubeb_device() {
+    assert_eq!(::std::mem::size_of::<Device>(),
+               16usize,
+               concat!("Size of: ", stringify!(Device)));
+    assert_eq!(::std::mem::align_of::<Device>(),
+               8usize,
+               concat!("Alignment of ", stringify!(Device)));
+    assert_eq!(unsafe { &(*(0 as *const Device)).output_name as *const _ as usize },
+               0usize,
+               concat!("Alignment of field: ",
+                       stringify!(Device),
+                       "::",
+                       stringify!(output_name)));
+    assert_eq!(unsafe { &(*(0 as *const Device)).input_name as *const _ as usize },
+               8usize,
+               concat!("Alignment of field: ",
+                       stringify!(Device),
+                       "::",
+                       stringify!(input_name)));
+}
+
+#[test]
+fn bindgen_test_layout_cubeb_device_info() {
+    assert_eq!(::std::mem::size_of::<DeviceInfo>(),
+               88usize,
+               concat!("Size of: ", stringify!(DeviceInfo)));
+    assert_eq!(::std::mem::align_of::<DeviceInfo>(),
+               8usize,
+               concat!("Alignment of ", stringify!(DeviceInfo)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).devid as *const _ as usize },
+               0usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(devid)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).device_id as *const _ as usize },
+               8usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(device_id)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).friendly_name as *const _ as usize },
+               16usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(friendly_name)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).group_id as *const _ as usize },
+               24usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(group_id)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).vendor_name as *const _ as usize },
+               32usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(vendor_name)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).devtype as *const _ as usize },
+               40usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(type_)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).state as *const _ as usize },
+               44usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(state)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).preferred as *const _ as usize },
+               48usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(preferred)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).format as *const _ as usize },
+               52usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(format)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).default_format as *const _ as usize },
+               56usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(default_format)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).max_channels as *const _ as usize },
+               60usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(max_channels)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).default_rate as *const _ as usize },
+               64usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(default_rate)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).max_rate as *const _ as usize },
+               68usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(max_rate)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).min_rate as *const _ as usize },
+               72usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(min_rate)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).latency_lo as *const _ as usize },
+               76usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(latency_lo)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).latency_hi as *const _ as usize },
+               80usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(latency_hi)));
+}
+
+#[test]
+fn bindgen_test_layout_cubeb_device_collection() {
+    assert_eq!(::std::mem::size_of::<DeviceCollection>(),
+               16usize,
+               concat!("Size of: ", stringify!(DeviceCollection)));
+    assert_eq!(::std::mem::align_of::<DeviceCollection>(),
+               8usize,
+               concat!("Alignment of ", stringify!(DeviceCollection)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceCollection)).count as *const _ as usize },
+               0usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceCollection),
+                       "::",
+                       stringify!(count)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceCollection)).device as *const _ as usize },
+               8usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceCollection),
+                       "::",
+                       stringify!(device)));
+
+}
+
+#[test]
+fn test_normal_logging() {
+    log!("This is log at normal level");
+    log!("This is {} at normal level", "log with param");
+}
+
+#[test]
+fn test_verbose_logging() {
+    logv!("This is a log at verbose level");
+    logv!("This is {} at verbose level", "log with param");
+}
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/internal.rs
@@ -0,0 +1,34 @@
+use libc::c_void;
+use *;
+
+#[repr(C)]
+#[derive(Clone,Copy,Debug)]
+pub struct LayoutMap {
+    pub name: *const i8,
+    pub channels: u32,
+    pub layout: ChannelLayout
+}
+
+#[repr(C)]
+pub struct Ops {
+    pub init: Option<unsafe extern "C" fn(context: *mut *mut Context, context_name: *const i8) -> i32>,
+    pub get_backend_id: Option<unsafe extern "C" fn(context: *mut Context) -> *const i8>,
+    pub get_max_channel_count: Option<unsafe extern "C" fn(context: *mut Context, max_channels: *mut u32) -> i32>,
+    pub get_min_latency:  Option<unsafe extern "C" fn(context: *mut Context, params: StreamParams, latency_ms: *mut u32) -> i32>,
+    pub get_preferred_sample_rate:  Option<unsafe extern "C" fn(context: *mut Context, rate: *mut u32) -> i32>,
+    pub get_preferred_channel_layout:  Option<unsafe extern "C" fn(context: *mut Context, layout: *mut ChannelLayout) -> i32>,
+    pub enumerate_devices:  Option<unsafe extern "C" fn(context: *mut Context, devtype: DeviceType, collection: *mut *mut DeviceCollection) -> i32>,
+    pub destroy:  Option<unsafe extern "C" fn(context: *mut Context)>,
+    pub stream_init: Option<unsafe extern "C" fn(context: *mut Context, stream: *mut *mut Stream, stream_name: *const i8, input_device: DeviceId, input_stream_params: *mut StreamParams, output_device: DeviceId, output_stream_params: *mut StreamParams, latency: u32, data_callback: DataCallback, state_callback: StateCallback, user_ptr: *mut c_void) -> i32>,
+    pub stream_destroy: Option<unsafe extern "C" fn(stream: *mut Stream)>,
+    pub stream_start: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
+    pub stream_stop: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
+    pub stream_get_position: Option<unsafe extern "C" fn(stream: *mut Stream, position: *mut u64) -> i32>,
+    pub stream_get_latency: Option<unsafe extern "C" fn(stream: *mut Stream, latency: *mut u32) -> i32>,
+    pub stream_set_volume: Option<unsafe extern "C" fn(stream: *mut Stream, volumes: f32) -> i32>,
+    pub stream_set_panning: Option<unsafe extern "C" fn(stream: *mut Stream, panning: f32)-> i32>,
+    pub stream_get_current_device: Option<unsafe extern "C" fn(stream: *mut Stream, device: *mut *const Device) -> i32>,
+    pub stream_device_destroy: Option<unsafe extern "C" fn(stream: *mut Stream, device: *mut Device) -> i32>,
+    pub stream_register_device_changed_callback: Option<unsafe extern "C" fn(stream: *mut Stream, device_changed_callback: DeviceChangedCallback) -> i32>,
+    pub register_device_collection_changed: Option<unsafe extern "C" fn(context: *mut Context, devtype: DeviceType, callback: DeviceCollectionChangedCallback, user_ptr: *mut c_void) -> i32>
+}
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/lib.rs
@@ -0,0 +1,14 @@
+// Copyright © 2017 Mozilla Foundation
+//
+// This program is made available under an ISC-style license.  See the
+// accompanying file LICENSE for details.
+
+#[macro_use]
+extern crate bitflags;
+
+mod ffi;
+mod log;
+pub mod mixer;
+
+pub use ffi::*;
+pub use log::*;
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/log.rs
@@ -0,0 +1,66 @@
+use std::os::raw::c_char;
+
+#[macro_export]
+macro_rules! log_internal {
+    ($level: expr, $msg: expr) => {
+        #[allow(unused_unsafe)]
+        unsafe {
+            if $level <= $crate::g_cubeb_log_level {
+                if let Some(log_callback) = $crate::g_cubeb_log_callback {
+                    let cstr = ::std::ffi::CString::new(concat!("%s:%d: ", $msg, "\n")).unwrap();
+                    log_callback(cstr.as_ptr(), file!(), line!());
+                }
+            }
+        }
+    };
+    ($level: expr, $fmt: expr, $($arg:tt)+) => {
+        #[allow(unused_unsafe)]
+        unsafe {
+            if $level <= $crate::g_cubeb_log_level {
+                if let Some(log_callback) = $crate::g_cubeb_log_callback {
+                    let cstr = ::std::ffi::CString::new(concat!("%s:%d: ", $fmt, "\n")).unwrap();
+                    log_callback(cstr.as_ptr(), file!(), line!(), $($arg)+);
+                }
+            }
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! logv {
+    ($msg: expr) => (log_internal!($crate::LogLevel::Verbose, $msg));
+    ($fmt: expr, $($arg: tt)+) => (log_internal!($crate::LogLevel::Verbose, $fmt, $($arg)*));
+}
+
+#[macro_export]
+macro_rules! log {
+    ($msg: expr) => (log_internal!($crate::LogLevel::Normal, $msg));
+    ($fmt: expr, $($arg: tt)+) => (log_internal!($crate::LogLevel::Normal, $fmt, $($arg)*));
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum LogLevel {
+    Disabled = 0,
+    Normal = 1,
+    Verbose = 2,
+}
+
+pub type LogCallback = Option<unsafe extern "C" fn(fmt: *const c_char, ...)>;
+
+extern "C" {
+    pub static g_cubeb_log_level: LogLevel;
+    pub static g_cubeb_log_callback: LogCallback;
+}
+
+#[test]
+fn test_normal_logging() {
+    log!("This is log at normal level");
+    log!("Formatted log %d", 1);
+}
+
+#[test]
+fn test_verbose_logging() {
+    logv!("This is a log at verbose level");
+    logv!("Formatted log %d", 1);
+}
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/mixer.rs
@@ -0,0 +1,80 @@
+// Copyright © 2017 Mozilla Foundation
+//
+// This program is made available under an ISC-style license.  See the
+// accompanying file LICENSE for details.
+
+use ::*;
+
+static CHANNEL_LAYOUT_UNDEFINED: &'static [Channel] = &[CHANNEL_INVALID];
+static CHANNEL_LAYOUT_DUAL_MONO: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT];
+static CHANNEL_LAYOUT_DUAL_MONO_LFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE];
+static CHANNEL_LAYOUT_MONO: &'static [Channel] = &[CHANNEL_MONO];
+static CHANNEL_LAYOUT_MONO_LFE: &'static [Channel] = &[CHANNEL_MONO, CHANNEL_LFE];
+static CHANNEL_LAYOUT_STEREO: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT];
+static CHANNEL_LAYOUT_STEREO_LFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE];
+static CHANNEL_LAYOUT_3F: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER];
+static CHANNEL_LAYOUT_3FLFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE];
+static CHANNEL_LAYOUT_2F1: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_RCENTER];
+static CHANNEL_LAYOUT_2F1LFE: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE, CHANNEL_RCENTER];
+static CHANNEL_LAYOUT_3F1: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_RCENTER];
+static CHANNEL_LAYOUT_3F1LFE: &'static [Channel] = &[CHANNEL_LEFT,
+                                                     CHANNEL_RIGHT,
+                                                     CHANNEL_CENTER,
+                                                     CHANNEL_LFE,
+                                                     CHANNEL_RCENTER];
+static CHANNEL_LAYOUT_2F2: &'static [Channel] = &[CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LS, CHANNEL_RS];
+static CHANNEL_LAYOUT_2F2LFE: &'static [Channel] = &[CHANNEL_LEFT,
+                                                     CHANNEL_RIGHT,
+                                                     CHANNEL_LFE,
+                                                     CHANNEL_LS,
+                                                     CHANNEL_RS];
+static CHANNEL_LAYOUT_3F2: &'static [Channel] = &[CHANNEL_LEFT,
+                                                  CHANNEL_RIGHT,
+                                                  CHANNEL_CENTER,
+                                                  CHANNEL_LS,
+                                                  CHANNEL_RS];
+static CHANNEL_LAYOUT_3F2LFE: &'static [Channel] = &[CHANNEL_LEFT,
+                                                     CHANNEL_RIGHT,
+                                                     CHANNEL_CENTER,
+                                                     CHANNEL_LFE,
+                                                     CHANNEL_LS,
+                                                     CHANNEL_RS];
+static CHANNEL_LAYOUT_3F3RLFE: &'static [Channel] = &[CHANNEL_LEFT,
+                                                      CHANNEL_RIGHT,
+                                                      CHANNEL_CENTER,
+                                                      CHANNEL_LFE,
+                                                      CHANNEL_RCENTER,
+                                                      CHANNEL_LS,
+                                                      CHANNEL_RS];
+static CHANNEL_LAYOUT_3F4LFE: &'static [Channel] = &[CHANNEL_LEFT,
+                                                     CHANNEL_RIGHT,
+                                                     CHANNEL_CENTER,
+                                                     CHANNEL_LFE,
+                                                     CHANNEL_RLS,
+                                                     CHANNEL_RRS,
+                                                     CHANNEL_LS,
+                                                     CHANNEL_RS];
+
+pub fn channel_index_to_order(layout: ChannelLayout) -> &'static [Channel] {
+    match layout {
+        LAYOUT_DUAL_MONO => CHANNEL_LAYOUT_DUAL_MONO,
+        LAYOUT_DUAL_MONO_LFE => CHANNEL_LAYOUT_DUAL_MONO_LFE,
+        LAYOUT_MONO => CHANNEL_LAYOUT_MONO,
+        LAYOUT_MONO_LFE => CHANNEL_LAYOUT_MONO_LFE,
+        LAYOUT_STEREO => CHANNEL_LAYOUT_STEREO,
+        LAYOUT_STEREO_LFE => CHANNEL_LAYOUT_STEREO_LFE,
+        LAYOUT_3F => CHANNEL_LAYOUT_3F,
+        LAYOUT_3F_LFE => CHANNEL_LAYOUT_3FLFE,
+        LAYOUT_2F1 => CHANNEL_LAYOUT_2F1,
+        LAYOUT_2F1_LFE => CHANNEL_LAYOUT_2F1LFE,
+        LAYOUT_3F1 => CHANNEL_LAYOUT_3F1,
+        LAYOUT_3F1_LFE => CHANNEL_LAYOUT_3F1LFE,
+        LAYOUT_2F2 => CHANNEL_LAYOUT_2F2,
+        LAYOUT_2F2_LFE => CHANNEL_LAYOUT_2F2LFE,
+        LAYOUT_3F2 => CHANNEL_LAYOUT_3F2,
+        LAYOUT_3F2_LFE => CHANNEL_LAYOUT_3F2LFE,
+        LAYOUT_3F3R_LFE => CHANNEL_LAYOUT_3F3RLFE,
+        LAYOUT_3F4_LFE => CHANNEL_LAYOUT_3F4LFE,
+        _ => CHANNEL_LAYOUT_UNDEFINED,
+    }
+}
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/types/device_fmt.rs
@@ -0,0 +1,89 @@
+use std::ops;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct DeviceFmt(u32);
+
+const DEVICE_FMT_S16LE: u32 = 0x0010;
+const DEVICE_FMT_S16BE: u32 = 0x0020;
+const DEVICE_FMT_F32LE: u32 = 0x1000;
+const DEVICE_FMT_F32BE: u32 = 0x2000;
+const DEVICE_FMT_S16_MASK: u32 = DEVICE_FMT_S16LE | DEVICE_FMT_S16BE;
+const DEVICE_FMT_F32_MASK: u32 = DEVICE_FMT_F32LE | DEVICE_FMT_F32BE;
+const DEVICE_FMT_ALL: u32   = DEVICE_FMT_S16_MASK | DEVICE_FMT_F32_MASK;
+
+
+impl DeviceFmt {
+    pub fn empty() -> Self { DeviceFmt(0) }
+
+    #[inline] pub fn s16le() -> Self { DeviceFmt(DEVICE_FMT_S16LE) }
+    #[inline] pub fn s16be() -> Self { DeviceFmt(DEVICE_FMT_S16BE) }
+    #[inline] pub fn f32le() -> Self { DeviceFmt(DEVICE_FMT_F32LE) }
+    #[inline] pub fn f32be() -> Self { DeviceFmt(DEVICE_FMT_F32BE) }
+    #[inline] pub fn all() -> Self { DeviceFmt(DEVICE_FMT_ALL) }
+
+    #[inline] pub fn s16ne() -> Self {
+        if cfg!(target_endian = "little") {
+            DeviceFmt::s16le()
+        } else {
+            DeviceFmt::s16be()
+        }
+    }
+
+    #[inline] pub fn f32ne() -> Self {
+        if cfg!(target_endian = "little") {
+            DeviceFmt::f32le()
+        } else {
+            DeviceFmt::f32be()
+        }
+    }
+
+    #[inline] pub fn contains(&self, other: Self) -> bool { (*self & other) == other }
+    #[inline] pub fn insert(&mut self, other: Self) { self.0 |= other.0; }
+    #[inline] pub fn remove(&mut self, other: Self) { self.0 &= !other.0; }
+}
+
+impl ops::BitOr for DeviceFmt {
+    type Output = DeviceFmt;
+
+    #[inline]
+    fn bitor(self, other: Self) -> Self {
+        DeviceFmt(self.0 | other.0)
+    }
+}
+
+impl ops::BitXor for DeviceFmt {
+    type Output = DeviceFmt;
+
+    #[inline]
+    fn bitxor(self, other: Self) -> Self {
+        DeviceFmt(self.0 ^ other.0)
+    }
+}
+
+impl ops::BitAnd for DeviceFmt {
+    type Output = DeviceFmt;
+
+    #[inline]
+    fn bitand(self, other: Self) -> Self {
+        DeviceFmt(self.0 & other.0)
+    }
+}
+
+impl ops::Sub for DeviceFmt {
+    type Output = DeviceFmt;
+
+    #[inline]
+    fn sub(self, other: Self) -> Self {
+        DeviceFmt(self.0 & !other.0)
+    }
+}
+
+impl ops::Not for DeviceFmt {
+    type Output = DeviceFmt;
+
+    #[inline]
+    fn not(self) -> Self {
+        DeviceFmt(!self.0 & DEVICE_FMT_ALL)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/types/device_pref.rs
@@ -0,0 +1,68 @@
+use std::ops;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct DevicePref(u32);
+
+const DEVICE_PREF_MULTIMEDIA: u32 = 0x1;
+const DEVICE_PREF_VOICE: u32 = 0x2;
+const DEVICE_PREF_NOTIFICATION: u32 = 0x4;
+const DEVICE_PREF_ALL: u32 = 0xF;
+
+impl DevicePref {
+    pub fn none() -> Self { DevicePref(0) }
+
+    #[inline] pub fn multimedia() -> Self { DevicePref(DEVICE_PREF_MULTIMEDIA) }
+    #[inline] pub fn voice() -> Self { DevicePref(DEVICE_PREF_VOICE) }
+    #[inline] pub fn notification() -> Self { DevicePref(DEVICE_PREF_NOTIFICATION) }
+    #[inline] pub fn all() -> Self { DevicePref(DEVICE_PREF_ALL) }
+
+    #[inline] pub fn contains(&self, other: Self) -> bool { (*self & other) == other }
+    #[inline] pub fn insert(&mut self, other: Self) { self.0 |= other.0; }
+    #[inline] pub fn remove(&mut self, other: Self) { self.0 &= !other.0; }
+}
+
+impl ops::BitOr for DevicePref {
+    type Output = DevicePref;
+
+    #[inline]
+    fn bitor(self, other: Self) -> Self {
+        DevicePref(self.0 | other.0)
+    }
+}
+
+impl ops::BitXor for DevicePref {
+    type Output = DevicePref;
+
+    #[inline]
+    fn bitxor(self, other: Self) -> Self {
+        DevicePref(self.0 ^ other.0)
+    }
+}
+
+impl ops::BitAnd for DevicePref {
+    type Output = DevicePref;
+
+    #[inline]
+    fn bitand(self, other: Self) -> Self {
+        DevicePref(self.0 & other.0)
+    }
+}
+
+impl ops::Sub for DevicePref {
+    type Output = DevicePref;
+
+    #[inline]
+    fn sub(self, other: Self) -> Self {
+        DevicePref(self.0 & !other.0)
+    }
+}
+
+impl ops::Not for DevicePref {
+    type Output = DevicePref;
+
+    #[inline]
+    fn not(self) -> Self {
+        DevicePref(!self.0 & DEVICE_PREF_ALL)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/types/device_type.rs
@@ -0,0 +1,70 @@
+use std::ops;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct DeviceType(u32);
+
+const DEVICE_TYPE_UNKNOWN: u32 = 0b00;
+const DEVICE_TYPE_INPUT:u32 = 0b01;
+const DEVICE_TYPE_OUTPUT: u32 = 0b10;
+const DEVICE_TYPE_ALL: u32 = 0b11;
+
+impl DeviceType {
+    pub fn unknown() -> Self { DeviceType(DEVICE_TYPE_UNKNOWN) }
+
+    #[inline] pub fn input() -> Self { DeviceType(DEVICE_TYPE_INPUT) }
+    #[inline] pub fn output() -> Self { DeviceType(DEVICE_TYPE_OUTPUT) }
+    #[inline] pub fn all() -> Self { DeviceType(DEVICE_TYPE_ALL) }
+
+    #[inline] pub fn is_input(&self) -> bool { self.contains(DeviceType::input()) }
+    #[inline] pub fn is_output(&self) -> bool { self.contains(DeviceType::output()) }
+
+    #[inline] pub fn contains(&self, other: Self) -> bool { (*self & other) == other }
+    #[inline] pub fn insert(&mut self, other: Self) { self.0 |= other.0; }
+    #[inline] pub fn remove(&mut self, other: Self) { self.0 &= !other.0; }
+}
+
+impl ops::BitOr for DeviceType {
+    type Output = DeviceType;
+
+    #[inline]
+    fn bitor(self, other: Self) -> Self {
+        DeviceType(self.0 | other.0)
+    }
+}
+
+impl ops::BitXor for DeviceType {
+    type Output = DeviceType;
+
+    #[inline]
+    fn bitxor(self, other: Self) -> Self {
+        DeviceType(self.0 ^ other.0)
+    }
+}
+
+impl ops::BitAnd for DeviceType {
+    type Output = DeviceType;
+
+    #[inline]
+    fn bitand(self, other: Self) -> Self {
+        DeviceType(self.0 & other.0)
+    }
+}
+
+impl ops::Sub for DeviceType {
+    type Output = DeviceType;
+
+    #[inline]
+    fn sub(self, other: Self) -> Self {
+        DeviceType(self.0 & !other.0)
+    }
+}
+
+impl ops::Not for DeviceType {
+    type Output = DeviceType;
+
+    #[inline]
+    fn not(self) -> Self {
+        DeviceType(!self.0 & DEVICE_TYPE_ALL)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/types/mod.rs
@@ -0,0 +1,323 @@
+use libc::c_void;
+
+mod device_pref;
+mod device_fmt;
+mod device_type;
+
+pub use self::device_pref::*;
+pub use self::device_fmt::*;
+pub use self::device_type::*;
+
+/// Opaque handle to cubeb context.
+pub enum Context {}
+
+/// Opaque handle to cubeb stream.
+pub enum Stream {}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum SampleFormat {
+    Signed16LE = 0,
+    Signed16BE = 1,
+    Float32LE = 2,
+    Float32BE = 3,
+}
+
+#[cfg(target_endian = "little")]
+pub const SAMPLE_S16NE: SampleFormat = SampleFormat::Signed16LE;
+#[cfg(target_endian = "little")]
+pub const SAMPLE_FLOAT32NE: SampleFormat = SampleFormat::Float32LE;
+#[cfg(target_endian = "big")]
+pub const SAMPLE_S16NE: SampleFormat = SampleFormat::Signed16BE;
+#[cfg(target_endian = "big")]
+pub const SAMPLE_FLOAT32NE: SampleFormat = SampleFormat::Float32BE;
+
+pub type DeviceId = *const c_void;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ChannelLayout {
+    Undefined = 0,
+    DualMono = 1,
+    DualMonoLfe = 2,
+    Mono = 3,
+    MonoLfe = 4,
+    Stereo = 5,
+    StereoLfe = 6,
+    Layout3F = 7,
+    Layout3FLfe = 8,
+    Layout2F1 = 9,
+    Layout2F1Lfe = 10,
+    Layout3F1 = 11,
+    Layout3F1Lfe = 12,
+    Layout2F2 = 13,
+    Layout2F2Lfe = 14,
+    Layout3F2 = 15,
+    Layout3F2Lfe = 16,
+    Layout3F3RLfe = 17,
+    Layout3F4Lfe = 18,
+    Max = 19,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub struct StreamParams {
+    pub format: SampleFormat,
+    pub rate: u32,
+    pub channels: u32,
+    pub layout: ChannelLayout,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub struct Device {
+    pub output_name: *mut i8,
+    pub input_name: *mut i8,
+}
+
+impl Default for Device {
+    fn default() -> Self {
+        Device {
+            output_name: 0 as *mut _,
+            input_name: 0 as *mut _,
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum State {
+    Started = 0,
+    Stopped = 1,
+    Drained = 2,
+    Error = 3,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum DeviceState {
+    Disabled = 0,
+    Unplugged = 1,
+    Enabled = 2,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub struct DeviceInfo {
+    pub devid: DeviceId,
+    pub device_id: *const i8,
+    pub friendly_name: *const i8,
+    pub group_id: *const i8,
+    pub vendor_name: *const i8,
+    pub devtype: DeviceType,
+    pub state: DeviceState,
+    pub preferred: DevicePref,
+    pub format: DeviceFmt,
+    pub default_format: DeviceFmt,
+    pub max_channels: u32,
+    pub default_rate: u32,
+    pub max_rate: u32,
+    pub min_rate: u32,
+    pub latency_lo: u32,
+    pub latency_hi: u32,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub struct DeviceCollection {
+    /// Device count in collection.
+    pub count: u32,
+    /// Array of pointers to device info.
+    pub device: [*const DeviceInfo; 0],
+}
+
+pub type DataCallback = Option<unsafe extern "C" fn(stream: *mut Stream, user_ptr: *mut c_void, input_buffer: *const c_void, output_buffer: *mut c_void, nframes: i64) -> i64>;
+pub type StateCallback = Option<unsafe extern "C" fn(stream: *mut Stream, user_ptr: *mut c_void, state: State)>;
+pub type DeviceChangedCallback = Option<unsafe extern "C" fn(user_ptr: *mut c_void)>;
+pub type DeviceCollectionChangedCallback = Option<unsafe extern "C" fn(context: *mut Context, user_ptr: *mut c_void)>;
+pub type LogCallback = Option<unsafe extern "C" fn(fmt: *const i8, ...)>;
+
+#[test]
+fn bindgen_test_layout_stream_params() {
+    assert_eq!(::std::mem::size_of::<StreamParams>(),
+               16usize,
+               concat!("Size of: ", stringify!(StreamParams)));
+    assert_eq!(::std::mem::align_of::<StreamParams>(),
+               4usize,
+               concat!("Alignment of ", stringify!(StreamParams)));
+    assert_eq!(unsafe { &(*(0 as *const StreamParams)).format as *const _ as usize },
+               0usize,
+               concat!("Alignment of field: ",
+                       stringify!(StreamParams),
+                       "::",
+                       stringify!(format)));
+    assert_eq!(unsafe { &(*(0 as *const StreamParams)).rate as *const _ as usize },
+               4usize,
+               concat!("Alignment of field: ",
+                       stringify!(StreamParams),
+                       "::",
+                       stringify!(rate)));
+    assert_eq!(unsafe { &(*(0 as *const StreamParams)).channels as *const _ as usize },
+               8usize,
+               concat!("Alignment of field: ",
+                       stringify!(StreamParams),
+                       "::",
+                       stringify!(channels)));
+    assert_eq!(unsafe { &(*(0 as *const StreamParams)).layout as *const _ as usize },
+               12usize,
+               concat!("Alignment of field: ",
+                       stringify!(StreamParams),
+                       "::",
+                       stringify!(layout)));
+}
+
+#[test]
+fn bindgen_test_layout_cubeb_device() {
+    assert_eq!(::std::mem::size_of::<Device>(),
+               16usize,
+               concat!("Size of: ", stringify!(Device)));
+    assert_eq!(::std::mem::align_of::<Device>(),
+               8usize,
+               concat!("Alignment of ", stringify!(Device)));
+    assert_eq!(unsafe { &(*(0 as *const Device)).output_name as *const _ as usize },
+               0usize,
+               concat!("Alignment of field: ",
+                       stringify!(Device),
+                       "::",
+                       stringify!(output_name)));
+    assert_eq!(unsafe { &(*(0 as *const Device)).input_name as *const _ as usize },
+               8usize,
+               concat!("Alignment of field: ",
+                       stringify!(Device),
+                       "::",
+                       stringify!(input_name)));
+}
+
+#[test]
+fn bindgen_test_layout_cubeb_device_info() {
+    assert_eq!(::std::mem::size_of::<DeviceInfo>(),
+               88usize,
+               concat!("Size of: ", stringify!(DeviceInfo)));
+    assert_eq!(::std::mem::align_of::<DeviceInfo>(),
+               8usize,
+               concat!("Alignment of ", stringify!(DeviceInfo)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).devid as *const _ as usize },
+               0usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(devid)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).device_id as *const _ as usize },
+               8usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(device_id)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).friendly_name as *const _ as usize },
+               16usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(friendly_name)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).group_id as *const _ as usize },
+               24usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(group_id)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).vendor_name as *const _ as usize },
+               32usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(vendor_name)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).devtype as *const _ as usize },
+               40usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(type_)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).state as *const _ as usize },
+               44usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(state)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).preferred as *const _ as usize },
+               48usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(preferred)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).format as *const _ as usize },
+               52usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(format)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).default_format as *const _ as usize },
+               56usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(default_format)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).max_channels as *const _ as usize },
+               60usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(max_channels)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).default_rate as *const _ as usize },
+               64usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(default_rate)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).max_rate as *const _ as usize },
+               68usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(max_rate)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).min_rate as *const _ as usize },
+               72usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(min_rate)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).latency_lo as *const _ as usize },
+               76usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(latency_lo)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceInfo)).latency_hi as *const _ as usize },
+               80usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceInfo),
+                       "::",
+                       stringify!(latency_hi)));
+}
+
+#[test]
+fn bindgen_test_layout_cubeb_device_collection() {
+    assert_eq!(::std::mem::size_of::<DeviceCollection>(),
+               8usize,
+               concat!("Size of: ", stringify!(DeviceCollection)));
+    assert_eq!(::std::mem::align_of::<DeviceCollection>(),
+               8usize,
+               concat!("Alignment of ", stringify!(DeviceCollection)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceCollection)).count as *const _ as usize },
+               0usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceCollection),
+                       "::",
+                       stringify!(count)));
+    assert_eq!(unsafe { &(*(0 as *const DeviceCollection)).device as *const _ as usize },
+               8usize,
+               concat!("Alignment of field: ",
+                       stringify!(DeviceCollection),
+                       "::",
+                       stringify!(device)));
+
+}
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/pulse-ffi/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "pulse-ffi"
+version = "0.1.0"
+authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
+description = "FFI for libpulse.so supporting static linking and dynamic loading."
+
+[features]
+dlopen = []
+
+[dependencies]
+libc = "^0.2.20"
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/pulse-ffi/src/ffi_funcs.rs
@@ -0,0 +1,1277 @@
+// This code is generated.
+
+use super::*;
+
+macro_rules! cstr {
+  ($x:expr) => { concat!($x, "\0").as_bytes().as_ptr() as *const c_char }
+}
+
+#[cfg(not(feature = "dlopen"))]
+mod static_fns {
+    use std::os::raw::{c_char, c_double, c_int, c_float, c_uint, c_void};
+    use super::*;
+
+    #[link(name = "pulse")]
+    extern "C" {
+        pub fn pa_get_library_version() -> *const c_char;
+        pub fn pa_channel_map_can_balance(map: *const pa_channel_map) -> c_int;
+        pub fn pa_channel_map_init(m: *mut pa_channel_map) -> *mut pa_channel_map;
+        pub fn pa_context_connect(c: *mut pa_context,
+                                  server: *const c_char,
+                                  flags: pa_context_flags_t,
+                                  api: *const pa_spawn_api)
+                                  -> c_int;
+        pub fn pa_context_disconnect(c: *mut pa_context);
+        pub fn pa_context_drain(c: *mut pa_context,
+                                cb: pa_context_notify_cb_t,
+                                userdata: *mut c_void)
+                                -> *mut pa_operation;
+        pub fn pa_context_get_server_info(c: *const pa_context,
+                                          cb: pa_server_info_cb_t,
+                                          userdata: *mut c_void)
+                                          -> *mut pa_operation;
+        pub fn pa_context_get_sink_info_by_name(c: *const pa_context,
+                                                name: *const c_char,
+                                                cb: pa_sink_info_cb_t,
+                                                userdata: *mut c_void)
+                                                -> *mut pa_operation;
+        pub fn pa_context_get_sink_info_list(c: *const pa_context,
+                                             cb: pa_sink_info_cb_t,
+                                             userdata: *mut c_void)
+                                             -> *mut pa_operation;
+        pub fn pa_context_get_sink_input_info(c: *const pa_context,
+                                              idx: u32,
+                                              cb: pa_sink_input_info_cb_t,
+                                              userdata: *mut c_void)
+                                              -> *mut pa_operation;
+        pub fn pa_context_get_source_info_list(c: *const pa_context,
+                                               cb: pa_source_info_cb_t,
+                                               userdata: *mut c_void)
+                                               -> *mut pa_operation;
+        pub fn pa_context_get_state(c: *const pa_context) -> pa_context_state_t;
+        pub fn pa_context_new(mainloop: *mut pa_mainloop_api, name: *const c_char) -> *mut pa_context;
+        pub fn pa_context_rttime_new(c: *const pa_context,
+                                     usec: pa_usec_t,
+                                     cb: pa_time_event_cb_t,
+                                     userdata: *mut c_void)
+                                     -> *mut pa_time_event;
+        pub fn pa_context_set_sink_input_volume(c: *mut pa_context,
+                                                idx: u32,
+                                                volume: *const pa_cvolume,
+                                                cb: pa_context_success_cb_t,
+                                                userdata: *mut c_void)
+                                                -> *mut pa_operation;
+        pub fn pa_context_set_state_callback(c: *mut pa_context, cb: pa_context_notify_cb_t, userdata: *mut c_void);
+        pub fn pa_context_set_subscribe_callback(c: *mut pa_context,
+                                                 cb: pa_context_subscribe_cb_t,
+                                                 userdata: *mut c_void);
+        pub fn pa_context_subscribe(c: *mut pa_context,
+                                    m: pa_subscription_mask_t,
+                                    cb: pa_context_success_cb_t,
+                                    userdata: *mut c_void)
+                                    -> *mut pa_operation;
+        pub fn pa_context_unref(c: *mut pa_context);
+        pub fn pa_cvolume_set(a: *mut pa_cvolume, channels: c_uint, v: pa_volume_t) -> *mut pa_cvolume;
+        pub fn pa_cvolume_set_balance(v: *mut pa_cvolume,
+                                      map: *const pa_channel_map,
+                                      new_balance: c_float)
+                                      -> *mut pa_cvolume;
+        pub fn pa_frame_size(spec: *const pa_sample_spec) -> usize;
+        pub fn pa_mainloop_api_once(m: *mut pa_mainloop_api,
+                                    callback: pa_mainloop_api_once_cb_t,
+                                    userdata: *mut c_void);
+        pub fn pa_operation_get_state(o: *const pa_operation) -> pa_operation_state_t;
+        pub fn pa_operation_unref(o: *mut pa_operation);
+        pub fn pa_proplist_gets(p: *mut pa_proplist, key: *const c_char) -> *const c_char;
+        pub fn pa_rtclock_now() -> pa_usec_t;
+        pub fn pa_stream_begin_write(p: *mut pa_stream, data: *mut *mut c_void, nbytes: *mut usize) -> c_int;
+        pub fn pa_stream_cancel_write(p: *mut pa_stream) -> c_int;
+        pub fn pa_stream_connect_playback(s: *mut pa_stream,
+                                          dev: *const c_char,
+                                          attr: *const pa_buffer_attr,
+                                          flags: pa_stream_flags_t,
+                                          volume: *const pa_cvolume,
+                                          sync_stream: *const pa_stream)
+                                          -> c_int;
+        pub fn pa_stream_connect_record(s: *mut pa_stream,
+                                        dev: *const c_char,
+                                        attr: *const pa_buffer_attr,
+                                        flags: pa_stream_flags_t)
+                                        -> c_int;
+        pub fn pa_stream_cork(s: *mut pa_stream,
+                              b: c_int,
+                              cb: pa_stream_success_cb_t,
+                              userdata: *mut c_void)
+                              -> *mut pa_operation;
+        pub fn pa_stream_disconnect(s: *mut pa_stream) -> c_int;
+        pub fn pa_stream_drop(p: *mut pa_stream) -> c_int;
+        pub fn pa_stream_get_buffer_attr(s: *const pa_stream) -> *const pa_buffer_attr;
+        pub fn pa_stream_get_channel_map(s: *const pa_stream) -> *const pa_channel_map;
+        pub fn pa_stream_get_device_name(s: *const pa_stream) -> *const c_char;
+        pub fn pa_stream_get_index(s: *const pa_stream) -> u32;
+        pub fn pa_stream_get_latency(s: *const pa_stream, r_usec: *mut pa_usec_t, negative: *mut c_int) -> c_int;
+        pub fn pa_stream_get_sample_spec(s: *const pa_stream) -> *const pa_sample_spec;
+        pub fn pa_stream_get_state(p: *const pa_stream) -> pa_stream_state_t;
+        pub fn pa_stream_get_time(s: *const pa_stream, r_usec: *mut pa_usec_t) -> c_int;
+        pub fn pa_stream_new(c: *mut pa_context,
+                             name: *const c_char,
+                             ss: *const pa_sample_spec,
+                             map: *const pa_channel_map)
+                             -> *mut pa_stream;
+        pub fn pa_stream_peek(p: *mut pa_stream, data: *mut *const c_void, nbytes: *mut usize) -> c_int;
+        pub fn pa_stream_readable_size(p: *const pa_stream) -> usize;
+        pub fn pa_stream_set_state_callback(s: *mut pa_stream, cb: pa_stream_notify_cb_t, userdata: *mut c_void);
+        pub fn pa_stream_set_write_callback(p: *mut pa_stream, cb: pa_stream_request_cb_t, userdata: *mut c_void);
+        pub fn pa_stream_set_read_callback(p: *mut pa_stream, cb: pa_stream_request_cb_t, userdata: *mut c_void);
+        pub fn pa_stream_unref(s: *mut pa_stream);
+        pub fn pa_stream_update_timing_info(p: *mut pa_stream,
+                                            cb: pa_stream_success_cb_t,
+                                            userdata: *mut c_void)
+                                            -> *mut pa_operation;
+        pub fn pa_stream_writable_size(p: *const pa_stream) -> usize;
+        pub fn pa_stream_write(p: *mut pa_stream,
+                               data: *const c_void,
+                               nbytes: usize,
+                               free_cb: pa_free_cb_t,
+                               offset: i64,
+                               seek: pa_seek_mode_t)
+                               -> c_int;
+        pub fn pa_sw_volume_from_linear(v: c_double) -> pa_volume_t;
+        pub fn pa_threaded_mainloop_free(m: *mut pa_threaded_mainloop);
+        pub fn pa_threaded_mainloop_get_api(m: *mut pa_threaded_mainloop) -> *mut pa_mainloop_api;
+        pub fn pa_threaded_mainloop_in_thread(m: *mut pa_threaded_mainloop) -> c_int;
+        pub fn pa_threaded_mainloop_lock(m: *mut pa_threaded_mainloop);
+        pub fn pa_threaded_mainloop_new() -> *mut pa_threaded_mainloop;
+        pub fn pa_threaded_mainloop_signal(m: *mut pa_threaded_mainloop, wait_for_accept: c_int);
+        pub fn pa_threaded_mainloop_start(m: *mut pa_threaded_mainloop) -> c_int;
+        pub fn pa_threaded_mainloop_stop(m: *mut pa_threaded_mainloop);
+        pub fn pa_threaded_mainloop_unlock(m: *mut pa_threaded_mainloop);
+        pub fn pa_threaded_mainloop_wait(m: *mut pa_threaded_mainloop);
+        pub fn pa_usec_to_bytes(t: pa_usec_t, spec: *const pa_sample_spec) -> usize;
+        pub fn pa_xfree(ptr: *mut c_void);
+        pub fn pa_xstrdup(str: *const c_char) -> *mut c_char;
+        pub fn pa_xrealloc(ptr: *mut c_void, size: usize) -> *mut c_void;
+    }
+}
+
+#[cfg(not(feature = "dlopen"))]
+pub use self::static_fns::*;
+
+#[cfg(feature = "dlopen")]
+mod dynamic_fns {
+    use std::os::raw::{c_char, c_double, c_int, c_float, c_uint, c_void};
+    use libc::{dlclose, dlopen, dlsym, RTLD_LAZY};
+    use super::*;
+
+    #[derive(Debug)]
+    pub struct LibLoader {
+        _lib: *mut ::libc::c_void,
+    }
+
+    impl LibLoader {
+        pub unsafe fn open() -> Option<LibLoader> {
+            let h = dlopen(cstr!("libpulse.so.0"), RTLD_LAZY);
+            if h.is_null() {
+                return None;
+            }
+
+            PA_GET_LIBRARY_VERSION = {
+                let fp = dlsym(h, cstr!("pa_get_library_version"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CHANNEL_MAP_CAN_BALANCE = {
+                let fp = dlsym(h, cstr!("pa_channel_map_can_balance"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CHANNEL_MAP_INIT = {
+                let fp = dlsym(h, cstr!("pa_channel_map_init"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_CONNECT = {
+                let fp = dlsym(h, cstr!("pa_context_connect"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_DISCONNECT = {
+                let fp = dlsym(h, cstr!("pa_context_disconnect"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_DRAIN = {
+                let fp = dlsym(h, cstr!("pa_context_drain"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_GET_SERVER_INFO = {
+                let fp = dlsym(h, cstr!("pa_context_get_server_info"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_GET_SINK_INFO_BY_NAME = {
+                let fp = dlsym(h, cstr!("pa_context_get_sink_info_by_name"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_GET_SINK_INFO_LIST = {
+                let fp = dlsym(h, cstr!("pa_context_get_sink_info_list"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_GET_SINK_INPUT_INFO = {
+                let fp = dlsym(h, cstr!("pa_context_get_sink_input_info"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_GET_SOURCE_INFO_LIST = {
+                let fp = dlsym(h, cstr!("pa_context_get_source_info_list"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_GET_STATE = {
+                let fp = dlsym(h, cstr!("pa_context_get_state"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_NEW = {
+                let fp = dlsym(h, cstr!("pa_context_new"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_RTTIME_NEW = {
+                let fp = dlsym(h, cstr!("pa_context_rttime_new"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_SET_SINK_INPUT_VOLUME = {
+                let fp = dlsym(h, cstr!("pa_context_set_sink_input_volume"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_SET_STATE_CALLBACK = {
+                let fp = dlsym(h, cstr!("pa_context_set_state_callback"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_SET_SUBSCRIBE_CALLBACK = {
+                let fp = dlsym(h, cstr!("pa_context_set_subscribe_callback"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_SUBSCRIBE = {
+                let fp = dlsym(h, cstr!("pa_context_subscribe"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CONTEXT_UNREF = {
+                let fp = dlsym(h, cstr!("pa_context_unref"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CVOLUME_SET = {
+                let fp = dlsym(h, cstr!("pa_cvolume_set"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_CVOLUME_SET_BALANCE = {
+                let fp = dlsym(h, cstr!("pa_cvolume_set_balance"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_FRAME_SIZE = {
+                let fp = dlsym(h, cstr!("pa_frame_size"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_MAINLOOP_API_ONCE = {
+                let fp = dlsym(h, cstr!("pa_mainloop_api_once"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_OPERATION_GET_STATE = {
+                let fp = dlsym(h, cstr!("pa_operation_get_state"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_OPERATION_UNREF = {
+                let fp = dlsym(h, cstr!("pa_operation_unref"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_PROPLIST_GETS = {
+                let fp = dlsym(h, cstr!("pa_proplist_gets"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_RTCLOCK_NOW = {
+                let fp = dlsym(h, cstr!("pa_rtclock_now"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_BEGIN_WRITE = {
+                let fp = dlsym(h, cstr!("pa_stream_begin_write"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_CANCEL_WRITE = {
+                let fp = dlsym(h, cstr!("pa_stream_cancel_write"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_CONNECT_PLAYBACK = {
+                let fp = dlsym(h, cstr!("pa_stream_connect_playback"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_CONNECT_RECORD = {
+                let fp = dlsym(h, cstr!("pa_stream_connect_record"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_CORK = {
+                let fp = dlsym(h, cstr!("pa_stream_cork"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_DISCONNECT = {
+                let fp = dlsym(h, cstr!("pa_stream_disconnect"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_DROP = {
+                let fp = dlsym(h, cstr!("pa_stream_drop"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_GET_BUFFER_ATTR = {
+                let fp = dlsym(h, cstr!("pa_stream_get_buffer_attr"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_GET_CHANNEL_MAP = {
+                let fp = dlsym(h, cstr!("pa_stream_get_channel_map"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_GET_DEVICE_NAME = {
+                let fp = dlsym(h, cstr!("pa_stream_get_device_name"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_GET_INDEX = {
+                let fp = dlsym(h, cstr!("pa_stream_get_index"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_GET_LATENCY = {
+                let fp = dlsym(h, cstr!("pa_stream_get_latency"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_GET_SAMPLE_SPEC = {
+                let fp = dlsym(h, cstr!("pa_stream_get_sample_spec"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_GET_STATE = {
+                let fp = dlsym(h, cstr!("pa_stream_get_state"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_GET_TIME = {
+                let fp = dlsym(h, cstr!("pa_stream_get_time"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_NEW = {
+                let fp = dlsym(h, cstr!("pa_stream_new"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_PEEK = {
+                let fp = dlsym(h, cstr!("pa_stream_peek"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_READABLE_SIZE = {
+                let fp = dlsym(h, cstr!("pa_stream_readable_size"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_SET_STATE_CALLBACK = {
+                let fp = dlsym(h, cstr!("pa_stream_set_state_callback"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_SET_WRITE_CALLBACK = {
+                let fp = dlsym(h, cstr!("pa_stream_set_write_callback"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_SET_READ_CALLBACK = {
+                let fp = dlsym(h, cstr!("pa_stream_set_read_callback"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_UNREF = {
+                let fp = dlsym(h, cstr!("pa_stream_unref"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_UPDATE_TIMING_INFO = {
+                let fp = dlsym(h, cstr!("pa_stream_update_timing_info"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_WRITABLE_SIZE = {
+                let fp = dlsym(h, cstr!("pa_stream_writable_size"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_STREAM_WRITE = {
+                let fp = dlsym(h, cstr!("pa_stream_write"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_SW_VOLUME_FROM_LINEAR = {
+                let fp = dlsym(h, cstr!("pa_sw_volume_from_linear"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_THREADED_MAINLOOP_FREE = {
+                let fp = dlsym(h, cstr!("pa_threaded_mainloop_free"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_THREADED_MAINLOOP_GET_API = {
+                let fp = dlsym(h, cstr!("pa_threaded_mainloop_get_api"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_THREADED_MAINLOOP_IN_THREAD = {
+                let fp = dlsym(h, cstr!("pa_threaded_mainloop_in_thread"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_THREADED_MAINLOOP_LOCK = {
+                let fp = dlsym(h, cstr!("pa_threaded_mainloop_lock"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_THREADED_MAINLOOP_NEW = {
+                let fp = dlsym(h, cstr!("pa_threaded_mainloop_new"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_THREADED_MAINLOOP_SIGNAL = {
+                let fp = dlsym(h, cstr!("pa_threaded_mainloop_signal"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_THREADED_MAINLOOP_START = {
+                let fp = dlsym(h, cstr!("pa_threaded_mainloop_start"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_THREADED_MAINLOOP_STOP = {
+                let fp = dlsym(h, cstr!("pa_threaded_mainloop_stop"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_THREADED_MAINLOOP_UNLOCK = {
+                let fp = dlsym(h, cstr!("pa_threaded_mainloop_unlock"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_THREADED_MAINLOOP_WAIT = {
+                let fp = dlsym(h, cstr!("pa_threaded_mainloop_wait"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_USEC_TO_BYTES = {
+                let fp = dlsym(h, cstr!("pa_usec_to_bytes"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_XFREE = {
+                let fp = dlsym(h, cstr!("pa_xfree"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_XSTRDUP = {
+                let fp = dlsym(h, cstr!("pa_xstrdup"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+            PA_XREALLOC = {
+                let fp = dlsym(h, cstr!("pa_xrealloc"));
+                if fp.is_null() {
+                    return None;
+                }
+                fp
+            };
+
+            Some(LibLoader {
+                     _lib: h,
+                 })
+        }
+    }
+
+    impl ::std::ops::Drop for LibLoader {
+        #[inline]
+        fn drop(&mut self) {
+            unsafe {
+                dlclose(self._lib);
+            }
+        }
+    }
+
+    static mut PA_GET_LIBRARY_VERSION: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_get_library_version() -> *const c_char {
+        (::std::mem::transmute::<_, extern "C" fn() -> *const c_char>(PA_GET_LIBRARY_VERSION))()
+    }
+
+    static mut PA_CHANNEL_MAP_CAN_BALANCE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_channel_map_can_balance(map: *const pa_channel_map) -> c_int {
+        (::std::mem::transmute::<_, extern "C" fn(*const pa_channel_map) -> c_int>(PA_CHANNEL_MAP_CAN_BALANCE))(map)
+    }
+
+    static mut PA_CHANNEL_MAP_INIT: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_channel_map_init(m: *mut pa_channel_map) -> *mut pa_channel_map {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_channel_map) -> *mut pa_channel_map>(PA_CHANNEL_MAP_INIT))(m)
+    }
+
+    static mut PA_CONTEXT_CONNECT: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_connect(c: *mut pa_context,
+                                     server: *const c_char,
+                                     flags: pa_context_flags_t,
+                                     api: *const pa_spawn_api)
+                                     -> c_int {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_context,
+                                               *const c_char,
+                                               pa_context_flags_t,
+                                               *const pa_spawn_api)
+                                               -> c_int>(PA_CONTEXT_CONNECT))(c, server, flags, api)
+    }
+
+    static mut PA_CONTEXT_DISCONNECT: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_disconnect(c: *mut pa_context) {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_context)>(PA_CONTEXT_DISCONNECT))(c)
+    }
+
+    static mut PA_CONTEXT_DRAIN: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_drain(c: *mut pa_context,
+                                   cb: pa_context_notify_cb_t,
+                                   userdata: *mut c_void)
+                                   -> *mut pa_operation {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_context, pa_context_notify_cb_t, *mut c_void)
+                                               -> *mut pa_operation>(PA_CONTEXT_DRAIN))(c, cb, userdata)
+    }
+
+    static mut PA_CONTEXT_GET_SERVER_INFO: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_get_server_info(c: *const pa_context,
+                                             cb: pa_server_info_cb_t,
+                                             userdata: *mut c_void)
+                                             -> *mut pa_operation {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_context, pa_server_info_cb_t, *mut c_void)
+                                               -> *mut pa_operation>(PA_CONTEXT_GET_SERVER_INFO))(c, cb, userdata)
+    }
+
+    static mut PA_CONTEXT_GET_SINK_INFO_BY_NAME: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_get_sink_info_by_name(c: *const pa_context,
+                                                   name: *const c_char,
+                                                   cb: pa_sink_info_cb_t,
+                                                   userdata: *mut c_void)
+                                                   -> *mut pa_operation {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_context,
+                                               *const c_char,
+                                               pa_sink_info_cb_t,
+                                               *mut c_void)
+                                               -> *mut pa_operation>(PA_CONTEXT_GET_SINK_INFO_BY_NAME))(c,
+                                                                                                        name,
+                                                                                                        cb,
+                                                                                                        userdata)
+    }
+
+    static mut PA_CONTEXT_GET_SINK_INFO_LIST: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_get_sink_info_list(c: *const pa_context,
+                                                cb: pa_sink_info_cb_t,
+                                                userdata: *mut c_void)
+                                                -> *mut pa_operation {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_context, pa_sink_info_cb_t, *mut c_void)
+                                               -> *mut pa_operation>(PA_CONTEXT_GET_SINK_INFO_LIST))(c, cb, userdata)
+    }
+
+    static mut PA_CONTEXT_GET_SINK_INPUT_INFO: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_get_sink_input_info(c: *const pa_context,
+                                                 idx: u32,
+                                                 cb: pa_sink_input_info_cb_t,
+                                                 userdata: *mut c_void)
+                                                 -> *mut pa_operation {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_context, u32, pa_sink_input_info_cb_t, *mut c_void)
+                                               -> *mut pa_operation>(PA_CONTEXT_GET_SINK_INPUT_INFO))(c,
+                                                                                                      idx,
+                                                                                                      cb,
+                                                                                                      userdata)
+    }
+
+    static mut PA_CONTEXT_GET_SOURCE_INFO_LIST: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_get_source_info_list(c: *const pa_context,
+                                                  cb: pa_source_info_cb_t,
+                                                  userdata: *mut c_void)
+                                                  -> *mut pa_operation {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_context, pa_source_info_cb_t, *mut c_void)
+                                               -> *mut pa_operation>(PA_CONTEXT_GET_SOURCE_INFO_LIST))(c, cb, userdata)
+    }
+
+    static mut PA_CONTEXT_GET_STATE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_get_state(c: *const pa_context) -> pa_context_state_t {
+        (::std::mem::transmute::<_, extern "C" fn(*const pa_context) -> pa_context_state_t>(PA_CONTEXT_GET_STATE))(c)
+    }
+
+    static mut PA_CONTEXT_NEW: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_new(mainloop: *mut pa_mainloop_api, name: *const c_char) -> *mut pa_context {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_mainloop_api, *const c_char)
+                                               -> *mut pa_context>(PA_CONTEXT_NEW))(mainloop, name)
+    }
+
+    static mut PA_CONTEXT_RTTIME_NEW: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_rttime_new(c: *const pa_context,
+                                        usec: pa_usec_t,
+                                        cb: pa_time_event_cb_t,
+                                        userdata: *mut c_void)
+                                        -> *mut pa_time_event {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_context,
+                                               pa_usec_t,
+                                               pa_time_event_cb_t,
+                                               *mut c_void)
+                                               -> *mut pa_time_event>(PA_CONTEXT_RTTIME_NEW))(c, usec, cb, userdata)
+    }
+
+    static mut PA_CONTEXT_SET_SINK_INPUT_VOLUME: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_set_sink_input_volume(c: *mut pa_context,
+                                                   idx: u32,
+                                                   volume: *const pa_cvolume,
+                                                   cb: pa_context_success_cb_t,
+                                                   userdata: *mut c_void)
+                                                   -> *mut pa_operation {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_context,
+                                               u32,
+                                               *const pa_cvolume,
+                                               pa_context_success_cb_t,
+                                               *mut c_void)
+                                               -> *mut pa_operation>(PA_CONTEXT_SET_SINK_INPUT_VOLUME))(c,
+                                                                                                        idx,
+                                                                                                        volume,
+                                                                                                        cb,
+                                                                                                        userdata)
+    }
+
+    static mut PA_CONTEXT_SET_STATE_CALLBACK: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_set_state_callback(c: *mut pa_context,
+                                                cb: pa_context_notify_cb_t,
+                                                userdata: *mut c_void) {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_context,
+                                               pa_context_notify_cb_t,
+                                               *mut c_void)>(PA_CONTEXT_SET_STATE_CALLBACK))(c, cb, userdata)
+    }
+
+    static mut PA_CONTEXT_SET_SUBSCRIBE_CALLBACK: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_set_subscribe_callback(c: *mut pa_context,
+                                                    cb: pa_context_subscribe_cb_t,
+                                                    userdata: *mut c_void) {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_context,
+                                               pa_context_subscribe_cb_t,
+                                               *mut c_void)>(PA_CONTEXT_SET_SUBSCRIBE_CALLBACK))(c, cb, userdata)
+    }
+
+    static mut PA_CONTEXT_SUBSCRIBE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_subscribe(c: *mut pa_context,
+                                       m: pa_subscription_mask_t,
+                                       cb: pa_context_success_cb_t,
+                                       userdata: *mut c_void)
+                                       -> *mut pa_operation {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_context,
+                                               pa_subscription_mask_t,
+                                               pa_context_success_cb_t,
+                                               *mut c_void)
+                                               -> *mut pa_operation>(PA_CONTEXT_SUBSCRIBE))(c, m, cb, userdata)
+    }
+
+    static mut PA_CONTEXT_UNREF: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_context_unref(c: *mut pa_context) {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_context)>(PA_CONTEXT_UNREF))(c)
+    }
+
+    static mut PA_CVOLUME_SET: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_cvolume_set(a: *mut pa_cvolume, channels: c_uint, v: pa_volume_t) -> *mut pa_cvolume {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_cvolume, c_uint, pa_volume_t)
+                                               -> *mut pa_cvolume>(PA_CVOLUME_SET))(a, channels, v)
+    }
+
+    static mut PA_CVOLUME_SET_BALANCE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_cvolume_set_balance(v: *mut pa_cvolume,
+                                         map: *const pa_channel_map,
+                                         new_balance: c_float)
+                                         -> *mut pa_cvolume {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_cvolume,
+                                               *const pa_channel_map,
+                                               c_float)
+                                               -> *mut pa_cvolume>(PA_CVOLUME_SET_BALANCE))(v, map, new_balance)
+    }
+
+    static mut PA_FRAME_SIZE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_frame_size(spec: *const pa_sample_spec) -> usize {
+        (::std::mem::transmute::<_, extern "C" fn(*const pa_sample_spec) -> usize>(PA_FRAME_SIZE))(spec)
+    }
+
+    static mut PA_MAINLOOP_API_ONCE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_mainloop_api_once(m: *mut pa_mainloop_api,
+                                       callback: pa_mainloop_api_once_cb_t,
+                                       userdata: *mut c_void) {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_mainloop_api,
+                                               pa_mainloop_api_once_cb_t,
+                                               *mut c_void)>(PA_MAINLOOP_API_ONCE))(m, callback, userdata)
+    }
+
+    static mut PA_OPERATION_GET_STATE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_operation_get_state(o: *const pa_operation) -> pa_operation_state_t {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_operation)
+                                               -> pa_operation_state_t>(PA_OPERATION_GET_STATE))(o)
+    }
+
+    static mut PA_OPERATION_UNREF: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_operation_unref(o: *mut pa_operation) {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_operation)>(PA_OPERATION_UNREF))(o)
+    }
+
+    static mut PA_PROPLIST_GETS: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_proplist_gets(p: *mut pa_proplist, key: *const c_char) -> *const c_char {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_proplist, *const c_char)
+                                               -> *const c_char>(PA_PROPLIST_GETS))(p, key)
+    }
+
+    static mut PA_RTCLOCK_NOW: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_rtclock_now() -> pa_usec_t {
+        (::std::mem::transmute::<_, extern "C" fn() -> pa_usec_t>(PA_RTCLOCK_NOW))()
+    }
+
+    static mut PA_STREAM_BEGIN_WRITE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_begin_write(p: *mut pa_stream, data: *mut *mut c_void, nbytes: *mut usize) -> c_int {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_stream,
+                                               *mut *mut c_void,
+                                               *mut usize)
+                                               -> c_int>(PA_STREAM_BEGIN_WRITE))(p, data, nbytes)
+    }
+
+    static mut PA_STREAM_CANCEL_WRITE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_cancel_write(p: *mut pa_stream) -> c_int {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_stream) -> c_int>(PA_STREAM_CANCEL_WRITE))(p)
+    }
+
+    static mut PA_STREAM_CONNECT_PLAYBACK: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_connect_playback(s: *mut pa_stream,
+                                             dev: *const c_char,
+                                             attr: *const pa_buffer_attr,
+                                             flags: pa_stream_flags_t,
+                                             volume: *const pa_cvolume,
+                                             sync_stream: *const pa_stream)
+                                             -> c_int {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_stream,
+                                               *const c_char,
+                                               *const pa_buffer_attr,
+                                               pa_stream_flags_t,
+                                               *const pa_cvolume,
+                                               *const pa_stream)
+                                               -> c_int>(PA_STREAM_CONNECT_PLAYBACK))(s,
+                                                                                      dev,
+                                                                                      attr,
+                                                                                      flags,
+                                                                                      volume,
+                                                                                      sync_stream)
+    }
+
+    static mut PA_STREAM_CONNECT_RECORD: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_connect_record(s: *mut pa_stream,
+                                           dev: *const c_char,
+                                           attr: *const pa_buffer_attr,
+                                           flags: pa_stream_flags_t)
+                                           -> c_int {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_stream,
+                                               *const c_char,
+                                               *const pa_buffer_attr,
+                                               pa_stream_flags_t)
+                                               -> c_int>(PA_STREAM_CONNECT_RECORD))(s, dev, attr, flags)
+    }
+
+    static mut PA_STREAM_CORK: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_cork(s: *mut pa_stream,
+                                 b: c_int,
+                                 cb: pa_stream_success_cb_t,
+                                 userdata: *mut c_void)
+                                 -> *mut pa_operation {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_stream, c_int, pa_stream_success_cb_t, *mut c_void)
+                                               -> *mut pa_operation>(PA_STREAM_CORK))(s, b, cb, userdata)
+    }
+
+    static mut PA_STREAM_DISCONNECT: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_disconnect(s: *mut pa_stream) -> c_int {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_stream) -> c_int>(PA_STREAM_DISCONNECT))(s)
+    }
+
+    static mut PA_STREAM_DROP: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_drop(p: *mut pa_stream) -> c_int {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_stream) -> c_int>(PA_STREAM_DROP))(p)
+    }
+
+    static mut PA_STREAM_GET_BUFFER_ATTR: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_get_buffer_attr(s: *const pa_stream) -> *const pa_buffer_attr {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_stream)
+                                               -> *const pa_buffer_attr>(PA_STREAM_GET_BUFFER_ATTR))(s)
+    }
+
+    static mut PA_STREAM_GET_CHANNEL_MAP: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_get_channel_map(s: *const pa_stream) -> *const pa_channel_map {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_stream)
+                                               -> *const pa_channel_map>(PA_STREAM_GET_CHANNEL_MAP))(s)
+    }
+
+    static mut PA_STREAM_GET_DEVICE_NAME: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_get_device_name(s: *const pa_stream) -> *const c_char {
+        (::std::mem::transmute::<_, extern "C" fn(*const pa_stream) -> *const c_char>(PA_STREAM_GET_DEVICE_NAME))(s)
+    }
+
+    static mut PA_STREAM_GET_INDEX: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_get_index(s: *const pa_stream) -> u32 {
+        (::std::mem::transmute::<_, extern "C" fn(*const pa_stream) -> u32>(PA_STREAM_GET_INDEX))(s)
+    }
+
+    static mut PA_STREAM_GET_LATENCY: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_get_latency(s: *const pa_stream, r_usec: *mut pa_usec_t, negative: *mut c_int) -> c_int {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_stream,
+                                               *mut pa_usec_t,
+                                               *mut c_int)
+                                               -> c_int>(PA_STREAM_GET_LATENCY))(s, r_usec, negative)
+    }
+
+    static mut PA_STREAM_GET_SAMPLE_SPEC: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_get_sample_spec(s: *const pa_stream) -> *const pa_sample_spec {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_stream)
+                                               -> *const pa_sample_spec>(PA_STREAM_GET_SAMPLE_SPEC))(s)
+    }
+
+    static mut PA_STREAM_GET_STATE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_get_state(p: *const pa_stream) -> pa_stream_state_t {
+        (::std::mem::transmute::<_, extern "C" fn(*const pa_stream) -> pa_stream_state_t>(PA_STREAM_GET_STATE))(p)
+    }
+
+    static mut PA_STREAM_GET_TIME: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_get_time(s: *const pa_stream, r_usec: *mut pa_usec_t) -> c_int {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*const pa_stream, *mut pa_usec_t)
+                                               -> c_int>(PA_STREAM_GET_TIME))(s, r_usec)
+    }
+
+    static mut PA_STREAM_NEW: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_new(c: *mut pa_context,
+                                name: *const c_char,
+                                ss: *const pa_sample_spec,
+                                map: *const pa_channel_map)
+                                -> *mut pa_stream {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_context,
+                                               *const c_char,
+                                               *const pa_sample_spec,
+                                               *const pa_channel_map)
+                                               -> *mut pa_stream>(PA_STREAM_NEW))(c, name, ss, map)
+    }
+
+    static mut PA_STREAM_PEEK: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_peek(p: *mut pa_stream, data: *mut *const c_void, nbytes: *mut usize) -> c_int {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_stream,
+                                               *mut *const c_void,
+                                               *mut usize)
+                                               -> c_int>(PA_STREAM_PEEK))(p, data, nbytes)
+    }
+
+    static mut PA_STREAM_READABLE_SIZE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_readable_size(p: *const pa_stream) -> usize {
+        (::std::mem::transmute::<_, extern "C" fn(*const pa_stream) -> usize>(PA_STREAM_READABLE_SIZE))(p)
+    }
+
+    static mut PA_STREAM_SET_STATE_CALLBACK: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_set_state_callback(s: *mut pa_stream, cb: pa_stream_notify_cb_t, userdata: *mut c_void) {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_stream,
+                                               pa_stream_notify_cb_t,
+                                               *mut c_void)>(PA_STREAM_SET_STATE_CALLBACK))(s, cb, userdata)
+    }
+
+    static mut PA_STREAM_SET_WRITE_CALLBACK: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_set_write_callback(p: *mut pa_stream, cb: pa_stream_request_cb_t, userdata: *mut c_void) {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_stream,
+                                               pa_stream_request_cb_t,
+                                               *mut c_void)>(PA_STREAM_SET_WRITE_CALLBACK))(p, cb, userdata)
+    }
+
+    static mut PA_STREAM_SET_READ_CALLBACK: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_set_read_callback(p: *mut pa_stream, cb: pa_stream_request_cb_t, userdata: *mut c_void) {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_stream,
+                                               pa_stream_request_cb_t,
+                                               *mut c_void)>(PA_STREAM_SET_READ_CALLBACK))(p, cb, userdata)
+    }
+
+    static mut PA_STREAM_UNREF: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_unref(s: *mut pa_stream) {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_stream)>(PA_STREAM_UNREF))(s)
+    }
+
+    static mut PA_STREAM_UPDATE_TIMING_INFO: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_update_timing_info(p: *mut pa_stream,
+                                               cb: pa_stream_success_cb_t,
+                                               userdata: *mut c_void)
+                                               -> *mut pa_operation {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_stream, pa_stream_success_cb_t, *mut c_void)
+                                               -> *mut pa_operation>(PA_STREAM_UPDATE_TIMING_INFO))(p, cb, userdata)
+    }
+
+    static mut PA_STREAM_WRITABLE_SIZE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_writable_size(p: *const pa_stream) -> usize {
+        (::std::mem::transmute::<_, extern "C" fn(*const pa_stream) -> usize>(PA_STREAM_WRITABLE_SIZE))(p)
+    }
+
+    static mut PA_STREAM_WRITE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_stream_write(p: *mut pa_stream,
+                                  data: *const c_void,
+                                  nbytes: usize,
+                                  free_cb: pa_free_cb_t,
+                                  offset: i64,
+                                  seek: pa_seek_mode_t)
+                                  -> c_int {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_stream,
+                                               *const c_void,
+                                               usize,
+                                               pa_free_cb_t,
+                                               i64,
+                                               pa_seek_mode_t)
+                                               -> c_int>(PA_STREAM_WRITE))(p, data, nbytes, free_cb, offset, seek)
+    }
+
+    static mut PA_SW_VOLUME_FROM_LINEAR: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_sw_volume_from_linear(v: c_double) -> pa_volume_t {
+        (::std::mem::transmute::<_, extern "C" fn(c_double) -> pa_volume_t>(PA_SW_VOLUME_FROM_LINEAR))(v)
+    }
+
+    static mut PA_THREADED_MAINLOOP_FREE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_threaded_mainloop_free(m: *mut pa_threaded_mainloop) {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_threaded_mainloop)>(PA_THREADED_MAINLOOP_FREE))(m)
+    }
+
+    static mut PA_THREADED_MAINLOOP_GET_API: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_threaded_mainloop_get_api(m: *mut pa_threaded_mainloop) -> *mut pa_mainloop_api {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_threaded_mainloop)
+                                               -> *mut pa_mainloop_api>(PA_THREADED_MAINLOOP_GET_API))(m)
+    }
+
+    static mut PA_THREADED_MAINLOOP_IN_THREAD: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_threaded_mainloop_in_thread(m: *mut pa_threaded_mainloop) -> c_int {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_threaded_mainloop)
+                                               -> c_int>(PA_THREADED_MAINLOOP_IN_THREAD))(m)
+    }
+
+    static mut PA_THREADED_MAINLOOP_LOCK: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_threaded_mainloop_lock(m: *mut pa_threaded_mainloop) {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_threaded_mainloop)>(PA_THREADED_MAINLOOP_LOCK))(m)
+    }
+
+    static mut PA_THREADED_MAINLOOP_NEW: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_threaded_mainloop_new() -> *mut pa_threaded_mainloop {
+        (::std::mem::transmute::<_, extern "C" fn() -> *mut pa_threaded_mainloop>(PA_THREADED_MAINLOOP_NEW))()
+    }
+
+    static mut PA_THREADED_MAINLOOP_SIGNAL: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_threaded_mainloop_signal(m: *mut pa_threaded_mainloop, wait_for_accept: c_int) {
+        (::std::mem::transmute::<_,
+                                 extern "C" fn(*mut pa_threaded_mainloop,
+                                               c_int)>(PA_THREADED_MAINLOOP_SIGNAL))(m, wait_for_accept)
+    }
+
+    static mut PA_THREADED_MAINLOOP_START: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_threaded_mainloop_start(m: *mut pa_threaded_mainloop) -> c_int {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_threaded_mainloop) -> c_int>(PA_THREADED_MAINLOOP_START))(m)
+    }
+
+    static mut PA_THREADED_MAINLOOP_STOP: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_threaded_mainloop_stop(m: *mut pa_threaded_mainloop) {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_threaded_mainloop)>(PA_THREADED_MAINLOOP_STOP))(m)
+    }
+
+    static mut PA_THREADED_MAINLOOP_UNLOCK: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_threaded_mainloop_unlock(m: *mut pa_threaded_mainloop) {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_threaded_mainloop)>(PA_THREADED_MAINLOOP_UNLOCK))(m)
+    }
+
+    static mut PA_THREADED_MAINLOOP_WAIT: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_threaded_mainloop_wait(m: *mut pa_threaded_mainloop) {
+        (::std::mem::transmute::<_, extern "C" fn(*mut pa_threaded_mainloop)>(PA_THREADED_MAINLOOP_WAIT))(m)
+    }
+
+    static mut PA_USEC_TO_BYTES: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_usec_to_bytes(t: pa_usec_t, spec: *const pa_sample_spec) -> usize {
+        (::std::mem::transmute::<_, extern "C" fn(pa_usec_t, *const pa_sample_spec) -> usize>(PA_USEC_TO_BYTES))(t,
+                                                                                                                 spec)
+    }
+
+    static mut PA_XFREE: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_xfree(ptr: *mut c_void) {
+        (::std::mem::transmute::<_, extern "C" fn(*mut c_void)>(PA_XFREE))(ptr)
+    }
+
+    static mut PA_XSTRDUP: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_xstrdup(str: *const c_char) -> *mut c_char {
+        (::std::mem::transmute::<_, extern "C" fn(*const c_char) -> *mut c_char>(PA_XSTRDUP))(str)
+    }
+
+    static mut PA_XREALLOC: *mut ::libc::c_void = 0 as *mut _;
+    #[inline]
+    pub unsafe fn pa_xrealloc(ptr: *mut c_void, size: usize) -> *mut c_void {
+        (::std::mem::transmute::<_, extern "C" fn(*mut c_void, usize) -> *mut c_void>(PA_XREALLOC))(ptr, size)
+    }
+
+}
+
+#[cfg(feature = "dlopen")]
+pub use self::dynamic_fns::*;
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/cubeb-pulse-rs/pulse-ffi/src/ffi_types.rs
@@ -0,0 +1,936 @@
+#![allow(non_camel_case_types)]
+
+use std::os::raw::{c_char, c_int, c_long, c_ulong, c_void};
+
+/* automatically generated by rust-bindgen */
+pub const PA_RATE_MAX: u32 = 48000 * 8;
+
+pub const PA_SAMPLE_U8: c_int = 0;
+pub const PA_SAMPLE_ALAW: c_int = 1;
+pub const PA_SAMPLE_ULAW: c_int = 2;
+pub const PA_SAMPLE_S16LE: c_int = 3;
+pub const PA_SAMPLE_S16BE: c_int = 4;
+pub const PA_SAMPLE_FLOAT32LE: c_int = 5;
+pub const PA_SAMPLE_FLOAT32BE: c_int = 6;
+pub const PA_SAMPLE_S32LE: c_int = 7;
+pub const PA_SAMPLE_S32BE: c_int = 8;
+pub const PA_SAMPLE_S24LE: c_int = 9;
+pub const PA_SAMPLE_S24BE: c_int = 10;
+pub const PA_SAMPLE_S24_32LE: c_int = 11;
+pub const PA_SAMPLE_S24_32BE: c_int = 12;
+pub const PA_SAMPLE_MAX: c_int = 13;
+pub const PA_SAMPLE_INVALID: c_int = -1;
+pub type pa_sample_format_t = c_int;
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub struct Struct_pa_sample_spec {
+    pub format: pa_sample_format_t,
+    pub rate: u32,
+    pub channels: u8,
+}
+
+impl ::std::default::Default for Struct_pa_sample_spec {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+
+pub type pa_sample_spec = Struct_pa_sample_spec;
+pub type pa_usec_t = u64;
+
+// From pulse/timeval.h
+pub const PA_USEC_PER_MSEC: pa_usec_t = 1_000;
+pub const PA_USEC_PER_SEC: pa_usec_t = 1_000_000;
+
+pub const PA_CONTEXT_UNCONNECTED: c_int = 0;
+pub const PA_CONTEXT_CONNECTING: c_int = 1;
+pub const PA_CONTEXT_AUTHORIZING: c_int = 2;
+pub const PA_CONTEXT_SETTING_NAME: c_int = 3;
+pub const PA_CONTEXT_READY: c_int = 4;
+pub const PA_CONTEXT_FAILED: c_int = 5;
+pub const PA_CONTEXT_TERMINATED: c_int = 6;
+pub type pa_context_state_t = c_int;
+
+#[allow(non_snake_case)]
+pub fn PA_CONTEXT_IS_GOOD(x: pa_context_state_t) -> bool {
+    x == PA_CONTEXT_CONNECTING || x == PA_CONTEXT_AUTHORIZING || x == PA_CONTEXT_SETTING_NAME || x == PA_CONTEXT_READY
+}
+
+pub const PA_STREAM_UNCONNECTED: c_int = 0;
+pub const PA_STREAM_CREATING: c_int = 1;
+pub const PA_STREAM_READY: c_int = 2;
+pub const PA_STREAM_FAILED: c_int = 3;
+pub const PA_STREAM_TERMINATED: c_int = 4;
+pub type pa_stream_state_t = c_int;
+
+#[allow(non_snake_case)]
+pub fn PA_STREAM_IS_GOOD(x: pa_stream_state_t) -> bool {
+    x == PA_STREAM_CREATING || x == PA_STREAM_READY
+}
+
+pub const PA_OPERATION_RUNNING: c_int = 0;
+pub const PA_OPERATION_DONE: c_int = 1;
+pub const PA_OPERATION_CANCELLED: c_int = 2;
+pub type pa_operation_state_t = c_int;
+
+pub const PA_CONTEXT_NOFLAGS: c_int = 0;
+pub const PA_CONTEXT_NOAUTOSPAWN: c_int = 1;
+pub const PA_CONTEXT_NOFAIL: c_int = 2;
+pub type pa_context_flags_t = c_int;
+
+pub const PA_DIRECTION_OUTPUT: c_int = 1;
+pub const PA_DIRECTION_INPUT: c_int = 2;
+pub type pa_direction_t = c_int;
+
+pub const PA_DEVICE_TYPE_SINK: c_int = 0;
+pub const PA_DEVICE_TYPE_SOURCE: c_int = 1;
+pub type pa_device_type_t = c_int;
+
+pub const PA_STREAM_NODIRECTION: c_int = 0;
+pub const PA_STREAM_PLAYBACK: c_int = 1;
+pub const PA_STREAM_RECORD: c_int = 2;
+pub const PA_STREAM_UPLOAD: c_int = 3;
+pub type pa_stream_direction_t = c_int;
+
+pub const PA_STREAM_NOFLAGS: c_int = 0x0_0000;
+pub const PA_STREAM_START_CORKED: c_int = 0x0_0001;
+pub const PA_STREAM_INTERPOLATE_TIMING: c_int = 0x0_0002;
+pub const PA_STREAM_NOT_MONOTONIC: c_int = 0x0_0004;
+pub const PA_STREAM_AUTO_TIMING_UPDATE: c_int = 0x0_0008;
+pub const PA_STREAM_NO_REMAP_CHANNELS: c_int = 0x0_0010;
+pub const PA_STREAM_NO_REMIX_CHANNELS: c_int = 0x0_0020;
+pub const PA_STREAM_FIX_FORMAT: c_int = 0x0_0040;
+pub const PA_STREAM_FIX_RATE: c_int = 0x0_0080;
+pub const PA_STREAM_FIX_CHANNELS: c_int = 0x0_0100;
+pub const PA_STREAM_DONT_MOVE: c_int = 0x0_0200;
+pub const PA_STREAM_VARIABLE_RATE: c_int = 0x0_0400;
+pub const PA_STREAM_PEAK_DETECT: c_int = 0x0_0800;
+pub const PA_STREAM_START_MUTED: c_int = 0x0_1000;
+pub const PA_STREAM_ADJUST_LATENCY: c_int = 0x0_2000;
+pub const PA_STREAM_EARLY_REQUESTS: c_int = 0x0_4000;
+pub const PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND: c_int = 0x0_8000;
+pub const PA_STREAM_START_UNMUTED: c_int = 0x1_0000;
+pub const PA_STREAM_FAIL_ON_SUSPEND: c_int = 0x2_0000;
+pub const PA_STREAM_RELATIVE_VOLUME: c_int = 0x4_0000;
+pub const PA_STREAM_PASSTHROUGH: c_int = 0x8_0000;
+pub type pa_stream_flags_t = c_int;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct pa_buffer_attr {
+    pub maxlength: u32,
+    pub tlength: u32,
+    pub prebuf: u32,
+    pub minreq: u32,
+    pub fragsize: u32,
+}
+
+impl ::std::default::Default for pa_buffer_attr {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+
+pub const PA_OK: c_int = 0;
+pub const PA_ERR_ACCESS: c_int = 1;
+pub const PA_ERR_COMMAND: c_int = 2;
+pub const PA_ERR_INVALID: c_int = 3;
+pub const PA_ERR_EXIST: c_int = 4;
+pub const PA_ERR_NOENTITY: c_int = 5;
+pub const PA_ERR_CONNECTIONREFUSED: c_int = 6;
+pub const PA_ERR_PROTOCOL: c_int = 7;
+pub const PA_ERR_TIMEOUT: c_int = 8;
+pub const PA_ERR_AUTHKEY: c_int = 9;
+pub const PA_ERR_INTERNAL: c_int = 10;
+pub const PA_ERR_CONNECTIONTERMINATED: c_int = 11;
+pub const PA_ERR_KILLED: c_int = 12;
+pub const PA_ERR_INVALIDSERVER: c_int = 13;
+pub const PA_ERR_MODINITFAILED: c_int = 14;
+pub const PA_ERR_BADSTATE: c_int = 15;
+pub const PA_ERR_NODATA: c_int = 16;
+pub const PA_ERR_VERSION: c_int = 17;
+pub const PA_ERR_TOOLARGE: c_int = 18;
+pub const PA_ERR_NOTSUPPORTED: c_int = 19;
+pub const PA_ERR_UNKNOWN: c_int = 20;
+pub const PA_ERR_NOEXTENSION: c_int = 21;
+pub const PA_ERR_OBSOLETE: c_int = 22;
+pub const PA_ERR_NOTIMPLEMENTED: c_int = 23;
+pub const PA_ERR_FORKED: c_int = 24;
+pub const PA_ERR_IO: c_int = 25;
+pub const PA_ERR_BUSY: c_int = 26;
+pub const PA_ERR_MAX: c_int = 27;
+pub type pa_error_code_t = c_int;
+
+pub const PA_SUBSCRIPTION_MASK_NULL: c_int = 0;
+pub const PA_SUBSCRIPTION_MASK_SINK: c_int = 1;
+pub const PA_SUBSCRIPTION_MASK_SOURCE: c_int = 2;
+pub const PA_SUBSCRIPTION_MASK_SINK_INPUT: c_int = 4;
+pub const PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT: c_int = 8;
+pub const PA_SUBSCRIPTION_MASK_MODULE: c_int = 16;
+pub const PA_SUBSCRIPTION_MASK_CLIENT: c_int = 32;
+pub const PA_SUBSCRIPTION_MASK_SAMPLE_CACHE: c_int = 64;
+pub const PA_SUBSCRIPTION_MASK_SERVER: c_int = 128;
+pub const PA_SUBSCRIPTION_MASK_AUTOLOAD: c_int = 256;
+pub const PA_SUBSCRIPTION_MASK_CARD: c_int = 512;
+pub const PA_SUBSCRIPTION_MASK_ALL: c_int = 767;
+pub type pa_subscription_mask_t = c_int;
+
+pub const PA_SUBSCRIPTION_EVENT_SINK: c_int = 0;
+pub const PA_SUBSCRIPTION_EVENT_SOURCE: c_int = 1;
+pub const PA_SUBSCRIPTION_EVENT_SINK_INPUT: c_int = 2;
+pub const PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: c_int = 3;
+pub const PA_SUBSCRIPTION_EVENT_MODULE: c_int = 4;
+pub const PA_SUBSCRIPTION_EVENT_CLIENT: c_int = 5;
+pub const PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE: c_int = 6;
+pub const PA_SUBSCRIPTION_EVENT_SERVER: c_int = 7;
+pub const PA_SUBSCRIPTION_EVENT_AUTOLOAD: c_int = 8;
+pub const PA_SUBSCRIPTION_EVENT_CARD: c_int = 9;
+pub const PA_SUBSCRIPTION_EVENT_FACILITY_MASK: c_int = 15;
+pub const PA_SUBSCRIPTION_EVENT_NEW: c_int = 0;
+pub const PA_SUBSCRIPTION_EVENT_CHANGE: c_int = 16;
+pub const PA_SUBSCRIPTION_EVENT_REMOVE: c_int = 32;
+pub const PA_SUBSCRIPTION_EVENT_TYPE_MASK: c_int = 48;
+pub type pa_subscription_event_type_t = c_int;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct timeval {
+    pub tv_sec: c_long,
+    pub tv_usec: c_long,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct pa_timing_info {
+    pub timestamp: timeval,
+    pub synchronized_clocks: c_int,
+    pub sink_usec: pa_usec_t,
+    pub source_usec: pa_usec_t,
+    pub transport_usec: pa_usec_t,
+    pub playing: c_int,
+    pub write_index_corrupt: c_int,
+    pub write_index: i64,
+    pub read_index_corrupt: c_int,
+    pub read_index: i64,
+    pub configured_sink_usec: pa_usec_t,
+    pub configured_source_usec: pa_usec_t,
+    pub since_underrun: i64,
+}
+
+impl ::std::default::Default for pa_timing_info {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct pa_spawn_api {
+    pub prefork: Option<extern "C" fn() -> ()>,
+    pub postfork: Option<extern "C" fn() -> ()>,
+    pub atfork: Option<extern "C" fn() -> ()>,
+}
+
+impl ::std::default::Default for pa_spawn_api {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+
+pub const PA_SEEK_RELATIVE: c_int = 0;
+pub const PA_SEEK_ABSOLUTE: c_int = 1;
+pub const PA_SEEK_RELATIVE_ON_READ: c_int = 2;
+pub const PA_SEEK_RELATIVE_END: c_int = 3;
+pub type pa_seek_mode_t = c_int;
+
+pub const PA_SINK_NOFLAGS: c_int = 0;
+pub const PA_SINK_HW_VOLUME_CTRL: c_int = 1;
+pub const PA_SINK_LATENCY: c_int = 2;
+pub const PA_SINK_HARDWARE: c_int = 4;
+pub const PA_SINK_NETWORK: c_int = 8;
+pub const PA_SINK_HW_MUTE_CTRL: c_int = 16;
+pub const PA_SINK_DECIBEL_VOLUME: c_int = 32;
+pub const PA_SINK_FLAT_VOLUME: c_int = 64;
+pub const PA_SINK_DYNAMIC_LATENCY: c_int = 128;
+pub const PA_SINK_SET_FORMATS: c_int = 256;
+pub type pa_sink_flags_t = c_int;
+
+pub const PA_SINK_INVALID_STATE: c_int = -1;
+pub const PA_SINK_RUNNING: c_int = 0;
+pub const PA_SINK_IDLE: c_int = 1;
+pub const PA_SINK_SUSPENDED: c_int = 2;
+pub const PA_SINK_INIT: c_int = -2;
+pub const PA_SINK_UNLINKED: c_int = -3;
+pub type pa_sink_state_t = c_int;
+
+pub const PA_SOURCE_NOFLAGS: c_int = 0x00;
+pub const PA_SOURCE_HW_VOLUME_CTRL: c_int = 0x01;
+pub const PA_SOURCE_LATENCY: c_int = 0x02;
+pub const PA_SOURCE_HARDWARE: c_int = 0x04;
+pub const PA_SOURCE_NETWORK: c_int = 0x08;
+pub const PA_SOURCE_HW_MUTE_CTRL: c_int = 0x10;
+pub const PA_SOURCE_DECIBEL_VOLUME: c_int = 0x20;
+pub const PA_SOURCE_DYNAMIC_LATENCY: c_int = 0x40;
+pub const PA_SOURCE_FLAT_VOLUME: c_int = 0x80;
+pub type pa_source_flags_t = c_int;
+
+pub const PA_SOURCE_INVALID_STATE: c_int = -1;
+pub const PA_SOURCE_RUNNING: c_int = 0;
+pub const PA_SOURCE_IDLE: c_int = 1;
+pub const PA_SOURCE_SUSPENDED: c_int = 2;
+pub const PA_SOURCE_INIT: c_int = -2;
+pub const PA_SOURCE_UNLINKED: c_int = -3;
+pub type pa_source_state_t = c_int;
+
+pub type pa_free_cb_t = Option<unsafe extern "C" fn(p: *mut c_void) -> ()>;
+
+pub const PA_PORT_AVAILABLE_UNKNOWN: c_int = 0;
+pub const PA_PORT_AVAILABLE_NO: c_int = 1;
+pub const PA_PORT_AVAILABLE_YES: c_int = 2;
+pub type pa_port_available_t = c_int;
+
+pub const PA_IO_EVENT_NULL: c_int = 0;
+pub const PA_IO_EVENT_INPUT: c_int = 1;
+pub const PA_IO_EVENT_OUTPUT: c_int = 2;
+pub const PA_IO_EVENT_HANGUP: c_int = 4;
+pub const PA_IO_EVENT_ERROR: c_int = 8;
+pub type pa_io_event_flags_t = c_int;
+
+pub enum pa_io_event { }
+pub type pa_io_event_cb_t = Option<unsafe extern "C" fn(ea: *mut pa_mainloop_api,
+                                                        e: *mut pa_io_event,
+                                                        fd: c_int,
+                                                        events: pa_io_event_flags_t,
+                                                        userdata: *mut c_void)>;
+pub type pa_io_event_destroy_cb_t = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
+                                                                e: *mut pa_io_event,
+                                                                userdata: *mut c_void)>;
+pub enum pa_time_event { }
+pub type pa_time_event_cb_t = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
+                                                          e: *mut pa_time_event,
+                                                          tv: *const timeval,
+                                                          userdata: *mut c_void)>;
+pub type pa_time_event_destroy_cb_t = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
+                                                                  e: *mut pa_time_event,
+                                                                  userdata: *mut c_void)>;
+
+pub enum pa_defer_event { }
+pub type pa_defer_event_cb_t = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
+                                                           e: *mut pa_defer_event,
+                                                           userdata: *mut c_void)>;
+pub type pa_defer_event_destroy_cb_t = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
+                                                                   e: *mut pa_defer_event,
+                                                                   userdata: *mut c_void)>;
+pub type IoNewFn = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
+                                               fd: c_int,
+                                               events: pa_io_event_flags_t,
+                                               cb: pa_io_event_cb_t,
+                                               userdata: *mut c_void)
+                                               -> *mut pa_io_event>;
+pub type TimeNewFn = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
+                                                 tv: *const timeval,
+                                                 cb: pa_time_event_cb_t,
+                                                 userdata: *mut c_void)
+                                                 -> *mut pa_time_event>;
+pub type DeferNewFn = Option<unsafe extern "C" fn(a: *mut pa_mainloop_api,
+                                                  cb: pa_defer_event_cb_t,
+                                                  userdata: *mut c_void)
+                                                  -> *mut pa_defer_event>;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct pa_mainloop_api {
+    pub userdata: *mut c_void,
+    pub io_new: IoNewFn,
+    pub io_enable: Option<unsafe extern "C" fn(e: *mut pa_io_event, events: pa_io_event_flags_t)>,
+    pub io_free: Option<unsafe extern "C" fn(e: *mut pa_io_event)>,
+    pub io_set_destroy: Option<unsafe extern "C" fn(e: *mut pa_io_event, cb: pa_io_event_destroy_cb_t)>,
+    pub time_new: TimeNewFn,
+    pub time_restart: Option<unsafe extern "C" fn(e: *mut pa_time_event, tv: *const timeval)>,
+    pub time_free: Option<unsafe extern "C" fn(e: *mut pa_time_event)>,
+    pub time_set_destroy: Option<unsafe extern "C" fn(e: *mut pa_time_event, cb: pa_time_event_destroy_cb_t)>,
+    pub defer_new: DeferNewFn,
+    pub defer_enable: Option<unsafe extern "C" fn(e: *mut pa_defer_event, b: c_int)>,
+    pub defer_free: Option<unsafe extern "C" fn(e: *mut pa_defer_event)>,
+    pub defer_set_destroy: Option<unsafe extern "C" fn(e: *mut pa_defer_event, cb: pa_defer_event_destroy_cb_t)>,
+    pub quit: Option<unsafe extern "C" fn(a: *mut pa_mainloop_api, retval: c_int)>,
+}
+
+impl ::std::default::Default for pa_mainloop_api {
+    fn default() -> Self {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+
+pub type pa_mainloop_api_once_cb_t = Option<unsafe extern "C" fn(m: *mut pa_mainloop_api, userdata: *mut c_void)>;
+
+pub enum pa_proplist { }
+
+pub const PA_UPDATE_SET: c_int = 0;
+pub const PA_UPDATE_MERGE: c_int = 1;
+pub const PA_UPDATE_REPLACE: c_int = 2;
+pub type pa_update_mode_t = c_int;
+
+pub const PA_CHANNEL_POSITION_INVALID: c_int = -1;
+pub const PA_CHANNEL_POSITION_MONO: c_int = 0;
+pub const PA_CHANNEL_POSITION_FRONT_LEFT: c_int = 1;
+pub const PA_CHANNEL_POSITION_FRONT_RIGHT: c_int = 2;
+pub const PA_CHANNEL_POSITION_FRONT_CENTER: c_int = 3;
+pub const PA_CHANNEL_POSITION_LEFT: c_int = 1;
+pub const PA_CHANNEL_POSITION_RIGHT: c_int = 2;
+pub const PA_CHANNEL_POSITION_CENTER: c_int = 3;
+pub const PA_CHANNEL_POSITION_REAR_CENTER: c_int = 4;
+pub const PA_CHANNEL_POSITION_REAR_LEFT: c_int = 5;
+pub const PA_CHANNEL_POSITION_REAR_RIGHT: c_int = 6;
+pub const PA_CHANNEL_POSITION_LFE: c_int = 7;
+pub const PA_CHANNEL_POSITION_SUBWOOFER: c_int = 7;
+pub const PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: c_int = 8;
+pub const PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: c_int = 9;
+pub const PA_CHANNEL_POSITION_SIDE_LEFT: c_int = 10;
+pub const PA_CHANNEL_POSITION_SIDE_RIGHT: c_int = 11;
+pub const PA_CHANNEL_POSITION_AUX0: c_int = 12;
+pub const PA_CHANNEL_POSITION_AUX1: c_int = 13;
+pub const PA_CHANNEL_POSITION_AUX2: c_int = 14;
+pub const PA_CHANNEL_POSITION_AUX3: c_int = 15;
+pub const PA_CHANNEL_POSITION_AUX4: c_int = 16;
+pub const PA_CHANNEL_POSITION_AUX5: c_int = 17;
+pub const PA_CHANNEL_POSITION_AUX6: c_int = 18;
+pub const PA_CHANNEL_POSITION_AUX7: c_int = 19;
+pub const PA_CHANNEL_POSITION_AUX8: c_int = 20;
+pub const PA_CHANNEL_POSITION_AUX9: c_int = 21;
+pub const PA_CHANNEL_POSITION_AUX10: c_int = 22;
+pub const PA_CHANNEL_POSITION_AUX11: c_int = 23;
+pub const PA_CHANNEL_POSITION_AUX12: c_int = 24;
+pub const PA_CHANNEL_POSITION_AUX13: c_int = 25;
+pub const PA_CHANNEL_POSITION_AUX14: c_int = 26;
+pub const PA_CHANNEL_POSITION_AUX15: c_int = 27;
+pub const PA_CHANNEL_POSITION_AUX16: c_int = 28;
+pub const PA_CHANNEL_POSITION_AUX17: c_int = 29;
+pub const PA_CHANNEL_POSITION_AUX18: c_int = 30;
+pub const PA_CHANNEL_POSITION_AUX19: c_int = 31;
+pub const PA_CHANNEL_POSITION_AUX20: c_int = 32;
+pub const PA_CHANNEL_POSITION_AUX21: c_int = 33;
+pub const PA_CHANNEL_POSITION_AUX22: c_int = 34;
+pub const PA_CHANNEL_POSITION_AUX23: c_int = 35;
+pub const PA_CHANNEL_POSITION_AUX24: c_int = 36;
+pub const PA_CHANNEL_POSITION_AUX25: c_int = 37;
+pub const PA_CHANNEL_POSITION_AUX26: c_int = 38;
+pub const PA_CHANNEL_POSITION_AUX27: c_int = 39;
+pub const PA_CHANNEL_POSITION_AUX28: c_int = 40;
+pub const PA_CHANNEL_POSITION_AUX29: c_int = 41;
+pub const PA_CHANNEL_POSITION_AUX30: c_int = 42;
+pub const PA_CHANNEL_POSITION_AUX31: c_int = 43;
+pub const PA_CHANNEL_POSITION_TOP_CENTER: c_int = 44;
+pub const PA_CHANNEL_POSITION_TOP_FRONT_LEFT: c_int = 45;
+pub const PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: c_int = 46;
+pub const PA_CHANNEL_POSITION_TOP_FRONT_CENTER: c_int = 47;
+pub const PA_CHANNEL_POSITION_TOP_REAR_LEFT: c_int = 48;
+pub const PA_CHANNEL_POSITION_TOP_REAR_RIGHT: c_int = 49;
+pub const PA_CHANNEL_POSITION_TOP_REAR_CENTER: c_int = 50;
+pub const PA_CHANNEL_POSITION_MAX: c_int = 51;
+pub type pa_channel_position_t = c_int;
+pub type pa_channel_position_mask_t = u64;
+
+pub const PA_CHANNEL_MAP_AIFF: c_int = 0;
+pub const PA_CHANNEL_MAP_ALSA: c_int = 1;
+pub const PA_CHANNEL_MAP_AUX: c_int = 2;
+pub const PA_CHANNEL_MAP_WAVEEX: c_int = 3;
+pub const PA_CHANNEL_MAP_OSS: c_int = 4;
+pub const PA_CHANNEL_MAP_DEF_MAX: c_int = 5;
+pub const PA_CHANNEL_MAP_DEFAULT: c_int = 0;
+pub type pa_channel_map_def_t = c_int;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct pa_channel_map {
+    pub channels: u8,
+    pub map: [pa_channel_position_t; 32usize],
+}