merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 14 Oct 2014 17:17:55 -0700
changeset 234680 62f0b771583c09fa24583da75ab08115fa04ce79
parent 234633 c76eb2abb768775a1300591b6fd18f38f65ca1b3 (current diff)
parent 234679 980d33b2ca6cd0d3ac8260c9744abcee1da32335 (diff)
child 234701 bd8a3f92093d1598fcd13ddbdb23a181071455f7
child 234731 a974e5f136ac1aa05ce9b516c18c8ce382c062bf
child 234792 2d1c7353557ea5e3ea888f9eabad4c64a4ff12ad
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.0a1
first release with
nightly linux32
62f0b771583c / 36.0a1 / 20141015030202 / files
nightly linux64
62f0b771583c / 36.0a1 / 20141015030202 / files
nightly mac
62f0b771583c / 36.0a1 / 20141015030202 / files
nightly win32
62f0b771583c / 36.0a1 / 20141015030202 / files
nightly win64
62f0b771583c / 36.0a1 / 20141015030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge inbound to m-c a=merge
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -783,16 +783,19 @@ pref("dom.ipc.reuse_parent_app", false);
 // When a process receives a system message, we hold a CPU wake lock on its
 // behalf for this many seconds, or until it handles the system message,
 // whichever comes first.
 pref("dom.ipc.systemMessageCPULockTimeoutSec", 30);
 
 // Ignore the "dialog=1" feature in window.open.
 pref("dom.disable_window_open_dialog_feature", true);
 
+// Enable before keyboard events and after keyboard events.
+pref("dom.beforeAfterKeyboardEvent.enabled", true);
+
 // Screen reader support
 pref("accessibility.accessfu.activate", 2);
 pref("accessibility.accessfu.quicknav_modes", "Link,Heading,FormElement,Landmark,ListItem");
 // Setting for an utterance order (0 - description first, 1 - description last).
 pref("accessibility.accessfu.utterance", 1);
 // Whether to skip images with empty alt text
 pref("accessibility.accessfu.skip_empty_images", true);
 
@@ -868,16 +871,18 @@ pref("webgl.can-lose-context-in-foregrou
 pref("memory_info_dumper.watch_fifo.enabled", true);
 pref("memory_info_dumper.watch_fifo.directory", "/data/local");
 
 // See ua-update.json.in for the packaged UA override list
 pref("general.useragent.updates.enabled", true);
 pref("general.useragent.updates.url", "https://dynamicua.cdn.mozilla.net/0/%APP_ID%");
 pref("general.useragent.updates.interval", 604800); // 1 week
 pref("general.useragent.updates.retry", 86400); // 1 day
+// Device ID can be composed of letter, numbers, hyphen ("-") and dot (".")
+pref("general.useragent.device_id", "");
 
 // Make <audio> and <video> talk to the AudioChannelService.
 pref("media.useAudioChannelService", true);
 
 pref("b2g.version", @MOZ_B2G_VERSION@);
 pref("b2g.osName", @MOZ_B2G_OS_NAME@);
 
 // Disable console buffering to save memory.
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -319,16 +319,18 @@ var shell = {
     // Capture all key events so we can filter out hardware buttons
     // And send them to Gaia via mozChromeEvents.
     // Ideally, hardware buttons wouldn't generate key events at all, or
     // if they did, they would use keycodes that conform to DOM 3 Events.
     // See discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=762362
     chromeEventHandler.addEventListener('keydown', this, true);
     chromeEventHandler.addEventListener('keypress', this, true);
     chromeEventHandler.addEventListener('keyup', this, true);
+    chromeEventHandler.addEventListener('mozbrowserbeforekeydown', this, true);
+    chromeEventHandler.addEventListener('mozbrowserbeforekeyup', this, true);
 
     window.addEventListener('MozApplicationManifest', this);
     window.addEventListener('mozfullscreenchange', this);
     window.addEventListener('MozAfterPaint', this);
     window.addEventListener('sizemodechange', this);
     window.addEventListener('unload', this);
     this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.addEventListener('mozbrowserselectionchange', this, true);
@@ -351,16 +353,18 @@ var shell = {
     ppmm.addMessageListener("file-picker", this);
   },
 
   stop: function shell_stop() {
     window.removeEventListener('unload', this);
     window.removeEventListener('keydown', this, true);
     window.removeEventListener('keypress', this, true);
     window.removeEventListener('keyup', this, true);
+    window.removeEventListener('mozbrowserbeforekeydown', this, true);
+    window.removeEventListener('mozbrowserbeforekeyup', this, true);
     window.removeEventListener('MozApplicationManifest', this);
     window.removeEventListener('mozfullscreenchange', this);
     window.removeEventListener('sizemodechange', this);
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.removeEventListener('mozbrowserselectionchange', this, true);
     this.contentBrowser.removeEventListener('mozbrowserscrollviewchange', this, true);
     this.contentBrowser.removeEventListener('mozbrowsertouchcarettap', this, true);
     ppmm.removeMessageListener("content-handler", this);
@@ -415,27 +419,24 @@ var shell = {
       isMediaKey = true;
       type = mediaKeys[evt.key];
     }
 
     if (!type) {
       return;
     }
 
-    // If we didn't return, then the key event represents a hardware key
-    // and we need to prevent it from propagating to Gaia
-    evt.stopImmediatePropagation();
-    evt.preventDefault(); // Prevent keypress events (when #501496 is fixed).
-
     // If it is a key down or key up event, we send a chrome event to Gaia.
     // If it is a keypress event we just ignore it.
     switch (evt.type) {
+      case 'mozbrowserbeforekeydown':
       case 'keydown':
         type = type + '-press';
         break;
+      case 'mozbrowserbeforekeyup':
       case 'keyup':
         type = type + '-release';
         break;
       case 'keypress':
         return;
     }
 
     // Let applications receive the headset button key press/release event.
@@ -468,16 +469,18 @@ var shell = {
   visibleNormalAudioActive: false,
 
   handleEvent: function shell_handleEvent(evt) {
     let content = this.contentBrowser.contentWindow;
     switch (evt.type) {
       case 'keydown':
       case 'keyup':
       case 'keypress':
+      case 'mozbrowserbeforekeydown':
+      case 'mozbrowserbeforekeyup':
         this.filterHardwareKeys(evt);
         break;
       case 'mozfullscreenchange':
         // When the screen goes fullscreen make sure to set the focus to the
         // main window so noboby can prevent the ESC key to get out fullscreen
         // mode
         if (document.mozFullScreen)
           Services.fm.focusedWindow = window;
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -2356,18 +2356,18 @@ public:
 
   // ParentNode
   nsIHTMLCollection* Children();
   uint32_t ChildElementCount();
 
   virtual nsHTMLDocument* AsHTMLDocument() { return nullptr; }
   virtual mozilla::dom::SVGDocument* AsSVGDocument() { return nullptr; }
 
-  // Each import tree has exactly one master document which is
-  // the root of the tree, and owns the browser context.
+  // The root document of the import tree. If this document is not an import
+  // this will return the document itself.
   virtual nsIDocument* MasterDocument() = 0;
   virtual void SetMasterDocument(nsIDocument* master) = 0;
   virtual bool IsMasterDocument() = 0;
   virtual mozilla::dom::ImportManager* ImportManager() = 0;
   // We keep track of the order of sub imports were added to the document.
   virtual bool HasSubImportLink(nsINode* aLink) = 0;
   virtual uint32_t IndexOfSubImportLink(nsINode* aLink) = 0;
   virtual void AddSubImportLink(nsINode* aLink) = 0;
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -78,99 +78,44 @@ public:
 
   WebSocketImpl(WebSocket* aWebSocket)
   : mWebSocket(aWebSocket)
   , mOnCloseScheduled(false)
   , mFailed(false)
   , mDisconnected(false)
   , mCloseEventWasClean(false)
   , mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL)
-  , mOutgoingBufferedAmount(0)
-  , mBinaryType(dom::BinaryType::Blob)
   , mScriptLine(0)
   , mInnerWindowID(0)
-  , mMutex("WebSocketImpl::mMutex")
   , mWorkerPrivate(nullptr)
-  , mReadyState(WebSocket::CONNECTING)
   {
     if (!NS_IsMainThread()) {
       mWorkerPrivate = GetCurrentThreadWorkerPrivate();
       MOZ_ASSERT(mWorkerPrivate);
     }
   }
 
   void AssertIsOnTargetThread() const
   {
     MOZ_ASSERT(IsTargetThread());
   }
 
   bool IsTargetThread() const;
 
-  uint16_t ReadyState()
-  {
-    MutexAutoLock lock(mMutex);
-    return mReadyState;
-  }
-
-  void SetReadyState(uint16_t aReadyState)
-  {
-    MutexAutoLock lock(mMutex);
-    mReadyState = aReadyState;
-  }
-
-  uint32_t BufferedAmount() const
-  {
-    AssertIsOnTargetThread();
-    return mOutgoingBufferedAmount;
-  }
-
-  dom::BinaryType BinaryType() const
-  {
-    AssertIsOnTargetThread();
-    return mBinaryType;
-  }
-
-  void SetBinaryType(dom::BinaryType aData)
-  {
-    AssertIsOnTargetThread();
-    mBinaryType = aData;
-  }
-
-  void GetUrl(nsAString& aURL) const
-  {
-    AssertIsOnTargetThread();
-
-    if (mEffectiveURL.IsEmpty()) {
-      aURL = mOriginalURL;
-    } else {
-      aURL = mEffectiveURL;
-    }
-  }
-
-  void Close(const Optional<uint16_t>& aCode,
-             const Optional<nsAString>& aReason,
-             ErrorResult& aRv);
-
   void Init(JSContext* aCx,
             nsIPrincipal* aPrincipal,
             const nsAString& aURL,
             nsTArray<nsString>& aProtocolArray,
             const nsACString& aScriptFile,
             uint32_t aScriptLine,
             ErrorResult& aRv,
             bool* aConnectionFailed);
 
   void AsyncOpen(ErrorResult& aRv);
 
-  void Send(nsIInputStream* aMsgStream,
-            const nsACString& aMsgString,
-            uint32_t aMsgLength,
-            bool aIsBinary,
-            ErrorResult& aRv);
-
   nsresult ParseURL(const nsAString& aURL);
   nsresult InitializeConnection();
 
   // These methods when called can release the WebSocket object
   void FailConnection(uint16_t reasonCode,
                       const nsACString& aReasonString = EmptyCString());
   nsresult CloseConnection(uint16_t reasonCode,
                            const nsACString& aReasonString = EmptyCString());
@@ -207,19 +152,16 @@ public:
   void UnregisterFeature();
 
   nsresult CancelInternal();
 
   nsRefPtr<WebSocket> mWebSocket;
 
   nsCOMPtr<nsIWebSocketChannel> mChannel;
 
-  // related to the WebSocket constructor steps
-  nsString mOriginalURL;
-  nsString mEffectiveURL;   // after redirects
   bool mSecure; // if true it is using SSL and the wss scheme,
                 // otherwise it is using the ws scheme with no SSL
 
   bool mOnCloseScheduled;
   bool mFailed;
   bool mDisconnected;
 
   // Set attributes of DOM 'onclose' message
@@ -229,54 +171,41 @@ public:
 
   nsCString mAsciiHost;  // hostname
   uint32_t  mPort;
   nsCString mResource; // [filepath[?query]]
   nsString  mUTF16Origin;
 
   nsCString mURI;
   nsCString mRequestedProtocolList;
-  nsCString mEstablishedProtocol;
-  nsCString mEstablishedExtensions;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsWeakPtr              mOriginDocument;
 
-  uint32_t mOutgoingBufferedAmount;
-
-  dom::BinaryType mBinaryType;
-
   // Web Socket owner information:
   // - the script file name, UTF8 encoded.
   // - source code line number where the Web Socket object was constructed.
   // - the ID of the inner window where the script lives. Note that this may not
   //   be the same as the Web Socket owner window.
   // These attributes are used for error reporting.
   nsCString mScriptFile;
   uint32_t mScriptLine;
   uint64_t mInnerWindowID;
 
-  // This mutex protects mReadyState that is the only variable that is used in
-  // different threads.
-  mozilla::Mutex mMutex;
-
   WorkerPrivate* mWorkerPrivate;
   nsAutoPtr<WorkerFeature> mWorkerFeature;
 
 private:
   ~WebSocketImpl()
   {
     // If we threw during Init we never called disconnect
     if (!mDisconnected) {
       Disconnect();
     }
   }
-
-  // This value should not be used directly but use ReadyState() instead.
-  uint16_t mReadyState;
 };
 
 NS_IMPL_ISUPPORTS(WebSocketImpl,
                   nsIInterfaceRequestor,
                   nsIWebSocketListener,
                   nsIObserver,
                   nsISupportsWeakReference,
                   nsIRequest,
@@ -445,25 +374,25 @@ private:
 } // anonymous namespace
 
 nsresult
 WebSocketImpl::CloseConnection(uint16_t aReasonCode,
                                const nsACString& aReasonString)
 {
   AssertIsOnTargetThread();
 
-  uint16_t readyState = ReadyState();
+  uint16_t readyState = mWebSocket->ReadyState();
   if (readyState == WebSocket::CLOSING ||
       readyState == WebSocket::CLOSED) {
     return NS_OK;
   }
 
   // The common case...
   if (mChannel) {
-    SetReadyState(WebSocket::CLOSING);
+    mWebSocket->SetReadyState(WebSocket::CLOSING);
 
     // The channel has to be closed on the main-thread.
 
     if (NS_IsMainThread()) {
       return mChannel->Close(aReasonCode, aReasonString);
     }
 
     nsRefPtr<CloseRunnable> runnable =
@@ -476,17 +405,17 @@ WebSocketImpl::CloseConnection(uint16_t 
   //
   MOZ_ASSERT(readyState == WebSocket::CONNECTING,
              "Should only get here for early websocket cancel/error");
 
   // Server won't be sending us a close code, so use what's passed in here.
   mCloseEventCode = aReasonCode;
   CopyUTF8toUTF16(aReasonString, mCloseEventReason);
 
-  SetReadyState(WebSocket::CLOSING);
+  mWebSocket->SetReadyState(WebSocket::CLOSING);
 
   // Can be called from Cancel() or Init() codepaths, so need to dispatch
   // onerror/onclose asynchronously
   ScheduleConnectionCloseEvents(
                     nullptr,
                     (aReasonCode == nsIWebSocketChannel::CLOSE_NORMAL ||
                      aReasonCode == nsIWebSocketChannel::CLOSE_GOING_AWAY) ?
                      NS_OK : NS_ERROR_FAILURE,
@@ -498,17 +427,17 @@ WebSocketImpl::CloseConnection(uint16_t 
 nsresult
 WebSocketImpl::ConsoleError()
 {
   AssertIsOnTargetThread();
 
   NS_ConvertUTF8toUTF16 specUTF16(mURI);
   const char16_t* formatStrings[] = { specUTF16.get() };
 
-  if (ReadyState() < WebSocket::OPEN) {
+  if (mWebSocket->ReadyState() < WebSocket::OPEN) {
     PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
                         MOZ_UTF16("connectionFailure"),
                         formatStrings, ArrayLength(formatStrings));
   } else {
     PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
                         MOZ_UTF16("netInterrupt"),
                         formatStrings, ArrayLength(formatStrings));
   }
@@ -616,17 +545,17 @@ WebSocketImpl::DisconnectInternal()
 // WebSocketImpl::nsIWebSocketListener methods:
 //-----------------------------------------------------------------------------
 
 nsresult
 WebSocketImpl::DoOnMessageAvailable(const nsACString& aMsg, bool isBinary)
 {
   AssertIsOnTargetThread();
 
-  int16_t readyState = ReadyState();
+  int16_t readyState = mWebSocket->ReadyState();
   if (readyState == WebSocket::CLOSED) {
     NS_ERROR("Received message after CLOSED");
     return NS_ERROR_UNEXPECTED;
   }
 
   if (readyState == WebSocket::OPEN) {
     // Dispatch New Message
     nsresult rv = mWebSocket->CreateAndDispatchMessageEvent(aMsg, isBinary);
@@ -659,17 +588,17 @@ WebSocketImpl::OnBinaryMessageAvailable(
   return DoOnMessageAvailable(aMsg, true);
 }
 
 NS_IMETHODIMP
 WebSocketImpl::OnStart(nsISupports* aContext)
 {
   AssertIsOnTargetThread();
 
-  int16_t readyState = ReadyState();
+  int16_t readyState = mWebSocket->ReadyState();
 
   // This is the only function that sets OPEN, and should be called only once
   MOZ_ASSERT(readyState != WebSocket::OPEN,
              "readyState already OPEN! OnStart called twice?");
 
   // Nothing to do if we've already closed/closing
   if (readyState != WebSocket::CONNECTING) {
     return NS_OK;
@@ -678,23 +607,23 @@ WebSocketImpl::OnStart(nsISupports* aCon
   // Attempt to kill "ghost" websocket: but usually too early for check to fail
   nsresult rv = mWebSocket->CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
     return rv;
   }
 
   if (!mRequestedProtocolList.IsEmpty()) {
-    mChannel->GetProtocol(mEstablishedProtocol);
+    mChannel->GetProtocol(mWebSocket->mEstablishedProtocol);
   }
 
-  mChannel->GetExtensions(mEstablishedExtensions);
+  mChannel->GetExtensions(mWebSocket->mEstablishedExtensions);
   UpdateURI();
 
-  SetReadyState(WebSocket::OPEN);
+  mWebSocket->SetReadyState(WebSocket::OPEN);
 
   // Call 'onopen'
   rv = mWebSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch the open event");
   }
 
   mWebSocket->UpdateMustKeepAlive();
@@ -705,17 +634,17 @@ WebSocketImpl::OnStart(nsISupports* aCon
 NS_IMETHODIMP
 WebSocketImpl::OnStop(nsISupports* aContext, nsresult aStatusCode)
 {
   AssertIsOnTargetThread();
 
   // We can be CONNECTING here if connection failed.
   // We can be OPEN if we have encountered a fatal protocol error
   // We can be CLOSING if close() was called and/or server initiated close.
-  MOZ_ASSERT(ReadyState() != WebSocket::CLOSED,
+  MOZ_ASSERT(mWebSocket->ReadyState() != WebSocket::CLOSED,
              "Shouldn't already be CLOSED when OnStop called");
 
   // called by network stack, not JS, so can dispatch JS events synchronously
   return ScheduleConnectionCloseEvents(aContext, aStatusCode, true);
 }
 
 nsresult
 WebSocketImpl::ScheduleConnectionCloseEvents(nsISupports* aContext,
@@ -750,31 +679,31 @@ WebSocketImpl::ScheduleConnectionCloseEv
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketImpl::OnAcknowledge(nsISupports *aContext, uint32_t aSize)
 {
   AssertIsOnTargetThread();
 
-  if (aSize > mOutgoingBufferedAmount) {
+  if (aSize > mWebSocket->mOutgoingBufferedAmount) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  mOutgoingBufferedAmount -= aSize;
+  mWebSocket->mOutgoingBufferedAmount -= aSize;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketImpl::OnServerClose(nsISupports *aContext, uint16_t aCode,
                              const nsACString &aReason)
 {
   AssertIsOnTargetThread();
 
-  int16_t readyState = ReadyState();
+  int16_t readyState = mWebSocket->ReadyState();
 
   MOZ_ASSERT(readyState != WebSocket::CONNECTING,
              "Received server close before connected?");
   MOZ_ASSERT(readyState != WebSocket::CLOSED,
              "Received server close after already closed!");
 
   // store code/string for onclose DOM event
   mCloseEventCode = aCode;
@@ -802,17 +731,17 @@ WebSocketImpl::OnServerClose(nsISupports
 // WebSocketImpl::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 WebSocketImpl::GetInterface(const nsIID& aIID, void** aResult)
 {
   AssertIsOnMainThread();
 
-  if (ReadyState() == WebSocket::CLOSED) {
+  if (!mWebSocket || mWebSocket->ReadyState() == WebSocket::CLOSED) {
     return NS_ERROR_FAILURE;
   }
 
   if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
       aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
     nsresult rv;
     nsIScriptContext* sc = mWebSocket->GetContextForEventHandlers(&rv);
     nsCOMPtr<nsIDocument> doc =
@@ -836,16 +765,20 @@ WebSocketImpl::GetInterface(const nsIID&
 // WebSocket
 ////////////////////////////////////////////////////////////////////////////////
 
 WebSocket::WebSocket(nsPIDOMWindow* aOwnerWindow)
   : DOMEventTargetHelper(aOwnerWindow)
   , mWorkerPrivate(nullptr)
   , mKeepingAlive(false)
   , mCheckMustKeepAlive(true)
+  , mOutgoingBufferedAmount(0)
+  , mBinaryType(dom::BinaryType::Blob)
+  , mMutex("WebSocketImpl::mMutex")
+  , mReadyState(CONNECTING)
 {
   mImpl = new WebSocketImpl(this);
   mWorkerPrivate = mImpl->mWorkerPrivate;
 }
 
 WebSocket::~WebSocket()
 {
 }
@@ -1485,17 +1418,17 @@ WebSocketImpl::InitializeConnection()
 
   return NS_OK;
 }
 
 void
 WebSocketImpl::DispatchConnectionCloseEvents()
 {
   AssertIsOnTargetThread();
-  SetReadyState(WebSocket::CLOSED);
+  mWebSocket->SetReadyState(WebSocket::CLOSED);
 
   // Call 'onerror' if needed
   if (mFailed) {
     nsresult rv =
       mWebSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch the error event");
     }
@@ -1511,17 +1444,17 @@ WebSocketImpl::DispatchConnectionCloseEv
   mWebSocket->UpdateMustKeepAlive();
   Disconnect();
 }
 
 nsresult
 WebSocket::CreateAndDispatchSimpleEvent(const nsAString& aName)
 {
   MOZ_ASSERT(mImpl);
-  mImpl->AssertIsOnTargetThread();
+  AssertIsOnTargetThread();
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMEvent> event;
   rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
@@ -1536,17 +1469,17 @@ WebSocket::CreateAndDispatchSimpleEvent(
   return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 }
 
 nsresult
 WebSocket::CreateAndDispatchMessageEvent(const nsACString& aData,
                                          bool aIsBinary)
 {
   MOZ_ASSERT(mImpl);
-  mImpl->AssertIsOnTargetThread();
+  AssertIsOnTargetThread();
 
   AutoJSAPI jsapi;
 
   if (NS_IsMainThread()) {
     if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
       return NS_ERROR_FAILURE;
     }
   } else {
@@ -1560,31 +1493,31 @@ WebSocket::CreateAndDispatchMessageEvent
 }
 
 nsresult
 WebSocket::CreateAndDispatchMessageEvent(JSContext* aCx,
                                          const nsACString& aData,
                                          bool aIsBinary)
 {
   MOZ_ASSERT(mImpl);
-  mImpl->AssertIsOnTargetThread();
+  AssertIsOnTargetThread();
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
   // Create appropriate JS object for message
   JS::Rooted<JS::Value> jsData(aCx);
   if (aIsBinary) {
-    if (mImpl->mBinaryType == dom::BinaryType::Blob) {
+    if (mBinaryType == dom::BinaryType::Blob) {
       nsresult rv = nsContentUtils::CreateBlobBuffer(aCx, GetOwner(), aData,
                                                      &jsData);
       NS_ENSURE_SUCCESS(rv, rv);
-    } else if (mImpl->mBinaryType == dom::BinaryType::Arraybuffer) {
+    } else if (mBinaryType == dom::BinaryType::Arraybuffer) {
       JS::Rooted<JSObject*> arrayBuf(aCx);
       nsresult rv = nsContentUtils::CreateArrayBuffer(aCx, aData,
                                                       arrayBuf.address());
       NS_ENSURE_SUCCESS(rv, rv);
       jsData = OBJECT_TO_JSVAL(arrayBuf);
     } else {
       NS_RUNTIMEABORT("Unknown binary type!");
       return NS_ERROR_UNEXPECTED;
@@ -1620,17 +1553,17 @@ WebSocket::CreateAndDispatchMessageEvent
 }
 
 nsresult
 WebSocket::CreateAndDispatchCloseEvent(bool aWasClean,
                                        uint16_t aCode,
                                        const nsAString &aReason)
 {
   MOZ_ASSERT(mImpl);
-  mImpl->AssertIsOnTargetThread();
+  AssertIsOnTargetThread();
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
   CloseEventInit init;
   init.mBubbles = false;
@@ -1762,17 +1695,17 @@ WebSocketImpl::ParseURL(const nsAString&
   uint32_t i;
   for (i = 0; i < length; ++i) {
     if (mResource[i] < static_cast<char16_t>(0x0021) ||
         mResource[i] > static_cast<char16_t>(0x007E)) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
   }
 
-  mOriginalURL = aURL;
+  mWebSocket->mOriginalURL = aURL;
 
   rv = parsedURL->GetSpec(mURI);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
@@ -1790,42 +1723,42 @@ WebSocket::UpdateMustKeepAlive()
 
   if (!mCheckMustKeepAlive || !mImpl) {
     return;
   }
 
   bool shouldKeepAlive = false;
 
   if (mListenerManager) {
-    switch (mImpl->ReadyState())
+    switch (ReadyState())
     {
-      case WebSocket::CONNECTING:
+      case CONNECTING:
       {
         if (mListenerManager->HasListenersFor(nsGkAtoms::onopen) ||
             mListenerManager->HasListenersFor(nsGkAtoms::onmessage) ||
             mListenerManager->HasListenersFor(nsGkAtoms::onerror) ||
             mListenerManager->HasListenersFor(nsGkAtoms::onclose)) {
           shouldKeepAlive = true;
         }
       }
       break;
 
-      case WebSocket::OPEN:
-      case WebSocket::CLOSING:
+      case OPEN:
+      case CLOSING:
       {
         if (mListenerManager->HasListenersFor(nsGkAtoms::onmessage) ||
             mListenerManager->HasListenersFor(nsGkAtoms::onerror) ||
             mListenerManager->HasListenersFor(nsGkAtoms::onclose) ||
-            mImpl->mOutgoingBufferedAmount != 0) {
+            mOutgoingBufferedAmount != 0) {
           shouldKeepAlive = true;
         }
       }
       break;
 
-      case WebSocket::CLOSED:
+      case CLOSED:
       {
         shouldKeepAlive = false;
       }
     }
   }
 
   if (mKeepingAlive && !shouldKeepAlive) {
     mKeepingAlive = false;
@@ -1935,17 +1868,17 @@ WebSocketImpl::UpdateURI()
 {
   AssertIsOnTargetThread();
 
   // Check for Redirections
   nsRefPtr<BaseWebSocketChannel> channel;
   channel = static_cast<BaseWebSocketChannel*>(mChannel.get());
   MOZ_ASSERT(channel);
 
-  channel->GetEffectiveURL(mEffectiveURL);
+  channel->GetEffectiveURL(mWebSocket->mEffectiveURL);
   mSecure = channel->IsEncrypted();
 
   return NS_OK;
 }
 
 void
 WebSocket::EventListenerAdded(nsIAtom* aType)
 {
@@ -1961,89 +1894,98 @@ WebSocket::EventListenerRemoved(nsIAtom*
 }
 
 //-----------------------------------------------------------------------------
 // WebSocket - methods
 //-----------------------------------------------------------------------------
 
 // webIDL: readonly attribute unsigned short readyState;
 uint16_t
-WebSocket::ReadyState() const
+WebSocket::ReadyState()
 {
-  MOZ_ASSERT(mImpl);
-  return mImpl->ReadyState();
+  MutexAutoLock lock(mMutex);
+  return mReadyState;
+}
+
+void
+WebSocket::SetReadyState(uint16_t aReadyState)
+{
+  MutexAutoLock lock(mMutex);
+  mReadyState = aReadyState;
 }
 
 // webIDL: readonly attribute unsigned long bufferedAmount;
 uint32_t
 WebSocket::BufferedAmount() const
 {
-  MOZ_ASSERT(mImpl);
-  return mImpl->BufferedAmount();
+  AssertIsOnTargetThread();
+  return mOutgoingBufferedAmount;
 }
 
 // webIDL: attribute BinaryType binaryType;
 dom::BinaryType
 WebSocket::BinaryType() const
 {
-  MOZ_ASSERT(mImpl);
-  return mImpl->BinaryType();
+  AssertIsOnTargetThread();
+  return mBinaryType;
 }
 
 // webIDL: attribute BinaryType binaryType;
 void
 WebSocket::SetBinaryType(dom::BinaryType aData)
 {
-  MOZ_ASSERT(mImpl);
-  mImpl->SetBinaryType(aData);
+  AssertIsOnTargetThread();
+  mBinaryType = aData;
 }
 
 // webIDL: readonly attribute DOMString url
 void
 WebSocket::GetUrl(nsAString& aURL)
 {
-  MOZ_ASSERT(mImpl);
-  mImpl->GetUrl(aURL);
+  AssertIsOnTargetThread();
+
+  if (mEffectiveURL.IsEmpty()) {
+    aURL = mOriginalURL;
+  } else {
+    aURL = mEffectiveURL;
+  }
 }
 
 // webIDL: readonly attribute DOMString extensions;
 void
 WebSocket::GetExtensions(nsAString& aExtensions)
 {
-  MOZ_ASSERT(mImpl);
-  mImpl->AssertIsOnTargetThread();
-
-  CopyUTF8toUTF16(mImpl->mEstablishedExtensions, aExtensions);
+  AssertIsOnTargetThread();
+  CopyUTF8toUTF16(mEstablishedExtensions, aExtensions);
 }
 
 // webIDL: readonly attribute DOMString protocol;
 void
 WebSocket::GetProtocol(nsAString& aProtocol)
 {
-  MOZ_ASSERT(mImpl);
-  mImpl->AssertIsOnTargetThread();
-
-  CopyUTF8toUTF16(mImpl->mEstablishedProtocol, aProtocol);
+  AssertIsOnTargetThread();
+  CopyUTF8toUTF16(mEstablishedProtocol, aProtocol);
 }
 
 // webIDL: void send(DOMString data);
 void
 WebSocket::Send(const nsAString& aData,
                 ErrorResult& aRv)
 {
-  MOZ_ASSERT(mImpl);
-  mImpl->AssertIsOnTargetThread();
+  AssertIsOnTargetThread();
 
   NS_ConvertUTF16toUTF8 msgString(aData);
-  mImpl->Send(nullptr, msgString, msgString.Length(), false, aRv);
+  Send(nullptr, msgString, msgString.Length(), false, aRv);
 }
 
 void
 WebSocket::Send(File& aData, ErrorResult& aRv)
 {
+  AssertIsOnTargetThread();
+
   nsCOMPtr<nsIInputStream> msgStream;
   nsresult rv = aData.GetInternalStream(getter_AddRefs(msgStream));
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return;
   }
 
   uint64_t msgLength;
@@ -2053,115 +1995,105 @@ WebSocket::Send(File& aData, ErrorResult
     return;
   }
 
   if (msgLength > UINT32_MAX) {
     aRv.Throw(NS_ERROR_FILE_TOO_BIG);
     return;
   }
 
-  mImpl->Send(msgStream, EmptyCString(), msgLength, true, aRv);
+  Send(msgStream, EmptyCString(), msgLength, true, aRv);
 }
 
 void
 WebSocket::Send(const ArrayBuffer& aData,
                 ErrorResult& aRv)
 {
-  MOZ_ASSERT(mImpl);
-  mImpl->AssertIsOnTargetThread();
+  AssertIsOnTargetThread();
 
   aData.ComputeLengthAndData();
 
   static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
 
   uint32_t len = aData.Length();
   char* data = reinterpret_cast<char*>(aData.Data());
 
   nsDependentCSubstring msgString(data, len);
-  mImpl->Send(nullptr, msgString, len, true, aRv);
+  Send(nullptr, msgString, len, true, aRv);
 }
 
 void
 WebSocket::Send(const ArrayBufferView& aData,
                 ErrorResult& aRv)
 {
-  MOZ_ASSERT(mImpl);
-  mImpl->AssertIsOnTargetThread();
+  AssertIsOnTargetThread();
 
   aData.ComputeLengthAndData();
 
   static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
 
   uint32_t len = aData.Length();
   char* data = reinterpret_cast<char*>(aData.Data());
 
   nsDependentCSubstring msgString(data, len);
-  mImpl->Send(nullptr, msgString, len, true, aRv);
+  Send(nullptr, msgString, len, true, aRv);
 }
 
 void
-WebSocketImpl::Send(nsIInputStream* aMsgStream,
-                    const nsACString& aMsgString,
-                    uint32_t aMsgLength,
-                    bool aIsBinary,
-                    ErrorResult& aRv)
+WebSocket::Send(nsIInputStream* aMsgStream,
+                const nsACString& aMsgString,
+                uint32_t aMsgLength,
+                bool aIsBinary,
+                ErrorResult& aRv)
 {
   AssertIsOnTargetThread();
 
   int64_t readyState = ReadyState();
-  if (readyState == WebSocket::CONNECTING) {
+  if (readyState == CONNECTING) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // Always increment outgoing buffer len, even if closed
   mOutgoingBufferedAmount += aMsgLength;
 
-  if (readyState == WebSocket::CLOSING ||
-      readyState == WebSocket::CLOSED) {
+  if (readyState == CLOSING ||
+      readyState == CLOSED) {
     return;
   }
 
-  MOZ_ASSERT(readyState == WebSocket::OPEN,
-             "Unknown state in WebSocket::Send");
+  // We must have mImpl when connected.
+  MOZ_ASSERT(mImpl);
+  MOZ_ASSERT(readyState == OPEN, "Unknown state in WebSocket::Send");
 
   nsresult rv;
   if (aMsgStream) {
-    rv = mChannel->SendBinaryStream(aMsgStream, aMsgLength);
+    rv = mImpl->mChannel->SendBinaryStream(aMsgStream, aMsgLength);
   } else {
     if (aIsBinary) {
-      rv = mChannel->SendBinaryMsg(aMsgString);
+      rv = mImpl->mChannel->SendBinaryMsg(aMsgString);
     } else {
-      rv = mChannel->SendMsg(aMsgString);
+      rv = mImpl->mChannel->SendMsg(aMsgString);
     }
   }
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return;
   }
 
-  mWebSocket->UpdateMustKeepAlive();
+  UpdateMustKeepAlive();
 }
 
 // webIDL: void close(optional unsigned short code, optional DOMString reason):
 void
 WebSocket::Close(const Optional<uint16_t>& aCode,
                  const Optional<nsAString>& aReason,
                  ErrorResult& aRv)
 {
-  MOZ_ASSERT(mImpl);
-  mImpl->Close(aCode, aReason, aRv);
-}
-
-void
-WebSocketImpl::Close(const Optional<uint16_t>& aCode,
-                     const Optional<nsAString>& aReason,
-                     ErrorResult& aRv)
-{
   AssertIsOnTargetThread();
 
   // the reason code is optional, but if provided it must be in a specific range
   uint16_t closeCode = 0;
   if (aCode.WasPassed()) {
     if (aCode.Value() != 1000 && (aCode.Value() < 3000 || aCode.Value() > 4999)) {
       aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
       return;
@@ -2176,42 +2108,45 @@ WebSocketImpl::Close(const Optional<uint
     // The API requires the UTF-8 string to be 123 or less bytes
     if (closeReason.Length() > 123) {
       aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
       return;
     }
   }
 
   int64_t readyState = ReadyState();
-  if (readyState == WebSocket::CLOSING ||
-      readyState == WebSocket::CLOSED) {
+  if (readyState == CLOSING ||
+      readyState == CLOSED) {
     return;
   }
 
-  if (readyState == WebSocket::CONNECTING) {
-    FailConnection(closeCode, closeReason);
+  // If the webSocket is not closed we MUST have a mImpl.
+  MOZ_ASSERT(mImpl);
+
+  if (readyState == CONNECTING) {
+    mImpl->FailConnection(closeCode, closeReason);
     return;
   }
 
-  MOZ_ASSERT(readyState == WebSocket::OPEN);
-  CloseConnection(closeCode, closeReason);
+  MOZ_ASSERT(readyState == OPEN);
+  mImpl->CloseConnection(closeCode, closeReason);
 }
 
 //-----------------------------------------------------------------------------
 // WebSocketImpl::nsIObserver
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 WebSocketImpl::Observe(nsISupports* aSubject,
                        const char* aTopic,
                        const char16_t* aData)
 {
   AssertIsOnMainThread();
 
-  int64_t readyState = ReadyState();
+  int64_t readyState = mWebSocket->ReadyState();
   if ((readyState == WebSocket::CLOSING) ||
       (readyState == WebSocket::CLOSED)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
   if (!mWebSocket->GetOwner() || window != mWebSocket->GetOwner()) {
     return NS_OK;
@@ -2230,26 +2165,26 @@ WebSocketImpl::Observe(nsISupports* aSub
 // WebSocketImpl::nsIRequest
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 WebSocketImpl::GetName(nsACString& aName)
 {
   AssertIsOnMainThread();
 
-  CopyUTF16toUTF8(mOriginalURL, aName);
+  CopyUTF16toUTF8(mWebSocket->mOriginalURL, aName);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketImpl::IsPending(bool* aValue)
 {
   AssertIsOnTargetThread();
 
-  int64_t readyState = ReadyState();
+  int64_t readyState = mWebSocket->ReadyState();
   *aValue = (readyState != WebSocket::CLOSED);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketImpl::GetStatus(nsresult* aStatus)
 {
   AssertIsOnTargetThread();
@@ -2318,17 +2253,17 @@ WebSocketImpl::Cancel(nsresult aStatus)
   return CancelInternal();
 }
 
 nsresult
 WebSocketImpl::CancelInternal()
 {
   AssertIsOnTargetThread();
 
-  int64_t readyState = ReadyState();
+  int64_t readyState = mWebSocket->ReadyState();
   if (readyState == WebSocket::CLOSING || readyState == WebSocket::CLOSED) {
     return NS_OK;
   }
 
   ConsoleError();
 
   return CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
 }
@@ -2479,10 +2414,16 @@ WebSocketImpl::IsOnCurrentThread(bool* a
 }
 
 bool
 WebSocketImpl::IsTargetThread() const
 {
   return NS_IsMainThread() == !mWorkerPrivate;
 }
 
+void
+WebSocket::AssertIsOnTargetThread() const
+{
+  MOZ_ASSERT(NS_IsMainThread() == !mWorkerPrivate);
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/content/base/src/WebSocket.h
+++ b/content/base/src/WebSocket.h
@@ -18,16 +18,18 @@
 #include "nsISupports.h"
 #include "nsISupportsUtils.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 #define DEFAULT_WS_SCHEME_PORT  80
 #define DEFAULT_WSS_SCHEME_PORT 443
 
+class nsIInputStream;
+
 namespace mozilla {
 namespace dom {
 
 namespace workers {
 class WorkerPrivate;
 }
 
 class File;
@@ -83,17 +85,17 @@ public: // WebIDL interface:
                                                  const nsAString& aUrl,
                                                  const Sequence<nsString>& aProtocols,
                                                  ErrorResult& rv);
 
   // webIDL: readonly attribute DOMString url
   void GetUrl(nsAString& aResult);
 
   // webIDL: readonly attribute unsigned short readyState;
-  uint16_t ReadyState() const;
+  uint16_t ReadyState();
 
   // webIDL: readonly attribute unsigned long bufferedAmount;
   uint32_t BufferedAmount() const;
 
   // webIDL: attribute Function? onopen;
   IMPL_EVENT_HANDLER(open)
 
   // webIDL: attribute Function? onerror;
@@ -129,16 +131,18 @@ public: // WebIDL interface:
             ErrorResult& aRv);
   void Send(const ArrayBufferView& aData,
             ErrorResult& aRv);
 
 private: // constructor && distructor
   explicit WebSocket(nsPIDOMWindow* aOwnerWindow);
   virtual ~WebSocket();
 
+  void SetReadyState(uint16_t aReadyState);
+
   // These methods actually do the dispatch for various events.
   nsresult CreateAndDispatchSimpleEvent(const nsAString& aName);
   nsresult CreateAndDispatchMessageEvent(const nsACString& aData,
                                          bool aIsBinary);
   nsresult CreateAndDispatchMessageEvent(JSContext* aCx,
                                          const nsACString& aData,
                                          bool aIsBinary);
   nsresult CreateAndDispatchCloseEvent(bool aWasClean,
@@ -152,24 +156,47 @@ private: // constructor && distructor
   // ATTENTION, when calling this method the object can be released
   // (and possibly collected).
   void DontKeepAliveAnyMore();
 
 private:
   WebSocket(const WebSocket& x) MOZ_DELETE;   // prevent bad usage
   WebSocket& operator=(const WebSocket& x) MOZ_DELETE;
 
+  void Send(nsIInputStream* aMsgStream,
+            const nsACString& aMsgString,
+            uint32_t aMsgLength,
+            bool aIsBinary,
+            ErrorResult& aRv);
+
+  void AssertIsOnTargetThread() const;
+
   // Raw pointer because this WebSocketImpl is created, managed and destroyed by
   // WebSocket.
   WebSocketImpl* mImpl;
 
-  // This is used just to check in which thread this object is used when mImpl
-  // is null.
   workers::WorkerPrivate* mWorkerPrivate;
 
   bool mKeepingAlive;
   bool mCheckMustKeepAlive;
+
+  uint32_t mOutgoingBufferedAmount;
+
+  // related to the WebSocket constructor steps
+  nsString mOriginalURL;
+  nsString mEffectiveURL;   // after redirects
+  nsCString mEstablishedExtensions;
+  nsCString mEstablishedProtocol;
+
+  dom::BinaryType mBinaryType;
+
+  // This mutex protects mReadyState that is the only variable that is used in
+  // different threads.
+  mozilla::Mutex mMutex;
+
+  // This value should not be used directly but use ReadyState() instead.
+  uint16_t mReadyState;
 };
 
 } //namespace dom
 } //namespace mozilla
 
 #endif
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -4698,20 +4698,16 @@ nsDocument::GetWindowInternal() const
     if (mDocumentContainer) {
       win = mDocumentContainer->GetWindow();
     }
   } else {
     win = do_QueryInterface(mScriptGlobalObject);
     if (win) {
       // mScriptGlobalObject is always the inner window, let's get the outer.
       win = win->GetOuterWindow();
-    } else if (mMasterDocument) {
-      // For script execution in the imported document we need the window of
-      // the master document.
-      win = mMasterDocument->GetWindow();
     }
   }
 
   return win;
 }
 
 nsScriptLoader*
 nsDocument::ScriptLoader()
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -788,16 +788,20 @@ GK_ATOM(onmousedown, "onmousedown")
 GK_ATOM(onmouseenter, "onmouseenter")
 GK_ATOM(onmouseleave, "onmouseleave")
 GK_ATOM(onmousemove, "onmousemove")
 GK_ATOM(onmouseout, "onmouseout")
 GK_ATOM(onmouseover, "onmouseover")
 GK_ATOM(onMozMouseHittest, "onMozMouseHittest")
 GK_ATOM(onmouseup, "onmouseup")
 GK_ATOM(onMozAfterPaint, "onMozAfterPaint")
+GK_ATOM(onmozbrowserafterkeydown, "onmozbrowserafterkeydown")
+GK_ATOM(onmozbrowserafterkeyup, "onmozbrowserafterkeyup")
+GK_ATOM(onmozbrowserbeforekeydown, "onmozbrowserbeforekeydown")
+GK_ATOM(onmozbrowserbeforekeyup, "onmozbrowserbeforekeyup")
 GK_ATOM(onmozfullscreenchange, "onmozfullscreenchange")
 GK_ATOM(onmozfullscreenerror, "onmozfullscreenerror")
 GK_ATOM(onmozpointerlockchange, "onmozpointerlockchange")
 GK_ATOM(onmozpointerlockerror, "onmozpointerlockerror")
 GK_ATOM(onmoztimechange, "onmoztimechange")
 GK_ATOM(onMozMousePixelScroll, "onMozMousePixelScroll")
 GK_ATOM(onMozScrolledAreaChanged, "onMozScrolledAreaChanged")
 GK_ATOM(onmoznetworkupload, "onmoznetworkupload")
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -287,17 +287,18 @@ nsScriptLoader::StartLoad(nsScriptLoadRe
                          : static_cast<nsISupports *>(mDocument);
   nsresult rv = ShouldLoadScript(mDocument, context, aRequest->mURI, aType);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
 
-  nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mDocument->GetWindow()));
+  nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mDocument->MasterDocument()->GetWindow()));
+
   if (!window) {
     return NS_ERROR_NULL_POINTER;
   }
 
   nsIDocShell *docshell = window->GetDocShell();
 
   nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
 
--- a/content/base/test/mochitest.ini
+++ b/content/base/test/mochitest.ini
@@ -654,8 +654,10 @@ disabled = Disabled due to making the ha
 support-files = file_bug503473-frame.sjs
 [test_bug1011748.html]
 skip-if = buildapp == 'b2g' || e10s
 support-files = file_bug1011748_redirect.sjs file_bug1011748_OK.sjs
 [test_bug1025933.html]
 [test_element.matches.html]
 [test_user_select.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g'
+[test_bug1081686.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s
copy from content/base/test/test_websocket_hello.html
copy to content/base/test/test_bug1081686.html
--- a/content/base/test/test_websocket_hello.html
+++ b/content/base/test/test_bug1081686.html
@@ -1,42 +1,63 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 -->
 <head>
-  <title>Basic websocket test</title>
+  <title>bug 1081686</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body onload="testWebSocket()">
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=472529">Mozilla Bug </a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var ws;
 
+function forcegc()
+{
+  SpecialPowers.forceGC();
+  SpecialPowers.gc();
+  setTimeout(function()
+  {
+    SpecialPowers.gc();
+  }, 0);
+}
+
 function testWebSocket () {
   ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket_hello");
   ws.onopen = function(e) {
     ws.send("data");
   }
   ws.onclose = function(e) {
+    forcegc();
+    setTimeout(function() {
+      is(ws.readyState, 3, 'WebSocket is closed');
+      is(ws.bufferedAmount, 0, 'WebSocket.bufferedAmount should be empty.');
+      is(ws.binaryType, 'blob', 'WebSocket.binaryType is blob');
+      ws.binaryType = 'arraybuffer';
+      is(ws.binaryType, 'arraybuffer', 'WebSocket.binaryType is arraybuffer');
+      is(ws.url, 'ws://mochi.test:8888/tests/content/base/test/file_websocket_hello', 'WebSocket.url is correct');
+      ws.close();
+      ws.send('foobar');
+      SimpleTest.finish();
+    }, 1000);
   }
+
   ws.onerror = function(e) {
     ok(false, "onerror called!");
     SimpleTest.finish();
   }
   ws.onmessage = function(e) {
     is(e.data, "Hello world!", "Wrong data");
     ws.close();
-    SimpleTest.finish();
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 <div>
--- a/content/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/content/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -440,39 +440,42 @@ MediaEngineGonkVideoSource::OnTakePictur
   // Create a main thread runnable to generate a blob and call all current queued
   // PhotoCallbacks.
   class GenerateBlobRunnable : public nsRunnable {
   public:
     GenerateBlobRunnable(nsTArray<nsRefPtr<PhotoCallback>>& aCallbacks,
                          uint8_t* aData,
                          uint32_t aLength,
                          const nsAString& aMimeType)
+      : mPhotoDataLength(aLength)
     {
       mCallbacks.SwapElements(aCallbacks);
-      mPhoto.AppendElements(aData, aLength);
+      mPhotoData = (uint8_t*) moz_malloc(aLength);
+      memcpy(mPhotoData, aData, mPhotoDataLength);
       mMimeType = aMimeType;
     }
 
     NS_IMETHOD Run()
     {
       nsRefPtr<dom::File> blob =
-        dom::File::CreateMemoryFile(nullptr, mPhoto.Elements(), mPhoto.Length(), mMimeType);
+        dom::File::CreateMemoryFile(nullptr, mPhotoData, mPhotoDataLength, mMimeType);
       uint32_t callbackCounts = mCallbacks.Length();
       for (uint8_t i = 0; i < callbackCounts; i++) {
         nsRefPtr<dom::File> tempBlob = blob;
         mCallbacks[i]->PhotoComplete(tempBlob.forget());
       }
       // PhotoCallback needs to dereference on main thread.
       mCallbacks.Clear();
       return NS_OK;
     }
 
     nsTArray<nsRefPtr<PhotoCallback>> mCallbacks;
-    nsTArray<uint8_t> mPhoto;
+    uint8_t* mPhotoData;
     nsString mMimeType;
+    uint32_t mPhotoDataLength;
   };
 
   // All elements in mPhotoCallbacks will be swapped in GenerateBlobRunnable
   // constructor. This captured image will be sent to all the queued
   // PhotoCallbacks in this runnable.
   MonitorAutoLock lock(mMonitor);
   if (mPhotoCallbacks.Length()) {
     NS_DispatchToMainThread(
--- a/dom/apps/PermissionsTable.jsm
+++ b/dom/apps/PermissionsTable.jsm
@@ -488,16 +488,22 @@ this.PermissionsTable =  { geolocation: 
                              access: ["read", "write"],
                              additional: ["settings-api"]
                            },
                            "engineering-mode": {
                              app: DENY_ACTION,
                              trusted: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
+                           },
+                           "before-after-keyboard-event": {
+                             app: DENY_ACTION,
+                             trusted: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
                            }
                          };
 
 /**
  * Append access modes to the permission name as suffixes.
  *   e.g. permission name 'contacts' with ['read', 'write'] =
  *   ['contacts-read', contacts-write']
  * @param string aPermName
--- a/dom/base/DOMCursor.h
+++ b/dom/base/DOMCursor.h
@@ -38,16 +38,21 @@ public:
   void Reset();
   void FireDone();
 
 protected:
   ~DOMCursor() {}
 
 private:
   DOMCursor() MOZ_DELETE;
+  // Calling Then() on DOMCursor is a mistake, since the DOMCursor object
+  // should not have a .then() method from JS' point of view.
+  already_AddRefed<mozilla::dom::Promise>
+  Then(JSContext* aCx, AnyCallback* aResolveCallback,
+       AnyCallback* aRejectCallback, ErrorResult& aRv) MOZ_DELETE;
 
   nsCOMPtr<nsICursorContinueCallback> mCallback;
   bool mFinished;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/DOMRequest.cpp
+++ b/dom/base/DOMRequest.cpp
@@ -5,41 +5,54 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMRequest.h"
 
 #include "DOMError.h"
 #include "nsThreadUtils.h"
 #include "DOMCursor.h"
 #include "nsIDOMEvent.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ScriptSettings.h"
 
+using mozilla::dom::AnyCallback;
 using mozilla::dom::DOMError;
 using mozilla::dom::DOMRequest;
 using mozilla::dom::DOMRequestService;
 using mozilla::dom::DOMCursor;
+using mozilla::dom::Promise;
 using mozilla::AutoSafeJSContext;
 
 DOMRequest::DOMRequest(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow->IsInnerWindow() ?
                            aWindow : aWindow->GetCurrentInnerWindow())
   , mResult(JSVAL_VOID)
   , mDone(false)
 {
 }
 
+DOMRequest::~DOMRequest()
+{
+  mResult.setUndefined();
+  mozilla::DropJSObjects(this);
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest,
                                                 DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
   tmp->mResult = JSVAL_VOID;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest,
                                                DOMEventTargetHelper)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
   // DOMEventTargetHelper does it for us.
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
@@ -102,56 +115,72 @@ DOMRequest::FireSuccess(JS::Handle<JS::V
 
   mDone = true;
   if (aResult.isGCThing()) {
     RootResultVal();
   }
   mResult = aResult;
 
   FireEvent(NS_LITERAL_STRING("success"), false, false);
+
+  if (mPromise) {
+    mPromise->MaybeResolve(mResult);
+  }
 }
 
 void
 DOMRequest::FireError(const nsAString& aError)
 {
   NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
   NS_ASSERTION(!mError, "mError shouldn't have been set!");
   NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
 
   mDone = true;
   mError = new DOMError(GetOwner(), aError);
 
   FireEvent(NS_LITERAL_STRING("error"), true, true);
+
+  if (mPromise) {
+    mPromise->MaybeRejectBrokenly(mError);
+  }
 }
 
 void
 DOMRequest::FireError(nsresult aError)
 {
   NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
   NS_ASSERTION(!mError, "mError shouldn't have been set!");
   NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
 
   mDone = true;
   mError = new DOMError(GetOwner(), aError);
 
   FireEvent(NS_LITERAL_STRING("error"), true, true);
+
+  if (mPromise) {
+    mPromise->MaybeRejectBrokenly(mError);
+  }
 }
 
 void
 DOMRequest::FireDetailedError(DOMError* aError)
 {
   NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
   NS_ASSERTION(!mError, "mError shouldn't have been set!");
   NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
   NS_ASSERTION(aError, "No detailed error provided");
 
   mDone = true;
   mError = aError;
 
   FireEvent(NS_LITERAL_STRING("error"), true, true);
+
+  if (mPromise) {
+    mPromise->MaybeRejectBrokenly(mError);
+  }
 }
 
 void
 DOMRequest::FireEvent(const nsAString& aType, bool aBubble, bool aCancelable)
 {
   if (NS_FAILED(CheckInnerWindowCorrectness())) {
     return;
   }
@@ -170,16 +199,41 @@ DOMRequest::FireEvent(const nsAString& a
 }
 
 void
 DOMRequest::RootResultVal()
 {
   mozilla::HoldJSObjects(this);
 }
 
+already_AddRefed<Promise>
+DOMRequest::Then(JSContext* aCx, AnyCallback* aResolveCallback,
+                 AnyCallback* aRejectCallback, mozilla::ErrorResult& aRv)
+{
+  if (!mPromise) {
+    mPromise = Promise::Create(DOMEventTargetHelper::GetParentObject(), aRv);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+    if (mDone) {
+      // Since we create mPromise lazily, it's possible that the DOMRequest object
+      // has already fired its success/error event.  In that case we should
+      // manually resolve/reject mPromise here.  mPromise will take care of
+      // calling the callbacks on |promise| as needed.
+      if (mError) {
+        mPromise->MaybeRejectBrokenly(mError);
+      } else {
+        mPromise->MaybeResolve(mResult);
+      }
+    }
+  }
+
+  return mPromise->Then(aCx, aResolveCallback, aRejectCallback, aRv);
+}
+
 NS_IMPL_ISUPPORTS(DOMRequestService, nsIDOMRequestService)
 
 NS_IMETHODIMP
 DOMRequestService::CreateRequest(nsIDOMWindow* aWindow,
                                  nsIDOMDOMRequest** aRequest)
 {
   nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aWindow));
   NS_ENSURE_STATE(win);
@@ -248,17 +302,17 @@ public:
   // Due to the fact that initialization can fail during shutdown (since we
   // can't fetch a js context), set up an initiatization function to make sure
   // we can return the failure appropriately
   static nsresult
   Dispatch(DOMRequest* aRequest,
            const JS::Value& aResult)
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-    AutoSafeJSContext cx;
+    mozilla::ThreadsafeAutoSafeJSContext cx;
     nsRefPtr<FireSuccessAsyncTask> asyncTask = new FireSuccessAsyncTask(cx, aRequest, aResult);
     if (NS_FAILED(NS_DispatchToMainThread(asyncTask))) {
       NS_WARNING("Failed to dispatch to main thread!");
       return NS_ERROR_FAILURE;
     }
     return NS_OK;
   }
 
--- a/dom/base/DOMRequest.h
+++ b/dom/base/DOMRequest.h
@@ -11,24 +11,31 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/DOMRequestBinding.h"
 
 #include "nsCOMPtr.h"
 
 namespace mozilla {
+
+class ErrorResult;
+
 namespace dom {
 
+class AnyCallback;
+class Promise;
+
 class DOMRequest : public DOMEventTargetHelper,
                    public nsIDOMDOMRequest
 {
 protected:
   JS::Heap<JS::Value> mResult;
   nsRefPtr<DOMError> mError;
+  nsRefPtr<Promise> mPromise;
   bool mDone;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMDOMREQUEST
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(DOMRequest,
@@ -62,30 +69,29 @@ public:
     NS_ASSERTION(mDone || !mError,
                  "Error should be null when pending");
     return mError;
   }
 
   IMPL_EVENT_HANDLER(success)
   IMPL_EVENT_HANDLER(error)
 
+  already_AddRefed<mozilla::dom::Promise>
+  Then(JSContext* aCx, AnyCallback* aResolveCallback,
+       AnyCallback* aRejectCallback, mozilla::ErrorResult& aRv);
 
   void FireSuccess(JS::Handle<JS::Value> aResult);
   void FireError(const nsAString& aError);
   void FireError(nsresult aError);
   void FireDetailedError(DOMError* aError);
 
   explicit DOMRequest(nsPIDOMWindow* aWindow);
 
 protected:
-  virtual ~DOMRequest()
-  {
-    mResult = JSVAL_VOID;
-    mozilla::DropJSObjects(this);
-  }
+  virtual ~DOMRequest();
 
   void FireEvent(const nsAString& aType, bool aBubble, bool aCancelable);
 
   void RootResultVal();
 };
 
 class DOMRequestService MOZ_FINAL : public nsIDOMRequestService
 {
--- a/dom/base/test/test_domcursor.html
+++ b/dom/base/test/test_domcursor.html
@@ -46,16 +46,17 @@ var tests = [
     });
     ok("result" in req, "cursor has result");
     ok("error" in req, "cursor has error");
     ok("onsuccess" in req, "cursor has onsuccess");
     ok("onerror" in req, "cursor has onerror");
     ok("readyState" in req, "cursor has readyState");
     ok("done" in req, "cursor has finished");
     ok("continue" in req, "cursor has continue");
+    ok(!("then" in req), "cursor should not have a then method");
 
     is(req.readyState, "pending", "readyState is pending");
     is(req.result, undefined, "result is undefined");
     is(req.onsuccess, null, "onsuccess is null");
     is(req.onerror, null, "onerror is null");
     next();
   },
   function() {
--- a/dom/base/test/test_domrequest.html
+++ b/dom/base/test/test_domrequest.html
@@ -12,65 +12,218 @@
 </div>
 <pre id="test">
 <script class="testbody" type="application/javascript;version=1.7">
 "use strict";
 
 var reqserv = SpecialPowers.getDOMRequestService();
 ok("createRequest" in reqserv, "appears to be a service");
 
-// create a request
-var req = reqserv.createRequest(window);
-ok("result" in req, "request has result");
-ok("error" in req, "request has error");
-ok("onsuccess" in req, "request has onsuccess");
-ok("onerror" in req, "request has onerror");
-ok("readyState" in req, "request has readyState");
+function testBasics() {
+  // create a request
+  var req = reqserv.createRequest(window);
+  ok("result" in req, "request has result");
+  ok("error" in req, "request has error");
+  ok("onsuccess" in req, "request has onsuccess");
+  ok("onerror" in req, "request has onerror");
+  ok("readyState" in req, "request has readyState");
+  ok("then" in req, "request has then");
+
+  is(req.readyState, "pending", "readyState is pending");
+  is(req.result, undefined, "result is undefined");
+  is(req.onsuccess, null, "onsuccess is null");
+  is(req.onerror, null, "onerror is null");
+
+  runTest();
+}
 
-is(req.readyState, "pending", "readyState is pending");
-is(req.result, undefined, "result is undefined");
-is(req.onsuccess, null, "onsuccess is null");
-is(req.onerror, null, "onerror is null");
+function testSuccess() {
+  // fire success
+  var req = reqserv.createRequest(window);
+  var ev = null;
+  req.onsuccess = function(e) {
+    ev = e;
+  }
+  var result = null;
+  var promise = req.then(function(r) {
+    is(r, "my result", "correct result when resolving the promise");
+    result = r;
+    runTest();
+  }, function(e) {
+    ok(false, "promise should not be rejected");
+    runTest();
+  });
+  ok(promise instanceof Promise, "then() should return a Promise");
+  reqserv.fireSuccess(req, "my result");
+  ok(ev, "got success event");
+  is(ev.type, "success", "correct type during success");
+  is(ev.target, req, "correct target during success");
+  is(req.readyState, "done", "correct readyState after success");
+  is(req.error, null, "correct error after success");
+  is(req.result, "my result", "correct result after success");
+  is(result, null, "Promise should not be resolved synchronously");
+}
 
-// fire success
-var ev = null;
-req.onsuccess = function(e) {
-  ev = e;
+function testError() {
+  // fire error
+  var req = reqserv.createRequest(window);
+  var ev = null;
+  req.onerror = function(e) {
+    ev = e;
+  }
+  var error = null;
+  var promise = req.then(function(r) {
+    ok(false, "promise should not be resolved");
+    runTest();
+  }, function(e) {
+    ok(e instanceof DOMError, "got error rejection");
+    ok(e === req.error, "got correct error when rejecting the promise");
+    error = e;
+    runTest();
+  });
+  ok(promise instanceof Promise, "then() should return a Promise");
+  reqserv.fireError(req, "OhMyError");
+  ok(ev, "got error event");
+  is(ev.type, "error", "correct type during error");
+  is(ev.target, req, "correct target during error");
+  is(req.readyState, "done", "correct readyState after error");
+  is(req.error.name, "OhMyError", "correct error after error");
+  is(req.result, undefined, "correct result after error");
+  is(error, null, "Promise should not be rejected synchronously");
 }
-reqserv.fireSuccess(req, "my result");
-ok(ev, "got success event");
-is(ev.type, "success", "correct type during success");
-is(ev.target, req, "correct target during success");
-is(req.readyState, "done", "correct readyState after success");
-is(req.error, null, "correct error after success");
-is(req.result, "my result", "correct result after success");
+
+function testDetailedError() {
+  // fire detailed error
+  var req = reqserv.createRequest(window);
+  var ev = null;
+  req.onerror = function(e) {
+    ev = e;
+  };
+  var error = null;
+  var promise = req.then(function(r) {
+    ok(false, "promise should not be resolved");
+    runTest();
+  }, function(e) {
+    ok(e instanceof DOMError, "got error rejection");
+    ok(e === req.error, "got correct error when rejecting the promise");
+    error = e;
+    runTest();
+  });
+  ok(promise instanceof Promise, "then() should return a Promise");
+  reqserv.fireDetailedError(req, new DOMError("detailedError"));
+  ok(ev, "got error event");
+  is(ev.type, "error", "correct type during error");
+  is(ev.target, req, "correct target during error");
+  is(req.readyState, "done", "correct readyState after error");
+  is(req.error.name, "detailedError", "correct error after error");
+  is(req.result, undefined, "correct result after error");
+  is(error, null, "Promise should not be rejected synchronously");
+}
 
-// fire error
-req = reqserv.createRequest(window);
-ev = null;
-req.onerror = function(e) {
-  ev = e;
+function testThenAfterSuccess() {
+  // fire success
+  var req = reqserv.createRequest(window);
+  var ev = null;
+  req.onsuccess = function(e) {
+    ev = e;
+  }
+  reqserv.fireSuccess(req, "my result");
+  ok(ev, "got success event");
+  is(ev.type, "success", "correct type during success");
+  is(ev.target, req, "correct target during success");
+  is(req.readyState, "done", "correct readyState after success");
+  is(req.error, null, "correct error after success");
+  is(req.result, "my result", "correct result after success");
+  var result = null;
+  var promise = req.then(function(r) {
+    is(r, "my result", "correct result when resolving the promise");
+    result = r;
+    runTest();
+  }, function(e) {
+    ok(false, "promise should not be rejected");
+    runTest();
+  });
+  ok(promise instanceof Promise, "then() should return a Promise");
+  is(result, null, "Promise should not be resolved synchronously");
 }
-reqserv.fireError(req, "OhMyError");
-ok(ev, "got error event");
-is(ev.type, "error", "correct type during error");
-is(ev.target, req, "correct target during error");
-is(req.readyState, "done", "correct readyState after error");
-is(req.error.name, "OhMyError", "correct error after error");
-is(req.result, undefined, "correct result after error");
 
-// fire detailed error
-req = reqserv.createRequest(window);
-ev = null;
-req.onerror = function(e) {
-  ev = e;
-};
-reqserv.fireDetailedError(req, new DOMError("detailedError"));
-ok(ev, "got error event");
-is(ev.type, "error", "correct type during error");
-is(ev.target, req, "correct target during error");
-is(req.readyState, "done", "correct readyState after error");
-is(req.error.name, "detailedError", "correct error after error");
-is(req.result, undefined, "correct result after error");
+function testThenAfterError() {
+  // fire error
+  var req = reqserv.createRequest(window);
+  var ev = null;
+  req.onerror = function(e) {
+    ev = e;
+  }
+  reqserv.fireError(req, "OhMyError");
+  ok(ev, "got error event");
+  is(ev.type, "error", "correct type during error");
+  is(ev.target, req, "correct target during error");
+  is(req.readyState, "done", "correct readyState after error");
+  is(req.error.name, "OhMyError", "correct error after error");
+  is(req.result, undefined, "correct result after error");
+  var error = null;
+  var promise = req.then(function(r) {
+    ok(false, "promise should not be resolved");
+    runTest();
+  }, function(e) {
+    ok(e instanceof DOMError, "got error rejection");
+    ok(e === req.error, "got correct error when rejecting the promise");
+    error = e;
+    runTest();
+  });
+  ok(promise instanceof Promise, "then() should return a Promise");
+  is(error, null, "Promise should not be rejected synchronously");
+}
+
+function testDetailedError() {
+  // fire detailed error
+  var req = reqserv.createRequest(window);
+  var ev = null;
+  req.onerror = function(e) {
+    ev = e;
+  };
+  var error = null;
+  var promise = req.then(function(r) {
+    ok(false, "promise should not be resolved");
+    runTest();
+  }, function(e) {
+    ok(e instanceof DOMError, "got error rejection");
+    ok(e === req.error, "got correct error when rejecting the promise");
+    error = e;
+    runTest();
+  });
+  ok(promise instanceof Promise, "then() should return a Promise");
+  reqserv.fireDetailedError(req, new DOMError("detailedError"));
+  ok(ev, "got error event");
+  is(ev.type, "error", "correct type during error");
+  is(ev.target, req, "correct target during error");
+  is(req.readyState, "done", "correct readyState after error");
+  is(req.error.name, "detailedError", "correct error after error");
+  is(req.result, undefined, "correct result after error");
+  is(error, null, "Promise should not be rejected synchronously");
+}
+
+var tests = [
+  testBasics,
+  testSuccess,
+  testError,
+  testDetailedError,
+  testThenAfterSuccess,
+  testThenAfterError,
+];
+
+function runTest() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTest();
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -347,16 +347,20 @@ DOMInterfaces = {
 'DOMRectList': {
     'headerFile': 'mozilla/dom/DOMRect.h',
 },
 
 'DOMRectReadOnly': {
     'headerFile': 'mozilla/dom/DOMRect.h',
 },
 
+'DOMRequest': {
+    'implicitJSContext': [ 'then' ],
+},
+
 'DOMSettableTokenList': {
     'nativeType': 'nsDOMSettableTokenList',
 },
 
 'DOMStringMap': {
     'nativeType': 'nsDOMStringMap'
 },
 
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -231,16 +231,34 @@ ToJSValue(JSContext* aCx,
 inline bool
 ToJSValue(JSContext* aCx, JS::Handle<JS::Value> aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   aValue.set(aArgument);
   return MaybeWrapValue(aCx, aValue);
 }
 
+// Accept existing JS values on the Heap (which may not be same-compartment with us
+inline bool
+ToJSValue(JSContext* aCx, const JS::Heap<JS::Value>& aArgument,
+          JS::MutableHandle<JS::Value> aValue)
+{
+  aValue.set(aArgument);
+  return MaybeWrapValue(aCx, aValue);
+}
+
+// Accept existing rooted JS values (which may not be same-compartment with us
+inline bool
+ToJSValue(JSContext* aCx, const JS::Rooted<JS::Value>& aArgument,
+          JS::MutableHandle<JS::Value> aValue)
+{
+  aValue.set(aArgument);
+  return MaybeWrapValue(aCx, aValue);
+}
+
 // Accept nsresult, for use in rejections, and create an XPCOM
 // exception object representing that nsresult.
 bool
 ToJSValue(JSContext* aCx,
           nsresult aArgument,
           JS::MutableHandle<JS::Value> aValue);
 
 // Accept pointers to other things we accept
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -109,18 +109,18 @@
 #include "nsIScreenManager.h"
 #include "nsFilterInstance.h"
 #include "nsSVGLength2.h"
 #include "nsDeviceContext.h"
 #include "nsFontMetrics.h"
 
 #undef free // apparently defined by some windows header, clashing with a free()
             // method in SkTypes.h
+#include "SkiaGLGlue.h"
 #ifdef USE_SKIA
-#include "SkiaGLGlue.h"
 #include "SurfaceTypes.h"
 #endif
 
 using mozilla::gl::GLContext;
 using mozilla::gl::SkiaGLGlue;
 using mozilla::gl::GLContextProvider;
 
 #ifdef XP_WIN
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -136,67 +136,77 @@ WebGL2Context::TexStorage2D(GLenum targe
 
     // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants.
     if (target != LOCAL_GL_TEXTURE_2D && target != LOCAL_GL_TEXTURE_CUBE_MAP)
         return ErrorInvalidEnum("texStorage2D: target is not TEXTURE_2D or TEXTURE_CUBE_MAP");
 
     if (!ValidateTexStorage(target, levels, internalformat, width, height, 1, "texStorage2D"))
         return;
 
+    GetAndFlushUnderlyingGLErrors();
+    gl->fTexStorage2D(target, levels, internalformat, width, height);
+    GLenum error = GetAndFlushUnderlyingGLErrors();
+    if (error) {
+        return GenerateWarning("texStorage2D generated error %s", ErrorName(error));
+    }
+
     WebGLTexture* tex = activeBoundTextureForTarget(target);
     tex->SetImmutable();
 
     const size_t facesCount = (target == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
     GLsizei w = width;
     GLsizei h = height;
     for (size_t l = 0; l < size_t(levels); l++) {
         for (size_t f = 0; f < facesCount; f++) {
             tex->SetImageInfo(TexImageTargetForTargetAndFace(target, f),
                               l, w, h, 1,
                               internalformat,
                               WebGLImageDataStatus::UninitializedImageData);
         }
         w = std::max(1, w / 2);
         h = std::max(1, h / 2);
     }
-
-    gl->fTexStorage2D(target, levels, internalformat, width, height);
 }
 
 void
 WebGL2Context::TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat,
                             GLsizei width, GLsizei height, GLsizei depth)
 {
     if (IsContextLost())
         return;
 
     // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants.
     if (target != LOCAL_GL_TEXTURE_3D)
         return ErrorInvalidEnum("texStorage3D: target is not TEXTURE_3D");
 
     if (!ValidateTexStorage(target, levels, internalformat, width, height, depth, "texStorage3D"))
         return;
 
+    GetAndFlushUnderlyingGLErrors();
+    gl->fTexStorage3D(target, levels, internalformat, width, height, depth);
+    GLenum error = GetAndFlushUnderlyingGLErrors();
+    if (error) {
+        return GenerateWarning("texStorage3D generated error %s", ErrorName(error));
+    }
+
     WebGLTexture* tex = activeBoundTextureForTarget(target);
     tex->SetImmutable();
 
     GLsizei w = width;
     GLsizei h = height;
     GLsizei d = depth;
     for (size_t l = 0; l < size_t(levels); l++) {
         tex->SetImageInfo(TexImageTargetForTargetAndFace(target, 0),
                           l, w, h, d,
                           internalformat,
                           WebGLImageDataStatus::UninitializedImageData);
         w = std::max(1, w >> 1);
         h = std::max(1, h >> 1);
         d = std::max(1, d >> 1);
     }
-
-    gl->fTexStorage3D(target, levels, internalformat, width, height, depth);
 }
 
 void
 WebGL2Context::TexSubImage3D(GLenum rawTarget, GLint level,
                              GLint xoffset, GLint yoffset, GLint zoffset,
                              GLsizei width, GLsizei height, GLsizei depth,
                              GLenum format, GLenum type, const Nullable<dom::ArrayBufferView>& pixels,
                              ErrorResult& rv)
@@ -267,18 +277,29 @@ WebGL2Context::TexSubImage3D(GLenum rawT
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
 
     uint32_t bytesNeeded = checked_neededByteLength.value();
 
     if (dataLength < bytesNeeded)
         return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, dataLength);
 
-    if (imageInfo.HasUninitializedImageData())
-        tex->DoDeferredImageInitialization(texImageTarget, level);
+    if (imageInfo.HasUninitializedImageData()) {
+        bool coversWholeImage = xoffset == 0 &&
+                                yoffset == 0 &&
+                                zoffset == 0 &&
+                                width == imageInfo.Width() &&
+                                height == imageInfo.Height() &&
+                                depth == imageInfo.Depth();
+        if (coversWholeImage) {
+            tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
+        } else {
+            tex->DoDeferredImageInitialization(texImageTarget, level);
+        }
+    }
 
     GLenum driverType = LOCAL_GL_NONE;
     GLenum driverInternalFormat = LOCAL_GL_NONE;
     GLenum driverFormat = LOCAL_GL_NONE;
     DriverFormatsFromEffectiveInternalFormat(gl,
                                              existingEffectiveInternalFormat,
                                              &driverInternalFormat,
                                              &driverFormat,
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -589,17 +589,25 @@ WebGLContext::CopyTexSubImage2D(GLenum r
 
     if (yoffset + height > texHeight || yoffset + height < 0)
       return ErrorInvalidValue("copyTexSubImage2D: yoffset+height is too large");
 
     if (!mBoundFramebuffer)
         ClearBackbufferIfNeeded();
 
     if (imageInfo.HasUninitializedImageData()) {
-        tex->DoDeferredImageInitialization(texImageTarget, level);
+        bool coversWholeImage = xoffset == 0 &&
+                                yoffset == 0 &&
+                                width == texWidth &&
+                                height == texHeight;
+        if (coversWholeImage) {
+            tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
+        } else {
+            tex->DoDeferredImageInitialization(texImageTarget, level);
+        }
     }
 
     TexInternalFormat internalformat;
     TexType type;
     UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(imageInfo.EffectiveInternalFormat(),
                                              &internalformat, &type);
     return CopyTexSubImage2D_base(texImageTarget, level, internalformat, xoffset, yoffset, x, y, width, height, true);
 }
@@ -3409,18 +3417,27 @@ WebGLContext::CompressedTexSubImage2D(GL
                                   xoffset, yoffset,
                                   width, height,
                                   levelInfo.Width(), levelInfo.Height(),
                                   func, dims))
     {
         return;
     }
 
-    if (levelInfo.HasUninitializedImageData())
-        tex->DoDeferredImageInitialization(texImageTarget, level);
+    if (levelInfo.HasUninitializedImageData()) {
+        bool coversWholeImage = xoffset == 0 &&
+                                yoffset == 0 &&
+                                width == levelInfo.Width() &&
+                                height == levelInfo.Height();
+        if (coversWholeImage) {
+            tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
+        } else {
+            tex->DoDeferredImageInitialization(texImageTarget, level);
+        }
+    }
 
     MakeContextCurrent();
     gl->fCompressedTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, width, height, internalformat, byteLength, view.Data());
 }
 
 JS::Value
 WebGLContext::GetShaderParameter(WebGLShader *shader, GLenum pname)
 {
@@ -3936,19 +3953,27 @@ WebGLContext::TexSubImage2D_base(TexImag
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
 
     uint32_t bytesNeeded = checked_neededByteLength.value();
 
     if (byteLength < bytesNeeded)
         return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
 
-    if (imageInfo.HasUninitializedImageData())
-        tex->DoDeferredImageInitialization(texImageTarget, level);
-
+    if (imageInfo.HasUninitializedImageData()) {
+        bool coversWholeImage = xoffset == 0 &&
+                                yoffset == 0 &&
+                                width == imageInfo.Width() &&
+                                height == imageInfo.Height();
+        if (coversWholeImage) {
+            tex->SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData);
+        } else {
+            tex->DoDeferredImageInitialization(texImageTarget, level);
+        }
+    }
     MakeContextCurrent();
 
     size_t   srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
     uint32_t dstTexelSize = GetBitsPerTexel(existingEffectiveInternalFormat) / 8;
     size_t   dstPlainRowSize = dstTexelSize * width;
     // There are checks above to ensure that this won't overflow.
     size_t   dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
 
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -108,16 +108,17 @@ public:
                    is_pot_assuming_nonnegative(mHeight); // negative sizes should never happen (caught in texImage2D...)
         }
         bool HasUninitializedImageData() const {
             return mImageDataStatus == WebGLImageDataStatus::UninitializedImageData;
         }
         size_t MemoryUsage() const;
 
         TexInternalFormat EffectiveInternalFormat() const { return mEffectiveInternalFormat; }
+        GLsizei Depth() const { return mDepth; }
 
     protected:
         /*
          * This is the "effective internal format" of the texture,
          * an official OpenGL spec concept, see
          * OpenGL ES 3.0.3 spec, section 3.8.3, page 126 and below.
          */
         TexInternalFormat mEffectiveInternalFormat;
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -124,16 +124,17 @@ include('/ipc/chromium/chromium-config.m
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/content/base/src',
     '/content/html/content/src',
     '/content/svg/content/src',
     '/content/xul/content/src',
     '/dom/base',
+    '/gfx/gl',
     '/image/src',
     '/js/xpconnect/src',
     '/layout/generic',
     '/layout/style',
     '/layout/xul',
 ]
 
 CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
new file mode 100644
--- /dev/null
+++ b/dom/events/BeforeAfterKeyboardEvent.cpp
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; 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 "mozilla/dom/BeforeAfterKeyboardEvent.h"
+#include "mozilla/TextEvents.h"
+#include "prtime.h"
+
+namespace mozilla {
+namespace dom {
+
+BeforeAfterKeyboardEvent::BeforeAfterKeyboardEvent(
+                                       EventTarget* aOwner,
+                                       nsPresContext* aPresContext,
+                                       InternalBeforeAfterKeyboardEvent* aEvent)
+  : KeyboardEvent(aOwner, aPresContext,
+                  aEvent ? aEvent :
+                           new InternalBeforeAfterKeyboardEvent(false, 0,
+                                                                nullptr))
+{
+  MOZ_ASSERT(mEvent->mClass == eBeforeAfterKeyboardEventClass,
+             "event type mismatch eBeforeAfterKeyboardEventClass");
+
+  if (!aEvent) {
+    mEventIsInternal = true;
+    mEvent->time = PR_Now();
+  }
+}
+
+// static
+already_AddRefed<BeforeAfterKeyboardEvent>
+BeforeAfterKeyboardEvent::Constructor(
+                            EventTarget* aOwner,
+                            const nsAString& aType,
+                            const BeforeAfterKeyboardEventInit& aParam)
+{
+  nsRefPtr<BeforeAfterKeyboardEvent> event =
+    new BeforeAfterKeyboardEvent(aOwner, nullptr, nullptr);
+  ErrorResult rv;
+  event->InitWithKeyboardEventInit(aOwner, aType, aParam, rv);
+  NS_WARN_IF(rv.Failed());
+
+  event->mEvent->AsBeforeAfterKeyboardEvent()->mEmbeddedCancelled =
+    aParam.mEmbeddedCancelled;
+
+  return event.forget();
+}
+
+// static
+already_AddRefed<BeforeAfterKeyboardEvent>
+BeforeAfterKeyboardEvent::Constructor(
+                            const GlobalObject& aGlobal,
+                            const nsAString& aType,
+                            const BeforeAfterKeyboardEventInit& aParam,
+                            ErrorResult& aRv)
+{
+  nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
+  return Constructor(owner, aType, aParam);
+}
+
+Nullable<bool>
+BeforeAfterKeyboardEvent::GetEmbeddedCancelled()
+{
+  nsAutoString type;
+  GetType(type);
+  if (type.EqualsLiteral("mozbrowserafterkeydown") ||
+      type.EqualsLiteral("mozbrowserafterkeyup")) {
+    return mEvent->AsBeforeAfterKeyboardEvent()->mEmbeddedCancelled;
+  }
+  return Nullable<bool>();
+}
+
+} // namespace dom
+} // namespace mozilla
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+nsresult
+NS_NewDOMBeforeAfterKeyboardEvent(nsIDOMEvent** aInstancePtrResult,
+                                  EventTarget* aOwner,
+                                  nsPresContext* aPresContext,
+                                  InternalBeforeAfterKeyboardEvent* aEvent)
+{
+  BeforeAfterKeyboardEvent* it =
+    new BeforeAfterKeyboardEvent(aOwner, aPresContext, aEvent);
+
+  NS_ADDREF(it);
+  *aInstancePtrResult = static_cast<Event*>(it);
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/events/BeforeAfterKeyboardEvent.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#ifndef mozilla_dom_BeforeAfterKeyboardEvent_h_
+#define mozilla_dom_BeforeAfterKeyboardEvent_h_
+
+#include "mozilla/dom/KeyboardEvent.h"
+#include "mozilla/dom/BeforeAfterKeyboardEventBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+class BeforeAfterKeyboardEvent : public KeyboardEvent
+{
+public:
+  BeforeAfterKeyboardEvent(EventTarget* aOwner,
+                           nsPresContext* aPresContext,
+                           InternalBeforeAfterKeyboardEvent* aEvent);
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
+  {
+    return BeforeAfterKeyboardEventBinding::Wrap(aCx, this);
+  }
+
+  static already_AddRefed<BeforeAfterKeyboardEvent>
+  Constructor(const GlobalObject& aGlobal,
+              const nsAString& aType,
+              const BeforeAfterKeyboardEventInit& aParam,
+              ErrorResult& aRv);
+
+  static already_AddRefed<BeforeAfterKeyboardEvent>
+  Constructor(EventTarget* aOwner, const nsAString& aType,
+              const BeforeAfterKeyboardEventInit& aEventInitDict);
+
+  // This function returns a boolean value when event typs is either
+  // "mozbrowserafterkeydown" or "mozbrowserafterkeyup".
+  Nullable<bool> GetEmbeddedCancelled();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_BeforeAfterKeyboardEvent_h_
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -696,16 +696,19 @@ EventDispatcher::CreateEvent(EventTarget
       return NS_NewDOMUIEvent(aDOMEvent, aOwner, aPresContext,
                               aEvent->AsGUIEvent());
     case eScrollAreaEventClass:
       return NS_NewDOMScrollAreaEvent(aDOMEvent, aOwner, aPresContext,
                                       aEvent->AsScrollAreaEvent());
     case eKeyboardEventClass:
       return NS_NewDOMKeyboardEvent(aDOMEvent, aOwner, aPresContext,
                                     aEvent->AsKeyboardEvent());
+    case eBeforeAfterKeyboardEventClass:
+      return NS_NewDOMBeforeAfterKeyboardEvent(aDOMEvent, aOwner, aPresContext,
+                                               aEvent->AsBeforeAfterKeyboardEvent());
     case eCompositionEventClass:
       return NS_NewDOMCompositionEvent(aDOMEvent, aOwner, aPresContext,
                                        aEvent->AsCompositionEvent());
     case eMouseEventClass:
       return NS_NewDOMMouseEvent(aDOMEvent, aOwner, aPresContext,
                                  aEvent->AsMouseEvent());
     case eFocusEventClass:
       return NS_NewDOMFocusEvent(aDOMEvent, aOwner, aPresContext,
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -232,16 +232,32 @@ EVENT(keydown,
 EVENT(keypress,
       NS_KEY_PRESS,
       EventNameType_HTMLXUL,
       eKeyboardEventClass)
 EVENT(keyup,
       NS_KEY_UP,
       EventNameType_HTMLXUL,
       eKeyboardEventClass)
+NON_IDL_EVENT(mozbrowserbeforekeydown,
+              NS_KEY_BEFORE_DOWN,
+              EventNameType_None,
+              eBeforeAfterKeyboardEventClass)
+NON_IDL_EVENT(mozbrowserafterkeydown,
+              NS_KEY_AFTER_DOWN,
+              EventNameType_None,
+              eBeforeAfterKeyboardEventClass)
+NON_IDL_EVENT(mozbrowserbeforekeyup,
+              NS_KEY_BEFORE_UP,
+              EventNameType_None,
+              eBeforeAfterKeyboardEventClass)
+NON_IDL_EVENT(mozbrowserafterkeyup,
+              NS_KEY_AFTER_UP,
+              EventNameType_None,
+              eBeforeAfterKeyboardEventClass)
 EVENT(loadeddata,
       NS_LOADEDDATA,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(loadedmetadata,
       NS_LOADEDMETADATA,
       EventNameType_HTML,
       eBasicEventClass)
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -646,18 +646,22 @@ EventStateManager::PreHandleEvent(nsPres
       if (modifierMask &&
           (modifierMask == Prefs::ChromeAccessModifierMask() ||
            modifierMask == Prefs::ContentAccessModifierMask())) {
         HandleAccessKey(aPresContext, keyEvent, aStatus, nullptr,
                         eAccessKeyProcessingNormal, modifierMask);
       }
     }
     // then fall through...
+  case NS_KEY_BEFORE_DOWN:
   case NS_KEY_DOWN:
+  case NS_KEY_AFTER_DOWN:
+  case NS_KEY_BEFORE_UP:
   case NS_KEY_UP:
+  case NS_KEY_AFTER_UP:
     {
       nsIContent* content = GetFocusedContent();
       if (content)
         mCurrentTargetContent = content;
 
       // NOTE: Don't refer TextComposition::IsComposing() since DOM Level 3
       //       Events defines that KeyboardEvent.isComposing is true when it's
       //       dispatched after compositionstart and compositionend.
@@ -3154,17 +3158,19 @@ EventStateManager::PostHandleEvent(nsPre
       break;
     }
   case NS_DRAGDROP_EXIT:
      // make sure to fire the enter and exit_synth events after the
      // NS_DRAGDROP_EXIT event, otherwise we'll clean up too early
     GenerateDragDropEnterExit(presContext, aEvent->AsDragEvent());
     break;
 
+  case NS_KEY_BEFORE_UP:
   case NS_KEY_UP:
+  case NS_KEY_AFTER_UP:
     break;
 
   case NS_KEY_PRESS:
     if (nsEventStatus_eConsumeNoDefault != *aStatus) {
       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
       //This is to prevent keyboard scrolling while alt modifier in use.
       if (!keyEvent->IsAlt()) {
         switch(keyEvent->keyCode) {
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -974,16 +974,26 @@ IMEStateManager::OnCompositionEventDisca
   // Ignore compositionstart for now because sTextCompositions may not have
   // been created yet.
   if (aCompositionEvent->message == NS_COMPOSITION_START) {
     return;
   }
 
   nsRefPtr<TextComposition> composition =
     sTextCompositions->GetCompositionFor(aCompositionEvent->widget);
+  if (!composition) {
+    // If the PresShell has been being destroyed during composition,
+    // a TextComposition instance for the composition was already removed from
+    // the array and destroyed in OnDestroyPresContext().  Therefore, we may
+    // fail to retrieve a TextComposition instance here.
+    PR_LOG(sISMLog, PR_LOG_ALWAYS,
+      ("ISM:   IMEStateManager::OnCompositionEventDiscarded(), "
+       "TextComposition instance for the widget has already gone"));
+    return;
+  }
   composition->OnCompositionEventDiscarded(aCompositionEvent);
 }
 
 // static
 nsresult
 IMEStateManager::NotifyIME(IMEMessage aMessage,
                            nsIWidget* aWidget)
 {
--- a/dom/events/KeyboardEvent.cpp
+++ b/dom/events/KeyboardEvent.cpp
@@ -12,20 +12,18 @@ namespace mozilla {
 namespace dom {
 
 KeyboardEvent::KeyboardEvent(EventTarget* aOwner,
                              nsPresContext* aPresContext,
                              WidgetKeyboardEvent* aEvent)
   : UIEvent(aOwner, aPresContext,
             aEvent ? aEvent : new WidgetKeyboardEvent(false, 0, nullptr))
   , mInitializedByCtor(false)
-  , mInitialzedWhichValue(0)
+  , mInitializedWhichValue(0)
 {
-  NS_ASSERTION(mEvent->mClass == eKeyboardEventClass, "event type mismatch");
-
   if (aEvent) {
     mEventIsInternal = false;
   }
   else {
     mEventIsInternal = true;
     mEvent->time = PR_Now();
     mEvent->AsKeyboardEvent()->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
   }
@@ -252,18 +250,22 @@ uint32_t
 KeyboardEvent::CharCode()
 {
   // If this event is initialized with ctor, we shouldn't check event type.
   if (mInitializedByCtor) {
     return mEvent->AsKeyboardEvent()->charCode;
   }
 
   switch (mEvent->message) {
+  case NS_KEY_BEFORE_DOWN:
+  case NS_KEY_DOWN:
+  case NS_KEY_AFTER_DOWN:
+  case NS_KEY_BEFORE_UP:
   case NS_KEY_UP:
-  case NS_KEY_DOWN:
+  case NS_KEY_AFTER_UP:
     return 0;
   case NS_KEY_PRESS:
     return mEvent->AsKeyboardEvent()->charCode;
   }
   return 0;
 }
 
 NS_IMETHODIMP
@@ -277,36 +279,37 @@ KeyboardEvent::GetKeyCode(uint32_t* aKey
 uint32_t
 KeyboardEvent::KeyCode()
 {
   // If this event is initialized with ctor, we shouldn't check event type.
   if (mInitializedByCtor) {
     return mEvent->AsKeyboardEvent()->keyCode;
   }
 
-  switch (mEvent->message) {
-  case NS_KEY_UP:
-  case NS_KEY_PRESS:
-  case NS_KEY_DOWN:
+  if (mEvent->HasKeyEventMessage()) {
     return mEvent->AsKeyboardEvent()->keyCode;
   }
   return 0;
 }
 
 uint32_t
 KeyboardEvent::Which()
 {
   // If this event is initialized with ctor, which can have independent value.
   if (mInitializedByCtor) {
-    return mInitialzedWhichValue;
+    return mInitializedWhichValue;
   }
 
   switch (mEvent->message) {
+    case NS_KEY_BEFORE_DOWN:
+    case NS_KEY_DOWN:
+    case NS_KEY_AFTER_DOWN:
+    case NS_KEY_BEFORE_UP:
     case NS_KEY_UP:
-    case NS_KEY_DOWN:
+    case NS_KEY_AFTER_UP:
       return KeyCode();
     case NS_KEY_PRESS:
       //Special case for 4xp bug 62878.  Try to make value of which
       //more closely mirror the values that 4.x gave for RETURN and BACKSPACE
       {
         uint32_t keyCode = mEvent->AsKeyboardEvent()->keyCode;
         if (keyCode == NS_VK_RETURN || keyCode == NS_VK_BACK) {
           return keyCode;
@@ -338,36 +341,45 @@ already_AddRefed<KeyboardEvent>
 KeyboardEvent::Constructor(const GlobalObject& aGlobal,
                            const nsAString& aType,
                            const KeyboardEventInit& aParam,
                            ErrorResult& aRv)
 {
   nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
   nsRefPtr<KeyboardEvent> newEvent =
     new KeyboardEvent(target, nullptr, nullptr);
-  bool trusted = newEvent->Init(target);
-  aRv = newEvent->InitKeyEvent(aType, aParam.mBubbles, aParam.mCancelable,
-                               aParam.mView, aParam.mCtrlKey, aParam.mAltKey,
-                               aParam.mShiftKey, aParam.mMetaKey,
-                               aParam.mKeyCode, aParam.mCharCode);
-  newEvent->SetTrusted(trusted);
-  newEvent->mDetail = aParam.mDetail;
-  newEvent->mInitializedByCtor = true;
-  newEvent->mInitialzedWhichValue = aParam.mWhich;
+  newEvent->InitWithKeyboardEventInit(target, aType, aParam, aRv);
+
+  return newEvent.forget();
+}
 
-  WidgetKeyboardEvent* internalEvent = newEvent->mEvent->AsKeyboardEvent();
+void
+KeyboardEvent::InitWithKeyboardEventInit(EventTarget* aOwner,
+                                         const nsAString& aType,
+                                         const KeyboardEventInit& aParam,
+                                         ErrorResult& aRv)
+{
+  bool trusted = Init(aOwner);
+  aRv = InitKeyEvent(aType, aParam.mBubbles, aParam.mCancelable,
+                     aParam.mView, aParam.mCtrlKey, aParam.mAltKey,
+                     aParam.mShiftKey, aParam.mMetaKey,
+                     aParam.mKeyCode, aParam.mCharCode);
+  SetTrusted(trusted);
+  mDetail = aParam.mDetail;
+  mInitializedByCtor = true;
+  mInitializedWhichValue = aParam.mWhich;
+
+  WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent();
   internalEvent->location = aParam.mLocation;
   internalEvent->mIsRepeat = aParam.mRepeat;
   internalEvent->mIsComposing = aParam.mIsComposing;
   internalEvent->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
+  internalEvent->mCodeNameIndex = CODE_NAME_INDEX_USE_STRING;
   internalEvent->mKeyValue = aParam.mKey;
-  internalEvent->mCodeNameIndex = CODE_NAME_INDEX_USE_STRING;
   internalEvent->mCodeValue = aParam.mCode;
-
-  return newEvent.forget();
 }
 
 NS_IMETHODIMP
 KeyboardEvent::InitKeyEvent(const nsAString& aType,
                             bool aCanBubble,
                             bool aCancelable,
                             nsIDOMWindow* aView,
                             bool aCtrlKey,
--- a/dom/events/KeyboardEvent.h
+++ b/dom/events/KeyboardEvent.h
@@ -69,21 +69,27 @@ public:
     aRv = InitKeyEvent(aType, aCanBubble, aCancelable, aView,
                        aCtrlKey, aAltKey, aShiftKey,aMetaKey,
                        aKeyCode, aCharCode);
   }
 
 protected:
   ~KeyboardEvent() {}
 
+  void InitWithKeyboardEventInit(EventTarget* aOwner,
+                                 const nsAString& aType,
+                                 const KeyboardEventInit& aParam,
+                                 ErrorResult& aRv);
+
 private:
   // True, if the instance is created with Constructor().
   bool mInitializedByCtor;
+
   // If the instance is created with Constructor(), which may have independent
   // value.  mInitializedWhichValue stores it.  I.e., this is invalid when
   // mInitializedByCtor is false.
-  uint32_t mInitialzedWhichValue;
+  uint32_t mInitializedWhichValue;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_KeyboardEvent_h_
--- a/dom/events/moz.build
+++ b/dom/events/moz.build
@@ -27,16 +27,17 @@ EXPORTS.mozilla += [
     'KeyNameList.h',
     'PhysicalKeyCodeNameList.h',
     'TextComposition.h',
     'VirtualKeyCodeList.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'AnimationEvent.h',
+    'BeforeAfterKeyboardEvent.h',
     'BeforeUnloadEvent.h',
     'ClipboardEvent.h',
     'CommandEvent.h',
     'CompositionEvent.h',
     'CustomEvent.h',
     'DataContainerEvent.h',
     'DataTransfer.h',
     'DeviceMotionEvent.h',
@@ -66,16 +67,17 @@ EXPORTS.mozilla.dom += [
 ]
 
 if CONFIG['MOZ_WEBSPEECH']:
     EXPORTS.mozilla.dom += ['SpeechRecognitionError.h']
 
 UNIFIED_SOURCES += [
     'AnimationEvent.cpp',
     'AsyncEventDispatcher.cpp',
+    'BeforeAfterKeyboardEvent.cpp',
     'BeforeUnloadEvent.cpp',
     'ClipboardEvent.cpp',
     'CommandEvent.cpp',
     'CompositionEvent.cpp',
     'ContentEventHandler.cpp',
     'DataContainerEvent.cpp',
     'DataTransfer.cpp',
     'DeviceMotionEvent.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/events/test/bug989198_embedded.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Embedded iframe</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="getFocus();">
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <input id="input" style="display: block;">
+  <pre id="test">
+  <script type="application/javascript">
+    function getFocus() {
+      input = document.getElementById("input");
+      input.focus();
+    }
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/events/test/bug989198_helper.js
@@ -0,0 +1,200 @@
+/*
+ * Constants and helper functions for testing BeforeAfterKeyboardEvent.
+ */
+
+const kUnknownEvent       = 0x000;
+const kKeyDownEvent       = 0x001;
+const kKeyUpEvent         = 0x002;
+const kBeforeEvent        = 0x010;
+const kAfterEvent         = 0x020;
+const kParent             = 0x100;
+const kChild              = 0x200;
+
+var gCurrentTest;
+
+function frameScript()
+{
+  function handler(e) {
+    var results = sendSyncMessage("forwardevent", { type: e.type });
+    if (results[0]) {
+      e.preventDefault();
+    }
+  }
+  addEventListener('keydown', handler);
+  addEventListener('keyup', handler);
+  addEventListener('mozbrowserbeforekeydown', handler);
+  addEventListener('mozbrowserbeforekeyup', handler);
+  addEventListener('mozbrowserafterkeydown', handler);
+  addEventListener('mozbrowserafterkeyup', handler);
+}
+
+function prepareTest(useRemote)
+{
+  setupHandlers(window, embedderHandler);
+
+  var iframe = document.createElement("iframe");
+  iframe.id = "embedded";
+  iframe.src = "bug989198_embedded.html";
+  iframe.setAttribute("remote", useRemote ? "true" : "false");
+  SpecialPowers.wrap(iframe).mozbrowser = true;
+
+  iframe.addEventListener("mozbrowserloadend", function onloadend() {
+    iframe.removeEventListener("mozbrowserloadend", onloadend);
+    iframe.focus();
+    var mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+    mm.addMessageListener("forwardevent", function(msg) {
+      return embeddedHandler(msg.json);
+    });
+    mm.loadFrameScript("data:,(" + frameScript.toString() + ")();", false);
+    runTests();
+    return;
+  });
+
+  document.body.appendChild(iframe);
+}
+
+function cleanupTest()
+{
+  teardownHandlers(window, embedderHandler);
+  runTests();
+}
+
+function setupHandlers(element, handler)
+{
+  element.addEventListener('keydown', handler);
+  element.addEventListener('keyup', handler);
+  element.addEventListener('mozbrowserbeforekeydown', handler);
+  element.addEventListener('mozbrowserbeforekeyup', handler);
+  element.addEventListener('mozbrowserafterkeydown', handler);
+  element.addEventListener('mozbrowserafterkeyup', handler);
+}
+
+function teardownHandlers(element, handler)
+{
+  element.removeEventListener('keydown', handler);
+  element.removeEventListener('keyup', handler);
+  element.removeEventListener('mozbrowserbeforekeydown', handler);
+  element.removeEventListener('mozbrowserbeforekeyup', handler);
+  element.removeEventListener('mozbrowserafterkeydown', handler);
+  element.removeEventListener('mozbrowserafterkeyup', handler);
+}
+
+function convertNameToCode(name)
+{
+  switch (name) {
+    case "mozbrowserbeforekeydown":
+      return kBeforeEvent | kKeyDownEvent;
+    case "mozbrowserafterkeydown":
+      return kAfterEvent | kKeyDownEvent;
+    case "mozbrowserbeforekeyup":
+      return kBeforeEvent | kKeyUpEvent;
+    case "mozbrowserafterkeyup":
+      return kAfterEvent | kKeyUpEvent;
+    case "keydown":
+      return kKeyDownEvent;
+    case "keyup":
+      return kKeyUpEvent;
+    default:
+      return kUnknownEvent;
+  }
+}
+
+function classifyEvents(test)
+{
+  // Categorize resultEvents into KEYDOWN group and KEYUP group.
+  for (var i = 0; i < gCurrentTest.resultEvents.length ; i++) {
+    var code = test.resultEvents[i];
+    if ((code & 0xF) == 0x1) { // KEYDOWN
+      test.classifiedEvents[0].push(code);
+    } else if ((code & 0xF) == 0x2) { // KEYUP
+      test.classifiedEvents[1].push(code);
+    } else {
+      ok(false, "Invalid code for events");
+    }
+  }
+}
+
+function verifyResults(test)
+{
+  for (var i = 0; i < gCurrentTest.expectedEvents.length; i++) {
+    is(test.classifiedEvents[i].length,
+       test.expectedEvents[i].length,
+       test.description + ": Wrong number of events");
+
+    for (var j = 0; j < gCurrentTest.classifiedEvents[i].length; j++) {
+      var item = test.classifiedEvents[i][j];
+      is(item, test.expectedEvents[i][j],
+         test.description + ": Wrong order of events");
+     }
+  }
+}
+
+function embeddedHandler(e)
+{
+  return handler(e, kChild);
+}
+
+function embedderHandler(e)
+{
+  // Verify value of attribute embeddedCancelled
+  handler(e, kParent, function checkEmbeddedCancelled(code){
+    switch (code) {
+      case kBeforeEvent | kKeyDownEvent:
+      case kBeforeEvent | kKeyUpEvent:
+        is(e.embeddedCancelled, null,
+           gCurrentTest.description + ': embeddedCancelled should be null');
+        break;
+      case kAfterEvent | kKeyDownEvent:
+        if ((gCurrentTest.doPreventDefaultAt & 0xFF) == kKeyDownEvent) {
+          is(e.embeddedCancelled, true,
+             gCurrentTest.description + ': embeddedCancelled should be true');
+        } else {
+          is(e.embeddedCancelled, false,
+             gCurrentTest.description + ': embeddedCancelled should be false');
+        }
+        break;
+      case kAfterEvent | kKeyUpEvent:
+        if ((gCurrentTest.doPreventDefaultAt & 0xFF) == kKeyUpEvent) {
+          is(e.embeddedCancelled, true,
+             gCurrentTest.description + ': embeddedCancelled should be true');
+        } else {
+          is(e.embeddedCancelled, false,
+             gCurrentTest.description + ': embeddedCancelled should be false');
+        }
+        break;
+      default:
+        break;
+    }
+  });
+}
+
+function handler(e, highBit, callback)
+{
+  var code = convertNameToCode(e.type);
+  var newCode = highBit | code;
+  gCurrentTest.resultEvents.push(newCode);
+
+  if (callback) {
+    callback(code);
+  }
+
+  // Return and let frameScript to handle
+  if (highBit == kChild) {
+    return newCode == gCurrentTest.doPreventDefaultAt;
+  }
+
+  if (newCode == gCurrentTest.doPreventDefaultAt) {
+    e.preventDefault();
+  }
+}
+
+function runTests()
+{
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -161,8 +161,17 @@ skip-if = toolkit == 'android' #CRASH_DU
 [test_focus_disabled.html]
 skip-if = buildapp == 'mulet'
 [test_messageEvent.html]
 [test_moz_mouse_pixel_scroll_event.html]
 skip-if = buildapp == 'mulet'
 [test_onerror_handler_args.html]
 [test_wheel_default_action.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || e10s
+[test_dom_before_after_keyboard_event.html]
+support-files =
+  bug989198_embedded.html
+  bug989198_helper.js
+[test_dom_before_after_keyboard_event_remote.html]
+support-files =
+  bug989198_embedded.html
+  bug989198_helper.js
+skip-if = buildapp == 'b2g' || e10s
--- a/dom/events/test/test_all_synthetic_events.html
+++ b/dom/events/test/test_all_synthetic_events.html
@@ -29,16 +29,20 @@ const kEventConstructors = {
                                                        },
                                              },
   AnimationEvent:                            { create: function (aName, aProps) {
                                                          return new AnimationEvent(aName, aProps);
                                                        },
                                              },
   AudioProcessingEvent:                      { create: null, // Cannot create untrusted event from JS.
                                              },
+  BeforeAfterKeyboardEvent:                  { create: function (aName, aProps) {
+                                                         return new BeforeAfterKeyboardEvent(aName, aProps);
+                                                       },
+                                             },
   BeforeUnloadEvent:                         { create: function (aName, aProps) {
                                                          var e = document.createEvent("beforeunloadevent");
                                                          e.initEvent(aName, aProps.bubbles, aProps.cancelable);
                                                          return e;
                                                        },
                                              },
   BlobEvent:                                 { create: function (aName, aProps) {
                                                          return new BlobEvent(aName, aProps);
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_dom_before_after_keyboard_event.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 989198</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/NativeKeyCodes.js"></script>
+  <script type="text/javascript" src="bug989198_helper.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTests();">
+<a target="_blank"
+   href="https://bugzilla.mozilla.org/show_bug.cgi?id=989198">Mozilla Bug 989198</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function testEventOrderAndAttr()
+{
+  const mainDesc = 'Testing the order of the events';
+  const kTests = [
+    {
+      description: mainDesc,
+      expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
+                          kChild | kKeyDownEvent,
+                          kParent | kAfterEvent | kKeyDownEvent ],
+                        [ kParent | kBeforeEvent | kKeyUpEvent,
+                          kChild | kKeyUpEvent,
+                          kParent | kAfterEvent | kKeyUpEvent ] ],
+      resultEvents: [],
+      classifiedEvents: [ [], [] ],
+      doPreventDefaultAt: kUnknownEvent
+    },
+    {
+      description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeydown" event',
+      expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
+                          kParent | kAfterEvent | kKeyDownEvent ],
+                        [ kParent | kBeforeEvent | kKeyUpEvent,
+                          kChild | kKeyUpEvent,
+                          kParent | kAfterEvent | kKeyUpEvent ] ],
+      resultEvents: [],
+      classifiedEvents: [ [], [] ],
+      doPreventDefaultAt: kParent | kBeforeEvent | kKeyDownEvent
+    },
+    {
+      description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeyup" event',
+      expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
+                          kChild | kKeyDownEvent,
+                          kParent | kAfterEvent | kKeyDownEvent ],
+                        [ kParent | kBeforeEvent | kKeyUpEvent,
+                          kParent | kAfterEvent | kKeyUpEvent ] ],
+      resultEvents: [],
+      classifiedEvents: [ [], [] ],
+      doPreventDefaultAt: kParent | kBeforeEvent | kKeyUpEvent
+    },
+    {
+      description: mainDesc + ', calling preventDefault() at "keydown" event',
+      expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
+                          kChild | kKeyDownEvent,
+                          kParent | kAfterEvent | kKeyDownEvent ],
+                        [ kParent | kBeforeEvent | kKeyUpEvent,
+                          kChild | kKeyUpEvent,
+                          kParent | kAfterEvent | kKeyUpEvent ] ],
+      resultEvents: [],
+      classifiedEvents: [ [], [] ],
+      doPreventDefaultAt: kChild | kKeyDownEvent
+    },
+    {
+      description: mainDesc + ', calling preventDefault() at "keyup" event',
+      expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
+                          kChild | kKeyDownEvent,
+                          kParent | kAfterEvent | kKeyDownEvent ],
+                        [ kParent | kBeforeEvent | kKeyUpEvent,
+                          kChild | kKeyUpEvent,
+                          kParent | kAfterEvent | kKeyUpEvent ] ],
+      resultEvents: [],
+      classifiedEvents: [ [], [] ],
+      doPreventDefaultAt: kChild | kKeyUpEvent
+    }
+  ];
+
+  for (var k = 0; k < kTests.length; k++ ) {
+    gCurrentTest = kTests[k];
+    synthesizeKey('a', {}, document.getElementById("embedded").contentWindow);
+    classifyEvents(kTests[k]);
+    verifyResults(kTests[k]);
+  }
+
+  runTests();
+}
+
+var tests = [
+  function addPermissions() {
+    SpecialPowers.pushPermissions(
+      [{ type: "before-after-keyboard-event", allow: true, context: document },
+       { type: "browser", allow: true, context: document }],
+      runTests);
+  },
+  function addPreferences() {
+    SpecialPowers.pushPrefEnv(
+      { "set": [["dom.beforeAfterKeyboardEvent.enabled", true],
+                ["dom.mozBrowserFramesEnabled", true],
+                ["dom.ipc.tabs.disabled", false]] },
+      runTests);
+  },
+
+  // Tests for in-process iframe, i.e. <iframe mozbrowser>.
+  ()=>prepareTest(false),
+  testEventOrderAndAttr,
+  cleanupTest,
+];
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_dom_before_after_keyboard_event_remote.html
@@ -0,0 +1,142 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 989198</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/NativeKeyCodes.js"></script>
+  <script type="text/javascript" src="bug989198_helper.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTests();">
+<a target="_blank"
+   href="https://bugzilla.mozilla.org/show_bug.cgi?id=989198">Mozilla Bug 989198</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+const mainDesc = 'Testing the order of the events (OOP)';
+var testsForEventOrder = [
+  {
+    description: mainDesc,
+    expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
+                        kParent | kKeyDownEvent,
+                        kChild | kKeyDownEvent,
+                        kParent | kAfterEvent | kKeyDownEvent ],
+                      [ kParent | kBeforeEvent | kKeyUpEvent,
+                        kParent | kKeyUpEvent,
+                        kChild | kKeyUpEvent,
+                        kParent | kAfterEvent | kKeyUpEvent ] ],
+    resultEvents: [],
+    classifiedEvents: [ [], [] ],
+    doPreventDefaultAt: kUnknownEvent
+  },
+  {
+    description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeydown" event',
+    expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
+                        kParent | kAfterEvent | kKeyDownEvent ],
+                      [ kParent | kBeforeEvent | kKeyUpEvent,
+                        kParent | kKeyUpEvent,
+                        kChild | kKeyUpEvent,
+                        kParent | kAfterEvent | kKeyUpEvent ] ],
+    resultEvents: [],
+    classifiedEvents: [ [], [] ],
+    doPreventDefaultAt: kParent | kBeforeEvent | kKeyDownEvent
+  },
+  {
+    description: mainDesc + ', calling preventDefault() at "mozbrowserbeforekeyup" event',
+    expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
+                        kParent | kKeyDownEvent,
+                        kChild | kKeyDownEvent,
+                        kParent | kAfterEvent | kKeyDownEvent ],
+                      [ kParent | kBeforeEvent | kKeyUpEvent,
+                        kParent | kAfterEvent | kKeyUpEvent ] ],
+    resultEvents: [],
+    classifiedEvents: [ [], [] ],
+    doPreventDefaultAt: kParent | kBeforeEvent | kKeyUpEvent
+  },
+  {
+    description: mainDesc + ', calling preventDefault() at "keydown" event',
+    expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
+                        kParent | kKeyDownEvent,
+                        kChild | kKeyDownEvent,
+                        kParent | kAfterEvent | kKeyDownEvent ],
+                      [ kParent | kBeforeEvent | kKeyUpEvent,
+                        kParent | kKeyUpEvent,
+                        kChild | kKeyUpEvent,
+                        kParent | kAfterEvent | kKeyUpEvent ] ],
+    resultEvents: [],
+    classifiedEvents: [ [], [] ],
+    doPreventDefaultAt: kChild | kKeyDownEvent
+  },
+  {
+    description: mainDesc + ', calling preventDefault() at "keyup" event',
+    expectedEvents: [ [ kParent | kBeforeEvent | kKeyDownEvent,
+                        kParent | kKeyDownEvent,
+                        kChild | kKeyDownEvent,
+                        kParent | kAfterEvent | kKeyDownEvent ],
+                      [ kParent | kBeforeEvent | kKeyUpEvent,
+                        kParent | kKeyUpEvent,
+                        kChild | kKeyUpEvent,
+                        kParent | kAfterEvent | kKeyUpEvent ] ],
+    resultEvents: [],
+    classifiedEvents: [ [], [] ],
+    doPreventDefaultAt: kChild | kKeyUpEvent
+  }
+];
+
+function testEventOrder()
+{
+  if (!testsForEventOrder.length) {
+    runTests();
+    return;
+  }
+  gCurrentTest = testsForEventOrder.shift();
+
+  synthesizeKey('a', {}, document.getElementById("embedded").contentWindow);
+  // It take some time to propagate events to a remote iframe.
+
+  waitAndVerifyResult(0);
+}
+
+function waitAndVerifyResult(count) {
+  expectedEventLength = gCurrentTest.expectedEvents[0].length +
+                        gCurrentTest.expectedEvents[1].length;
+  if (gCurrentTest.resultEvents.length >= expectedEventLength || count > 10) {
+    classifyEvents(gCurrentTest);
+    verifyResults(gCurrentTest);
+    testEventOrder();
+  }
+  else {
+    setTimeout(()=>waitAndVerifyResult(count + 1), 100);
+  }
+}
+
+var tests = [
+  function addPermissions() {
+    SpecialPowers.pushPermissions(
+      [{ type: "before-after-keyboard-event", allow: true, context: document },
+       { type: "browser", allow: true, context: document }],
+      runTests);
+  },
+  function addPreferences() {
+    SpecialPowers.pushPrefEnv(
+      { "set": [["dom.beforeAfterKeyboardEvent.enabled", true],
+                ["dom.mozBrowserFramesEnabled", true],
+                ["dom.ipc.tabs.disabled", false]] },
+      runTests);
+  },
+
+  // Tests for out-of-process iframe, i.el. <iframe mozbrowser remote>
+  ()=>prepareTest(true),
+  testEventOrder,
+  cleanupTest
+];
+
+</script>
+</body>
+</html>
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -913,17 +913,19 @@ nsGeolocationService::HighAccuracyReques
 
 void
 nsGeolocationService::UpdateAccuracy(bool aForceHigh)
 {
   bool highRequired = aForceHigh || HighAccuracyRequested();
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     ContentChild* cpc = ContentChild::GetSingleton();
-    cpc->SendSetGeolocationHigherAccuracy(highRequired);
+    if (cpc->IsAlive()) {
+      cpc->SendSetGeolocationHigherAccuracy(highRequired);
+    }
     return;
   }
 
   if (!mHigherAccuracy && highRequired) {
       mProvider->SetHighAccuracy(true);
   }
 
   if (mHigherAccuracy && !highRequired) {
--- a/dom/interfaces/events/nsIDOMEvent.idl
+++ b/dom/interfaces/events/nsIDOMEvent.idl
@@ -267,16 +267,23 @@ NS_NewDOMInputEvent(nsIDOMEvent** aInsta
                     mozilla::dom::EventTarget* aOwner,
                     nsPresContext* aPresContext,
                     mozilla::InternalEditorInputEvent* aEvent);
 nsresult
 NS_NewDOMKeyboardEvent(nsIDOMEvent** aInstancePtrResult,
                        mozilla::dom::EventTarget* aOwner,
                        nsPresContext* aPresContext,
                        mozilla::WidgetKeyboardEvent* aEvent);
+
+nsresult
+NS_NewDOMBeforeAfterKeyboardEvent(nsIDOMEvent** aInstancePtrResult,
+                                  mozilla::dom::EventTarget* aOwner,
+                                  nsPresContext* aPresContext,
+                                  mozilla::InternalBeforeAfterKeyboardEvent* aEvent);
+
 nsresult
 NS_NewDOMCompositionEvent(nsIDOMEvent** aInstancePtrResult,
                           mozilla::dom::EventTarget* aOwner,
                           nsPresContext* aPresContext,
                           mozilla::WidgetCompositionEvent* aEvent);
 nsresult
 NS_NewDOMMutationEvent(nsIDOMEvent** aResult,
                        mozilla::dom::EventTarget* aOwner,
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -510,22 +510,32 @@ InitOnContentProcessCreated()
         return;
     }
 #endif
 
     // This will register cross-process observer.
     mozilla::dom::time::InitializeDateCacheCleaner();
 }
 
+#if defined(MOZ_TASK_TRACER) && defined(MOZ_NUWA_PROCESS)
+static void
+ReinitTaskTracer(void* /*aUnused*/)
+{
+    mozilla::tasktracer::InitTaskTracer(
+        mozilla::tasktracer::FORKED_AFTER_NUWA);
+}
+#endif
+
 ContentChild::ContentChild()
  : mID(uint64_t(-1))
 #ifdef ANDROID
    ,mScreenSize(0, 0)
 #endif
    , mCanOverrideProcessName(true)
+   , mIsAlive(true)
 {
     // This process is a content process, so it's clearly running in
     // multiprocess mode!
     nsDebugImpl::SetMultiprocessMode("Child");
 }
 
 ContentChild::~ContentChild()
 {
@@ -589,16 +599,22 @@ ContentChild::Init(MessageLoop* aIOLoop,
                                   XRE_GetProcessType());
 #endif
 
     GetCPOWManager();
 
     SendGetProcessAttributes(&mID, &mIsForApp, &mIsForBrowser);
     InitProcessAttributes();
 
+#if defined(MOZ_TASK_TRACER) && defined (MOZ_NUWA_PROCESS)
+    if (IsNuwaProcess()) {
+        NuwaAddConstructor(ReinitTaskTracer, nullptr);
+    }
+#endif
+
     return true;
 }
 
 void
 ContentChild::InitProcessAttributes()
 {
 #ifdef MOZ_WIDGET_GONK
 #ifdef MOZ_NUWA_PROCESS
@@ -648,16 +664,22 @@ ContentChild::SetProcessName(const nsASt
 }
 
 void
 ContentChild::GetProcessName(nsAString& aName)
 {
     aName.Assign(mProcessName);
 }
 
+bool
+ContentChild::IsAlive()
+{
+    return mIsAlive;
+}
+
 void
 ContentChild::GetProcessName(nsACString& aName)
 {
     aName.Assign(NS_ConvertUTF16toUTF8(mProcessName));
 }
 
 /* static */ void
 ContentChild::AppendProcessId(nsACString& aName)
@@ -1620,16 +1642,17 @@ ContentChild::ActorDestroy(ActorDestroyR
 
     mIdleObservers.Clear();
 
     nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
     if (svc) {
         svc->UnregisterListener(mConsoleListener);
         mConsoleListener->mChild = nullptr;
     }
+    mIsAlive = false;
 
     XRE_ShutdownChildProcess();
 }
 
 void
 ContentChild::ProcessingError(Result what)
 {
     switch (what) {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -82,20 +82,20 @@ public:
 
     static ContentChild* GetSingleton() {
         return sSingleton;
     }
 
     const AppInfo& GetAppInfo() {
         return mAppInfo;
     }
-
     void SetProcessName(const nsAString& aName, bool aDontOverride = false);
     void GetProcessName(nsAString& aName);
     void GetProcessName(nsACString& aName);
+    bool IsAlive();
     static void AppendProcessId(nsACString& aName);
 
     ContentBridgeParent* GetLastBridge() {
         MOZ_ASSERT(mLastBridge);
         ContentBridgeParent* parent = mLastBridge;
         mLastBridge = nullptr;
         return parent;
     }
@@ -416,16 +416,17 @@ private:
 
 #ifdef ANDROID
     gfxIntSize mScreenSize;
 #endif
 
     bool mIsForApp;
     bool mIsForBrowser;
     bool mCanOverrideProcessName;
+    bool mIsAlive;
     nsString mProcessName;
 
     static ContentChild* sSingleton;
 
     DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
 };
 
 } // namespace dom
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -393,16 +393,18 @@ parent:
      * OnAuthAvailable or OnAuthCancelled message.
      */
     AsyncAuthPrompt(nsCString uri, nsString realm, uint64_t aCallbackId);
 
     __delete__();
 
     ReplyKeyEvent(WidgetKeyboardEvent event);
 
+    DispatchAfterKeyboardEvent(WidgetKeyboardEvent event);
+
     sync RequestNativeKeyBindings(WidgetKeyboardEvent event)
         returns (MaybeNativeKeyBinding bindings);
 
     /**
      * Child informs the parent that the graphics objects are ready for
      * compositing.  This is sent when all pending changes have been
      * sent to the compositor and are ready to be shown on the next composite.
      * @see PCompositor
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -76,18 +76,18 @@
 #include "JavaScriptChild.h"
 #include "nsILoadContext.h"
 #include "ipc/nsGUIEventIPC.h"
 #include "mozilla/gfx/Matrix.h"
 #include "UnitTransforms.h"
 #include "ClientLayerManager.h"
 #include "LayersLogging.h"
 #include "nsIOService.h"
-
 #include "nsColorPickerProxy.h"
+#include "nsPresShell.h"
 
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
 #define TABC_LOG(...)
 // #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__)
 
 using namespace mozilla;
@@ -2357,16 +2357,20 @@ TabChild::RecvRealKeyEvent(const WidgetK
   if (event.message == NS_KEY_DOWN) {
     mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault;
   }
 
   if (localEvent.mFlags.mWantReplyFromContentProcess) {
     SendReplyKeyEvent(localEvent);
   }
 
+  if (PresShell::BeforeAfterKeyboardEventEnabled()) {
+    SendDispatchAfterKeyboardEvent(localEvent);
+  }
+
   return true;
 }
 
 bool
 TabChild::RecvKeyEvent(const nsString& aType,
                        const int32_t& aKeyCode,
                        const int32_t& aCharCode,
                        const int32_t& aModifiers,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -47,16 +47,17 @@
 #include "nsIWindowCreator2.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsIXULWindow.h"
 #include "nsViewManager.h"
 #include "nsIWidget.h"
 #include "nsIWindowWatcher.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowWatcher.h"
+#include "nsPresShell.h"
 #include "nsPrintfCString.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsWindowWatcher.h"
 #include "private/pprio.h"
 #include "PermissionMessageUtils.h"
 #include "StructuredCloneUtils.h"
 #include "ColorPickerParent.h"
@@ -1458,16 +1459,39 @@ TabParent::RecvReplyKeyEvent(const Widge
   NS_ENSURE_TRUE(presShell, true);
   nsPresContext* presContext = presShell->GetPresContext();
   NS_ENSURE_TRUE(presContext, true);
 
   EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent);
   return true;
 }
 
+bool
+TabParent::RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent)
+{
+  NS_ENSURE_TRUE(mFrameElement, true);
+
+  WidgetKeyboardEvent localEvent(aEvent);
+  localEvent.widget = GetWidget();
+
+  nsIDocument* doc = mFrameElement->OwnerDoc();
+  nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
+  NS_ENSURE_TRUE(presShell, true);
+
+  if (mFrameElement &&
+      PresShell::BeforeAfterKeyboardEventEnabled() &&
+      localEvent.message != NS_KEY_PRESS) {
+    nsCOMPtr<nsINode> node(do_QueryInterface(mFrameElement));
+    presShell->DispatchAfterKeyboardEvent(mFrameElement, localEvent,
+                                          aEvent.mFlags.mDefaultPrevented);
+  }
+
+  return true;
+}
+
 /**
  * Try to answer query event using cached text.
  *
  * For NS_QUERY_SELECTED_TEXT, fail if the cache doesn't contain the whole
  *  selected range. (This shouldn't happen because PuppetWidget should have
  *  already sent the whole selection.)
  *
  * For NS_QUERY_TEXT_CONTENT, fail only if the cache doesn't overlap with
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -117,16 +117,17 @@ public:
      */
     bool TryCapture(const WidgetGUIEvent& aEvent);
 
     void Destroy();
 
     virtual bool RecvMoveFocus(const bool& aForward) MOZ_OVERRIDE;
     virtual bool RecvEvent(const RemoteDOMEvent& aEvent) MOZ_OVERRIDE;
     virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& event);
+    virtual bool RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& event);
     virtual bool RecvPRenderFrameConstructor(PRenderFrameParent* aActor,
                                              ScrollingBehavior* aScrolling,
                                              TextureFactoryIdentifier* aFactoryIdentifier,
                                              uint64_t* aLayersId,
                                              bool* aSuccess) MOZ_OVERRIDE;
     virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
                                             const nsString& aURL,
                                             const nsString& aName,
--- a/dom/telephony/test/marionette/head.js
+++ b/dom/telephony/test/marionette/head.js
@@ -1311,25 +1311,16 @@ function startDSDSTest(test) {
     finish();
   }
 }
 
 function sendMMI(aMmi) {
   let deferred = Promise.defer();
 
   telephony.dial(aMmi)
-    .then(request => {
-      ok(request instanceof DOMRequest,
-         "request is instanceof " + request.constructor);
-
-      request.addEventListener("success", function(event) {
-        deferred.resolve(request.result);
-      });
-
-      request.addEventListener("error", function(event) {
-        deferred.reject(request.error);
-      });
+    .then(result => {
+      deferred.resolve(result);
     }, cause => {
       deferred.reject(cause);
     });
 
   return deferred.promise;
 }
--- a/dom/tests/mochitest/dom-level0/mochitest.ini
+++ b/dom/tests/mochitest/dom-level0/mochitest.ini
@@ -1,19 +1,19 @@
 [DEFAULT]
-skip-if = !debug || (buildapp == 'b2g') || (toolkit == 'android') #Bug 932350 - Frequent timeouts
 support-files =
   child_ip_address.html
   file_crossdomainprops_inner.html
   file_location.html
   framed_location.html
   idn_child.html
   innerWidthHeight_script.html
 
 [test_crossdomainprops.html]
 [test_innerWidthHeight_script.html]
+skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' # Bug 1075071 - Permafail on Android/B2G/Mulet
 [test_location.html]
 [test_location_framed.html]
 [test_location_getters.html]
 [test_location_sandboxed.html]
 [test_location_setters.html]
 [test_setting_document.domain_idn.html]
 [test_setting_document.domain_to_shortened_ipaddr.html]
--- a/dom/tests/mochitest/dom-level1-core/mochitest.ini
+++ b/dom/tests/mochitest/dom-level1-core/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = !debug || (buildapp == 'b2g') || (toolkit == 'android') #android(bug 910229) #Bug 932350 - Frequent timeouts
+skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 932350 - Frequent timeouts
 support-files =
   DOMTestCase.js
   activity-home.css
   exclusions.js
   files/hc_nodtdstaff.html
   files/hc_nodtdstaff.xhtml
   files/hc_staff.html
   files/hc_staff.xhtml
--- a/dom/tests/mochitest/dom-level2-core/mochitest.ini
+++ b/dom/tests/mochitest/dom-level2-core/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = !debug || (buildapp == 'b2g') || (toolkit == 'android') # android(bug 910229) #Bug 932350 - Frequent timeouts
+skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 932350 - Frequent timeouts
 support-files =
   DOMTestCase.js
   exclusions.js
   files/hc_staff.html
   files/hc_staff.svg
   files/hc_staff.xhtml
   files/hc_staff.xml
   files/internalSubset01.js
--- a/dom/tests/mochitest/dom-level2-html/mochitest.ini
+++ b/dom/tests/mochitest/dom-level2-html/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = !debug || (buildapp == 'b2g') || (toolkit == 'android') #android(bug 910229) #Bug 932350 - Frequent timeouts
+skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 932350 - Frequent timeouts
 support-files =
   DOMTestCase.js
   files/anchor.html
   files/anchor.xhtml
   files/anchor.xml
   files/anchor2.html
   files/anchor2.xhtml
   files/anchor2.xml
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -167,36 +167,40 @@ var interfaceNamesInGlobalScope =
     "AudioProcessingEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "AudioStreamTrack",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "BarProp",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "BatteryManager",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "BeforeAfterKeyboardEvent", b2g: true,
+     pref: "dom.beforeAfterKeyboardEvent.enabled",
+     permission: ["embed-apps", "before-after-keyboard-event"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "BeforeUnloadEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "BiquadFilterNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Blob",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "BlobEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BluetoothAdapter", b2g: true, permission: "bluetooth"},
+    {name: "BluetoothAdapter", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BluetoothDevice", b2g: true, permission: "bluetooth"},
+    {name: "BluetoothDevice", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BluetoothDeviceEvent", b2g: true, permission: "bluetooth"},
+    {name: "BluetoothDeviceEvent", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothDiscoveryStateChangedEvent", b2g: true,
-     permission: "bluetooth"},
+     permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BluetoothManager", b2g: true, permission: "bluetooth"},
+    {name: "BluetoothManager", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "BluetoothStatusChangedEvent", b2g: true, permission: "bluetooth"},
+    {name: "BluetoothStatusChangedEvent", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BoxObject", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CallEvent", b2g: true, pref: "dom.telephony.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CallGroupErrorEvent", b2g: true, pref: "dom.telephony.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraCapabilities", b2g: true},
@@ -746,17 +750,17 @@ var interfaceNamesInGlobalScope =
     {name: "mozRTCIceCandidate", pref: "media.peerconnection.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "mozRTCPeerConnection", pref: "media.peerconnection.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "mozRTCSessionDescription", pref: "media.peerconnection.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozSettingsEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozSettingsTransactionEvent", permission: "settings-api-read"},
+    {name: "MozSettingsTransactionEvent", permission: ["settings-api-read"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozSmsEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozSmsMessage",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozSpeakerManager", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozStkCommandEvent", b2g: true, pref: "dom.icc.enabled"},
@@ -766,33 +770,33 @@ var interfaceNamesInGlobalScope =
     {name: "MozVoicemail", b2g: true, pref: "dom.voicemail.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozVoicemailEvent", b2g: true, pref: "dom.voicemail.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozVoicemailStatus", b2g: true, pref: "dom.voicemail.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWakeLock", b2g: true, pref: "dom.wakelock.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozWifiCapabilities", b2g: true, permission: "wifi-manage"},
+    {name: "MozWifiCapabilities", b2g: true, permission: ["wifi-manage"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWifiConnectionInfoEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWifiStationInfoEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-   {name: "MozWifiManager", b2g: true, permission: "wifi-manage"},
+   {name: "MozWifiManager", b2g: true, permission: ["wifi-manage"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozWifiNetwork", b2g: true, permission: "wifi-manage"},
+    {name: "MozWifiNetwork", b2g: true, permission: ["wifi-manage"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozWifiStatusChangeEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozWifiP2pGroupOwner", b2g: true, permission: "wifi-manage"},
+    {name: "MozWifiP2pGroupOwner", b2g: true, permission: ["wifi-manage"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozWifiP2pManager", b2g: true, permission: "wifi-manage"},
+    {name: "MozWifiP2pManager", b2g: true, permission: ["wifi-manage"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MozWifiP2pStatusChangeEvent", b2g: true, permission: "wifi-manage"},
+    {name: "MozWifiP2pStatusChangeEvent", b2g: true, permission: ["wifi-manage"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MutationEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MutationObserver",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MutationRecord",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "NamedNodeMap",
@@ -840,19 +844,19 @@ var interfaceNamesInGlobalScope =
     "PerformanceNavigation",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceResourceTiming",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceTiming",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PeriodicWave",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PermissionSettings", b2g: true, permission: "permissions"},
+    {name: "PermissionSettings", b2g: true, permission: ["permissions"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PhoneNumberService", permission: "phonenumberservice"},
+    {name: "PhoneNumberService", permission: ["phonenumberservice"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Plugin",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PluginArray",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PointerEvent", pref: "dom.w3c_pointer_events.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PopStateEvent",
@@ -1222,19 +1226,21 @@ var interfaceNamesInGlobalScope =
     {name: "TreeColumns", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "TreeContentView", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "TreeSelection", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TreeWalker",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "UDPMessageEvent", pref: "dom.udpsocket.enabled", permission: "udp-socket"},
+    {name: "UDPMessageEvent", pref: "dom.udpsocket.enabled",
+     permission: ["udp-socket"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "UDPSocket", pref: "dom.udpsocket.enabled", permission: "udp-socket"},
+    {name: "UDPSocket", pref: "dom.udpsocket.enabled",
+     permission: ["udp-socket"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "UIEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "UndoManager",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "URL",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "URLSearchParams",
@@ -1334,18 +1340,22 @@ var interfaceNamesInGlobalScope =
 
 function createInterfaceMap(isXBLScope) {
   var prefs = SpecialPowers.Services.prefs;
   var version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(SpecialPowers.Ci.nsIXULAppInfo).version;
   var isNightly = version.endsWith("a1");
   var isRelease = !version.contains("a");
   var isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
   var isB2G = !isDesktop && !navigator.userAgent.contains("Android");
-  var hasPermission = function (aPermission) {
-    return SpecialPowers.hasPermission(aPermission, window.document);
+  var hasPermission = function (aPermissions) {
+    var result = false;
+    for (var p of aPermissions) {
+      result = result || SpecialPowers.hasPermission(p, window.document);
+    }
+    return result;
   };
 
   var interfaceMap = {};
 
   function addInterfaces(interfaces)
   {
     for (var entry of interfaces) {
       if (typeof(entry) === "string") {
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BeforeAfterKeyboardEvent.webidl
@@ -0,0 +1,24 @@
+/* -*- Mode: IDL; tab-width: 2; 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/.
+ */
+
+[Constructor(DOMString typeArg,
+ optional BeforeAfterKeyboardEventInit eventInitDict),
+ CheckPermissions="embed-apps before-after-keyboard-event",
+ Pref="dom.beforeAfterKeyboardEvent.enabled"]
+interface BeforeAfterKeyboardEvent : KeyboardEvent
+{
+  // The valid value of embeddedCancelled is:
+  // - "mozbrowserbeforekeydown": null
+  // - "mozbrowserbeforekeyup": null
+  // - "mozbrowserafterkeydown": true/false
+  // - "mozbrowserafterkeyup": true/false
+  readonly attribute boolean? embeddedCancelled;
+};
+
+dictionary BeforeAfterKeyboardEventInit : KeyboardEventInit
+{
+  boolean? embeddedCancelled = null;
+};
--- a/dom/webidl/DOMCursor.webidl
+++ b/dom/webidl/DOMCursor.webidl
@@ -1,10 +1,12 @@
 /* -*- Mode: IDL; tab-width: 2; 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/. */
 
-interface DOMCursor : DOMRequest {
+interface DOMCursor : EventTarget {
   readonly attribute boolean done;
   [Throws]
   void continue();
 };
+
+DOMCursor implements DOMRequestShared;
--- a/dom/webidl/DOMRequest.webidl
+++ b/dom/webidl/DOMRequest.webidl
@@ -1,16 +1,27 @@
 /* -*- Mode: IDL; tab-width: 2; 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/. */
 
 enum DOMRequestReadyState { "pending", "done" };
 
-interface DOMRequest : EventTarget {
+[NoInterfaceObject]
+interface DOMRequestShared {
   readonly attribute DOMRequestReadyState readyState;
 
   readonly attribute any result;
   readonly attribute DOMError? error;
 
   attribute EventHandler onsuccess;
   attribute EventHandler onerror;
 };
+
+interface DOMRequest : EventTarget {
+  // The [TreatNonCallableAsNull] annotation is required since then() should do
+  // nothing instead of throwing errors when non-callable arguments are passed.
+  [NewObject, Throws]
+  Promise<any> then([TreatNonCallableAsNull] optional AnyCallback? fulfillCallback = null,
+                    [TreatNonCallableAsNull] optional AnyCallback? rejectCallback = null);
+};
+
+DOMRequest implements DOMRequestShared;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -44,16 +44,17 @@ WEBIDL_FILES = [
     'AudioParam.webidl',
     'AudioProcessingEvent.webidl',
     'AudioStreamTrack.webidl',
     'AudioTrack.webidl',
     'AudioTrackList.webidl',
     'AutocompleteInfo.webidl',
     'BarProp.webidl',
     'BatteryManager.webidl',
+    'BeforeAfterKeyboardEvent.webidl',
     'BeforeUnloadEvent.webidl',
     'BiquadFilterNode.webidl',
     'Blob.webidl',
     'BrowserElementDictionaries.webidl',
     'CallsList.webidl',
     'CameraCapabilities.webidl',
     'CameraControl.webidl',
     'CameraManager.webidl',
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -540,19 +540,18 @@ static const FeatureInfo sFeatureInfoArr
              */
             GLContext::Extensions_End
         }
     },
     {
         "transform_feedback2",
         400, // OpenGL version
         300, // OpenGL ES version
-        GLContext::Extension_None,
+        GLContext::ARB_transform_feedback2,
         {
-            GLContext::ARB_transform_feedback2,
             GLContext::NV_transform_feedback2,
             GLContext::Extensions_End
         }
     },
     {
         "uniform_buffer_object",
         310, // OpenGL version
         300, // OpenGL ES version
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -647,17 +647,22 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
       case AsmJSImm_aeabi_idivmod:
         return RedirectCall(FuncCast(__aeabi_idivmod), Args_General2);
       case AsmJSImm_aeabi_uidivmod:
         return RedirectCall(FuncCast(__aeabi_uidivmod), Args_General2);
 #endif
       case AsmJSImm_ModD:
         return RedirectCall(FuncCast(NumberMod), Args_Double_DoubleDouble);
       case AsmJSImm_SinD:
+#ifdef _WIN64
+        // Workaround a VS 2013 sin issue, see math_sin_uncached.
+        return RedirectCall(FuncCast<double (double)>(js::math_sin_uncached), Args_Double_Double);
+#else
         return RedirectCall(FuncCast<double (double)>(sin), Args_Double_Double);
+#endif
       case AsmJSImm_CosD:
         return RedirectCall(FuncCast<double (double)>(cos), Args_Double_Double);
       case AsmJSImm_TanD:
         return RedirectCall(FuncCast<double (double)>(tan), Args_Double_Double);
       case AsmJSImm_ASinD:
         return RedirectCall(FuncCast<double (double)>(asin), Args_Double_Double);
       case AsmJSImm_ACosD:
         return RedirectCall(FuncCast<double (double)>(acos), Args_Double_Double);
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -511,17 +511,17 @@ class StructTypeDescr : public ComplexTy
     size_t maybeForwardedFieldOffset(size_t index) const;
 
   private:
     NativeObject &fieldInfoObject(size_t slot) const {
         return getReservedSlot(slot).toObject().as<NativeObject>();
     }
 
     NativeObject &maybeForwardedFieldInfoObject(size_t slot) const {
-        return *MaybeForwarded(&fieldInfoObject(slot));
+        return MaybeForwarded(&getReservedSlot(slot).toObject())->as<NativeObject>();
     }
 };
 
 typedef Handle<StructTypeDescr*> HandleStructTypeDescr;
 
 /*
  * This object exists in order to encapsulate the typed object types
  * somewhat, rather than sticking them all into the global object.
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -467,28 +467,28 @@ class GCRuntime
     void finishVerifier();
     bool isVerifyPreBarriersEnabled() const { return !!verifyPreData; }
 #else
     bool isVerifyPreBarriersEnabled() const { return false; }
 #endif
 
     template <AllowGC allowGC>
     static void *refillFreeListFromAnyThread(ThreadSafeContext *cx, AllocKind thingKind);
+    static void *refillFreeListInGC(Zone *zone, AllocKind thingKind);
 
   private:
     // For ArenaLists::allocateFromArena()
     friend class ArenaLists;
     Chunk *pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
     inline void arenaAllocatedDuringGC(JS::Zone *zone, ArenaHeader *arena);
 
     template <AllowGC allowGC>
     static void *refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind);
     static void *refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind);
     static void *refillFreeListPJS(ForkJoinContext *cx, AllocKind thingKind);
-    static void *refillFreeListInGC(Zone *zone, AllocKind thingKind);
 
     /*
      * Return the list of chunks that can be released outside the GC lock.
      * Must be called either during the GC or with the GC lock taken.
      */
     Chunk *expireChunkPool(bool shrinkBuffers, bool releaseAll);
     void expireAndFreeChunkPool(bool releaseAll);
     void freeChunkList(Chunk *chunkListHead);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4003,16 +4003,17 @@ CodeGenerator::emitDebugResultChecks(LIn
       case MIRType_String:
       case MIRType_Symbol:
         return emitObjectOrStringResultChecks(ins, mir);
       case MIRType_Value:
         return emitValueResultChecks(ins, mir);
       default:
         return true;
     }
+    return true;
 }
 #endif
 
 bool
 CodeGenerator::generateBody()
 {
     IonScriptCounts *counts = maybeCreateScriptCounts();
 
@@ -7454,20 +7455,28 @@ CodeGenerator::link(JSContext *cx, types
         }
     }
 
     if (scriptCounts_ && !script->hasScriptCounts() && !script->initScriptCounts(cx))
         return false;
 
     // Check to make sure we didn't have a mid-build invalidation. If so, we
     // will trickle to jit::Compile() and return Method_Skipped.
+    uint32_t warmUpCount = script->getWarmUpCount();
     types::RecompileInfo recompileInfo;
     if (!types::FinishCompilation(cx, script, executionMode, constraints, &recompileInfo))
         return true;
 
+    // IonMonkey could have inferred better type information during
+    // compilation. Since adding the new information to the actual type
+    // information can reset the usecount, increase it back to what it was
+    // before.
+    if (warmUpCount > script->getWarmUpCount())
+        script->incWarmUpCounter(warmUpCount - script->getWarmUpCount());
+
     uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None()
                            ? frameDepth_
                            : FrameSizeClass::FromDepth(frameDepth_).frameSize();
 
     // We encode safepoints after the OSI-point offsets have been determined.
     encodeSafepoints();
 
     // List of possible scripts that this graph may call. Currently this is
@@ -9715,28 +9724,41 @@ CodeGenerator::visitAsmJSInterruptCheck(
     }
     masm.bind(&rejoin);
     return true;
 }
 
 typedef bool (*RecompileFn)(JSContext *);
 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile);
 
+typedef bool (*ForcedRecompileFn)(JSContext *);
+static const VMFunction ForcedRecompileFnInfo = FunctionInfo<ForcedRecompileFn>(ForcedRecompile);
+
 bool
 CodeGenerator::visitRecompileCheck(LRecompileCheck *ins)
 {
     Label done;
     Register tmp = ToRegister(ins->scratch());
-    OutOfLineCode *ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
+    OutOfLineCode *ool;
+    if (ins->mir()->forceRecompilation())
+        ool = oolCallVM(ForcedRecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
+    else
+        ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
 
     // Check if warm-up counter is high enough.
-    masm.movePtr(ImmPtr(ins->mir()->script()->addressOfWarmUpCounter()), tmp);
-    Address ptr(tmp, 0);
-    masm.add32(Imm32(1), tmp);
-    masm.branch32(Assembler::BelowOrEqual, ptr, Imm32(ins->mir()->recompileThreshold()), &done);
+    AbsoluteAddress warmUpCount = AbsoluteAddress(ins->mir()->script()->addressOfWarmUpCounter());
+    if (ins->mir()->increaseWarmUpCounter()) {
+        masm.load32(warmUpCount, tmp);
+        masm.add32(Imm32(1), tmp);
+        masm.store32(tmp, warmUpCount);
+        masm.branch32(Assembler::BelowOrEqual, tmp, Imm32(ins->mir()->recompileThreshold()), &done);
+    } else {
+        masm.branch32(Assembler::BelowOrEqual, warmUpCount, Imm32(ins->mir()->recompileThreshold()),
+                      &done);
+    }
 
     // Check if not yet recompiling.
     CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), tmp);
     if (!ionScriptLabels_.append(label))
         return false;
     masm.branch32(Assembler::Equal,
                   Address(tmp, IonScript::offsetOfRecompiling()),
                   Imm32(0),
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2216,17 +2216,17 @@ GetOptimizationLevel(HandleScript script
 
     MOZ_ASSERT(executionMode == SequentialExecution);
 
     return js_IonOptimizations.levelForScript(script, pc);
 }
 
 static MethodStatus
 Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
-        bool constructing, ExecutionMode executionMode)
+        bool constructing, ExecutionMode executionMode, bool forceRecompile = false)
 {
     MOZ_ASSERT(jit::IsIonEnabled(cx));
     MOZ_ASSERT(jit::IsBaselineEnabled(cx));
     MOZ_ASSERT_IF(osrPc != nullptr, LoopEntryCanIonOsr(osrPc));
     MOZ_ASSERT_IF(executionMode == ParallelExecution, !osrFrame && !osrPc);
     MOZ_ASSERT_IF(executionMode == ParallelExecution, !HasIonScript(script, executionMode));
 
     if (!script->hasBaselineScript())
@@ -2253,45 +2253,27 @@ Compile(JSContext *cx, HandleScript scri
     if (optimizationLevel == Optimization_DontCompile)
         return Method_Skipped;
 
     IonScript *scriptIon = GetIonScript(script, executionMode);
     if (scriptIon) {
         if (!scriptIon->method())
             return Method_CantCompile;
 
-        MethodStatus failedState = Method_Compiled;
-
-        // If we keep failing to enter the script due to an OSR pc mismatch,
-        // recompile with the right pc.
-        if (osrPc && script->ionScript()->osrPc() != osrPc) {
-            uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
-            if (count <= js_JitOptions.osrPcMismatchesBeforeRecompile)
-                return Method_Skipped;
-
-            failedState = Method_Skipped;
-        }
-
         // Don't recompile/overwrite higher optimized code,
         // with a lower optimization level.
-        if (optimizationLevel < scriptIon->optimizationLevel())
-            return failedState;
-
-        if (optimizationLevel == scriptIon->optimizationLevel() &&
-            (!osrPc || script->ionScript()->osrPc() == osrPc))
-        {
-            return failedState;
-        }
+        if (optimizationLevel <= scriptIon->optimizationLevel() && !forceRecompile)
+            return Method_Compiled;
 
         // Don't start compiling if already compiling
         if (scriptIon->isRecompiling())
-            return failedState;
+            return Method_Compiled;
 
         if (osrPc)
-            script->ionScript()->resetOsrPcMismatchCounter();
+            scriptIon->resetOsrPcMismatchCounter();
 
         recompile = true;
     }
 
     AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing, executionMode,
                                     recompile, optimizationLevel);
     if (reason == AbortReason_Error)
         return Method_Error;
@@ -2300,21 +2282,18 @@ Compile(JSContext *cx, HandleScript scri
         return Method_CantCompile;
 
     if (reason == AbortReason_Alloc) {
         js_ReportOutOfMemory(cx);
         return Method_Error;
     }
 
     // Compilation succeeded or we invalidated right away or an inlining/alloc abort
-    if (HasIonScript(script, executionMode)) {
-        if (osrPc && script->ionScript()->osrPc() != osrPc)
-            return Method_Skipped;
+    if (HasIonScript(script, executionMode))
         return Method_Compiled;
-    }
     return Method_Skipped;
 }
 
 } // namespace jit
 } // namespace js
 
 // Decide if a transition from interpreter execution to Ion code should occur.
 // May compile or recompile the target JSScript.
@@ -2342,30 +2321,47 @@ jit::CanEnterAtBranch(JSContext *cx, JSS
         return Method_Skipped;
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(osrFrame)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
+    // By default a recompilation doesn't happen on osr mismatch.
+    // Decide if we want to force a recompilation if this happens too much.
+    bool force = false;
+    if (script->hasIonScript() && pc != script->ionScript()->osrPc()) {
+        uint32_t count = script->ionScript()->incrOsrPcMismatchCounter();
+        if (count <= js_JitOptions.osrPcMismatchesBeforeRecompile)
+            return Method_Skipped;
+        force = true;
+    }
+
     // Attempt compilation.
     // - Returns Method_Compiled if the right ionscript is present
     //   (Meaning it was present or a sequantial compile finished)
     // - Returns Method_Skipped if pc doesn't match
     //   (This means a background thread compilation with that pc could have started or not.)
     RootedScript rscript(cx, script);
     MethodStatus status = Compile(cx, rscript, osrFrame, pc, osrFrame->isConstructing(),
-                                  SequentialExecution);
+                                  SequentialExecution, force);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
+    // Return the compilation was skipped when the osr pc wasn't adjusted.
+    // This can happen when there was still an IonScript available and a
+    // background compilation started, but hasn't finished yet.
+    // Or when we didn't force a recompile.
+    if (pc != script->ionScript()->osrPc())
+        return Method_Skipped;
+
     return Method_Compiled;
 }
 
 MethodStatus
 jit::CanEnter(JSContext *cx, RunState &state)
 {
     MOZ_ASSERT(jit::IsIonEnabled(cx));
 
@@ -2456,24 +2452,24 @@ jit::CompileFunctionForBaseline(JSContex
         return status;
     }
 
     return Method_Compiled;
 }
 
 MethodStatus
 jit::Recompile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
-               bool constructing)
+               bool constructing, bool force)
 {
     MOZ_ASSERT(script->hasIonScript());
     if (script->ionScript()->isRecompiling())
         return Method_Compiled;
 
     MethodStatus status =
-        Compile(cx, script, osrFrame, osrPc, constructing, SequentialExecution);
+        Compile(cx, script, osrFrame, osrPc, constructing, SequentialExecution, force);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
 }
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -88,17 +88,17 @@ MethodStatus CanEnterAtBranch(JSContext 
 MethodStatus CanEnter(JSContext *cx, RunState &state);
 MethodStatus CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame);
 MethodStatus CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs);
 
 MethodStatus CanEnterInParallel(JSContext *cx, HandleScript script);
 
 MethodStatus
 Recompile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
-          bool constructing);
+          bool constructing, bool force);
 
 enum IonExecStatus
 {
     // The method call had to be aborted due to a stack limit check. This
     // error indicates that Ion never attempted to clean up frames.
     IonExec_Aborted,
 
     // The method call resulted in an error, and IonMonkey has cleaned up
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4449,17 +4449,19 @@ IonBuilder::makeInliningDecision(JSFunct
 
         // Callee must have been called a few times to have somewhat stable
         // type information, except for definite properties analysis,
         // as the caller has not run yet.
         if (targetScript->getWarmUpCount() < optimizationInfo().inliningWarmUpThreshold() &&
             !targetScript->baselineScript()->ionCompiledOrInlined() &&
             info().executionMode() != DefinitePropertiesAnalysis)
         {
-            return DontInline(targetScript, "Vetoed: callee is insufficiently hot.");
+            JitSpew(JitSpew_Inlining, "Cannot inline %s:%u: callee is insufficiently hot.",
+                    targetScript->filename(), targetScript->lineno());
+            return InliningDecision_WarmUpCountTooLow;
         }
     }
 
     // TI calls ObjectStateChange to trigger invalidation of the caller.
     types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
     targetType->watchStateChangeForInlinedCall(constraints());
 
     return InliningDecision_Inline;
@@ -4484,16 +4486,17 @@ IonBuilder::selectInliningTargets(Object
     for (size_t i = 0; i < targets.length(); i++) {
         JSFunction *target = &targets[i]->as<JSFunction>();
         bool inlineable;
         InliningDecision decision = makeInliningDecision(target, callInfo);
         switch (decision) {
           case InliningDecision_Error:
             return false;
           case InliningDecision_DontInline:
+          case InliningDecision_WarmUpCountTooLow:
             inlineable = false;
             break;
           case InliningDecision_Inline:
             inlineable = true;
             break;
           default:
             MOZ_CRASH("Unhandled InliningDecision value!");
         }
@@ -4659,16 +4662,18 @@ IonBuilder::inlineCallsite(ObjectVector 
     if (!propCache.get() && targets.length() == 1) {
         JSFunction *target = &targets[0]->as<JSFunction>();
         InliningDecision decision = makeInliningDecision(target, callInfo);
         switch (decision) {
           case InliningDecision_Error:
             return InliningStatus_Error;
           case InliningDecision_DontInline:
             return InliningStatus_NotInlined;
+          case InliningDecision_WarmUpCountTooLow:
+            return InliningStatus_WarmUpCountTooLow;
           case InliningDecision_Inline:
             break;
         }
 
         // Inlining will elminate uses of the original callee, but it needs to
         // be preserved in phis if we bail out.  Mark the old callee definition as
         // implicitly used to ensure this happens.
         callInfo.fun()->setImplicitlyUsedUnchecked();
@@ -5273,16 +5278,17 @@ IonBuilder::jsop_funcall(uint32_t argc)
 
     // Try to inline the call.
     if (!zeroArguments) {
         InliningDecision decision = makeInliningDecision(target, callInfo);
         switch (decision) {
           case InliningDecision_Error:
             return false;
           case InliningDecision_DontInline:
+          case InliningDecision_WarmUpCountTooLow:
             break;
           case InliningDecision_Inline:
             if (target->isInterpreted())
                 return inlineScriptedCall(callInfo, target);
             break;
         }
     }
 
@@ -5413,16 +5419,17 @@ IonBuilder::jsop_funapplyarguments(uint3
     nativeFunc->setImplicitlyUsedUnchecked();
 
     // Try to inline the call.
     InliningDecision decision = makeInliningDecision(target, callInfo);
     switch (decision) {
       case InliningDecision_Error:
         return false;
       case InliningDecision_DontInline:
+      case InliningDecision_WarmUpCountTooLow:
         break;
       case InliningDecision_Inline:
         if (target->isInterpreted())
             return inlineScriptedCall(callInfo, target);
     }
 
     return makeCall(target, callInfo, false);
 }
@@ -5483,16 +5490,24 @@ IonBuilder::jsop_call(uint32_t argc, boo
     if (status == InliningStatus_Error)
         return false;
 
     // No inline, just make the call.
     JSFunction *target = nullptr;
     if (targets.length() == 1)
         target = &targets[0]->as<JSFunction>();
 
+    if (target && status == InliningStatus_WarmUpCountTooLow) {
+        MRecompileCheck *check =
+            MRecompileCheck::New(alloc(), target->nonLazyScript(),
+                                 optimizationInfo().inliningRecompileThreshold(),
+                                 MRecompileCheck::RecompileCheck_Inlining);
+        current->add(check);
+    }
+
     return makeCall(target, callInfo, hasClones);
 }
 
 MDefinition *
 IonBuilder::makeCallsiteClone(JSFunction *target, MDefinition *fun)
 {
     // Bake in the clone eagerly if we have a known target. We have arrived here
     // because TI told us that the known target is a should-clone-at-callsite
@@ -6499,17 +6514,19 @@ IonBuilder::insertRecompileCheck()
     while (topBuilder->callerBuilder_)
         topBuilder = topBuilder->callerBuilder_;
 
     // Add recompile check to recompile when the warm-up count reaches the
     // threshold of the next optimization level.
     OptimizationLevel nextLevel = js_IonOptimizations.nextLevel(curLevel);
     const OptimizationInfo *info = js_IonOptimizations.get(nextLevel);
     uint32_t warmUpThreshold = info->compilerWarmUpThreshold(topBuilder->script());
-    current->add(MRecompileCheck::New(alloc(), topBuilder->script(), warmUpThreshold));
+    MRecompileCheck *check = MRecompileCheck::New(alloc(), topBuilder->script(), warmUpThreshold,
+                                MRecompileCheck::RecompileCheck_OptimizationLevel);
+    current->add(check);
 }
 
 JSObject *
 IonBuilder::testSingletonProperty(JSObject *obj, PropertyName *name)
 {
     // We would like to completely no-op property/global accesses which can
     // produce only a particular JSObject. When indicating the access result is
     // definitely an object, type inference does not account for the
@@ -9420,32 +9437,34 @@ IonBuilder::getPropTryCommonGetter(bool 
     if (!callInfo.init(current, 0))
         return false;
 
     if (commonGetter->isNative()) {
         InliningStatus status = inlineNativeGetter(callInfo, commonGetter);
         switch (status) {
           case InliningStatus_Error:
             return false;
+          case InliningStatus_WarmUpCountTooLow:
           case InliningStatus_NotInlined:
             break;
           case InliningStatus_Inlined:
             *emitted = true;
             return true;
         }
     }
 
     // Inline if we can, otherwise, forget it and just generate a call.
     bool inlineable = false;
     if (commonGetter->isInterpreted()) {
         InliningDecision decision = makeInliningDecision(commonGetter, callInfo);
         switch (decision) {
           case InliningDecision_Error:
             return false;
           case InliningDecision_DontInline:
+          case InliningDecision_WarmUpCountTooLow:
             break;
           case InliningDecision_Inline:
             inlineable = true;
             break;
         }
     }
 
     if (inlineable) {
@@ -9860,16 +9879,17 @@ IonBuilder::setPropTryCommonSetter(bool 
 
     // Inline the setter if we can.
     if (commonSetter->isInterpreted()) {
         InliningDecision decision = makeInliningDecision(commonSetter, callInfo);
         switch (decision) {
           case InliningDecision_Error:
             return false;
           case InliningDecision_DontInline:
+          case InliningDecision_WarmUpCountTooLow:
             break;
           case InliningDecision_Inline:
             if (!inlineScriptedCall(callInfo, commonSetter))
                 return false;
             *emitted = true;
             return true;
         }
     }
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -661,24 +661,26 @@ class IonBuilder
     bool jsop_setaliasedvar(ScopeCoordinate sc);
 
     /* Inlining. */
 
     enum InliningStatus
     {
         InliningStatus_Error,
         InliningStatus_NotInlined,
+        InliningStatus_WarmUpCountTooLow,
         InliningStatus_Inlined
     };
 
     enum InliningDecision
     {
         InliningDecision_Error,
         InliningDecision_Inline,
-        InliningDecision_DontInline
+        InliningDecision_DontInline,
+        InliningDecision_WarmUpCountTooLow
     };
 
     static InliningDecision DontInline(JSScript *targetScript, const char *reason);
 
     // Oracles.
     InliningDecision canInlineTarget(JSFunction *target, CallInfo &callInfo);
     InliningDecision makeInliningDecision(JSFunction *target, CallInfo &callInfo);
     bool selectInliningTargets(ObjectVector &targets, CallInfo &callInfo,
--- a/js/src/jit/IonOptimizationLevels.cpp
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -37,16 +37,17 @@ OptimizationInfo::initNormalOptimization
 
     inlineMaxTotalBytecodeLength_ = 1000;
     inliningMaxCallerBytecodeLength_ = 10000;
     maxInlineDepth_ = 3;
     scalarReplacement_ = true;
     smallFunctionMaxInlineDepth_ = 10;
     compilerWarmUpThreshold_ = 1000;
     inliningWarmUpThresholdFactor_ = 0.125;
+    inliningRecompileThresholdFactor_ = 4;
 }
 
 void
 OptimizationInfo::initAsmjsOptimizationInfo()
 {
     // The AsmJS optimization level
     // Disables some passes that don't work well with asmjs.
 
--- a/js/src/jit/IonOptimizationLevels.h
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -103,16 +103,21 @@ class OptimizationInfo
     // How many invocations or loop iterations are needed before functions
     // are compiled.
     uint32_t compilerWarmUpThreshold_;
 
     // How many invocations or loop iterations are needed before calls
     // are inlined, as a fraction of compilerWarmUpThreshold.
     double inliningWarmUpThresholdFactor_;
 
+    // How many invocations or loop iterations are needed before a function
+    // is hot enough to recompile the outerScript to inline that function,
+    // as a multiplication of inliningWarmUpThreshold.
+    uint32_t inliningRecompileThresholdFactor_;
+
     OptimizationInfo()
     { }
 
     void initNormalOptimizationInfo();
     void initAsmjsOptimizationInfo();
 
     OptimizationLevel level() const {
         return level_;
@@ -189,16 +194,20 @@ class OptimizationInfo
     }
 
     uint32_t inliningWarmUpThreshold() const {
         uint32_t compilerWarmUpThreshold = compilerWarmUpThreshold_;
         if (js_JitOptions.forceDefaultIonWarmUpThreshold)
             compilerWarmUpThreshold = js_JitOptions.forcedDefaultIonWarmUpThreshold;
         return compilerWarmUpThreshold * inliningWarmUpThresholdFactor_;
     }
+
+    uint32_t inliningRecompileThreshold() const {
+        return inliningWarmUpThreshold() * inliningRecompileThresholdFactor_;
+    }
 };
 
 class OptimizationInfos
 {
   private:
     OptimizationInfo infos_[Optimization_Count - 1];
 
   public:
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11200,41 +11200,73 @@ class MHasClass
     }
 };
 
 // Increase the warm-up counter of the provided script upon execution and test if
 // the warm-up counter surpasses the threshold. Upon hit it will recompile the
 // outermost script (i.e. not the inlined script).
 class MRecompileCheck : public MNullaryInstruction
 {
+  public:
+    enum RecompileCheckType {
+        RecompileCheck_OptimizationLevel,
+        RecompileCheck_Inlining
+    };
+
+  private:
     JSScript *script_;
     uint32_t recompileThreshold_;
-
-    MRecompileCheck(JSScript *script, uint32_t recompileThreshold)
+    bool forceRecompilation_;
+    bool increaseWarmUpCounter_;
+
+    MRecompileCheck(JSScript *script, uint32_t recompileThreshold, RecompileCheckType type)
       : script_(script),
         recompileThreshold_(recompileThreshold)
     {
+        switch (type) {
+          case RecompileCheck_OptimizationLevel:
+            forceRecompilation_ = false;
+            increaseWarmUpCounter_ = true;
+            break;
+          case RecompileCheck_Inlining:
+            forceRecompilation_ = true;
+            increaseWarmUpCounter_ = false;
+            break;
+          default:
+            MOZ_CRASH("Unexpected recompile check type");
+        }
+
         setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(RecompileCheck);
 
-    static MRecompileCheck *New(TempAllocator &alloc, JSScript *script_, uint32_t recompileThreshold) {
-        return new(alloc) MRecompileCheck(script_, recompileThreshold);
+    static MRecompileCheck *New(TempAllocator &alloc, JSScript *script_, uint32_t recompileThreshold,
+                                RecompileCheckType type)
+    {
+        return new(alloc) MRecompileCheck(script_, recompileThreshold, type);
     }
 
     JSScript *script() const {
         return script_;
     }
 
     uint32_t recompileThreshold() const {
         return recompileThreshold_;
     }
 
+    bool forceRecompilation() const {
+        return forceRecompilation_;
+    }
+
+    bool increaseWarmUpCounter() const {
+        return increaseWarmUpCounter_;
+    }
+
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 class MAsmJSNeg : public MUnaryInstruction
 {
     MAsmJSNeg(MDefinition *op, MIRType type)
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1044,40 +1044,52 @@ StringReplace(JSContext *cx, HandleStrin
     RootedValue rval(cx);
     if (!str_replace_string_raw(cx, string, pattern, repl, &rval))
         return nullptr;
 
     return rval.toString();
 }
 
 bool
-Recompile(JSContext *cx)
+RecompileImpl(JSContext *cx, bool force)
 {
     MOZ_ASSERT(cx->currentlyRunningInJit());
     JitActivationIterator activations(cx->runtime());
     JitFrameIterator iter(activations);
 
     MOZ_ASSERT(iter.type() == JitFrame_Exit);
     ++iter;
 
     bool isConstructing = iter.isConstructing();
     RootedScript script(cx, iter.script());
     MOZ_ASSERT(script->hasIonScript());
 
     if (!IsIonEnabled(cx))
         return true;
 
-    MethodStatus status = Recompile(cx, script, nullptr, nullptr, isConstructing);
+    MethodStatus status = Recompile(cx, script, nullptr, nullptr, isConstructing, force);
     if (status == Method_Error)
         return false;
 
     return true;
 }
 
 bool
+ForcedRecompile(JSContext *cx)
+{
+    return RecompileImpl(cx, /* force = */ true);
+}
+
+bool
+Recompile(JSContext *cx)
+{
+    return RecompileImpl(cx, /* force = */ false);
+}
+
+bool
 SetDenseElement(JSContext *cx, HandleNativeObject obj, int32_t index, HandleValue value,
                 bool strict)
 {
     // This function is called from Ion code for StoreElementHole's OOL path.
     // In this case we know the object is native, has no indexed properties
     // and we can use setDenseElement instead of setDenseElementWithType.
     MOZ_ASSERT(!obj->isIndexed());
 
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -719,16 +719,17 @@ bool InitBaselineFrameForOsr(BaselineFra
                              uint32_t numStackValues);
 
 JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject descr,
                                 HandleObject owner, int32_t offset);
 
 bool ArraySpliceDense(JSContext *cx, HandleObject obj, uint32_t start, uint32_t deleteCount);
 
 bool Recompile(JSContext *cx);
+bool ForcedRecompile(JSContext *cx);
 JSString *RegExpReplace(JSContext *cx, HandleString string, HandleObject regexp,
                         HandleString repl);
 JSString *StringReplace(JSContext *cx, HandleString string, HandleString pattern,
                         HandleString repl);
 
 bool SetDenseElement(JSContext *cx, HandleNativeObject obj, int32_t index, HandleValue value,
                      bool strict);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2152,17 +2152,17 @@ PtrIsInRange(const void *ptr, const void
 
 static bool
 RelocateCell(Zone *zone, TenuredCell *src, AllocKind thingKind, size_t thingSize)
 {
     // Allocate a new cell.
     MOZ_ASSERT(zone == src->zone());
     void *dstAlloc = zone->allocator.arenas.allocateFromFreeList(thingKind, thingSize);
     if (!dstAlloc)
-        dstAlloc = js::gc::ArenaLists::refillFreeListInGC(zone, thingKind);
+        dstAlloc = GCRuntime::refillFreeListInGC(zone, thingKind);
     if (!dstAlloc)
         return false;
     TenuredCell *dst = TenuredCell::fromPointer(dstAlloc);
 
     // Copy source cell contents to destination.
     memcpy(dst, src, thingSize);
 
     if (thingKind <= FINALIZE_OBJECT_LAST) {
@@ -2567,24 +2567,32 @@ ArenaLists::backgroundFinalize(FreeOp *f
     // arenas may be allocated before background finalization finishes; now that
     // finalization is complete, we want to merge these lists back together.
     ArenaLists *lists = &zone->allocator.arenas;
     ArenaList *al = &lists->arenaLists[thingKind];
 
     // Flatten |finalizedSorted| into a regular ArenaList.
     ArenaList finalized = finalizedSorted.toArenaList();
 
-    AutoLockGC lock(fop->runtime());
-    MOZ_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN);
-
-    // Join |al| and |finalized| into a single list.
-    *al = finalized.insertListWithCursorAtEnd(*al);
+    // We must take the GC lock to be able to safely modify the ArenaList;
+    // however, this does not by itself make the changes visible to all threads,
+    // as not all threads take the GC lock to read the ArenaLists.
+    // That safety is provided by the ReleaseAcquire memory ordering of the
+    // background finalize state, which we explicitly set as the final step.
+    {
+        AutoLockGC lock(fop->runtime());
+        MOZ_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN);
+
+        // Join |al| and |finalized| into a single list.
+        *al = finalized.insertListWithCursorAtEnd(*al);
+
+        lists->arenaListsToSweep[thingKind] = nullptr;
+    }
 
     lists->backgroundFinalizeState[thingKind] = BFS_DONE;
-    lists->arenaListsToSweep[thingKind] = nullptr;
 }
 
 void
 ArenaLists::queueObjectsForSweep(FreeOp *fop)
 {
     gcstats::AutoPhase ap(fop->runtime()->gc.stats, gcstats::PHASE_SWEEP_OBJECT);
 
     finalizeNow(fop, FINALIZE_OBJECT0);
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -73,16 +73,17 @@ template<class E> class nsCOMArray;
 class nsWeakFrame;
 class nsIScrollableFrame;
 class gfxContext;
 class nsIDOMEvent;
 class nsDisplayList;
 class nsDisplayListBuilder;
 class nsPIDOMWindow;
 struct nsPoint;
+class nsINode;
 struct nsIntPoint;
 struct nsIntRect;
 struct nsRect;
 class nsRegion;
 class nsRefreshDriver;
 class nsARefreshObserver;
 class nsAPostRefreshObserver;
 #ifdef ACCESSIBILITY
@@ -134,18 +135,18 @@ typedef struct CapturingContentInfo {
   bool mAllowed;
   bool mPointerLock;
   bool mRetargetToElement;
   bool mPreventDrag;
   nsIContent* mContent;
 } CapturingContentInfo;
 
 #define NS_IPRESSHELL_IID \
-		{ 0x42e9a352, 0x76f3, 0x4ba3, \
-		  { 0x94, 0x0b, 0x78, 0x9e, 0x58, 0x38, 0x73, 0x4f } }
+  { 0xa0a4b515, 0x0b91, 0x4f13, \
+    { 0xa0, 0x60, 0x4b, 0xfb, 0x35, 0x00, 0xdc, 0x00 } }
 
 // debug VerifyReflow flags
 #define VERIFY_REFLOW_ON                    0x01
 #define VERIFY_REFLOW_NOISY                 0x02
 #define VERIFY_REFLOW_ALL                   0x04
 #define VERIFY_REFLOW_DUMP_COMMANDS         0x08
 #define VERIFY_REFLOW_NOISY_RC              0x10
 #define VERIFY_REFLOW_REALLY_NOISY_RC       0x20
@@ -851,16 +852,23 @@ public:
    * Dispatch event to content only (NOT full processing)
    * @note The caller must have a strong reference to the PresShell.
    */
   virtual nsresult HandleDOMEventWithTarget(nsIContent* aTargetContent,
                                                         nsIDOMEvent* aEvent,
                                                         nsEventStatus* aStatus) = 0;
 
   /**
+   * Dispatch AfterKeyboardEvent with specific target.
+   */
+  virtual void DispatchAfterKeyboardEvent(nsINode* aTarget,
+                                          const mozilla::WidgetKeyboardEvent& aEvent,
+                                          bool aEmbeddedCancelled) = 0;
+
+  /**
     * Gets the current target event frame from the PresShell
     */
   virtual nsIFrame* GetEventTargetFrame() = 0;
 
   /**
     * Gets the current target event frame from the PresShell
     */
   virtual already_AddRefed<nsIContent> GetEventTargetContent(
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -37,16 +37,17 @@
 
 #ifdef XP_WIN
 #include "winuser.h"
 #endif
 
 #include "nsPresShell.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
+#include "mozilla/dom/BeforeAfterKeyboardEvent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState()
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/PointerEvent.h"
 #include "nsIDocument.h"
 #include "nsAnimationManager.h"
 #include "nsNameSpaceManager.h"  // for Pref-related rule management (bugs 22963,20760,31816)
 #include "nsFrame.h"
@@ -67,25 +68,28 @@
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMElement.h"
 #include "nsRange.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsReadableUtils.h"
 #include "nsIPageSequenceFrame.h"
+#include "nsIPermissionManager.h"
+#include "nsIMozBrowserFrame.h"
 #include "nsCaret.h"
 #include "TouchCaret.h"
 #include "SelectionCarets.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsFrameManager.h"
 #include "nsXPCOM.h"
 #include "nsILayoutHistoryState.h"
 #include "nsILineIterator.h" // for ScrollContentIntoView
 #include "pldhash.h"
+#include "mozilla/dom/BeforeAfterKeyboardEventBinding.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/dom/PointerEventBinding.h"
 #include "nsIObserverService.h"
 #include "nsDocShell.h"        // for reflow observation
 #include "nsIBaseWindow.h"
 #include "nsError.h"
 #include "nsLayoutUtils.h"
 #include "nsViewportInfo.h"
@@ -700,16 +704,17 @@ nsIPresShell::FrameSelection()
 
 //----------------------------------------------------------------------
 
 static bool sSynthMouseMove = true;
 static uint32_t sNextPresShellId;
 static bool sPointerEventEnabled = true;
 static bool sTouchCaretEnabled = false;
 static bool sSelectionCaretEnabled = false;
+static bool sBeforeAfterKeyboardEventEnabled = false;
 
 /* static */ bool
 PresShell::TouchCaretPrefEnabled()
 {
   static bool initialized = false;
   if (!initialized) {
     Preferences::AddBoolVarCache(&sTouchCaretEnabled, "touchcaret.enabled");
     initialized = true;
@@ -723,16 +728,28 @@ PresShell::SelectionCaretPrefEnabled()
   static bool initialized = false;
   if (!initialized) {
     Preferences::AddBoolVarCache(&sSelectionCaretEnabled, "selectioncaret.enabled");
     initialized = true;
   }
   return sSelectionCaretEnabled;
 }
 
+/* static */ bool
+PresShell::BeforeAfterKeyboardEventEnabled()
+{
+  static bool sInitialized = false;
+  if (!sInitialized) {
+    Preferences::AddBoolVarCache(&sBeforeAfterKeyboardEventEnabled,
+      "dom.beforeAfterKeyboardEvent.enabled");
+    sInitialized = true;
+  }
+  return sBeforeAfterKeyboardEventEnabled;
+}
+
 PresShell::PresShell()
   : mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)
 {
   mSelection = nullptr;
 #ifdef MOZ_REFLOW_PERF
   mReflowCountMgr = new ReflowCountMgr();
   mReflowCountMgr->SetPresContext(mPresContext);
   mReflowCountMgr->SetPresShell(this);
@@ -6857,16 +6874,246 @@ public:
     mContent = aContent;
   }
 
 private:
   int32_t mPointerId;
   nsCOMPtr<nsIContent> mContent;
 };
 
+static bool
+CheckPermissionForBeforeAfterKeyboardEvent(Element* aElement)
+{
+  // An element which is chrome-privileged should be able to handle before
+  // events and after events.
+  nsIPrincipal* principal = aElement->NodePrincipal();
+  if (nsContentUtils::IsSystemPrincipal(principal)) {
+    return true;
+  }
+
+  // An element which has "before-after-keyboard-event" permission should be
+  // able to handle before events and after events.
+  nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
+  uint32_t permission = nsIPermissionManager::DENY_ACTION;
+  if (permMgr) {
+    permMgr->TestPermissionFromPrincipal(principal, "before-after-keyboard-event", &permission);
+    if (permission == nsIPermissionManager::ALLOW_ACTION) {
+      return true;
+    }
+
+    // Check "embed-apps" permission for later use.
+    permission = nsIPermissionManager::DENY_ACTION;
+    permMgr->TestPermissionFromPrincipal(principal, "embed-apps", &permission);
+  }
+
+  // An element can handle before events and after events if the following
+  // conditions are met:
+  // 1) <iframe mozbrowser mozapp>
+  // 2) it has "embed-apps" permission.
+  nsCOMPtr<nsIMozBrowserFrame> browserFrame(do_QueryInterface(aElement));
+  if ((permission == nsIPermissionManager::ALLOW_ACTION) &&
+      browserFrame && browserFrame->GetReallyIsApp()) {
+    return true;
+  }
+
+  return false;
+}
+
+static void
+BuildTargetChainForBeforeAfterKeyboardEvent(nsINode* aTarget,
+                                            nsTArray<nsCOMPtr<Element> >& aChain,
+                                            bool& aTargetIsIframe)
+{
+  nsCOMPtr<nsIContent> content(do_QueryInterface(aTarget));
+  nsCOMPtr<nsPIDOMWindow> window;
+  Element* frameElement;
+
+  // Initialize frameElement.
+  if (content && content->IsHTML(nsGkAtoms::iframe)) {
+    aTargetIsIframe = true;
+    frameElement = aTarget->AsElement();
+  } else {
+    // If event target is not an iframe, dispatch keydown/keyup event to its
+    // window after dispatching before events to its ancestors.
+    aTargetIsIframe = false;
+
+    // And skip the event target and get its parent frame.
+    window = aTarget->OwnerDoc()->GetWindow();
+    if (window) {
+      frameElement = window->GetFrameElementInternal();
+    }
+  }
+
+  // Check permission for all ancestors and add them into the target chain.
+  while (frameElement) {
+    if (CheckPermissionForBeforeAfterKeyboardEvent(frameElement)) {
+      aChain.AppendElement(frameElement);
+    }
+    window = frameElement->OwnerDoc()->GetWindow();
+    frameElement = window ? window->GetFrameElementInternal() : nullptr;
+  }
+}
+
+void
+PresShell::DispatchBeforeKeyboardEventInternal(const nsTArray<nsCOMPtr<Element> >& aChain,
+                                               const WidgetKeyboardEvent& aEvent,
+                                               size_t& aChainIndex,
+                                               bool& aDefaultPrevented)
+{
+  size_t length = aChain.Length();
+  if (!CanDispatchEvent(&aEvent) || !length) {
+    return;
+  }
+
+  uint32_t message =
+    (aEvent.message == NS_KEY_DOWN) ? NS_KEY_BEFORE_DOWN : NS_KEY_BEFORE_UP;
+  nsCOMPtr<EventTarget> eventTarget;
+  // Dispatch before events from the outermost element.
+  for (int32_t i = length - 1; i >= 0; i--) {
+    eventTarget = do_QueryInterface(aChain[i]->OwnerDoc()->GetWindow());
+    if (!eventTarget || !CanDispatchEvent(&aEvent)) {
+      return;
+    }
+
+    aChainIndex = i;
+    InternalBeforeAfterKeyboardEvent beforeEvent(aEvent.mFlags.mIsTrusted,
+                                                 message, aEvent.widget);
+    beforeEvent.AssignBeforeAfterKeyEventData(aEvent, false);
+    EventDispatcher::Dispatch(eventTarget, mPresContext, &beforeEvent);
+
+    if (beforeEvent.mFlags.mDefaultPrevented) {
+      aDefaultPrevented = true;
+      return;
+    }
+  }
+}
+
+void
+PresShell::DispatchAfterKeyboardEventInternal(const nsTArray<nsCOMPtr<Element> >& aChain,
+                                              const WidgetKeyboardEvent& aEvent,
+                                              bool aEmbeddedCancelled,
+                                              size_t aStartOffset)
+{
+  size_t length = aChain.Length();
+  if (!CanDispatchEvent(&aEvent) || !length) {
+    return;
+  }
+
+  uint32_t message =
+    (aEvent.message == NS_KEY_DOWN) ? NS_KEY_AFTER_DOWN : NS_KEY_AFTER_UP;
+  bool embeddedCancelled = aEmbeddedCancelled;
+  nsCOMPtr<EventTarget> eventTarget;
+  // Dispatch after events from the innermost element.
+  for (uint32_t i = aStartOffset; i < length; i++) {
+    eventTarget = do_QueryInterface(aChain[i]->OwnerDoc()->GetWindow());
+    if (!eventTarget || !CanDispatchEvent(&aEvent)) {
+      return;
+    }
+
+    InternalBeforeAfterKeyboardEvent afterEvent(aEvent.mFlags.mIsTrusted,
+                                                message, aEvent.widget);
+    afterEvent.AssignBeforeAfterKeyEventData(aEvent, false);
+    afterEvent.mEmbeddedCancelled.SetValue(embeddedCancelled);
+    EventDispatcher::Dispatch(eventTarget, mPresContext, &afterEvent);
+    embeddedCancelled = afterEvent.mFlags.mDefaultPrevented;
+  }
+}
+
+void
+PresShell::DispatchAfterKeyboardEvent(nsINode* aTarget,
+                                      const WidgetKeyboardEvent& aEvent,
+                                      bool aEmbeddedCancelled)
+{
+  MOZ_ASSERT(aTarget);
+  MOZ_ASSERT(BeforeAfterKeyboardEventEnabled());
+
+  if (NS_WARN_IF(aEvent.message != NS_KEY_DOWN &&
+                 aEvent.message != NS_KEY_UP)) {
+    return;
+  }
+
+  // Build up a target chain. Each item in the chain will receive an after event.
+  nsAutoTArray<nsCOMPtr<Element>, 5> chain;
+  bool targetIsIframe = false;
+  BuildTargetChainForBeforeAfterKeyboardEvent(aTarget, chain, targetIsIframe);
+  DispatchAfterKeyboardEventInternal(chain, aEvent, aEmbeddedCancelled);
+}
+
+bool
+PresShell::CanDispatchEvent(const WidgetGUIEvent* aEvent) const
+{
+  bool rv =
+    mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
+  if (aEvent) {
+    rv &= (aEvent && aEvent->widget && !aEvent->widget->Destroyed());
+  }
+  return rv;
+}
+
+void
+PresShell::HandleKeyboardEvent(nsINode* aTarget,
+                               WidgetKeyboardEvent& aEvent,
+                               bool aEmbeddedCancelled,
+                               nsEventStatus* aStatus,
+                               EventDispatchingCallback* aEventCB)
+{
+  if (aEvent.message == NS_KEY_PRESS ||
+      !BeforeAfterKeyboardEventEnabled()) {
+    EventDispatcher::Dispatch(aTarget, mPresContext,
+                              &aEvent, nullptr, aStatus, aEventCB);
+    return;
+  }
+
+  MOZ_ASSERT(aTarget);
+  MOZ_ASSERT(aEvent.message == NS_KEY_DOWN || aEvent.message == NS_KEY_UP);
+
+  // Build up a target chain. Each item in the chain will receive a before event.
+  nsAutoTArray<nsCOMPtr<Element>, 5> chain;
+  bool targetIsIframe = false;
+  BuildTargetChainForBeforeAfterKeyboardEvent(aTarget, chain, targetIsIframe);
+
+  // Dispatch before events. If each item in the chain consumes the before
+  // event and doesn't prevent the default action, we will go further to
+  // dispatch the actual key event and after events in the reverse order.
+  // Otherwise, only items which has handled the before event will receive an
+  // after event.
+  size_t chainIndex;
+  bool defaultPrevented = false;
+  DispatchBeforeKeyboardEventInternal(chain, aEvent, chainIndex,
+                                      defaultPrevented);
+
+  // Dispatch after events to partial items.
+  if (defaultPrevented) {
+    DispatchAfterKeyboardEventInternal(chain, aEvent,
+                                       aEvent.mFlags.mDefaultPrevented, chainIndex);
+
+    // No need to forward the event to child process.
+    aEvent.mFlags.mNoCrossProcessBoundaryForwarding = true;
+    return;
+  }
+
+  // Event listeners may kill nsPresContext and nsPresShell.
+  if (!CanDispatchEvent()) {
+    return;
+  }
+
+  // Dispatch actual key event to event target.
+  EventDispatcher::Dispatch(aTarget, mPresContext,
+                            &aEvent, nullptr, aStatus, aEventCB);
+
+  // Event listeners may kill nsPresContext and nsPresShell.
+  if (targetIsIframe || !CanDispatchEvent()) {
+    return;
+  }
+
+  // Dispatch after events to all items in the chain.
+  DispatchAfterKeyboardEventInternal(chain, aEvent,
+                                     aEvent.mFlags.mDefaultPrevented);
+}
+
 nsresult
 PresShell::HandleEvent(nsIFrame* aFrame,
                        WidgetGUIEvent* aEvent,
                        bool aDontRetargetEvents,
                        nsEventStatus* aEventStatus)
 {
 #ifdef MOZ_TASK_TRACER
   // Make touch events, mouse events and hardware key events to be the source
@@ -7861,16 +8108,19 @@ PresShell::HandleEventInternal(WidgetEve
               eventCBPtr = nullptr;
             }
           }
           if (eventTarget) {
             if (aEvent->mClass == eCompositionEventClass) {
               IMEStateManager::DispatchCompositionEvent(eventTarget,
                 mPresContext, aEvent->AsCompositionEvent(), aStatus,
                 eventCBPtr);
+            } else if (aEvent->mClass == eKeyboardEventClass) {
+              HandleKeyboardEvent(eventTarget, *(aEvent->AsKeyboardEvent()),
+                                  false, aStatus, eventCBPtr);
             } else {
               EventDispatcher::Dispatch(eventTarget, mPresContext,
                                         aEvent, nullptr, aStatus, eventCBPtr);
             }
           }
         }
       }
 
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -45,16 +45,17 @@ struct nsCallbackEventRequest;
 class ReflowCountMgr;
 #endif
 
 class nsPresShellEventCB;
 class nsAutoCauseReflowNotifier;
 
 namespace mozilla {
 class CSSStyleSheet;
+class EventDispatchingCallback;
 } // namespace mozilla
 
 // 250ms.  This is actually pref-controlled, but we use this value if we fail
 // to get the pref for any reason.
 #define PAINTLOCK_EVENT_DELAY 250
 
 class PresShell MOZ_FINAL : public nsIPresShell,
                             public nsStubDocumentObserver,
@@ -70,16 +71,19 @@ public:
   NS_DECL_ISUPPORTS
 
   // Touch caret preference
   static bool TouchCaretPrefEnabled();
 
   // Selection caret preference
   static bool SelectionCaretPrefEnabled();
 
+  // BeforeAfterKeyboardEvent preference
+  static bool BeforeAfterKeyboardEventEnabled();
+
   void Init(nsIDocument* aDocument, nsPresContext* aPresContext,
             nsViewManager* aViewManager, nsStyleSet* aStyleSet,
             nsCompatibility aCompatMode);
   virtual void Destroy() MOZ_OVERRIDE;
   virtual void MakeZombie() MOZ_OVERRIDE;
 
   virtual nsresult SetPreferenceStyleRules(bool aForceReflow) MOZ_OVERRIDE;
 
@@ -364,16 +368,20 @@ public:
   virtual void EnsureImageInVisibleList(nsIImageLoadingContent* aImage) MOZ_OVERRIDE;
 
   virtual void RemoveImageFromVisibleList(nsIImageLoadingContent* aImage) MOZ_OVERRIDE;
 
   virtual bool AssumeAllImagesVisible() MOZ_OVERRIDE;
 
   virtual void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot);
 
+  virtual void DispatchAfterKeyboardEvent(nsINode* aTarget,
+                                          const mozilla::WidgetKeyboardEvent& aEvent,
+                                          bool aEmbeddedCancelled) MOZ_OVERRIDE;
+
   void SetNextPaintCompressed() { mNextPaintCompressed = true; }
 
 protected:
   virtual ~PresShell();
 
   void HandlePostedReflowCallbacks(bool aInterruptible);
   void CancelPostedReflowCallbacks();
 
@@ -714,16 +722,34 @@ protected:
 
   void ClearVisibleImagesList();
   static void ClearImageVisibilityVisited(nsView* aView, bool aClear);
   static void MarkImagesInListVisible(const nsDisplayList& aList);
   void MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect);
 
   void EvictTouches();
 
+  // Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent.
+  void HandleKeyboardEvent(nsINode* aTarget,
+                           mozilla::WidgetKeyboardEvent& aEvent,
+                           bool aEmbeddedCancelled,
+                           nsEventStatus* aStatus,
+                           mozilla::EventDispatchingCallback* aEventCB);
+  void DispatchBeforeKeyboardEventInternal(
+         const nsTArray<nsCOMPtr<mozilla::dom::Element> >& aChain,
+         const mozilla::WidgetKeyboardEvent& aEvent,
+         size_t& aChainIndex,
+         bool& aDefaultPrevented);
+  void DispatchAfterKeyboardEventInternal(
+         const nsTArray<nsCOMPtr<mozilla::dom::Element> >& aChain,
+         const mozilla::WidgetKeyboardEvent& aEvent,
+         bool aEmbeddedCancelled,
+         size_t aChainIndex = 0);
+  bool CanDispatchEvent(const mozilla::WidgetGUIEvent* aEvent = nullptr) const;
+
   // A list of images that are visible or almost visible.
   nsTHashtable< nsRefPtrHashKey<nsIImageLoadingContent> > mVisibleImages;
 
 #ifdef DEBUG
   // The reflow root under which we're currently reflowing.  Null when
   // not in reflow.
   nsIFrame*                 mCurrentReflowRoot;
   uint32_t                  mUpdateCount;
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -5178,18 +5178,16 @@ nsComputedDOMStyle::DoGetStopColor()
 CSSValue*
 nsComputedDOMStyle::CreatePrimitiveValueForClipPath(
   const nsStyleBasicShape* aStyleBasicShape, uint8_t aSizingBox)
 {
   nsDOMCSSValueList* valueList = GetROCSSValueList(false);
 
   if (aStyleBasicShape &&
       aStyleBasicShape->GetShapeType() == nsStyleBasicShape::Type::ePolygon) {
-    nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
-
     // Shape function name and opening parenthesis.
     nsAutoString shapeFunctionString;
     AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(eCSSKeyword_polygon),
                        shapeFunctionString);
     shapeFunctionString.Append('(');
     bool hasEvenOdd = aStyleBasicShape->GetFillRule() ==
                           NS_STYLE_FILL_RULE_EVENODD;
     if (hasEvenOdd) {
@@ -5204,31 +5202,31 @@ nsComputedDOMStyle::CreatePrimitiveValue
                         aStyleBasicShape->Coordinates()[i]);
       shapeFunctionString.Append(coordString);
       shapeFunctionString.Append(' ');
       SetCssTextToCoord(coordString,
                         aStyleBasicShape->Coordinates()[i + 1]);
       shapeFunctionString.Append(coordString);
     }
     shapeFunctionString.Append(')');
+    nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
     val->SetString(shapeFunctionString);
     valueList->AppendCSSValue(val);
   }
 
-  nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
-
   if (aSizingBox == NS_STYLE_CLIP_SHAPE_SIZING_NOBOX) {
     return valueList;
   }
 
   nsAutoString boxString;
   AppendASCIItoUTF16(
     nsCSSProps::ValueToKeyword(aSizingBox,
                                nsCSSProps::kClipShapeSizingKTable),
                                boxString);
+  nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
   val->SetString(boxString);
   valueList->AppendCSSValue(val);
 
   return valueList;
 }
 
 CSSValue*
 nsComputedDOMStyle::DoGetClipPath()
--- a/mobile/android/base/ChromeCast.java
+++ b/mobile/android/base/ChromeCast.java
@@ -371,25 +371,30 @@ class ChromeCast implements GeckoMediaPl
                                            });
                 } catch (Exception e) {
                     Log.e(LOGTAG, "Exception while sending message", e);
                 }
             }
         }
     }
     private class MirrorCallback implements ResultCallback<ApplicationConnectionResult> {
-
-        final EventCallback callback;
+        // See Bug 1055562, callback is set to null after it has been
+        // invoked so that it will not be called a second time.
+        EventCallback callback;
         MirrorCallback(final EventCallback callback) {
             this.callback = callback;
         }
 
 
         @Override
         public void onResult(ApplicationConnectionResult result) {
+            if (callback == null) {
+                Log.e(LOGTAG, "Attempting to invoke MirrorChannel callback more than once.");
+                return;
+            }
             Status status = result.getStatus();
             if (status.isSuccess()) {
                 ApplicationMetadata applicationMetadata = result.getApplicationMetadata();
                 mSessionId = result.getSessionId();
                 String applicationStatus = result.getApplicationStatus();
                 boolean wasLaunched = result.getWasLaunched();
                 mApplicationStarted = true;
 
@@ -397,23 +402,25 @@ class ChromeCast implements GeckoMediaPl
                 // channel
                 mMirrorChannel = new MirrorChannel();
                 try {
                     Cast.CastApi.setMessageReceivedCallbacks(apiClient,
                                                              mMirrorChannel
                                                              .getNamespace(),
                                                              mMirrorChannel);
                     callback.sendSuccess(null);
+                    callback = null;
                 } catch (IOException e) {
                     Log.e(LOGTAG, "Exception while creating channel", e);
                 }
 
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Casting:Mirror", route.getId()));
             } else {
                 callback.sendError(status.toString());
+                callback = null;
             }
         }
     }
 
     @Override
     public void message(String msg, final EventCallback callback) {
         if (mMirrorChannel != null) {
             mMirrorChannel.sendMessage(msg);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4342,16 +4342,19 @@ pref("dom.fetch.enabled", false);
 // supported will be disabled. This threshold can be adjusted to suit other
 // platforms; and set to 0 to disable the low-memory check altogether.
 pref("camera.control.low_memory_thresholdMB", 404);
 #endif
 
 // UDPSocket API
 pref("dom.udpsocket.enabled", false);
 
+// Disable before keyboard events and after keyboard events by default.
+pref("dom.beforeAfterKeyboardEvent.enabled", false);
+
 // Experiment: Get TTL from DNS records.
 //     Unset initially (0); Randomly chosen on first run; will remain unchanged
 //     unless adjusted by the user or experiment ends. Variants defined in
 //     nsHostResolver.cpp.
 pref("dns.ttl-experiment.variant", 0);
 pref("dns.ttl-experiment.enabled", true);
 
 // Use raw ICU instead of CoreServices API in Unicode collation
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -1484,17 +1484,16 @@ PLDHashOperator
 CacheIndex::UpdateEntryInIndex(CacheIndexEntry *aEntry, void* aClosure)
 {
   CacheIndex *index = static_cast<CacheIndex *>(aClosure);
 
   LOG(("CacheFile::UpdateEntryInIndex() [hash=%08x%08x%08x%08x%08x]",
        LOGSHA1(aEntry->Hash())));
 
   MOZ_ASSERT(aEntry->IsFresh());
-  MOZ_ASSERT(aEntry->IsDirty());
 
   CacheIndexEntry *entry = index->mIndex.GetEntry(*aEntry->Hash());
 
   CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
   emng.DoNotSearchInUpdates();
 
   if (aEntry->IsRemoved()) {
     if (entry) {
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -459,29 +459,34 @@ nsHostRecord::GetPriority(uint16_t aFlag
         return nsHostRecord::DNS_PRIORITY_HIGH;
     } else if (IsMediumPriority(aFlags)) {
         return nsHostRecord::DNS_PRIORITY_MEDIUM;
     }
 
     return nsHostRecord::DNS_PRIORITY_LOW;
 }
 
-// Returns true if the entry can be removed, or false if it was marked to get
-// refreshed.
+// Returns true if the entry can be removed, or false if it should be left.
+// Sets mResolveAgain true for entries being resolved right now.
 bool
 nsHostRecord::RemoveOrRefresh()
 {
-  // This condition implies that the request has been passed to the OS
-  // resolver. The resultant DNS record should be considered stale and not
-  // trusted; set a flag to ensure it is called again.
-    if (resolving && !onQueue) {
-        mResolveAgain = true;
+    if (resolving) {
+        if (!onQueue) {
+            // The request has been passed to the OS resolver. The resultant DNS
+            // record should be considered stale and not trusted; set a flag to
+            // ensure it is called again.
+            mResolveAgain = true;
+        }
+        // if Onqueue is true, the host entry is already added to the cache
+        // but is still pending to get resolved: just leave it in hash.
         return false;
     }
-    return true; // can be removed now
+    // Already resolved; not in a pending state; remove from cache.
+    return true;
 }
 
 //----------------------------------------------------------------------------
 
 struct nsHostDBEnt : PLDHashEntryHdr
 {
     nsHostRecord *rec;
 };
@@ -594,19 +599,19 @@ HostDB_RemoveEntry(PLDHashTable *table,
 
 static PLDHashOperator
 HostDB_PruneEntry(PLDHashTable *table,
                   PLDHashEntryHdr *hdr,
                   uint32_t number,
                   void *arg)
 {
     nsHostDBEnt* ent = static_cast<nsHostDBEnt *>(hdr);
-
     // Try to remove the record, or mark it for refresh
     if (ent->rec->RemoveOrRefresh()) {
+        PR_REMOVE_LINK(ent->rec);
         return PL_DHASH_REMOVE;
     }
     return PL_DHASH_NEXT;
 }
 
 //----------------------------------------------------------------------------
 
 #if TTL_AVAILABLE
@@ -781,36 +786,34 @@ nsHostResolver::ClearPendingQueue(PRCLis
 // This function removes all existing resolved host entries from the hash.
 // Names that are in the pending queues can be left there. Entries in the
 // cache that have 'Resolve' set true but not 'onQueue' are being resolved
 // right now, so we need to mark them to get re-resolved on completion!
 
 void
 nsHostResolver::FlushCache()
 {
-    PRCList evictionQ;
-    PR_INIT_CLIST(&evictionQ);
-
-    {
-        MutexAutoLock lock(mLock);
-        MoveCList(mEvictionQ, evictionQ);
-        mEvictionQSize = 0;
+  MutexAutoLock lock(mLock);
+  mEvictionQSize = 0;
 
-        // prune the hash from all hosts already resolved
-        PL_DHashTableEnumerate(&mDB, HostDB_PruneEntry, nullptr);
-    }
+  // Clear the evictionQ and remove all its corresponding entries from
+  // the cache first
+  if (!PR_CLIST_IS_EMPTY(&mEvictionQ)) {
+      PRCList *node = mEvictionQ.next;
+      while (node != &mEvictionQ) {
+          nsHostRecord *rec = static_cast<nsHostRecord *>(node);
+          node = node->next;
+          PR_REMOVE_AND_INIT_LINK(rec);
+          PL_DHashTableOperate(&mDB, (nsHostKey *) rec, PL_DHASH_REMOVE);
+          NS_RELEASE(rec);
+      }
+  }
 
-    if (!PR_CLIST_IS_EMPTY(&evictionQ)) {
-        PRCList *node = evictionQ.next;
-        while (node != &evictionQ) {
-            nsHostRecord *rec = static_cast<nsHostRecord *>(node);
-            node = node->next;
-            NS_RELEASE(rec);
-        }
-    }
+  // Refresh the cache entries that are resolving RIGHT now, remove the rest.
+  PL_DHashTableEnumerate(&mDB, HostDB_PruneEntry, nullptr);
 }
 
 void
 nsHostResolver::Shutdown()
 {
     LOG(("Shutting down host resolver.\n"));
 
 #if TTL_AVAILABLE
@@ -1454,47 +1457,43 @@ nsHostResolver::OnLookupComplete(nsHostR
                     TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart;
                     Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
                                           static_cast<uint32_t>(age.ToSeconds() / 60));
                 }
 
                 // release reference to rec owned by mEvictionQ
                 NS_RELEASE(head);
             }
+#if TTL_AVAILABLE
+            if (!rec->mGetTtl && sDnsVariant != DNS_EXP_VARIANT_CONTROL
+                && !rec->resolving) {
+                LOG(("Issuing second async lookup for TTL for %s.", rec->host));
+                rec->flags =
+                  (rec->flags & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW;
+                DebugOnly<nsresult> rv = IssueLookup(rec);
+                NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
+                                 "Could not issue second async lookup for TTL.");
+            }
+#endif
         }
     }
 
     MOZ_EVENT_TRACER_DONE(rec, "net::dns::resolve");
 
     if (!PR_CLIST_IS_EMPTY(&cbs)) {
         PRCList *node = cbs.next;
         while (node != &cbs) {
             nsResolveHostCallback *callback =
                     static_cast<nsResolveHostCallback *>(node);
             node = node->next;
             callback->OnLookupComplete(this, rec, status);
             // NOTE: callback must not be dereferenced after this point!!
         }
     }
 
-#if TTL_AVAILABLE
-    {
-        MutexAutoLock lock(mLock);
-        if (!mShutdown && !rec->mGetTtl
-                && sDnsVariant != DNS_EXP_VARIANT_CONTROL && !rec->resolving) {
-            LOG(("Issuing second async lookup for TTL for %s.", rec->host));
-            rec->flags =
-                (rec->flags & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW;
-            DebugOnly<nsresult> rv = IssueLookup(rec);
-            NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                             "Could not issue second async lookup for TTL.");
-        }
-    }
-#endif
-
     NS_RELEASE(rec);
 
     return LOOKUP_OK;
 }
 
 void
 nsHostResolver::CancelAsyncRequest(const char            *host,
                                    uint16_t               flags,
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -114,18 +114,18 @@ public:
 
     enum DnsPriority {
         DNS_PRIORITY_LOW,
         DNS_PRIORITY_MEDIUM,
         DNS_PRIORITY_HIGH,
     };
     static DnsPriority GetPriority(uint16_t aFlags);
 
-    bool RemoveOrRefresh(); // Returns whether the host record can be removed
-                            // or needs to be refreshed
+    bool RemoveOrRefresh(); // Mark records currently being resolved as needed
+                            // to resolve again.
 
 private:
     friend class nsHostResolver;
 
 
     PRCList callbacks; /* list of callbacks */
 
     bool    resolving; /* true if this record is being resolved, which means
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -611,16 +611,17 @@ nsHttpHandler::BuildUserAgent()
                            mOscpu.Length() +
                            mMisc.Length() +
                            mProduct.Length() +
                            mProductSub.Length() +
                            mAppName.Length() +
                            mAppVersion.Length() +
                            mCompatFirefox.Length() +
                            mCompatDevice.Length() +
+                           mDeviceModelId.Length() +
                            13);
 
     // Application portion
     mUserAgent.Assign(mLegacyAppName);
     mUserAgent += '/';
     mUserAgent += mLegacyAppVersion;
     mUserAgent += ' ';
 
@@ -635,16 +636,20 @@ nsHttpHandler::BuildUserAgent()
     if (!mCompatDevice.IsEmpty()) {
         mUserAgent += mCompatDevice;
         mUserAgent.AppendLiteral("; ");
     }
     else if (!mOscpu.IsEmpty()) {
       mUserAgent += mOscpu;
       mUserAgent.AppendLiteral("; ");
     }
+    if (!mDeviceModelId.IsEmpty()) {
+        mUserAgent += mDeviceModelId;
+        mUserAgent.AppendLiteral("; ");
+    }
     mUserAgent += mMisc;
     mUserAgent += ')';
 
     // Product portion
     mUserAgent += ' ';
     mUserAgent += mProduct;
     mUserAgent += '/';
     mUserAgent += mProductSub;
@@ -698,16 +703,41 @@ nsHttpHandler::InitUserAgentComponents()
     bool isTablet;
     nsresult rv = infoService->GetPropertyAsBool(NS_LITERAL_STRING("tablet"), &isTablet);
     if (NS_SUCCEEDED(rv) && isTablet)
         mCompatDevice.AssignLiteral("Tablet");
     else
         mCompatDevice.AssignLiteral("Mobile");
 #endif
 
+#if defined(MOZ_WIDGET_GONK)
+    // Device model identifier should be a simple token, which can be composed
+    // of letters, numbers, hyphen ("-") and dot (".").
+    // Any other characters means the identifier is invalid and ignored.
+    nsCString deviceId;
+    rv = Preferences::GetCString("general.useragent.device_id", &deviceId);
+    if (NS_SUCCEEDED(rv)) {
+        bool valid = true;
+        deviceId.Trim(" ", true, true);
+        for (int i = 0; i < deviceId.Length(); i++) {
+            char c = deviceId.CharAt(i);
+            if (!(isalnum(c) || c == '-' || c == '.')) {
+                valid = false;
+                break;
+            }
+        }
+        if (valid) {
+            mDeviceModelId = deviceId;
+        } else {
+            LOG(("nsHttpHandler: Ignore invalid device ID: [%s]\n",
+                  deviceId.get()));
+        }
+    }
+#endif
+
 #ifndef MOZ_UA_OS_AGNOSTIC
     // Gather OS/CPU.
 #if defined(XP_WIN)
     OSVERSIONINFO info = { sizeof(OSVERSIONINFO) };
 #pragma warning(push)
 #pragma warning(disable:4996)
     if (GetVersionEx(&info)) {
 #pragma warning(pop)
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -427,16 +427,17 @@ private:
     nsCString      mMisc;
     nsCString      mProduct;
     nsXPIDLCString mProductSub;
     nsXPIDLCString mAppName;
     nsXPIDLCString mAppVersion;
     nsCString      mCompatFirefox;
     bool           mCompatFirefoxEnabled;
     nsXPIDLCString mCompatDevice;
+    nsCString      mDeviceModelId;
 
     nsCString      mUserAgent;
     nsXPIDLCString mUserAgentOverride;
     bool           mUserAgentIsDirty; // true if mUserAgent should be rebuilt
 
     bool           mUseCache;
 
     bool           mPromptTempRedirect;
--- a/testing/marionette/client/docs/conf.py
+++ b/testing/marionette/client/docs/conf.py
@@ -119,17 +119,17 @@ html_theme = 'default'
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
 #html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+#html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
 #html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
 #html_use_smartypants = True
--- a/testing/marionette/client/docs/index.rst
+++ b/testing/marionette/client/docs/index.rst
@@ -95,111 +95,148 @@ Please visit, our `Marionette Tests`_ se
 
 .. automodule:: marionette
 
 Marionette Objects
 ------------------
 .. autoclass:: Marionette
 
 Session Management
-``````````````````
+^^^^^^^^^^^^^^^^^^
 .. automethod:: Marionette.start_session
 .. automethod:: Marionette.delete_session
 .. autoattribute:: Marionette.session_capabilities
 .. automethod:: Marionette.get_cookie
 .. automethod:: Marionette.get_cookies
 .. automethod:: Marionette.add_cookie
 .. automethod:: Marionette.delete_all_cookies
 
 Context Management
-``````````````````
+^^^^^^^^^^^^^^^^^^
 .. autoattribute:: Marionette.current_window_handle
 .. autoattribute:: Marionette.window_handles
 .. automethod:: Marionette.set_context
 .. automethod:: Marionette.switch_to_frame
 .. automethod:: Marionette.switch_to_window
 .. automethod:: Marionette.get_active_frame
 .. automethod:: Marionette.close
 
 Navigation Methods
-``````````````````
+^^^^^^^^^^^^^^^^^^
 .. autoattribute:: Marionette.title
 .. automethod:: Marionette.navigate
 .. automethod:: Marionette.get_url
 .. automethod:: Marionette.go_back
 .. automethod:: Marionette.go_forward
 .. automethod:: Marionette.refresh
 .. automethod:: Marionette.absolute_url
 .. automethod:: Marionette.get_window_type
 
 DOM Element Methods
-```````````````````
+^^^^^^^^^^^^^^^^^^^
 .. automethod:: Marionette.set_search_timeout
 .. automethod:: Marionette.find_element
 .. automethod:: Marionette.find_elements
 
 Script Execution
-````````````````
+^^^^^^^^^^^^^^^^
 .. automethod:: Marionette.execute_script
 .. automethod:: Marionette.execute_async_script
 .. automethod:: Marionette.set_script_timeout
 
 Debugging
-`````````
+^^^^^^^^^
 .. autoattribute:: Marionette.page_source
 .. automethod:: Marionette.log
 .. automethod:: Marionette.get_logs
 .. automethod:: Marionette.screenshot
 
 Querying and Modifying Document Content
 ---------------------------------------
 .. autoclass:: HTMLElement
-
 .. autoattribute:: HTMLElement.text
 .. autoattribute:: HTMLElement.location
 .. autoattribute:: HTMLElement.size
 .. autoattribute:: HTMLElement.tag_name
 .. automethod:: HTMLElement.send_keys
 .. automethod:: HTMLElement.clear
 .. automethod:: HTMLElement.click
 .. automethod:: HTMLElement.is_selected
 .. automethod:: HTMLElement.is_enabled
 .. automethod:: HTMLElement.is_displayed
 .. automethod:: HTMLElement.value_of_css_property
 
 .. autoclass:: DateTimeValue
-
 .. autoattribute:: DateTimeValue.date
 .. autoattribute:: DateTimeValue.time
 
 Action Objects
 --------------
 
 Action Sequences
-````````````````
+^^^^^^^^^^^^^^^^
 .. autoclass:: Actions
-
 .. automethod:: Actions.press
 .. automethod:: Actions.release
 .. automethod:: Actions.move
 .. automethod:: Actions.move_by_offset
 .. automethod:: Actions.wait
 .. automethod:: Actions.cancel
 .. automethod:: Actions.long_press
 .. automethod:: Actions.flick
 .. automethod:: Actions.tap
 .. automethod:: Actions.double_tap
 .. automethod:: Actions.perform
 
 Multi-action Sequences
-``````````````````````
+^^^^^^^^^^^^^^^^^^^^^^
 .. autoclass:: MultiActions
-
 .. automethod:: MultiActions.add
 .. automethod:: MultiActions.perform
 
+Explicit Waiting and Expected Conditions
+----------------------------------------
+
+Waits are used to pause program execution
+until a given condition is true.
+This is a useful technique to employ
+when documents load new content or change
+after ``Document.readyState``'s value changes to "complete".
+
+Because Marionette returns control to the user
+when the document is completely loaded,
+any subsequent interaction with elements
+are subject to manual synchronisation.
+The reason for this is that Marionette
+does not keep a direct representation of the DOM,
+but instead exposes a way for the user to
+query the browser's DOM state.
+
+The `Wait` helper class provided by Marionette
+avoids some of the caveats of ``time.sleep(n)``,
+which sets the condition to an exact time period to wait.
+It will return immediately
+once the provided condition evaluates to true.
+
+In addition to writing your own custom conditions
+you can combine `Wait`
+with a number of ready-made expected conditions
+that are listed below.
+
+Waits
+^^^^^
+.. autoclass:: marionette.wait.Wait
+   :members:
+   :special-members:
+.. autoattribute marionette.wait.DEFAULT_TIMEOUT
+.. autoattribute marionette.wait.DEFAULT_INTERVAL
+
+Expected Conditions
+^^^^^^^^^^^^^^^^^^^
+.. automodule:: marionette.expected
+   :members:
 
 Indices and tables
 ==================
 
 * :ref:`genindex`
 * :ref:`modindex`
 * :ref:`search`
--- a/testing/marionette/client/marionette/expected.py
+++ b/testing/marionette/client/marionette/expected.py
@@ -14,21 +14,21 @@ of times until they are either successfu
 """
 
 class element_present(object):
     """Checks that a web element is present in the DOM of the current
     context.  This does not necessarily mean that the element is
     visible.
 
     You can select which element to be checked for presence by
-    supplying a locator:
+    supplying a locator::
 
         el = Wait(marionette).until(expected.element_present(By.ID, "foo"))
 
-    Or by using a function/lambda returning an element:
+    Or by using a function/lambda returning an element::
 
         el = Wait(marionette).until(expected.element_present(lambda m: m.find_element(By.ID, "foo")))
 
     :param args: locator or function returning web element
     :returns: the web element once it is located, or False
 
     """
 
@@ -41,21 +41,21 @@ class element_present(object):
     def __call__(self, marionette):
         return _find(marionette, self.locator)
 
 class element_not_present(element_present):
     """Checks that a web element is not present in the DOM of the current
     context.
 
     You can select which element to be checked for lack of presence by
-    supplying a locator:
+    supplying a locator::
 
         r = Wait(marionette).until(expected.element_not_present(By.ID, "foo"))
 
-    Or by using a function/lambda returning an element:
+    Or by using a function/lambda returning an element::
 
         r = Wait(marionette).until(expected.element_present(lambda m: m.find_element(By.ID, "foo")))
 
     :param args: locator or function returning web element
     :returns: True if element is not present, or False if it is present
 
     """
 
@@ -67,17 +67,17 @@ class element_not_present(element_presen
 
 class element_stale(object):
     """Check that the given element is no longer attached to DOM of the
     current context.
 
     This can be useful for waiting until an element is no longer
     present.
 
-    Sample usage:
+    Sample usage::
 
         el = marionette.find_element(By.ID, "foo")
         # ...
         Wait(marionette).until(expected.element_stale(el))
 
     :param element: the element to wait for
     :returns: False if the element is still attached to the DOM, True
         otherwise
@@ -96,21 +96,21 @@ class element_stale(object):
             return True
 
 class elements_present(object):
     """Checks that web elements are present in the DOM of the current
     context.  This does not necessarily mean that the elements are
     visible.
 
     You can select which elements to be checked for presence by
-    supplying a locator:
+    supplying a locator::
 
         els = Wait(marionette).until(expected.elements_present(By.TAG_NAME, "a"))
 
-    Or by using a function/lambda returning a list of elements:
+    Or by using a function/lambda returning a list of elements::
 
         els = Wait(marionette).until(expected.elements_present(lambda m: m.find_elements(By.TAG_NAME, "a")))
 
     :param args: locator or function returning a list of web elements
     :returns: list of web elements once they are located, or False
 
     """
 
@@ -123,21 +123,21 @@ class elements_present(object):
     def __call__(self, marionette):
         return _find(marionette, self.locator)
 
 class elements_not_present(elements_present):
     """Checks that web elements are not present in the DOM of the
     current context.
 
     You can select which elements to be checked for not being present
-    by supplying a locator:
+    by supplying a locator::
 
         r = Wait(marionette).until(expected.elements_not_present(By.TAG_NAME, "a"))
 
-    Or by using a function/lambda returning a list of elements:
+    Or by using a function/lambda returning a list of elements::
 
         r = Wait(marionette).until(expected.elements_not_present(lambda m: m.find_elements(By.TAG_NAME, "a")))
 
     :param args: locator or function returning a list of web elements
     :returns: True if elements are missing, False if one or more are
         present
 
     """
@@ -209,17 +209,17 @@ class element_selected(object):
     def __call__(self, marionette):
         return self.el.is_selected()
 
 class element_not_selected(element_selected):
     """An expectation for checking that the given element is not
     selected.
 
     :param element: the element to not be selected
-    :returns True if element is not selected, False if selected
+    :returns: True if element is not selected, False if selected
 
     """
 
     def __init__(self, element):
         super(element_not_selected, self).__init__(element)
 
     def __call__(self, marionette):
         return not super(element_not_selected, self).__call__(marionette)
--- a/testing/marionette/client/marionette/wait.py
+++ b/testing/marionette/client/marionette/wait.py
@@ -30,17 +30,17 @@ class Wait(object):
     def __init__(self, marionette, timeout=None,
                  interval=DEFAULT_INTERVAL, ignored_exceptions=None,
                  clock=None):
         """Configure the Wait instance to have a custom timeout, interval, and
         list of ignored exceptions.  Optionally a different time
         implementation than the one provided by the standard library
         (time) can also be provided.
 
-        Sample usage:
+        Sample usage::
 
             # Wait 30 seconds for window to open, checking for its presence once
             # every 5 seconds.
             wait = Wait(marionette, timeout=30, interval=5,
                         ignored_exceptions=errors.NoSuchWindowException)
             window = wait.until(lambda m: m.switch_to_window(42))
 
         :param marionette: The input value to be provided to
--- a/testing/mochitest/manifest.webapp
+++ b/testing/mochitest/manifest.webapp
@@ -22,17 +22,18 @@
     "camera":{},
     "geolocation":{},
     "wifi-manage":{},
     "desktop-notification":{},
     "idle":{},
     "network-events":{},
     "embed-apps":{},
     "audio-channel-content":{},
-    "audio-channel-alarm":{}
+    "audio-channel-alarm":{},
+    "before-after-keyboard-event":{}
   },
   "locales": {
     "en-US": {
       "name": "Mochitest",
       "description": "Mochitests"
     }
   },
   "datastores-access" : {
--- a/testing/mozbase/mozdevice/mozdevice/devicemanager.py
+++ b/testing/mozbase/mozdevice/mozdevice/devicemanager.py
@@ -107,16 +107,17 @@ class DeviceManager(object):
 
           - `os` - name of the os
           - `id` - unique id of the device
           - `uptime` - uptime of the device
           - `uptimemillis` - uptime of the device in milliseconds (NOT supported on all implementations)
           - `systime` - system time of the device
           - `screen` - screen resolution
           - `memory` - memory stats
+          - `memtotal` - total memory available on the device, for example 927208 kB
           - `process` - list of running processes (same as ps)
           - `disk` - total, free, available bytes on disk
           - `power` - power status (charge, battery temp)
           - `temperature` - device temperature
 
          If `directive` is `None`, will return all available information
         """
 
--- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
+++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
@@ -509,16 +509,22 @@ class DeviceManagerADB(DeviceManager):
                 raise DMError("error getting uptime")
             m = re.match("up time: ((\d+) days, )*(\d{2}):(\d{2}):(\d{2})", utime)
             ret["uptime"] = "%d days %d hours %d minutes %d seconds" % tuple(
                 [int(g or 0) for g in m.groups()[1:]])
         if directive == "process" or directive == "all":
             ret["process"] = self.shellCheckOutput(["ps"])
         if directive == "systime" or directive == "all":
             ret["systime"] = self.shellCheckOutput(["date"])
+        if directive == "memtotal" or directive == "all":
+            meminfo = {}
+            for line in self.pullFile("/proc/meminfo").splitlines():
+                key, value = line.split(":")
+                meminfo[key] = value.strip()
+            ret["memtotal"] = meminfo["MemTotal"]
         self._logger.info(ret)
         return ret
 
     def uninstallApp(self, appName, installPath=None):
         status = self._runCmd(["uninstall", appName]).output[0].strip()
         if status != 'Success':
             raise DMError("uninstall failed for %s. Got: %s" % (appName, status))
 
--- a/testing/mozbase/mozversion/tests/test_binary.py
+++ b/testing/mozbase/mozversion/tests/test_binary.py
@@ -44,64 +44,77 @@ SourceRepository = PlatformSourceRepo
 
     @unittest.skipIf(not os.environ.get('BROWSER_PATH'),
                      'No binary has been specified.')
     def test_real_binary(self):
         v = get_version(os.environ.get('BROWSER_PATH'))
         self.assertTrue(isinstance(v, dict))
 
     def test_binary(self):
-        with open(os.path.join(self.tempdir, 'application.ini'), 'w') as f:
-            f.writelines(self.application_ini)
-
-        with open(os.path.join(self.tempdir, 'platform.ini'), 'w') as f:
-            f.writelines(self.platform_ini)
+        self._write_ini_files()
 
         self._check_version(get_version(self.binary))
 
+    @unittest.skipIf(not hasattr(os, 'symlink'),
+                     'os.symlink not supported on this platform')
+    def test_symlinked_binary(self):
+        self._write_ini_files()
+
+        # create a symlink of the binary in another directory and check
+        # version against this symlink
+        tempdir = tempfile.mkdtemp()
+        try:
+            browser_link = os.path.join(tempdir,
+                                        os.path.basename(self.binary))
+            os.symlink(self.binary, browser_link)
+
+            self._check_version(get_version(browser_link))
+        finally:
+            mozfile.remove(tempdir)
+
     def test_binary_in_current_path(self):
-        with open(os.path.join(self.tempdir, 'application.ini'), 'w') as f:
-            f.writelines(self.application_ini)
+        self._write_ini_files()
 
-        with open(os.path.join(self.tempdir, 'platform.ini'), 'w') as f:
-            f.writelines(self.platform_ini)
         os.chdir(self.tempdir)
         self._check_version(get_version())
 
     def test_invalid_binary_path(self):
         self.assertRaises(IOError, get_version,
                           os.path.join(self.tempdir, 'invalid'))
 
     def test_without_ini_files(self):
         """With missing ini files an exception should be thrown"""
         self.assertRaises(errors.AppNotFoundError, get_version,
                           self.binary)
 
     def test_without_platform_file(self):
         """With a missing platform file no exception should be thrown"""
-        with open(os.path.join(self.tempdir, 'application.ini'), 'w') as f:
-            f.writelines(self.application_ini)
+        self._write_ini_files(platform=False)
 
         v = get_version(self.binary)
         self.assertTrue(isinstance(v, dict))
 
     def test_with_exe(self):
         """Test that we can resolve .exe files"""
-        with open(os.path.join(self.tempdir, 'application.ini'), 'w') as f:
-            f.writelines(self.application_ini)
-
-        with open(os.path.join(self.tempdir, 'platform.ini'), 'w') as f:
-            f.writelines(self.platform_ini)
+        self._write_ini_files()
 
         exe_name_unprefixed = self.binary + '1'
         exe_name = exe_name_unprefixed + '.exe'
         with open(exe_name, 'w') as f:
             f.write('foobar')
         self._check_version(get_version(exe_name_unprefixed))
 
+    def _write_ini_files(self, application=True, platform=True):
+        if application:
+            with open(os.path.join(self.tempdir, 'application.ini'), 'w') as f:
+                f.writelines(self.application_ini)
+        if platform:
+            with open(os.path.join(self.tempdir, 'platform.ini'), 'w') as f:
+                f.writelines(self.platform_ini)
+
     def _check_version(self, version):
         self.assertEqual(version.get('application_name'), 'AppName')
         self.assertEqual(version.get('application_display_name'), 'AppCodeName')
         self.assertEqual(version.get('application_version'), 'AppVersion')
         self.assertEqual(version.get('application_buildid'), 'AppBuildID')
         self.assertEqual(
             version.get('application_repository'), 'AppSourceRepo')
         self.assertEqual(
--- a/toolkit/components/url-classifier/LookupCache.cpp
+++ b/toolkit/components/url-classifier/LookupCache.cpp
@@ -627,17 +627,17 @@ LookupCache::ConstructPrefixSet(AddPrefi
   // construct new one, replace old entries
   nsresult rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length());
   if (NS_FAILED(rv)) {
     goto error_bailout;
   }
 
 #ifdef DEBUG
   uint32_t size;
-  size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
+  size = mPrefixSet->SizeInMemory();
   LOG(("SB tree done, size = %d bytes\n", size));
 #endif
 
   mPrimed = true;
 
   return NS_OK;
 
  error_bailout:
@@ -670,17 +670,17 @@ LookupCache::LoadPrefixSet()
     }
     mPrimed = true;
   } else {
     LOG(("no (usable) stored PrefixSet found"));
   }
 
 #ifdef DEBUG
   if (mPrimed) {
-    uint32_t size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
+    uint32_t size = mPrefixSet->SizeInMemory();
     LOG(("SB tree done, size = %d bytes\n", size));
   }
 #endif
 
   return NS_OK;
 }
 
 nsresult
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
@@ -33,19 +33,21 @@ static const PRLogModuleInfo *gUrlClassi
 #else
 #define LOG(args)
 #define LOG_ENABLED() (false)
 #endif
 
 NS_IMPL_ISUPPORTS(
   nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet, nsIMemoryReporter)
 
+MOZ_DEFINE_MALLOC_SIZE_OF(UrlClassifierMallocSizeOf)
+
 nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
-  : mHasPrefixes(false)
-  , mTotalPrefixes(0)
+  : mTotalPrefixes(0)
+  , mMemoryInUse(0)
   , mMemoryReportPath()
 {
 #if defined(PR_LOGGING)
   if (!gUrlClassifierPrefixSetLog)
     gUrlClassifierPrefixSetLog = PR_NewLogModule("UrlClassifierPrefixSet");
 #endif
 }
 
@@ -66,29 +68,32 @@ nsUrlClassifierPrefixSet::Init(const nsA
 nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet()
 {
   UnregisterWeakMemoryReporter(this);
 }
 
 NS_IMETHODIMP
 nsUrlClassifierPrefixSet::SetPrefixes(const uint32_t* aArray, uint32_t aLength)
 {
+  nsresult rv = NS_OK;
+
   if (aLength <= 0) {
-    if (mHasPrefixes) {
+    if (mIndexPrefixes.Length() > 0) {
       LOG(("Clearing PrefixSet"));
       mIndexDeltas.Clear();
       mIndexPrefixes.Clear();
       mTotalPrefixes = 0;
-      mHasPrefixes = false;
     }
   } else {
-    return MakePrefixSet(aArray, aLength);
+    rv = MakePrefixSet(aArray, aLength);
   }
 
-  return NS_OK;
+  mMemoryInUse = SizeOfIncludingThis(UrlClassifierMallocSizeOf);
+
+  return rv;
 }
 
 nsresult
 nsUrlClassifierPrefixSet::MakePrefixSet(const uint32_t* aPrefixes, uint32_t aLength)
 {
   if (aLength == 0) {
     return NS_OK;
   }
@@ -127,18 +132,16 @@ nsUrlClassifierPrefixSet::MakePrefixSet(
 
   mIndexPrefixes.Compact();
   mIndexDeltas.Compact();
 
   LOG(("Total number of indices: %d", aLength));
   LOG(("Total number of deltas: %d", totalDeltas));
   LOG(("Total number of delta chunks: %d", mIndexDeltas.Length()));
 
-  mHasPrefixes = true;
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierPrefixSet::GetPrefixes(uint32_t* aCount,
                                       uint32_t** aPrefixes)
 {
   NS_ENSURE_ARG_POINTER(aCount);
@@ -189,17 +192,17 @@ uint32_t nsUrlClassifierPrefixSet::BinSe
   return end;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierPrefixSet::Contains(uint32_t aPrefix, bool* aFound)
 {
   *aFound = false;
 
-  if (!mHasPrefixes) {
+  if (mIndexPrefixes.Length() == 0) {
     return NS_OK;
   }
 
   uint32_t target = aPrefix;
 
   // We want to do a "Price is Right" binary search, that is, we want to find
   // the index of the value either equal to the target or the closest value
   // that is less than the target.
@@ -231,25 +234,23 @@ nsUrlClassifierPrefixSet::Contains(uint3
 
   if (diff == 0) {
     *aFound = true;
   }
 
   return NS_OK;
 }
 
-MOZ_DEFINE_MALLOC_SIZE_OF(UrlClassifierMallocSizeOf)
-
 NS_IMETHODIMP
 nsUrlClassifierPrefixSet::CollectReports(nsIHandleReportCallback* aHandleReport,
                                          nsISupports* aData, bool aAnonymize)
 {
   return aHandleReport->Callback(
     EmptyCString(), mMemoryReportPath, KIND_HEAP, UNITS_BYTES,
-    SizeOfIncludingThis(UrlClassifierMallocSizeOf),
+    mMemoryInUse,
     NS_LITERAL_CSTRING("Memory used by the prefix set for a URL classifier."),
     aData);
 }
 
 size_t
 nsUrlClassifierPrefixSet::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
 {
   size_t n = 0;
@@ -260,17 +261,17 @@ nsUrlClassifierPrefixSet::SizeOfIncludin
   }
   n += mIndexPrefixes.SizeOfExcludingThis(aMallocSizeOf);
   return n;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierPrefixSet::IsEmpty(bool * aEmpty)
 {
-  *aEmpty = !mHasPrefixes;
+  *aEmpty = (mIndexPrefixes.Length() == 0);
   return NS_OK;
 }
 
 nsresult
 nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd)
 {
   uint32_t magic;
   int32_t read;
@@ -318,18 +319,16 @@ nsUrlClassifierPrefixSet::LoadFromFd(Aut
       if (numInDelta > 0) {
         mIndexDeltas[i].SetLength(numInDelta);
         mTotalPrefixes += numInDelta;
         toRead = numInDelta * sizeof(uint16_t);
         read = PR_Read(fileFd, mIndexDeltas[i].Elements(), toRead);
         NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
       }
     }
-
-    mHasPrefixes = true;
   } else {
     LOG(("Version magic mismatch, not loading"));
     return NS_ERROR_FILE_CORRUPTED;
   }
 
   MOZ_ASSERT(mIndexPrefixes.Length() == mIndexDeltas.Length());
   LOG(("Loading PrefixSet successful"));
 
@@ -340,19 +339,22 @@ NS_IMETHODIMP
 nsUrlClassifierPrefixSet::LoadFromFile(nsIFile* aFile)
 {
   Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FILELOAD_TIME> timer;
 
   nsresult rv;
   AutoFDClose fileFd;
   rv = aFile->OpenNSPRFileDesc(PR_RDONLY | nsIFile::OS_READAHEAD,
                                0, &fileFd.rwget());
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (!NS_FAILED(rv)) {
+    rv = LoadFromFd(fileFd);
+    mMemoryInUse = SizeOfIncludingThis(UrlClassifierMallocSizeOf);
+  }
 
-  return LoadFromFd(fileFd);
+  return rv;
 }
 
 nsresult
 nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose& fileFd)
 {
   {
       Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FALLOCATE_TIME> timer;
       int64_t size = 4 * sizeof(uint32_t);
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
@@ -14,60 +14,63 @@
 #include "nsIMutableArray.h"
 #include "nsIUrlClassifierPrefixSet.h"
 #include "nsTArray.h"
 #include "nsToolkitCompsCID.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/FileUtils.h"
+#include "mozilla/Atomics.h"
 
 class nsUrlClassifierPrefixSet MOZ_FINAL
   : public nsIUrlClassifierPrefixSet
   , public nsIMemoryReporter
 {
 public:
   nsUrlClassifierPrefixSet();
 
   NS_IMETHOD Init(const nsACString& aName);
   NS_IMETHOD SetPrefixes(const uint32_t* aArray, uint32_t aLength);
   NS_IMETHOD GetPrefixes(uint32_t* aCount, uint32_t** aPrefixes);
   NS_IMETHOD Contains(uint32_t aPrefix, bool* aFound);
   NS_IMETHOD IsEmpty(bool* aEmpty);
   NS_IMETHOD LoadFromFile(nsIFile* aFile);
   NS_IMETHOD StoreToFile(nsIFile* aFile);
 
+  size_t SizeInMemory() { return mMemoryInUse; };
+
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
 
-  // Return the estimated size of the set on disk and in memory, in bytes.
-  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
-
 protected:
   virtual ~nsUrlClassifierPrefixSet();
 
   static const uint32_t DELTAS_LIMIT = 120;
   static const uint32_t MAX_INDEX_DIFF = (1 << 16);
   static const uint32_t PREFIXSET_VERSION_MAGIC = 1;
 
   nsresult MakePrefixSet(const uint32_t* aArray, uint32_t aLength);
   uint32_t BinSearch(uint32_t start, uint32_t end, uint32_t target);
   nsresult LoadFromFd(mozilla::AutoFDClose& fileFd);
   nsresult StoreToFd(mozilla::AutoFDClose& fileFd);
 
-  // boolean indicating whether |setPrefixes| has been
-  // called with a non-empty array.
-  bool mHasPrefixes;
+  // Return the estimated size of the set on disk and in memory, in bytes.
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
   // list of fully stored prefixes, that also form the
   // start of a run of deltas in mIndexDeltas.
   nsTArray<uint32_t> mIndexPrefixes;
   // array containing arrays of deltas from indices.
   // Index to the place that matches the closest lower
   // prefix from mIndexPrefix. Then every "delta" corresponds
   // to a prefix in the PrefixSet.
   nsTArray<nsTArray<uint16_t> > mIndexDeltas;
   // how many prefixes we have.
   uint32_t mTotalPrefixes;
 
+  // memory report collection might happen while we're updating the prefixset
+  // on another thread, so pre-compute and remember the size (bug 1050108).
+  mozilla::Atomic<size_t> mMemoryInUse;
   nsCString mMemoryReportPath;
 };
 
 #endif
--- a/toolkit/mozapps/extensions/internal/OpenH264Provider.jsm
+++ b/toolkit/mozapps/extensions/internal/OpenH264Provider.jsm
@@ -268,17 +268,17 @@ let OpenH264Provider = {
     prefs.observe(OPENH264_PREF_VERSION, this.onPrefVersionChanged, this);
     prefs.observe(OPENH264_PREF_LOGGING, configureLogging);
 
     if (this.gmpPath && enabled) {
       this._log.info("startup() - adding gmp directory " + this.gmpPath);
       try {
         gmpService.addPluginDirectory(this.gmpPath);
       } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
-        this._log.warning("startup() - adding gmp directory failed with " + e.name + " - sandboxing not available?");
+        this._log.warn("startup() - adding gmp directory failed with " + e.name + " - sandboxing not available?");
       }
     }
 
     let telemetry = {};
     if (this.isEnabled) {
       telemetry[OPENH264_PLUGIN_ID] = {
 	userDisabled: OpenH264Wrapper.userDisabled,
 	version: OpenH264Wrapper.version,
--- a/tools/profiler/GeckoProfilerImpl.h
+++ b/tools/profiler/GeckoProfilerImpl.h
@@ -13,17 +13,17 @@
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/Assertions.h"
 #include "nscore.h"
 #include "GeckoProfilerFunc.h"
 #include "PseudoStack.h"
 #include "nsISupports.h"
 
 #ifdef MOZ_TASK_TRACER
-#include "GeckoTaskTracerImpl.h"
+#include "GeckoTaskTracer.h"
 #endif
 
 /* QT has a #define for the word "slots" and jsfriendapi.h has a struct with
  * this variable name, causing compilation problems. Alleviate this for now by
  * removing this #define */
 #ifdef MOZ_WIDGET_QT
 #undef slots
 #endif
--- a/tools/profiler/GeckoTaskTracer.cpp
+++ b/tools/profiler/GeckoTaskTracer.cpp
@@ -4,84 +4,81 @@
  * 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 "GeckoTaskTracer.h"
 #include "GeckoTaskTracerImpl.h"
 
 #include "mozilla/StaticMutex.h"
 #include "mozilla/ThreadLocal.h"
+#include "mozilla/TimeStamp.h"
 #include "mozilla/unused.h"
 
-#include "nsClassHashtable.h"
+#include "nsString.h"
 #include "nsThreadUtils.h"
+#include "prtime.h"
 
 #include <stdarg.h>
-#include <stdio.h>
-#include <unistd.h>
 
 #if defined(__GLIBC__)
 // glibc doesn't implement gettid(2).
 #include <sys/syscall.h>
 static pid_t gettid()
 {
   return (pid_t) syscall(SYS_gettid);
 }
 #endif
 
-#define MAX_USER_LABEL_LEN 512
-
 namespace mozilla {
 namespace tasktracer {
 
-static mozilla::ThreadLocal<TraceInfo*> sTraceInfoTLS;
-static StaticMutex sMutex;
-static nsClassHashtable<nsUint32HashKey, TraceInfo>* sTraceInfos = nullptr;
+static mozilla::ThreadLocal<TraceInfo*>* sTraceInfoTLS = nullptr;
+static mozilla::StaticMutex sMutex;
+static nsTArray<nsAutoPtr<TraceInfo>>* sTraceInfos = nullptr;
+static bool sIsLoggingStarted = false;
 
 namespace {
 
 static TraceInfo*
 AllocTraceInfo(int aTid)
 {
   StaticMutexAutoLock lock(sMutex);
 
-  sTraceInfos->Put(aTid, new TraceInfo(aTid));
-  return sTraceInfos->Get(aTid);
-}
+  nsAutoPtr<TraceInfo>* info = sTraceInfos->AppendElement(
+                                 new TraceInfo(aTid, sIsLoggingStarted));
 
-static void
-FreeTraceInfo(int aTid)
-{
-  StaticMutexAutoLock lock(sMutex);
-
-  sTraceInfos->Remove(aTid);
+  return info->get();
 }
 
 static bool
 IsInitialized()
 {
-  return sTraceInfoTLS.initialized();
+  return sTraceInfoTLS ? sTraceInfoTLS->initialized() : false;
 }
 
 static void
 SaveCurTraceInfo()
 {
   TraceInfo* info = GetOrCreateTraceInfo();
-  NS_ENSURE_TRUE_VOID(info);
+  if (!info) {
+    return;
+  }
 
   info->mSavedCurTraceSourceId = info->mCurTraceSourceId;
   info->mSavedCurTraceSourceType = info->mCurTraceSourceType;
   info->mSavedCurTaskId = info->mCurTaskId;
 }
 
 static void
 RestoreCurTraceInfo()
 {
   TraceInfo* info = GetOrCreateTraceInfo();
-  NS_ENSURE_TRUE_VOID(info);
+  if (!info) {
+    return;
+  }
 
   info->mCurTraceSourceId = info->mSavedCurTraceSourceId;
   info->mCurTraceSourceType = info->mSavedCurTraceSourceType;
   info->mCurTaskId = info->mSavedCurTaskId;
 }
 
 static void
 CreateSourceEvent(SourceEventType aType)
@@ -111,46 +108,103 @@ DestroySourceEvent()
   // Log a fake end for this source event.
   TraceInfo* info = GetOrCreateTraceInfo();
   LogEnd(info->mCurTraceSourceId, info->mCurTraceSourceId);
 
   // Restore the previously saved source event info.
   RestoreCurTraceInfo();
 }
 
+static void
+CleanUp()
+{
+  StaticMutexAutoLock lock(sMutex);
+
+  if (sTraceInfos) {
+    delete sTraceInfos;
+    sTraceInfos = nullptr;
+  }
+
+  // pthread_key_delete() is not called at the destructor of
+  // mozilla::ThreadLocal (Bug 1064672).
+  if (sTraceInfoTLS) {
+    delete sTraceInfoTLS;
+    sTraceInfoTLS = nullptr;
+  }
+}
+
+static void
+SetLogStarted(bool aIsStartLogging)
+{
+  // TODO: This is called from a signal handler. Use semaphore instead.
+  StaticMutexAutoLock lock(sMutex);
+
+  for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
+    (*sTraceInfos)[i]->mStartLogging = aIsStartLogging;
+  }
+
+  sIsLoggingStarted = aIsStartLogging;
+}
+
+static bool
+IsStartLogging(TraceInfo* aInfo)
+{
+  StaticMutexAutoLock lock(sMutex);
+  return aInfo ? aInfo->mStartLogging : false;
+}
+
 } // namespace anonymous
 
+nsCString*
+TraceInfo::AppendLog()
+{
+  MutexAutoLock lock(mLogsMutex);
+  return mLogs.AppendElement();
+}
+
 void
-InitTaskTracer()
+TraceInfo::MoveLogsInto(TraceInfoLogsType& aResult)
 {
-  MOZ_ASSERT(!sTraceInfos);
+  MutexAutoLock lock(mLogsMutex);
+  aResult.MoveElementsFrom(mLogs);
+}
 
-  sTraceInfos = new nsClassHashtable<nsUint32HashKey, TraceInfo>();
+void
+InitTaskTracer(uint32_t aFlags)
+{
+  if (aFlags & FORKED_AFTER_NUWA) {
+    CleanUp();
+  }
 
-  if (!sTraceInfoTLS.initialized()) {
-    unused << sTraceInfoTLS.init();
+  MOZ_ASSERT(!sTraceInfoTLS);
+  sTraceInfoTLS = new ThreadLocal<TraceInfo*>();
+
+  MOZ_ASSERT(!sTraceInfos);
+  sTraceInfos = new nsTArray<nsAutoPtr<TraceInfo>>();
+
+  if (!sTraceInfoTLS->initialized()) {
+    unused << sTraceInfoTLS->init();
   }
 }
 
 void
 ShutdownTaskTracer()
 {
-  delete sTraceInfos;
-  sTraceInfos = nullptr;
+  CleanUp();
 }
 
 TraceInfo*
 GetOrCreateTraceInfo()
 {
   NS_ENSURE_TRUE(IsInitialized(), nullptr);
 
-  TraceInfo* info = sTraceInfoTLS.get();
+  TraceInfo* info = sTraceInfoTLS->get();
   if (!info) {
     info = AllocTraceInfo(gettid());
-    sTraceInfoTLS.set(info);
+    sTraceInfoTLS->set(info);
   }
 
   return info;
 }
 
 uint64_t
 GenNewUniqueTaskId()
 {
@@ -195,75 +249,147 @@ GetCurTraceInfo(uint64_t* aOutSourceEven
   *aOutParentTaskId = info->mCurTaskId;
   *aOutSourceEventType = info->mCurTraceSourceType;
 }
 
 void
 LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId,
             SourceEventType aSourceEventType)
 {
-  NS_ENSURE_TRUE_VOID(IsInitialized());
+  TraceInfo* info = GetOrCreateTraceInfo();
+  if (!IsStartLogging(info)) {
+    return;
+  }
 
   // Log format:
   // [0 taskId dispatchTime sourceEventId sourceEventType parentTaskId]
+  nsCString* log = info->AppendLog();
+  if (log) {
+    log->AppendPrintf("%d %lld %lld %lld %d %lld",
+                      ACTION_DISPATCH, aTaskId, PR_Now(), aSourceEventId,
+                      aSourceEventType, aParentTaskId);
+  }
 }
 
 void
 LogBegin(uint64_t aTaskId, uint64_t aSourceEventId)
 {
-  NS_ENSURE_TRUE_VOID(IsInitialized());
+  TraceInfo* info = GetOrCreateTraceInfo();
+  if (!IsStartLogging(info)) {
+    return;
+  }
 
   // Log format:
   // [1 taskId beginTime processId threadId]
+  nsCString* log = info->AppendLog();
+  if (log) {
+    log->AppendPrintf("%d %lld %lld %d %d",
+                      ACTION_BEGIN, aTaskId, PR_Now(), getpid(), gettid());
+  }
 }
 
 void
 LogEnd(uint64_t aTaskId, uint64_t aSourceEventId)
 {
-  NS_ENSURE_TRUE_VOID(IsInitialized());
+  TraceInfo* info = GetOrCreateTraceInfo();
+  if (!IsStartLogging(info)) {
+    return;
+  }
 
   // Log format:
   // [2 taskId endTime]
+  nsCString* log = info->AppendLog();
+  if (log) {
+    log->AppendPrintf("%d %lld %lld", ACTION_END, aTaskId, PR_Now());
+  }
 }
 
 void
 LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId, int* aVptr)
 {
-  NS_ENSURE_TRUE_VOID(IsInitialized());
+  TraceInfo* info = GetOrCreateTraceInfo();
+  if (!IsStartLogging(info)) {
+    return;
+  }
 
   // Log format:
   // [4 taskId address]
+  nsCString* log = info->AppendLog();
+  if (log) {
+    log->AppendPrintf("%d %lld %p", ACTION_GET_VTABLE, aTaskId, aVptr);
+  }
 }
 
 void
 FreeTraceInfo()
 {
   NS_ENSURE_TRUE_VOID(IsInitialized());
 
-  FreeTraceInfo(gettid());
+  StaticMutexAutoLock lock(sMutex);
+  TraceInfo* info = GetOrCreateTraceInfo();
+  if (info) {
+    sTraceInfos->RemoveElement(info);
+  }
 }
 
 AutoSourceEvent::AutoSourceEvent(SourceEventType aType)
 {
   CreateSourceEvent(aType);
 }
 
 AutoSourceEvent::~AutoSourceEvent()
 {
   DestroySourceEvent();
 }
 
 void AddLabel(const char* aFormat, ...)
 {
-  NS_ENSURE_TRUE_VOID(IsInitialized());
+  TraceInfo* info = GetOrCreateTraceInfo();
+  if (!IsStartLogging(info)) {
+    return;
+  }
 
   va_list args;
   va_start(args, aFormat);
-  char buffer[MAX_USER_LABEL_LEN] = {0};
-  vsnprintf(buffer, MAX_USER_LABEL_LEN, aFormat, args);
+  nsAutoCString buffer;
+  buffer.AppendPrintf(aFormat, args);
   va_end(args);
 
   // Log format:
   // [3 taskId "label"]
+  nsCString* log = info->AppendLog();
+  if (log) {
+    log->AppendPrintf("%d %lld %lld \"%s\"", ACTION_ADD_LABEL, info->mCurTaskId,
+                      PR_Now(), buffer.get());
+  }
+}
+
+// Functions used by GeckoProfiler.
+
+void
+StartLogging()
+{
+  SetLogStarted(true);
+}
+
+void
+StopLogging()
+{
+  SetLogStarted(false);
+}
+
+TraceInfoLogsType*
+GetLoggedData(TimeStamp aStartTime)
+{
+  TraceInfoLogsType* result = new TraceInfoLogsType();
+
+  // TODO: This is called from a signal handler. Use semaphore instead.
+  StaticMutexAutoLock lock(sMutex);
+
+  for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
+    (*sTraceInfos)[i]->MoveLogsInto(*result);
+  }
+
+  return result;
 }
 
 } // namespace tasktracer
 } // namespace mozilla
--- a/tools/profiler/GeckoTaskTracer.h
+++ b/tools/profiler/GeckoTaskTracer.h
@@ -20,20 +20,31 @@
  * created, TaskTracer records the entire chain of Tasks and nsRunnables as they
  * are dispatched to different threads and processes. It records latency,
  * execution time, etc. for each Task and nsRunnable that chains back to the
  * original source event.
  */
 
 class Task;
 class nsIRunnable;
+class nsCString;
+template <class> class nsTArray;
 
 namespace mozilla {
+
+class TimeStamp;
+
 namespace tasktracer {
 
+enum {
+  FORKED_AFTER_NUWA = 1 << 0
+};
+void InitTaskTracer(uint32_t aFlags = 0);
+void ShutdownTaskTracer();
+
 class FakeTracedTask;
 
 enum SourceEventType {
   UNKNOWN = 0,
   TOUCH,
   MOUSE,
   KEY,
   BLUETOOTH,
@@ -47,16 +58,20 @@ public:
   AutoSourceEvent(SourceEventType aType);
   ~AutoSourceEvent();
 };
 
 // Add a label to the currently running task, aFormat is the message to log,
 // followed by corresponding parameters.
 void AddLabel(const char* aFormat, ...);
 
+void StartLogging();
+void StopLogging();
+nsTArray<nsCString>* GetLoggedData(TimeStamp aStartTime);
+
 /**
  * Internal functions.
  */
 
 Task* CreateTracedTask(Task* aTask);
 
 already_AddRefed<nsIRunnable> CreateTracedRunnable(nsIRunnable* aRunnable);
 
--- a/tools/profiler/GeckoTaskTracerImpl.h
+++ b/tools/profiler/GeckoTaskTracerImpl.h
@@ -3,48 +3,61 @@
 /* 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 GECKO_TASK_TRACER_IMPL_H
 #define GECKO_TASK_TRACER_IMPL_H
 
 #include "GeckoTaskTracer.h"
+#include "mozilla/Mutex.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace tasktracer {
 
+typedef nsTArray<nsCString> TraceInfoLogsType;
+
 struct TraceInfo
 {
-  TraceInfo(uint32_t aThreadId) : mCurTraceSourceId(0)
-                                , mCurTaskId(0)
-                                , mSavedCurTraceSourceId(0)
-                                , mSavedCurTaskId(0)
-                                , mCurTraceSourceType(UNKNOWN)
-                                , mSavedCurTraceSourceType(UNKNOWN)
-                                , mThreadId(aThreadId)
-                                , mLastUniqueTaskId(0)
+  TraceInfo(uint32_t aThreadId, bool aStartLogging)
+    : mCurTraceSourceId(0)
+    , mCurTaskId(0)
+    , mSavedCurTraceSourceId(0)
+    , mSavedCurTaskId(0)
+    , mCurTraceSourceType(UNKNOWN)
+    , mSavedCurTraceSourceType(UNKNOWN)
+    , mThreadId(aThreadId)
+    , mLastUniqueTaskId(0)
+    , mStartLogging(aStartLogging)
+    , mLogsMutex("TraceInfoMutex")
   {
     MOZ_COUNT_CTOR(TraceInfo);
   }
 
   ~TraceInfo() { MOZ_COUNT_DTOR(TraceInfo); }
 
+  nsCString* AppendLog();
+  void MoveLogsInto(TraceInfoLogsType& aResult);
+
   uint64_t mCurTraceSourceId;
   uint64_t mCurTaskId;
   uint64_t mSavedCurTraceSourceId;
   uint64_t mSavedCurTaskId;
   SourceEventType mCurTraceSourceType;
   SourceEventType mSavedCurTraceSourceType;
   uint32_t mThreadId;
   uint32_t mLastUniqueTaskId;
-};
+  bool mStartLogging;
 
-void InitTaskTracer();
-void ShutdownTaskTracer();
+  // This mutex protects the following log array because MoveLogsInto() might
+  // be called on another thread.
+  mozilla::Mutex mLogsMutex;
+  TraceInfoLogsType mLogs;
+};
 
 // Return the TraceInfo of current thread, allocate a new one if not exit.
 TraceInfo* GetOrCreateTraceInfo();
 
 uint64_t GenNewUniqueTaskId();
 
 class AutoSaveCurTraceInfo
 {
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -31,16 +31,17 @@
 #include "nsServiceManagerUtils.h"
 #include "nsIXULRuntime.h"
 #include "nsIXULAppInfo.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "PlatformMacros.h"
+#include "nsTArray.h"
 
 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
   #include "AndroidBridge.h"
 #endif
 
 // JS
 #include "jsfriendapi.h"
 #include "js/ProfilingFrameIterator.h"
@@ -101,16 +102,53 @@ void TableTicker::HandleSaveRequest()
   mSaveRequested = false;
 
   // TODO: Use use the ipc/chromium Tasks here to support processes
   // without XPCOM.
   nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
   NS_DispatchToMainThread(runnable);
 }
 
+
+void TableTicker::StreamTaskTracer(JSStreamWriter& b)
+{
+  b.BeginObject();
+#ifdef MOZ_TASK_TRACER
+    b.Name("data");
+    b.BeginArray();
+      nsAutoPtr<nsTArray<nsCString>> data(
+        mozilla::tasktracer::GetLoggedData(sStartTime));
+      for (uint32_t i = 0; i < data->Length(); ++i) {
+        b.Value((data->ElementAt(i)).get());
+      }
+      mozilla::tasktracer::StartLogging();
+    b.EndArray();
+
+    b.Name("threads");
+    b.BeginArray();
+      mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
+      for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
+        // Thread meta data
+        ThreadInfo* info = sRegisteredThreads->at(i);
+        b.BeginObject();
+        if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
+          // TODO Add the proper plugin name
+          b.NameValue("name", "Plugin");
+        } else {
+          b.NameValue("name", info->Name());
+        }
+        b.NameValue("tid", static_cast<int>(info->ThreadId()));
+        b.EndObject();
+      }
+    b.EndArray();
+#endif
+  b.EndObject();
+}
+
+
 void TableTicker::StreamMetaJSCustomObject(JSStreamWriter& b)
 {
   b.BeginObject();
 
     b.NameValue("version", 2);
     b.NameValue("interval", interval());
     b.NameValue("stackwalk", mUseStackWalk);
     b.NameValue("jank", mJankOnly);
@@ -265,16 +303,22 @@ void TableTicker::StreamJSObject(JSStrea
   b.BeginObject();
     // Put shared library info
     b.NameValue("libs", GetSharedLibraryInfoString().c_str());
 
     // Put meta data
     b.Name("meta");
     StreamMetaJSCustomObject(b);
 
+    // Data of TaskTracer doesn't belong in the circular buffer.
+    if (TaskTracer()) {
+      b.Name("tasktracer");
+      StreamTaskTracer(b);
+    }
+
     // Lists the samples for each ThreadProfile
     b.Name("threads");
     b.BeginArray();
 
       SetPaused(true);
 
       {
         mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
--- a/tools/profiler/TableTicker.h
+++ b/tools/profiler/TableTicker.h
@@ -5,16 +5,19 @@
 
 #ifndef TableTicker_h
 #define TableTicker_h
 
 #include "platform.h"
 #include "ProfileEntry.h"
 #include "mozilla/Mutex.h"
 #include "IntelPowerGadget.h"
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+#endif
 
 static bool
 hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature) {
   for(size_t i = 0; i < aFeatureCount; i++) {
     if (strcmp(aFeatures[i], aFeature) == 0)
       return true;
   }
   return false;
@@ -68,16 +71,17 @@ class TableTicker: public Sampler {
     // Users sometimes ask to filter by a list of threads but forget to request
     // profiling non main threads. Let's make it implificit if we have a filter
     mProfileThreads = hasFeature(aFeatures, aFeatureCount, "threads") || aFilterCount > 0;
     mUnwinderThread = hasFeature(aFeatures, aFeatureCount, "unwinder") || sps_version2();
     mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
     mPrivacyMode = hasFeature(aFeatures, aFeatureCount, "privacy");
     mAddMainThreadIO = hasFeature(aFeatures, aFeatureCount, "mainthreadio");
     mProfileMemory = hasFeature(aFeatures, aFeatureCount, "memory");
+    mTaskTracer = hasFeature(aFeatures, aFeatureCount, "tasktracer");
 
 #if defined(XP_WIN)
     if (mProfilePower) {
       mIntelPowerGadget = new IntelPowerGadget();
       mProfilePower = mIntelPowerGadget->Init();
     }
 #endif
 
@@ -96,16 +100,22 @@ class TableTicker: public Sampler {
       for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
         ThreadInfo* info = sRegisteredThreads->at(i);
 
         RegisterThread(info);
       }
 
       SetActiveSampler(this);
     }
+
+#ifdef MOZ_TASK_TRACER
+    if (mTaskTracer) {
+      mozilla::tasktracer::StartLogging();
+    }
+#endif
   }
 
   ~TableTicker() {
     if (IsActive())
       Stop();
 
     SetActiveSampler(nullptr);
 
@@ -152,16 +162,21 @@ class TableTicker: public Sampler {
 
   // Immediately captures the calling thread's call stack and returns it.
   virtual SyncProfile* GetBacktrace();
 
   // Called within a signal. This function must be reentrant
   virtual void RequestSave()
   {
     mSaveRequested = true;
+#ifdef MOZ_TASK_TRACER
+    if (mTaskTracer) {
+      mozilla::tasktracer::StopLogging();
+    }
+#endif
   }
 
   virtual void HandleSaveRequest();
 
   ThreadProfile* GetPrimaryThreadProfile()
   {
     if (!mPrimaryThreadProfile) {
       mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
@@ -176,24 +191,26 @@ class TableTicker: public Sampler {
     }
 
     return mPrimaryThreadProfile;
   }
 
   void ToStreamAsJSON(std::ostream& stream);
   virtual JSObject *ToJSObject(JSContext *aCx);
   void StreamMetaJSCustomObject(JSStreamWriter& b);
+  void StreamTaskTracer(JSStreamWriter& b);
   bool HasUnwinderThread() const { return mUnwinderThread; }
   bool ProfileJS() const { return mProfileJS; }
   bool ProfileJava() const { return mProfileJava; }
   bool ProfilePower() const { return mProfilePower; }
   bool ProfileThreads() const { return mProfileThreads; }
   bool InPrivacyMode() const { return mPrivacyMode; }
   bool AddMainThreadIO() const { return mAddMainThreadIO; }
   bool ProfileMemory() const { return mProfileMemory; }
+  bool TaskTracer() const { return mTaskTracer; }
 
 protected:
   // Called within a signal. This function must be reentrant
   virtual void UnwinderTick(TickSample* sample);
 
   // Called within a signal. This function must be reentrant
   virtual void InplaceTick(TickSample* sample);
 
@@ -216,15 +233,16 @@ protected:
 
   // Keep the thread filter to check against new thread that
   // are started while profiling
   char** mThreadNameFilters;
   uint32_t mFilterCount;
   bool mPrivacyMode;
   bool mAddMainThreadIO;
   bool mProfileMemory;
+  bool mTaskTracer;
 #if defined(XP_WIN)
   IntelPowerGadget* mIntelPowerGadget;
 #endif
 };
 
 #endif
 
--- a/tools/profiler/TracedTaskCommon.cpp
+++ b/tools/profiler/TracedTaskCommon.cpp
@@ -29,28 +29,32 @@ TracedTaskCommon::Init()
 
   LogDispatch(mTaskId, info->mCurTaskId, mSourceEventId, mSourceEventType);
 }
 
 void
 TracedTaskCommon::SetTraceInfo()
 {
   TraceInfo* info = GetOrCreateTraceInfo();
-  NS_ENSURE_TRUE_VOID(info);
+  if (!info) {
+    return;
+  }
 
   info->mCurTraceSourceId = mSourceEventId;
   info->mCurTraceSourceType = mSourceEventType;
   info->mCurTaskId = mTaskId;
 }
 
 void
 TracedTaskCommon::ClearTraceInfo()
 {
   TraceInfo* info = GetOrCreateTraceInfo();
-  NS_ENSURE_TRUE_VOID(info);
+  if (!info) {
+    return;
+  }
 
   info->mCurTraceSourceId = 0;
   info->mCurTraceSourceType = SourceEventType::UNKNOWN;
   info->mCurTaskId = 0;
 }
 
 /**
  * Implementation of class TracedRunnable.
--- a/tools/profiler/platform.cpp
+++ b/tools/profiler/platform.cpp
@@ -684,16 +684,20 @@ const char** mozilla_sampler_get_feature
     // Profile the registered secondary threads.
     "threads",
     // Do not include user-identifiable information
     "privacy",
     // Add main thread I/O to the profile
     "mainthreadio",
     // Add RSS collection
     "memory",
+#ifdef MOZ_TASK_TRACER
+    // Start profiling with feature TaskTracer.
+    "tasktracer",
+#endif
 #if defined(XP_WIN)
     // Add power collection
     "power",
 #endif
     nullptr
   };
 
   return features;
--- a/widget/BasicEvents.h
+++ b/widget/BasicEvents.h
@@ -36,16 +36,21 @@
 #define NS_XUL_CLOSE                    (NS_WINDOW_START + 1)
 // Key is pressed within a window
 #define NS_KEY_PRESS                    (NS_WINDOW_START + 31)
 // Key is released within a window
 #define NS_KEY_UP                       (NS_WINDOW_START + 32)
 // Key is pressed within a window
 #define NS_KEY_DOWN                     (NS_WINDOW_START + 33)
 
+#define NS_KEY_BEFORE_DOWN              (NS_WINDOW_START + 34)
+#define NS_KEY_AFTER_DOWN               (NS_WINDOW_START + 35)
+#define NS_KEY_BEFORE_UP                (NS_WINDOW_START + 36)
+#define NS_KEY_AFTER_UP                 (NS_WINDOW_START + 37)
+
 #define NS_RESIZE_EVENT                 (NS_WINDOW_START + 60)
 #define NS_SCROLL_EVENT                 (NS_WINDOW_START + 61)
 
 // A plugin was clicked or otherwise focused. NS_PLUGIN_ACTIVATE should be
 // used when the window is not active. NS_PLUGIN_FOCUS should be used when
 // the window is active. In the latter case, the dispatcher of the event
 // is expected to ensure that the plugin's widget is focused beforehand.
 #define NS_PLUGIN_ACTIVATE               (NS_WINDOW_START + 62)
--- a/widget/EventClassList.h
+++ b/widget/EventClassList.h
@@ -21,16 +21,17 @@ NS_EVENT_CLASS(Widget, InputEvent)
 NS_EVENT_CLASS(Internal, UIEvent)
 
 // TextEvents.h
 NS_EVENT_CLASS(Widget, KeyboardEvent)
 NS_EVENT_CLASS(Widget, CompositionEvent)
 NS_EVENT_CLASS(Widget, QueryContentEvent)
 NS_EVENT_CLASS(Widget, SelectionEvent)
 NS_EVENT_CLASS(Internal, EditorInputEvent)
+NS_EVENT_CLASS(Internal, BeforeAfterKeyboardEvent)
 
 // MouseEvents.h
 NS_EVENT_CLASS(Widget, MouseEventBase)
 NS_EVENT_CLASS(Widget, MouseEvent)
 NS_EVENT_CLASS(Widget, DragEvent)
 NS_EVENT_CLASS(Widget, MouseScrollEvent)
 NS_EVENT_CLASS(Widget, WheelEvent)
 NS_EVENT_CLASS(Widget, PointerEvent)
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -70,25 +70,27 @@ struct AlternativeCharCode
  ******************************************************************************/
 
 class WidgetKeyboardEvent : public WidgetInputEvent
 {
 private:
   friend class dom::PBrowserParent;
   friend class dom::PBrowserChild;
 
+protected:
   WidgetKeyboardEvent()
   {
   }
 
 public:
   virtual WidgetKeyboardEvent* AsKeyboardEvent() MOZ_OVERRIDE { return this; }
 
-  WidgetKeyboardEvent(bool aIsTrusted, uint32_t aMessage, nsIWidget* aWidget)
-    : WidgetInputEvent(aIsTrusted, aMessage, aWidget, eKeyboardEventClass)
+  WidgetKeyboardEvent(bool aIsTrusted, uint32_t aMessage, nsIWidget* aWidget,
+                      EventClassID aEventClassID = eKeyboardEventClass)
+    : WidgetInputEvent(aIsTrusted, aMessage, aWidget, aEventClassID)
     , keyCode(0)
     , charCode(0)
     , location(nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD)
     , isChar(false)
     , mIsRepeat(false)
     , mIsComposing(false)
     , mKeyNameIndex(mozilla::KEY_NAME_INDEX_Unidentified)
     , mCodeNameIndex(CODE_NAME_INDEX_UNKNOWN)
@@ -189,16 +191,82 @@ public:
     mCodeValue = aEvent.mCodeValue;
     // Don't copy mNativeKeyEvent because it may be referred after its instance
     // is destroyed.
     mNativeKeyEvent = nullptr;
     mUniqueId = aEvent.mUniqueId;
   }
 };
 
+
+/******************************************************************************
+ * mozilla::InternalBeforeAfterKeyboardEvent
+ *
+ * This is extended from WidgetKeyboardEvent and is mapped to DOM event
+ * "BeforeAfterKeyboardEvent".
+ *
+ * Event message: NS_KEY_BEFORE_DOWN
+ *                NS_KEY_BEFORE_UP
+ *                NS_KEY_AFTER_DOWN
+ *                NS_KEY_AFTER_UP
+ ******************************************************************************/
+class InternalBeforeAfterKeyboardEvent : public WidgetKeyboardEvent
+{
+private:
+  friend class dom::PBrowserParent;
+  friend class dom::PBrowserChild;
+
+  InternalBeforeAfterKeyboardEvent()
+  {
+  }
+
+public:
+  // Extra member for InternalBeforeAfterKeyboardEvent. Indicates whether
+  // default actions of keydown/keyup event is prevented.
+  Nullable<bool> mEmbeddedCancelled;
+
+  virtual InternalBeforeAfterKeyboardEvent* AsBeforeAfterKeyboardEvent() MOZ_OVERRIDE
+  {
+    return this;
+  }
+
+  InternalBeforeAfterKeyboardEvent(bool aIsTrusted, uint32_t aMessage,
+                                   nsIWidget* aWidget)
+    : WidgetKeyboardEvent(aIsTrusted, aMessage, aWidget, eBeforeAfterKeyboardEventClass)
+  {
+  }
+
+  virtual WidgetEvent* Duplicate() const MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(mClass == eBeforeAfterKeyboardEventClass,
+               "Duplicate() must be overridden by sub class");
+    // Not copying widget, it is a weak reference.
+    InternalBeforeAfterKeyboardEvent* result =
+      new InternalBeforeAfterKeyboardEvent(false, message, nullptr);
+    result->AssignBeforeAfterKeyEventData(*this, true);
+    result->mFlags = mFlags;
+    return result;
+  }
+
+  void AssignBeforeAfterKeyEventData(
+         const InternalBeforeAfterKeyboardEvent& aEvent,
+         bool aCopyTargets)
+  {
+    AssignKeyEventData(aEvent, aCopyTargets);
+    mEmbeddedCancelled = aEvent.mEmbeddedCancelled;
+  }
+
+  void AssignBeforeAfterKeyEventData(
+         const WidgetKeyboardEvent& aEvent,
+         bool aCopyTargets)
+  {
+    AssignKeyEventData(aEvent, aCopyTargets);
+  }
+};
+
 /******************************************************************************
  * mozilla::WidgetCompositionEvent
  ******************************************************************************/
 
 class WidgetCompositionEvent : public WidgetGUIEvent
 {
 private:
   friend class mozilla::dom::PBrowserParent;
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -919,39 +919,49 @@ struct ShadowParams {
   float density;
   int offsetX;
   int offsetY;
   unsigned int flags;
 };
 
 // These numbers have been determined by looking at the results of
 // CGSGetWindowShadowAndRimParameters for native window types.
-static const ShadowParams kWindowShadowParameters[] = {
+static const ShadowParams kWindowShadowParametersPreYosemite[] = {
   { 0.0f, 0.0f, 0, 0, 0 },        // none
   { 8.0f, 0.5f, 0, 6, 1 },        // default
   { 10.0f, 0.44f, 0, 10, 512 },   // menu
   { 8.0f, 0.5f, 0, 6, 1 },        // tooltip
   { 4.0f, 0.6f, 0, 4, 512 }       // sheet
 };
 
+static const ShadowParams kWindowShadowParametersPostYosemite[] = {
+  { 0.0f, 0.0f, 0, 0, 0 },        // none
+  { 8.0f, 0.5f, 0, 6, 1 },        // default
+  { 9.882353f, 0.3f, 0, 4, 0 },   // menu
+  { 3.294118f, 0.2f, 0, 1, 0 },   // tooltip
+  { 9.882353f, 0.3f, 0, 4, 0 }    // sheet
+};
+
 // This method will adjust the window shadow style for popup windows after
 // they have been made visible. Before they're visible, their window number
 // might be -1, which is not useful.
 // We won't attempt to change the shadow for windows that can acquire key state
 // since OS X will reset the shadow whenever that happens.
 void
 nsCocoaWindow::AdjustWindowShadow()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if (!mWindow || ![mWindow isVisible] || ![mWindow hasShadow] ||
       [mWindow canBecomeKeyWindow] || [mWindow windowNumber] == -1)
     return;
 
-  const ShadowParams& params = kWindowShadowParameters[mShadowStyle];
+  const ShadowParams& params = nsCocoaFeatures::OnYosemiteOrLater()
+    ? kWindowShadowParametersPostYosemite[mShadowStyle]
+    : kWindowShadowParametersPreYosemite[mShadowStyle];
   CGSConnection cid = _CGSDefaultConnection();
   CGSSetWindowShadowAndRimParameters(cid, [mWindow windowNumber],
                                      params.standardDeviation, params.density,
                                      params.offsetX, params.offsetY,
                                      params.flags);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -358,16 +358,47 @@ struct ParamTraits<mozilla::WidgetKeyboa
       aResult->mNativeKeyEvent = nullptr;
       return true;
     }
     return false;
   }
 };
 
 template<>
+struct ParamTraits<mozilla::InternalBeforeAfterKeyboardEvent>
+{
+  typedef mozilla::InternalBeforeAfterKeyboardEvent paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, static_cast<mozilla::WidgetKeyboardEvent>(aParam));
+    WriteParam(aMsg, aParam.mEmbeddedCancelled.IsNull());
+    WriteParam(aMsg, aParam.mEmbeddedCancelled.Value());
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    bool isNull;
+    bool value;
+    bool rv =
+      ReadParam(aMsg, aIter,
+                static_cast<mozilla::WidgetKeyboardEvent*>(aResult)) &&
+      ReadParam(aMsg, aIter, &isNull) &&
+      ReadParam(aMsg, aIter, &value);
+
+    aResult->mEmbeddedCancelled = Nullable<bool>();
+    if (!isNull) {
+      aResult->mEmbeddedCancelled.SetValue(value);
+    }
+
+    return rv;
+  }
+};
+
+template<>
 struct ParamTraits<mozilla::TextRangeStyle>
 {
   typedef mozilla::TextRangeStyle paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mDefinedStyles);
     WriteParam(aMsg, aParam.mLineStyle);
--- a/widget/shared/WidgetEventImpl.cpp
+++ b/widget/shared/WidgetEventImpl.cpp
@@ -117,16 +117,20 @@ WidgetEvent::HasDragEventMessage() const
 
 bool
 WidgetEvent::HasKeyEventMessage() const
 {
   switch (message) {
     case NS_KEY_DOWN:
     case NS_KEY_PRESS:
     case NS_KEY_UP:
+    case NS_KEY_BEFORE_DOWN:
+    case NS_KEY_BEFORE_UP:
+    case NS_KEY_AFTER_DOWN:
+    case NS_KEY_AFTER_UP:
       return true;
     default:
       return false;
   }
 }
 
 bool
 WidgetEvent::HasIMEEventMessage() const
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -558,21 +558,22 @@ nsComponentManagerImpl::RegisterContract
   mLock.AssertCurrentThreadOwns();
 
   if (!ProcessSelectorMatches(aEntry->processSelector)) {
     return;
   }
 
   nsFactoryEntry* f = mFactories.Get(*aEntry->cid);
   if (!f) {
-    NS_ERROR("No CID found when attempting to map contract ID");
+    NS_WARNING("No CID found when attempting to map contract ID");
 
     char idstr[NSID_LENGTH];
     aEntry->cid->ToProvidedString(idstr);
 
+    SafeMutexAutoUnlock unlock(mLock);
     LogMessage("Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.",
                aEntry->contractid,
                idstr);
 
     return;
   }
 
   mContractIDs.Put(nsDependentCString(aEntry->contractid), f);
--- a/xpcom/glue/standalone/nsXPCOMGlue.cpp
+++ b/xpcom/glue/standalone/nsXPCOMGlue.cpp
@@ -376,16 +376,17 @@ XPCOMGlueLoad(const char* aXPCOMFile)
   // load the libraries from Contents/MacOS.
   const char *tempSlash = strrchr(aXPCOMFile, '/');
   size_t tempLen = size_t(tempSlash - aXPCOMFile);
   if (tempLen > MAXPATHLEN) {
     return nullptr;
   }
   char tempBuffer[MAXPATHLEN];
   memcpy(tempBuffer, aXPCOMFile, tempLen);
+  tempBuffer[tempLen] = '\0';
   const char *slash = strrchr(tempBuffer, '/');
   tempLen = size_t(slash - tempBuffer);
   const char *lastSlash = aXPCOMFile + tempLen;
 #else
   const char* lastSlash = strrchr(aXPCOMFile, '/');
 #endif
   char* cursor;
   if (lastSlash) {
--- a/xpcom/tests/component/TestComponent.cpp
+++ b/xpcom/tests/component/TestComponent.cpp
@@ -3,31 +3,41 @@
  * 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/ModuleUtils.h"
 
 #define NS_TESTING_CID \
 { 0x335fb596, 0xe52d, 0x418f, \
   { 0xb0, 0x1c, 0x1b, 0xf1, 0x6c, 0xe5, 0xe7, 0xe4 } }
+#define NS_NONEXISTENT_CID \
+{ 0x1e61fb15, 0xead4, 0x45cd, \
+  { 0x80, 0x13, 0x40, 0x99, 0xa7, 0x10, 0xa2, 0xfa } }
 
 NS_DEFINE_NAMED_CID(NS_TESTING_CID);
+NS_DEFINE_NAMED_CID(NS_NONEXISTENT_CID);
 
 static nsresult
 DummyConstructorFunc(nsISupports* aOuter, const nsIID& aIID, void** aResult)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 static const mozilla::Module::CIDEntry kTestCIDs[] = {
   { &kNS_TESTING_CID, false, nullptr, DummyConstructorFunc },
   { &kNS_TESTING_CID, false, nullptr, DummyConstructorFunc },
   { nullptr }
 };
 
+static const mozilla::Module::ContractIDEntry kTestContractIDs[] = {
+  { "@testing/foo", &kNS_NONEXISTENT_CID },
+  { nullptr }
+};
+
 static const mozilla::Module kTestModule = {
   mozilla::Module::kVersion,
-  kTestCIDs
+  kTestCIDs,
+  kTestContractIDs
 };
 
 NSMODULE_DEFN(dummy) = &kTestModule;