Merge mozilla-beta to b2g37. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 17 Mar 2015 16:54:44 -0400
changeset 237736 83251e534b337bc0c428df7a2c830e9cb1651240
parent 237722 5c102190ee5537fb777341e94cc8f1574f999a5a (current diff)
parent 237735 2cc99febbda0d9f74cb6108ad6d02f03e4271d95 (diff)
child 237737 a6f5f4035ea5433b67e67165bc6b096a441d2c6b
push id340
push userryanvm@gmail.com
push dateTue, 17 Mar 2015 20:54:59 +0000
treeherdermozilla-b2g37_v2_2@83251e534b33 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone37.0
Merge mozilla-beta to b2g37. a=merge
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/media/MediaDecoderStateMachine.cpp
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -280,17 +280,17 @@ pref("browser.slowStartup.timeThreshold"
 pref("browser.slowStartup.maxSamples", 5);
 
 // This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
 // this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
 // repackager of this code using an alternate snippet url, please keep your users safe
 pref("browser.aboutHomeSnippets.updateUrl", "https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
 
 pref("browser.enable_automatic_image_resizing", true);
-pref("browser.casting.enabled", true);
+pref("browser.casting.enabled", false);
 pref("browser.chrome.site_icons", true);
 pref("browser.chrome.favicons", true);
 // browser.warnOnQuit == false will override all other possible prompts when quitting or restarting
 pref("browser.warnOnQuit", true);
 // browser.showQuitWarning specifically controls the quit warning dialog. We
 // might still show the window closing dialog with showQuitWarning == false.
 pref("browser.showQuitWarning", false);
 pref("browser.fullscreen.autohide", true);
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -337,16 +337,96 @@ nsBrowserContentHandler.prototype = {
     if (cmdLine.handleFlag("browser", false)) {
       // Passing defaultArgs, so use NO_EXTERNAL_URIS
       openWindow(null, this.chromeURL, "_blank",
                  "chrome,dialog=no,all" + this.getFeatures(cmdLine),
                  this.defaultArgs, NO_EXTERNAL_URIS);
       cmdLine.preventDefault = true;
     }
 
+    try {
+      var remoteCommand = cmdLine.handleFlagWithParam("remote", true);
+    }
+    catch (e) {
+      throw NS_ERROR_ABORT;
+    }
+
+    if (remoteCommand != null) {
+      try {
+        var a = /^\s*(\w+)\(([^\)]*)\)\s*$/.exec(remoteCommand);
+        var remoteVerb;
+        if (a) {
+          remoteVerb = a[1].toLowerCase();
+          var remoteParams = [];
+          var sepIndex = a[2].lastIndexOf(",");
+          if (sepIndex == -1)
+            remoteParams[0] = a[2];
+          else {
+            remoteParams[0] = a[2].substring(0, sepIndex);
+            remoteParams[1] = a[2].substring(sepIndex + 1);
+          }
+        }
+
+        switch (remoteVerb) {
+        case "openurl":
+        case "openfile":
+          // openURL(<url>)
+          // openURL(<url>,new-window)
+          // openURL(<url>,new-tab)
+
+          // First param is the URL, second param (if present) is the "target"
+          // (tab, window)
+          var url = remoteParams[0];
+          var target = nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW;
+          if (remoteParams[1]) {
+            var targetParam = remoteParams[1].toLowerCase()
+                                             .replace(/^\s*|\s*$/g, "");
+            if (targetParam == "new-tab")
+              target = nsIBrowserDOMWindow.OPEN_NEWTAB;
+            else if (targetParam == "new-window")
+              target = nsIBrowserDOMWindow.OPEN_NEWWINDOW;
+            else {
+              // The "target" param isn't one of our supported values, so
+              // assume it's part of a URL that contains commas.
+              url += "," + remoteParams[1];
+            }
+          }
+
+          var uri = resolveURIInternal(cmdLine, url);
+          handURIToExistingBrowser(uri, target, cmdLine);
+          break;
+
+        case "xfedocommand":
+          // xfeDoCommand(openBrowser)
+          if (remoteParams[0].toLowerCase() != "openbrowser")
+            throw NS_ERROR_ABORT;
+
+          // Passing defaultArgs, so use NO_EXTERNAL_URIS
+          openWindow(null, this.chromeURL, "_blank",
+                     "chrome,dialog=no,all" + this.getFeatures(cmdLine),
+                     this.defaultArgs, NO_EXTERNAL_URIS);
+          break;
+
+        default:
+          // Somebody sent us a remote command we don't know how to process:
+          // just abort.
+          throw "Unknown remote command.";
+        }
+
+        cmdLine.preventDefault = true;
+      }
+      catch (e) {
+        Components.utils.reportError(e);
+        // If we had a -remote flag but failed to process it, throw
+        // NS_ERROR_ABORT so that the xremote code knows to return a failure
+        // back to the handling code.
+        throw NS_ERROR_ABORT;
+      }
+    }
+
     var uriparam;
     try {
       while ((uriparam = cmdLine.handleFlagWithParam("new-window", false))) {
         var uri = resolveURIInternal(cmdLine, uriparam);
         if (!shouldLoadURI(uri))
           continue;
         openWindow(null, this.chromeURL, "_blank",
                    "chrome,dialog=no,all" + this.getFeatures(cmdLine),
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2511,30 +2511,16 @@ ContentChild::RecvLoadPluginResult(const
 bool
 ContentChild::RecvAssociatePluginId(const uint32_t& aPluginId,
                                     const base::ProcessId& aProcessId)
 {
     plugins::PluginModuleContentParent::AssociatePluginId(aPluginId, aProcessId);
     return true;
 }
 
-bool
-ContentChild::RecvShutdown()
-{
-    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-    if (os) {
-        os->NotifyObservers(this, "content-child-shutdown", nullptr);
-    }
-
-    // Ignore errors here. If this fails, the parent will kill us after a
-    // timeout.
-    unused << SendFinishShutdown();
-    return true;
-}
-
 PBrowserOrId
 ContentChild::GetBrowserOrId(TabChild* aTabChild)
 {
     if (!aTabChild ||
         this == aTabChild->Manager()) {
         return PBrowserOrId(aTabChild);
     }
     else {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -373,17 +373,16 @@ public:
                                       const bool& aResult) MOZ_OVERRIDE;
 
     virtual bool RecvStartProfiler(const uint32_t& aEntries,
                                    const double& aInterval,
                                    const nsTArray<nsCString>& aFeatures,
                                    const nsTArray<nsCString>& aThreadNameFilters) MOZ_OVERRIDE;
     virtual bool RecvStopProfiler() MOZ_OVERRIDE;
     virtual bool RecvGetProfile(nsCString* aProfile) MOZ_OVERRIDE;
-    virtual bool RecvShutdown() MOZ_OVERRIDE;
 
 #ifdef ANDROID
     gfxIntSize GetScreenSize() { return mScreenSize; }
 #endif
 
     // Get the directory for IndexedDB files. We query the parent for this and
     // cache the value
     nsString &GetIndexedDBPath();
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -112,17 +112,16 @@
 #include "nsIObserverService.h"
 #include "nsIPresShell.h"
 #include "nsIScriptError.h"
 #include "nsISiteSecurityService.h"
 #include "nsISpellChecker.h"
 #include "nsIStyleSheet.h"
 #include "nsISupportsPrimitives.h"
 #include "nsISystemMessagesInternal.h"
-#include "nsITimer.h"
 #include "nsIURIFixup.h"
 #include "nsIWindowWatcher.h"
 #include "nsIXULRuntime.h"
 #include "nsMemoryInfoDumper.h"
 #include "nsMemoryReporterManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStyleSheetService.h"
 #include "nsThreadUtils.h"
@@ -583,17 +582,16 @@ static uint64_t gContentChildID = 1;
 
 // We want the prelaunched process to know that it's for apps, but not
 // actually for any app in particular.  Use a magic manifest URL.
 // Can't be a static constant.
 #define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}")
 
 static const char* sObserverTopics[] = {
     "xpcom-shutdown",
-    "profile-before-change",
     NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
     "child-memory-reporter-request",
     "memory-pressure",
     "child-gc-request",
     "child-cc-request",
     "child-mmu-request",
     "last-pb-context-exited",
     "file-watcher-update",
@@ -1527,66 +1525,43 @@ ContentParent::TransformPreallocatedInto
     // Reset mAppManifestURL, mIsForBrowser and mOSPrivileges for browser.
     mMetamorphosed = true;
     mOpener = aOpener;
     mAppManifestURL.Truncate();
     mIsForBrowser = true;
 }
 
 void
-ContentParent::ShutDownProcess(ShutDownMethod aMethod)
+ContentParent::ShutDownProcess(bool aCloseWithError)
 {
-#ifdef MOZ_NUWA_PROCESS
-    if (aMethod == SEND_SHUTDOWN_MESSAGE && IsNuwaProcess()) {
-        // We shouldn't send shutdown messages to frozen Nuwa processes,
-        // so just close the channel.
-        aMethod = CLOSE_CHANNEL;
-    }
-#endif
-
-    // Shutting down by sending a shutdown message works differently than the
-    // other methods. We first call Shutdown() in the child. After the child is
-    // ready, it calls FinishShutdown() on us. Then we close the channel.
-    if (aMethod == SEND_SHUTDOWN_MESSAGE) {
-        if (mIPCOpen && !mShutdownPending && SendShutdown()) {
-            mShutdownPending = true;
-            // Start the force-kill timer if we haven't already.
-            StartForceKillTimer();
-        }
-
-        // If call was not successful, the channel must have been broken
-        // somehow, and we will clean up the error in ActorDestroy.
-        return;
-    }
-
     using mozilla::dom::quota::QuotaManager;
 
     if (QuotaManager* quotaManager = QuotaManager::Get()) {
         quotaManager->AbortCloseStoragesForProcess(this);
     }
 
     // If Close() fails with an error, we'll end up back in this function, but
-    // with aMethod = CLOSE_CHANNEL_WITH_ERROR.  It's important that we call
+    // with aCloseWithError = true.  It's important that we call
     // CloseWithError() in this case; see bug 895204.
 
-    if (aMethod == CLOSE_CHANNEL && !mCalledClose) {
+    if (!aCloseWithError && !mCalledClose) {
         // Close() can only be called once: It kicks off the destruction
         // sequence.
         mCalledClose = true;
         Close();
 #ifdef MOZ_NUWA_PROCESS
         // Kill Nuwa process forcibly to break its IPC channels and finalize
         // corresponding parents.
         if (IsNuwaProcess()) {
             KillHard();
         }
 #endif
     }
 
-    if (aMethod == CLOSE_CHANNEL_WITH_ERROR && !mCalledCloseWithError) {
+    if (aCloseWithError && !mCalledCloseWithError) {
         MessageChannel* channel = GetIPCChannel();
         if (channel) {
             mCalledCloseWithError = true;
             channel->CloseWithError();
         }
     }
 
     const InfallibleTArray<POfflineCacheUpdateParent*>& ocuParents =
@@ -1603,27 +1578,16 @@ ContentParent::ShutDownProcess(ShutDownM
 
     // A ContentParent object might not get freed until after XPCOM shutdown has
     // shut down the cycle collector.  But by then it's too late to release any
     // CC'ed objects, so we need to null them out here, while we still can.  See
     // bug 899761.
     ShutDownMessageManager();
 }
 
-bool
-ContentParent::RecvFinishShutdown()
-{
-    // At this point, we already called ShutDownProcess once with
-    // SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call
-    // ShutDownProcess again with CLOSE_CHANNEL.
-    MOZ_ASSERT(mShutdownPending);
-    ShutDownProcess(CLOSE_CHANNEL);
-    return true;
-}
-
 void
 ContentParent::ShutDownMessageManager()
 {
   if (!mMessageManager) {
     return;
   }
 
   mMessageManager->ReceiveMessage(
@@ -1809,35 +1773,22 @@ struct DelayedDeleteContentParentTask : 
     nsRefPtr<ContentParent> mObj;
 };
 
 }
 
 void
 ContentParent::ActorDestroy(ActorDestroyReason why)
 {
-    if (mForceKillTimer) {
-        mForceKillTimer->Cancel();
-        mForceKillTimer = nullptr;
+    if (mForceKillTask) {
+        mForceKillTask->Cancel();
+        mForceKillTask = nullptr;
     }
 
-    // Signal shutdown completion regardless of error state, so we can
-    // finish waiting in the xpcom-shutdown/profile-before-change observer.
-    mIPCOpen = false;
-
-    if (why == NormalShutdown && !mCalledClose) {
-        // If we shut down normally but haven't called Close, assume somebody
-        // else called Close on us. In that case, we still need to call
-        // ShutDownProcess below to perform other necessary clean up.
-        mCalledClose = true;
-    }
-
-    // Make sure we always clean up.
-    ShutDownProcess(why == NormalShutdown ? CLOSE_CHANNEL
-                                          : CLOSE_CHANNEL_WITH_ERROR);
+    ShutDownMessageManager();
 
     nsRefPtr<ContentParent> kungFuDeathGrip(this);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
         size_t length = ArrayLength(sObserverTopics);
         for (size_t i = 0; i < length; ++i) {
             obs->RemoveObserver(static_cast<nsIObserver*>(this),
                                 sObserverTopics[i]);
@@ -1866,16 +1817,18 @@ ContentParent::ActorDestroy(ActorDestroy
         sNuwaPrefUpdates = nullptr;
     }
 #endif
 
     RecvRemoveGeolocationListener();
 
     mConsoleService = nullptr;
 
+    MarkAsDead();
+
     if (obs) {
         nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
 
         props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);
 
         if (AbnormalShutdown == why) {
             Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
                                   NS_LITERAL_CSTRING("content"), 1);
@@ -1917,16 +1870,21 @@ ContentParent::ActorDestroy(ActorDestroy
             }
 #endif
         }
         obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr);
     }
 
     mIdleListeners.Clear();
 
+    // If the child process was terminated due to a SIGKIL, ShutDownProcess
+    // might not have been called yet.  We must call it to ensure that our
+    // channel is closed, etc.
+    ShutDownProcess(/* closeWithError */ true);
+
     MessageLoop::current()->
         PostTask(FROM_HERE,
                  NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess));
     mSubprocess = nullptr;
 
     // IPDL rules require actors to live on past ActorDestroy, but it
     // may be that the kungFuDeathGrip above is the last reference to
     // |this|.  If so, when we go out of scope here, we're deleted and
@@ -1940,17 +1898,17 @@ ContentParent::ActorDestroy(ActorDestroy
     ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
     nsTArray<ContentParentId> childIDArray =
         cpm->GetAllChildProcessById(this->ChildID());
     for(uint32_t i = 0; i < childIDArray.Length(); i++) {
         ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]);
         MessageLoop::current()->PostTask(
             FROM_HERE,
             NewRunnableMethod(cp, &ContentParent::ShutDownProcess,
-                              CLOSE_CHANNEL));
+                              /* closeWithError */ false));
     }
     cpm->RemoveContentProcess(this->ChildID());
 }
 
 void
 ContentParent::NotifyTabDestroying(PBrowserParent* aTab)
 {
     // There can be more than one PBrowser for a given app process
@@ -1962,56 +1920,44 @@ ContentParent::NotifyTabDestroying(PBrow
     ++mNumDestroyingTabs;
     if (mNumDestroyingTabs != numLiveTabs) {
         return;
     }
 
     // We're dying now, so prevent this content process from being
     // recycled during its shutdown procedure.
     MarkAsDead();
-    StartForceKillTimer();
-}
-
-void
-ContentParent::StartForceKillTimer()
-{
-    if (mForceKillTimer || !mIPCOpen) {
-        return;
-    }
-
+
+    MOZ_ASSERT(!mForceKillTask);
     int32_t timeoutSecs =
         Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5);
     if (timeoutSecs > 0) {
-        mForceKillTimer = do_CreateInstance("@mozilla.org/timer;1");
-        MOZ_ASSERT(mForceKillTimer);
-        mForceKillTimer->InitWithFuncCallback(ContentParent::ForceKillTimerCallback,
-                                              this,
-                                              timeoutSecs * 1000,
-                                              nsITimer::TYPE_ONE_SHOT);
+        MessageLoop::current()->PostDelayedTask(
+            FROM_HERE,
+            mForceKillTask = NewRunnableMethod(this, &ContentParent::KillHard),
+            timeoutSecs * 1000);
     }
 }
 
 void
 ContentParent::NotifyTabDestroyed(PBrowserParent* aTab,
                                   bool aNotifiedDestroying)
 {
     if (aNotifiedDestroying) {
         --mNumDestroyingTabs;
     }
 
     // There can be more than one PBrowser for a given app process
     // because of popup windows.  When the last one closes, shut
     // us down.
     if (ManagedPBrowserParent().Length() == 1) {
-        // In the case of normal shutdown, send a shutdown message to child to
-        // allow it to perform shutdown tasks.
         MessageLoop::current()->PostTask(
             FROM_HERE,
             NewRunnableMethod(this, &ContentParent::ShutDownProcess,
-                              SEND_SHUTDOWN_MESSAGE));
+                              /* force */ false));
     }
 }
 
 jsipc::JavaScriptShared*
 ContentParent::GetCPOWManager()
 {
     if (ManagedPJavaScriptParent().Length()) {
         return static_cast<JavaScriptParent*>(ManagedPJavaScriptParent()[0]);
@@ -2040,27 +1986,26 @@ ContentParent::GetTestShellSingleton()
 }
 
 void
 ContentParent::InitializeMembers()
 {
     mSubprocess = nullptr;
     mChildID = gContentChildID++;
     mGeolocationWatchID = -1;
+    mForceKillTask = nullptr;
     mNumDestroyingTabs = 0;
     mIsAlive = true;
     mMetamorphosed = false;
     mSendPermissionUpdates = false;
     mSendDataStoreInfos = false;
     mCalledClose = false;
     mCalledCloseWithError = false;
     mCalledKillHard = false;
     mCreatedPairedMinidumps = false;
-    mShutdownPending = false;
-    mIPCOpen = true;
 }
 
 ContentParent::ContentParent(mozIApplication* aApp,
                              ContentParent* aOpener,
                              bool aIsForBrowser,
                              bool aIsForPreallocated,
                              ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */,
                              bool aIsNuwaProcess /* = false */)
@@ -2222,18 +2167,18 @@ ContentParent::ContentParent(ContentPare
                  false  /* Send registered chrome */);
 
     ContentProcessManager::GetSingleton()->AddContentProcess(this);
 }
 #endif  // MOZ_NUWA_PROCESS
 
 ContentParent::~ContentParent()
 {
-    if (mForceKillTimer) {
-        mForceKillTimer->Cancel();
+    if (mForceKillTask) {
+        mForceKillTask->Cancel();
     }
 
     if (OtherProcess())
         base::CloseProcessHandle(OtherProcess());
 
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
     // We should be removed from all these lists in ActorDestroy.
@@ -2785,27 +2730,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMETHODIMP
 ContentParent::Observe(nsISupports* aSubject,
                        const char* aTopic,
                        const char16_t* aData)
 {
-    if (mSubprocess && (!strcmp(aTopic, "profile-before-change") ||
-                        !strcmp(aTopic, "xpcom-shutdown"))) {
-        // Okay to call ShutDownProcess multiple times.
-        ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
-
-        // Wait for shutdown to complete, so that we receive any shutdown
-        // data (e.g. telemetry) from the child before we quit.
-        // This loop terminate prematurely based on mForceKillTimer.
-        while (mIPCOpen) {
-            NS_ProcessNextEvent(nullptr, true);
-        }
+    if (!strcmp(aTopic, "xpcom-shutdown") && mSubprocess) {
+        ShutDownProcess(/* closeWithError */ false);
         NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
     }
 
     if (!mIsAlive || !mSubprocess)
         return NS_OK;
 
     // listening for memory pressure event
     if (!strcmp(aTopic, "memory-pressure") &&
@@ -3201,34 +3137,27 @@ ContentParent::AllocPRemoteSpellcheckEng
 
 bool
 ContentParent::DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent *parent)
 {
     delete parent;
     return true;
 }
 
-/* static */ void
-ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure)
-{
-    auto self = static_cast<ContentParent*>(aClosure);
-    self->KillHard();
-}
-
 void
 ContentParent::KillHard()
 {
     // On Windows, calling KillHard multiple times causes problems - the
     // process handle becomes invalid on the first call, causing a second call
     // to crash our process - more details in bug 890840.
     if (mCalledKillHard) {
         return;
     }
     mCalledKillHard = true;
-    mForceKillTimer = nullptr;
+    mForceKillTask = nullptr;
 
 #if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G)
     if (ManagedPCrashReporterParent().Length() > 0) {
         CrashReporterParent* crashReporter =
             static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
 
         // We're about to kill the child process associated with this
         // ContentParent. Something has gone wrong to get us here,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -28,17 +28,16 @@
 #define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown")
 
 class mozIApplication;
 class nsConsoleService;
 class nsICycleCollectorLogSink;
 class nsIDOMBlob;
 class nsIDumpGCAndCCLogsCallback;
 class nsIMemoryReporter;
-class nsITimer;
 class ParentIdleListener;
 
 namespace mozilla {
 class PRemoteSpellcheckEngineParent;
 
 namespace ipc {
 class OptionalURIParams;
 class PFileDescriptorSetParent;
@@ -330,18 +329,16 @@ public:
                                        const URIParams& aDocumentURI,
                                        const bool& stickDocument,
                                        const TabId& aTabId) MOZ_OVERRIDE;
     virtual bool
     DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* aActor) MOZ_OVERRIDE;
 
     virtual bool RecvSetOfflinePermission(const IPC::Principal& principal) MOZ_OVERRIDE;
 
-    virtual bool RecvFinishShutdown() MOZ_OVERRIDE;
-
 protected:
     void OnChannelConnected(int32_t pid) MOZ_OVERRIDE;
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
     void OnNuwaForkTimeout();
 
     bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
 
 private:
@@ -437,49 +434,32 @@ private:
 
     /**
      * Mark this ContentParent as dead for the purposes of Get*().
      * This method is idempotent.
      */
     void MarkAsDead();
 
     /**
-     * How we will shut down this ContentParent and its subprocess.
-     */
-    enum ShutDownMethod {
-        // Send a shutdown message and wait for FinishShutdown call back.
-        SEND_SHUTDOWN_MESSAGE,
-        // Close the channel ourselves and let the subprocess clean up itself.
-        CLOSE_CHANNEL,
-        // Close the channel with error and let the subprocess clean up itself.
-        CLOSE_CHANNEL_WITH_ERROR,
-    };
-
-    /**
      * Exit the subprocess and vamoose.  After this call IsAlive()
      * will return false and this ContentParent will not be returned
      * by the Get*() funtions.  However, the shutdown sequence itself
      * may be asynchronous.
      *
-     * If aMethod is CLOSE_CHANNEL_WITH_ERROR and this is the first call
-     * to ShutDownProcess, then we'll close our channel using CloseWithError()
+     * If aCloseWithError is true and this is the first call to
+     * ShutDownProcess, then we'll close our channel using CloseWithError()
      * rather than vanilla Close().  CloseWithError() indicates to IPC that this
      * is an abnormal shutdown (e.g. a crash).
      */
-    void ShutDownProcess(ShutDownMethod aMethod);
+    void ShutDownProcess(bool aCloseWithError);
 
     // Perform any steps necesssary to gracefully shtudown the message
     // manager and null out mMessageManager.
     void ShutDownMessageManager();
 
-    // Start the force-kill timer on shutdown.
-    void StartForceKillTimer();
-
-    static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
-
     PCompositorParent*
     AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) MOZ_OVERRIDE;
     PImageBridgeParent*
     AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
                             base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
     PSharedBufferManagerParent*
@@ -795,17 +775,17 @@ private:
      * expensive.
      */
     nsString mAppName;
 
     // After we initiate shutdown, we also start a timer to ensure
     // that even content processes that are 100% blocked (say from
     // SIGSTOP), are still killed eventually.  This task enforces that
     // timer.
-    nsCOMPtr<nsITimer> mForceKillTimer;
+    CancelableTask* mForceKillTask;
     // How many tabs we're waiting to finish their destruction
     // sequence.  Precisely, how many TabParents have called
     // NotifyTabDestroying() but not called NotifyTabDestroyed().
     int32_t mNumDestroyingTabs;
     // True only while this is ready to be used to host remote tabs.
     // This must not be used for new purposes after mIsAlive goes to
     // false, but some previously scheduled IPC traffic may still pass
     // through.
@@ -821,18 +801,16 @@ private:
     bool mIsNuwaProcess;
 
     // These variables track whether we've called Close(), CloseWithError()
     // and KillHard() on our channel.
     bool mCalledClose;
     bool mCalledCloseWithError;
     bool mCalledKillHard;
     bool mCreatedPairedMinidumps;
-    bool mShutdownPending;
-    bool mIPCOpen;
 
     friend class CrashReporterParent;
 
     nsRefPtr<nsConsoleService>  mConsoleService;
     nsConsoleService* GetConsoleService();
 
     nsDataHashtable<nsUint64HashKey, nsRefPtr<ParentIdleListener> > mIdleListeners;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -539,23 +539,16 @@ child:
      */
     async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures,
                         nsCString[] aThreadNameFilters);
     async StopProfiler();
     prio(high) sync GetProfile()
       returns (nsCString aProfile);
 
     NuwaFreeze();
-
-    /**
-     * Notify the child to shutdown. The child will in turn call FinishShutdown
-     * and let the parent close the channel.
-     */
-    async Shutdown();
-
 parent:
     /**
      * Tell the parent process a new accessible document has been created.
      * aParentDoc is the accessible document it was created in if any, and
      * aParentAcc is the id of the accessible in that document the new document
      * is a child of.
      */
     PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc);
@@ -879,21 +872,15 @@ parent:
 
     /**
      * Sets "offline-app" permission for the principal.  Called when we hit
      * a web app with the manifest attribute in <html> and
      * offline-apps.allow_by_default is set to true.
      */
     SetOfflinePermission(Principal principal);
 
-    /**
-     * Notifies the parent to continue shutting down after the child performs
-     * its shutdown tasks.
-     */
-    async FinishShutdown();
-
 both:
      AsyncMessage(nsString aMessage, ClonedMessageData aData,
                   CpowEntry[] aCpows, Principal aPrincipal);
 };
 
 }
 }
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -685,20 +685,17 @@ private:
     }
 
     NS_IMETHOD
     Run()
     {
         MOZ_ASSERT(NS_IsMainThread());
         MOZ_ASSERT(mTabChild);
 
-        // Check in case ActorDestroy was called after RecvDestroy message.
-        if (mTabChild->IPCOpen()) {
-            unused << PBrowserChild::Send__delete__(mTabChild);
-        }
+        unused << PBrowserChild::Send__delete__(mTabChild);
 
         mTabChild = nullptr;
         return NS_OK;
     }
 };
 
 StaticRefPtr<TabChild> sPreallocatedTab;
 
@@ -842,17 +839,16 @@ TabChild::TabChild(nsIContentChild* aMan
   , mUpdateHitRegion(false)
   , mIgnoreKeyPressEvent(false)
   , mSetTargetAPZCCallback(new TabChildSetTargetAPZCCallback(this))
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
   , mDPI(0)
   , mDefaultScale(0)
-  , mIPCOpen(true)
 {
   // preloaded TabChild should not be added to child map
   if (mUniqueId) {
     MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end());
     NestedTabChildMap()[mUniqueId] = this;
   }
 }
 
@@ -1577,18 +1573,16 @@ TabChild::DestroyWindow()
     }
 
     mCachedFileDescriptorInfos.Clear();
 }
 
 void
 TabChild::ActorDestroy(ActorDestroyReason why)
 {
-  mIPCOpen = false;
-
   DestroyWindow();
 
   if (mTabChildGlobal) {
     // The messageManager relays messages via the TabChild which
     // no longer exists.
     static_cast<nsFrameMessageManager*>
       (mTabChildGlobal->mMessageManager.get())->Disconnect();
     mTabChildGlobal->mMessageManager = nullptr;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -479,18 +479,16 @@ public:
      * Native widget remoting protocol for use with windowed plugins with e10s.
      */
     PPluginWidgetChild* AllocPPluginWidgetChild() MOZ_OVERRIDE;
     bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) MOZ_OVERRIDE;
     already_AddRefed<nsIWidget> CreatePluginWidget(nsIWidget* aParent);
 
     nsIntPoint GetChromeDisplacement() { return mChromeDisp; };
 
-    bool IPCOpen() { return mIPCOpen; }
-
 protected:
     virtual ~TabChild();
 
     virtual PRenderFrameChild* AllocPRenderFrameChild() MOZ_OVERRIDE;
     virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) MOZ_OVERRIDE;
     virtual bool RecvDestroy() MOZ_OVERRIDE;
     virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) MOZ_OVERRIDE;
     virtual bool RecvSetIsDocShellActive(const bool& aIsActive) MOZ_OVERRIDE;
@@ -617,17 +615,16 @@ private:
     nsRefPtr<SetTargetAPZCCallback> mSetTargetAPZCCallback;
     bool mHasValidInnerSize;
     bool mDestroyed;
     // Position of tab, relative to parent widget (typically the window)
     nsIntPoint mChromeDisp;
     TabId mUniqueId;
     float mDPI;
     double mDefaultScale;
-    bool mIPCOpen;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 }
 }
 
 #endif // mozilla_dom_TabChild_h
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1485,32 +1485,31 @@ void MediaDecoder::StopProgressUpdates()
 
 void MediaDecoder::StartProgressUpdates()
 {
   MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
   mIgnoreProgressData = false;
   if (mResource) {
     mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
-    mDecoderPosition = mPlaybackPosition = mResource->Tell();
   }
 }
 
 void MediaDecoder::SetLoadInBackground(bool aLoadInBackground)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mResource) {
     mResource->SetLoadInBackground(aLoadInBackground);
   }
 }
 
 void MediaDecoder::UpdatePlaybackOffset(int64_t aOffset)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  mPlaybackPosition = std::max(aOffset, mPlaybackPosition);
+  mPlaybackPosition = aOffset;
 }
 
 bool MediaDecoder::OnStateMachineThread() const
 {
   return mDecoderStateMachine->OnStateMachineThread();
 }
 
 void MediaDecoder::SetPlaybackRate(double aPlaybackRate)
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -933,17 +933,18 @@ public:
   class FrameStatistics {
   public:
 
     FrameStatistics() :
         mReentrantMonitor("MediaDecoder::FrameStats"),
         mParsedFrames(0),
         mDecodedFrames(0),
         mPresentedFrames(0),
-        mDroppedFrames(0) {}
+        mDroppedFrames(0),
+        mCorruptFrames(0) {}
 
     // Returns number of frames which have been parsed from the media.
     // Can be called on any thread.
     uint32_t GetParsedFrames() {
       ReentrantMonitorAutoEnter mon(mReentrantMonitor);
       return mParsedFrames;
     }
 
@@ -960,17 +961,23 @@ public:
     uint32_t GetPresentedFrames() {
       ReentrantMonitorAutoEnter mon(mReentrantMonitor);
       return mPresentedFrames;
     }
 
     // Number of frames that have been skipped because they have missed their
     // compoisition deadline.
     uint32_t GetDroppedFrames() {
-      return mDroppedFrames;
+      ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+      return mDroppedFrames + mCorruptFrames;
+    }
+
+    uint32_t GetCorruptedFrames() {
+      ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+      return mCorruptFrames;
     }
 
     // Increments the parsed and decoded frame counters by the passed in counts.
     // Can be called on any thread.
     void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
                              uint32_t aDropped) {
       if (aParsed == 0 && aDecoded == 0 && aDropped == 0)
         return;
@@ -982,16 +989,21 @@ public:
 
     // Increments the presented frame counters.
     // Can be called on any thread.
     void NotifyPresentedFrame() {
       ReentrantMonitorAutoEnter mon(mReentrantMonitor);
       ++mPresentedFrames;
     }
 
+    void NotifyCorruptFrame() {
+      ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+      ++mCorruptFrames;
+    }
+
   private:
 
     // ReentrantMonitor to protect access of playback statistics.
     ReentrantMonitor mReentrantMonitor;
 
     // Number of frames parsed and demuxed from media.
     // Access protected by mReentrantMonitor.
     uint32_t mParsedFrames;
@@ -1000,16 +1012,18 @@ public:
     // Access protected by mReentrantMonitor.
     uint32_t mDecodedFrames;
 
     // Number of decoded frames which were actually sent down the rendering
     // pipeline to be painted ("presented"). Access protected by mReentrantMonitor.
     uint32_t mPresentedFrames;
 
     uint32_t mDroppedFrames;
+
+    uint32_t mCorruptFrames;
   };
 
   // Return the frame decode/paint related statistics.
   FrameStatistics& GetFrameStatistics() { return mFrameStats; }
 
   // Increments the parsed and decoded frame counters by the passed in counts.
   // Can be called on any thread.
   virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -261,16 +261,18 @@ public:
   }
 
   // Returns true if the reader implements RequestAudioData()
   // and RequestVideoData() asynchronously, rather than using the
   // implementation in this class to adapt the old synchronous to
   // the newer async model.
   virtual bool IsAsync() const { return false; }
 
+  virtual void DisableHardwareAcceleration() {}
+
 protected:
   virtual ~MediaDecoderReader();
 
   // Overrides of this function should decodes an unspecified amount of
   // audio data, enqueuing the audio data in mAudioQueue. Returns true
   // when there's more audio to decode, false if the audio is finished,
   // end of file has been reached, or an un-recoverable read error has
   // occured. This function blocks until the decode is complete.
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -239,16 +239,18 @@ MediaDecoderStateMachine::MediaDecoderSt
   mDecodeThreadWaiting(false),
   mDropAudioUntilNextDiscontinuity(false),
   mDropVideoUntilNextDiscontinuity(false),
   mDecodeToSeekTarget(false),
   mWaitingForDecoderSeek(false),
   mCancelingSeek(false),
   mCurrentTimeBeforeSeek(0),
   mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
+  mCorruptFrames(30),
+  mDisabledHardwareAcceleration(false),
   mDecodingFrozenAtStateDecoding(false),
   mSentLoadedMetadataEvent(false),
   mSentFirstFrameLoadedEvent(false)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   static bool sPrefCacheInit = false;
@@ -257,17 +259,17 @@ MediaDecoderStateMachine::MediaDecoderSt
     Preferences::AddUintVarCache(&sVideoQueueDefaultSize,
                                  "media.video-queue.default-size",
                                  MAX_VIDEO_QUEUE_SIZE);
     Preferences::AddUintVarCache(&sVideoQueueHWAccelSize,
                                  "media.video-queue.hw-accel-size",
                                  MIN_VIDEO_QUEUE_SIZE);
   }
 
-  mBufferingWait = IsRealTime() ? 0 : 30;
+  mBufferingWait = IsRealTime() ? 0 : 15;
   mLowDataThresholdUsecs = IsRealTime() ? 0 : detail::LOW_DATA_THRESHOLD_USECS;
 
 #ifdef XP_WIN
   // Ensure high precision timers are enabled on Windows, otherwise the state
   // machine thread isn't woken up at reliable intervals to set the next frame,
   // and we drop frames while painting. Note that multiple calls to this
   // function per-process is OK, provided each call is matched by a corresponding
   // timeEndPeriod() call.
@@ -3007,16 +3009,32 @@ void MediaDecoderStateMachine::RenderVid
   }
 
   VERBOSE_LOG("playing video frame %lld (queued=%i, state-machine=%i, decoder-queued=%i)",
               aData->mTime, VideoQueue().GetSize() + mReader->SizeOfVideoQueueInFrames(),
               VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames());
 
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container) {
+    if (aData->mImage && !aData->mImage->IsValid()) {
+      MediaDecoder::FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
+      frameStats.NotifyCorruptFrame();
+      // If more than 10% of the last 30 frames have been corrupted, then try disabling
+      // hardware acceleration. We use 10 as the corrupt value because RollingMean<>
+      // only supports integer types.
+      mCorruptFrames.insert(10);
+      if (!mDisabledHardwareAcceleration &&
+          frameStats.GetPresentedFrames() > 30 &&
+          mCorruptFrames.mean() >= 1 /* 10% */) {
+        DecodeTaskQueue()->Dispatch(NS_NewRunnableMethod(mReader, &MediaDecoderReader::DisableHardwareAcceleration));
+        mDisabledHardwareAcceleration = true;
+      }
+    } else {
+      mCorruptFrames.insert(0);
+    }
     container->SetCurrentFrame(ThebesIntSize(aData->mDisplay), aData->mImage,
                                aTarget);
     MOZ_ASSERT(container->GetFrameDelay() >= 0 || IsRealTime());
   }
 }
 
 void MediaDecoderStateMachine::ResyncAudioClock()
 {
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -85,16 +85,17 @@ hardware (via AudioStream).
 #include "mozilla/Attributes.h"
 #include "nsThreadUtils.h"
 #include "MediaDecoder.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderOwner.h"
 #include "MediaMetadataManager.h"
 #include "MediaDecoderStateMachineScheduler.h"
+#include "mozilla/RollingMean.h"
 
 class nsITimer;
 
 namespace mozilla {
 
 class AudioSegment;
 class VideoSegment;
 class MediaTaskQueue;
@@ -1134,16 +1135,20 @@ protected:
   // Stores presentation info required for playback. The decoder monitor
   // must be held when accessing this.
   MediaInfo mInfo;
 
   mozilla::MediaMetadataManager mMetadataManager;
 
   MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
 
+  mozilla::RollingMean<uint32_t, uint32_t> mCorruptFrames;
+
+  bool mDisabledHardwareAcceleration;
+
   // mDecodingFrozenAtStateDecoding: turn on/off at
   //                                 SetDormant/Seek,Play.
   bool mDecodingFrozenAtStateDecoding;
 
   // True if we are back from DECODER_STATE_DORMANT state and
   // LoadedMetadataEvent was already sent.
   bool mSentLoadedMetadataEvent;
   // True if we are back from DECODER_STATE_DORMANT state and
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -570,16 +570,36 @@ MP4Reader::GetDecoderData(TrackType aTra
 
 Microseconds
 MP4Reader::GetNextKeyframeTime()
 {
   MonitorAutoLock mon(mDemuxerMonitor);
   return mDemuxer->GetNextKeyframeTime();
 }
 
+void
+MP4Reader::DisableHardwareAcceleration()
+{
+  if (HasVideo() && !mIsEncrypted && mSharedDecoderManager) {
+    mPlatform->DisableHardwareAcceleration();
+
+    const VideoDecoderConfig& video = mDemuxer->VideoConfig();
+    if (!mSharedDecoderManager->Recreate(mPlatform, video, mLayersBackendType, mDecoder->GetImageContainer())) {
+      MonitorAutoLock mon(mVideo.mMonitor);
+      mVideo.mError = true;
+      if (mVideo.HasPromise()) {
+        mVideo.RejectPromise(DECODE_ERROR, __func__);
+      }
+    } else {
+      MonitorAutoLock lock(mVideo.mMonitor);
+      ScheduleUpdate(kVideo);
+    }
+  }
+}
+
 bool
 MP4Reader::ShouldSkip(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
 {
   // The MP4Reader doesn't do normal skip-to-next-keyframe if the demuxer
   // has exposes where the next keyframe is. We can then instead skip only
   // if the time threshold (the current playback position) is after the next
   // keyframe in the stream. This means we'll only skip frames that we have
   // no hope of ever playing.
@@ -612,17 +632,19 @@ MP4Reader::RequestVideoData(bool aSkipTo
     if (!eos && NS_FAILED(mVideo.mDecoder->Flush())) {
       NS_WARNING("Failed to skip/flush video when skipping-to-next-keyframe.");
     }
     mDecoder->NotifyDecodedFrames(parsed, 0, parsed);
   }
 
   MonitorAutoLock lock(mVideo.mMonitor);
   nsRefPtr<VideoDataPromise> p = mVideo.mPromise.Ensure(__func__);
-  if (eos) {
+  if (mVideo.mError) {
+    mVideo.mPromise.Reject(DECODE_ERROR, __func__);
+  } else if (eos) {
     mVideo.mPromise.Reject(END_OF_STREAM, __func__);
   } else {
     ScheduleUpdate(kVideo);
   }
 
   return p;
 }
 
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -76,16 +76,18 @@ public:
     MOZ_OVERRIDE;
 
   virtual nsresult ResetDecode() MOZ_OVERRIDE;
 
   virtual nsRefPtr<ShutdownPromise> Shutdown() MOZ_OVERRIDE;
 
   virtual bool IsAsync() const MOZ_OVERRIDE { return true; }
 
+  virtual void DisableHardwareAcceleration() MOZ_OVERRIDE;
+
 private:
 
   bool InitDemuxer();
   void ReturnOutput(MediaData* aData, TrackType aTrack);
 
   // Sends input to decoder for aTrack, and output to the state machine,
   // if necessary.
   void Update(TrackType aTrack);
--- a/dom/media/fmp4/PlatformDecoderModule.h
+++ b/dom/media/fmp4/PlatformDecoderModule.h
@@ -130,16 +130,18 @@ public:
   // If more audio codec is to be supported, SupportsAudioMimeType will have
   // to be extended
   virtual bool SupportsAudioMimeType(const char* aMimeType);
   virtual bool SupportsVideoMimeType(const char* aMimeType);
 
   // Indicates if the video decoder requires AVCC format.
   virtual bool DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig);
 
+  virtual void DisableHardwareAcceleration() {}
+
 protected:
   PlatformDecoderModule() {}
   virtual ~PlatformDecoderModule() {}
   // Caches pref media.fragmented-mp4.use-blank-decoder
   static bool sUseBlankDecoder;
   static bool sFFmpegDecoderEnabled;
   static bool sGonkDecoderEnabled;
   static bool sAndroidMCDecoderPreferred;
--- a/dom/media/fmp4/SharedDecoderManager.cpp
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -88,16 +88,32 @@ SharedDecoderManager::CreateVideoDecoder
     nsresult rv = mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, nullptr);
   }
 
   nsRefPtr<SharedDecoderProxy> proxy(new SharedDecoderProxy(this, aCallback));
   return proxy.forget();
 }
 
+bool
+SharedDecoderManager::Recreate(PlatformDecoderModule* aPDM,
+                               const mp4_demuxer::VideoDecoderConfig& aConfig,
+                               layers::LayersBackend aLayersBackend,
+                               layers::ImageContainer* aImageContainer)
+{
+  mDecoder->Flush();
+  mDecoder->Shutdown();
+  mDecoder = aPDM->CreateVideoDecoder(aConfig, aLayersBackend, aImageContainer, mTaskQueue, mCallback);
+  if (!mDecoder) {
+    return false;
+  }
+  nsresult rv = mDecoder->Init();
+  return rv == NS_OK;
+}
+
 void
 SharedDecoderManager::Select(SharedDecoderProxy* aProxy)
 {
   if (mActiveProxy == aProxy) {
     return;
   }
   SetIdle(mActiveProxy);
 
--- a/dom/media/fmp4/SharedDecoderManager.h
+++ b/dom/media/fmp4/SharedDecoderManager.h
@@ -35,16 +35,21 @@ public:
   void Select(SharedDecoderProxy* aProxy);
   void SetIdle(MediaDataDecoder* aProxy);
   void ReleaseMediaResources();
   void Shutdown();
 
   friend class SharedDecoderProxy;
   friend class SharedDecoderCallback;
 
+  bool Recreate(PlatformDecoderModule* aPDM,
+    const mp4_demuxer::VideoDecoderConfig& aConfig,
+    layers::LayersBackend aLayersBackend,
+    layers::ImageContainer* aImageContainer);
+
 private:
   virtual ~SharedDecoderManager();
   void DrainComplete();
 
   nsRefPtr<MediaDataDecoder> mDecoder;
   nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
   SharedDecoderProxy* mActiveProxy;
   MediaDataDecoderCallback* mActiveCallback;
--- a/dom/media/fmp4/wmf/WMFDecoderModule.h
+++ b/dom/media/fmp4/wmf/WMFDecoderModule.h
@@ -32,16 +32,21 @@ public:
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                      FlushableMediaTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
 
   bool SupportsVideoMimeType(const char* aMimeType) MOZ_OVERRIDE;
   bool SupportsAudioMimeType(const char* aMimeType) MOZ_OVERRIDE;
 
+  virtual void DisableHardwareAcceleration() MOZ_OVERRIDE
+  {
+    sDXVAEnabled = false;
+  }
+
   // Accessors that report whether we have the required MFTs available
   // on the system to play various codecs. Windows Vista doesn't have the
   // H.264/AAC decoders if the "Platform Update Supplement for Windows Vista"
   // is not installed.
   static bool HasAAC();
   static bool HasH264();
 
   // Called on main thread.
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -79,16 +79,22 @@ public:
 
   bool HasAudio() MOZ_OVERRIDE
   {
     return mInfo.HasAudio();
   }
 
   void NotifyTimeRangesChanged();
 
+  virtual void DisableHardwareAcceleration() MOZ_OVERRIDE {
+    if (GetVideoReader()) {
+      GetVideoReader()->DisableHardwareAcceleration();
+    }
+  }
+
   // We can't compute a proper start time since we won't necessarily
   // have the first frame of the resource available. This does the same
   // as chrome/blink and assumes that we always start at t=0.
   virtual int64_t ComputeStartTime(const VideoData* aVideo, const AudioData* aAudio) MOZ_OVERRIDE { return 0; }
 
   // Buffering heuristics don't make sense for MSE, because the arrival of data
   // is at least partly controlled by javascript, and javascript does not expect
   // us to sit on unplayed data just because it may not be enough to play
--- a/gfx/layers/D3D9SurfaceImage.cpp
+++ b/gfx/layers/D3D9SurfaceImage.cpp
@@ -12,16 +12,17 @@
 
 namespace mozilla {
 namespace layers {
 
 
 D3D9SurfaceImage::D3D9SurfaceImage()
   : Image(nullptr, ImageFormat::D3D9_RGB32_TEXTURE)
   , mSize(0, 0)
+  , mValid(false)
 {}
 
 D3D9SurfaceImage::~D3D9SurfaceImage()
 {
   if (mTexture) {
     gfxWindowsPlatform::sD3D9SurfaceImageUsed -= mSize.width * mSize.height * 4;
   }
 }
@@ -148,28 +149,43 @@ D3D9SurfaceImage::SetData(const Data& aD
   mTexture = texture;
   mShareHandle = shareHandle;
   mSize = gfx::IntSize(region.width, region.height);
   mQuery = query;
 
   return S_OK;
 }
 
+bool
+D3D9SurfaceImage::IsValid()
+{
+  EnsureSynchronized();
+  return mValid;
+}
+
 void
 D3D9SurfaceImage::EnsureSynchronized()
 {
   RefPtr<IDirect3DQuery9> query = mQuery;
   if (!query) {
     // Not setup, or already synchronized.
     return;
   }
   int iterations = 0;
-  while (iterations < 10 && S_FALSE == query->GetData(nullptr, 0, D3DGETDATA_FLUSH)) {
-    Sleep(1);
-    iterations++;
+  while (iterations < 10) {
+    HRESULT hr = query->GetData(nullptr, 0, D3DGETDATA_FLUSH);
+    if (hr == S_FALSE) {
+      Sleep(1);
+      iterations++;
+      continue;
+    }
+    if (hr == S_OK) {
+      mValid = true;
+    }
+    break;
   }
   mQuery = nullptr;
 }
 
 HANDLE
 D3D9SurfaceImage::GetShareHandle()
 {
   // Ensure the image has completed its synchronization,
--- a/gfx/layers/D3D9SurfaceImage.h
+++ b/gfx/layers/D3D9SurfaceImage.h
@@ -49,26 +49,29 @@ public:
 
   gfx::IntSize GetSize() MOZ_OVERRIDE;
 
   virtual TemporaryRef<gfx::SourceSurface> GetAsSourceSurface() MOZ_OVERRIDE;
 
   virtual TextureClient* GetTextureClient(CompositableClient* aClient) MOZ_OVERRIDE;
   virtual uint8_t* GetBuffer() MOZ_OVERRIDE { return nullptr; }
 
+  virtual bool IsValid() MOZ_OVERRIDE;
+
 private:
 
   // Blocks the calling thread until the copy operation started in SetData()
   // is complete, whereupon the texture is safe to use.
   void EnsureSynchronized();
 
   gfx::IntSize mSize;
   RefPtr<IDirect3DTexture9> mTexture;
   RefPtr<IDirect3DQuery9> mQuery;
   RefPtr<TextureClient> mTextureClient;
   HANDLE mShareHandle;
   D3DSURFACE_DESC mDesc;
+  bool mValid;
 };
 
 } // namepace layers
 } // namespace mozilla
 
 #endif // GFX_D3DSURFACEIMAGE_H
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -168,16 +168,18 @@ public:
 
   virtual TemporaryRef<gfx::SourceSurface> GetAsSourceSurface() = 0;
 
   virtual GrallocImage* AsGrallocImage()
   {
     return nullptr;
   }
 
+  virtual bool IsValid() { return true; }
+
 protected:
   Image(void* aImplData, ImageFormat aFormat) :
     mImplData(aImplData),
     mSerial(++sSerialCounter),
     mFormat(aFormat),
     mSent(false)
   {}
 
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -142,16 +142,23 @@ ImageClientSingle::UpdateImage(ImageCont
 {
   AutoLockImage autoLock(aContainer);
 
   Image *image = autoLock.GetImage();
   if (!image) {
     return false;
   }
 
+  // Don't try to update to an invalid image. We return true because the caller
+  // would attempt to recreate the ImageClient otherwise, and that isn't going
+  // to help.
+  if (!image->IsValid()) {
+    return true;
+  }
+
   if (mLastPaintedImageSerial == image->GetSerial()) {
     return true;
   }
 
   RefPtr<TextureClient> texture = image->AsSharedImage()
                                 ? image->AsSharedImage()->GetTextureClient(this)
                                 : nullptr;
 
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -783,17 +783,22 @@ SyncObjectD3D11::FinalizeFrame()
   HRESULT hr;
 
   if (!mD3D10Texture && mD3D10SyncedTextures.size()) {
     ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
 
     hr = device->OpenSharedResource(mHandle, __uuidof(ID3D10Texture2D), (void**)(ID3D10Texture2D**)byRef(mD3D10Texture));
     
     if (FAILED(hr) || !mD3D10Texture) {
-      gfxCriticalError() << "Failed to OpenSharedResource: " << hexa(hr);
+      gfxCriticalError() << "Failed to D3D10 OpenSharedResource for frame finalization: " << hexa(hr);
+
+      if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+        return;
+      }
+
       MOZ_CRASH();
     }
 
     // test QI
     RefPtr<IDXGIKeyedMutex> mutex;
     hr = mD3D10Texture->QueryInterface((IDXGIKeyedMutex**)byRef(mutex));
 
     if (FAILED(hr) || !mutex) {
@@ -803,34 +808,39 @@ SyncObjectD3D11::FinalizeFrame()
   }
 
   if (!mD3D11Texture && mD3D11SyncedTextures.size()) {
     ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice();
 
     hr = device->OpenSharedResource(mHandle, __uuidof(ID3D11Texture2D), (void**)(ID3D11Texture2D**)byRef(mD3D11Texture));
     
     if (FAILED(hr) || !mD3D11Texture) {
-      gfxCriticalError() << "Failed to OpenSharedResource: " << hexa(hr);
+      gfxCriticalError() << "Failed to D3D11 OpenSharedResource for frame finalization: " << hexa(hr);
+
+      if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+        return;
+      }
+
       MOZ_CRASH();
     }
 
     // test QI
     RefPtr<IDXGIKeyedMutex> mutex;
     hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)byRef(mutex));
 
     if (FAILED(hr) || !mutex) {
       gfxCriticalError() << "Failed to get KeyedMutex: " << hexa(hr);
       MOZ_CRASH();
     }
   }
 
   if (mD3D10SyncedTextures.size()) {
     RefPtr<IDXGIKeyedMutex> mutex;
     hr = mD3D10Texture->QueryInterface((IDXGIKeyedMutex**)byRef(mutex));
-    hr = mutex->AcquireSync(0, 10000);
+    hr = mutex->AcquireSync(0, 20000);
 
     if (hr == WAIT_TIMEOUT) {
       MOZ_CRASH();
     }
 
     D3D10_BOX box;
     box.front = box.top = box.left = 0;
     box.back = box.bottom = box.right = 1;
@@ -844,27 +854,35 @@ SyncObjectD3D11::FinalizeFrame()
     mutex->ReleaseSync(0);
 
     mD3D10SyncedTextures.clear();
   }
 
   if (mD3D11SyncedTextures.size()) {
     RefPtr<IDXGIKeyedMutex> mutex;
     hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)byRef(mutex));
-    hr = mutex->AcquireSync(0, 10000);
+    hr = mutex->AcquireSync(0, 20000);
 
     if (hr == WAIT_TIMEOUT) {
       MOZ_CRASH();
     }
 
     D3D11_BOX box;
     box.front = box.top = box.left = 0;
     box.back = box.bottom = box.right = 1;
 
     ID3D11Device* dev = gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice();
+
+    if (!dev) {
+      if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+        return;
+      }
+      MOZ_CRASH();
+    }
+
     RefPtr<ID3D11DeviceContext> ctx;
     dev->GetImmediateContext(byRef(ctx));
 
     for (auto iter = mD3D11SyncedTextures.begin(); iter != mD3D11SyncedTextures.end(); iter++) {
       ctx->CopySubresourceRegion(mD3D11Texture, 0, 0, 0, 0, *iter, 0, &box);
     }
 
     mutex->ReleaseSync(0);
--- a/services/healthreport/docs/dataformat.rst
+++ b/services/healthreport/docs/dataformat.rst
@@ -1894,8 +1894,35 @@ Example
       "treatment": [
         "optin",
         "optin-DNT"
       ],
       "another-tag": [
         "foobar-value"
       ]
     }
+
+org.mozilla.passwordmgr.passwordmgr
+-----------------------------------
+
+Daily measurement reporting information about the Password Manager
+
+Version 1
+^^^^^^^^^
+
+Property:
+
+numSavedPasswords
+    number of passwords saved in the Password Manager
+
+enabled
+    Whether or not the user has disabled the Password Manager in prefernces
+
+Example
+^^^^^^^
+
+::
+
+    "org.mozilla.passwordmgr.passwordmgr": {
+      "_v": 1,
+      "numSavedPasswords": 5,
+      "enabled": 0,
+    }
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm
@@ -12,17 +12,17 @@ const Cc = Components.classes;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "UserAutoCompleteResult",
                                   "resource://gre/modules/LoginManagerContent.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AutoCompleteE10S",
                                   "resource://gre/modules/AutoCompleteE10S.jsm");
 
-this.EXPORTED_SYMBOLS = [ "LoginManagerParent" ];
+this.EXPORTED_SYMBOLS = [ "LoginManagerParent", "PasswordsMetricsProvider" ];
 
 var gDebug;
 
 function log(...pieces) {
   function generateLogMessage(args) {
     let strings = ['Login Manager (parent):'];
 
     args.forEach(function(arg) {
@@ -46,16 +46,71 @@ function log(...pieces) {
   if (!gDebug)
     return;
 
   let message = generateLogMessage(pieces);
   dump(message + "\n");
   Services.console.logStringMessage(message);
 }
 
+#ifndef ANDROID
+#ifdef MOZ_SERVICES_HEALTHREPORT
+XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
+                                  "resource://gre/modules/Metrics.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
+
+this.PasswordsMetricsProvider = function() {
+  Metrics.Provider.call(this);
+}
+
+PasswordsMetricsProvider.prototype = Object.freeze({
+  __proto__: Metrics.Provider.prototype,
+
+  name: "org.mozilla.passwordmgr",
+
+  measurementTypes: [
+    PasswordsMeasurement1,
+  ],
+
+  pullOnly: true,
+
+  collectDailyData: function* () {
+    return this.storage.enqueueTransaction(this._recordDailyPasswordData.bind(this));
+  },
+
+  _recordDailyPasswordData: function() {
+    let m = this.getMeasurement(PasswordsMeasurement1.prototype.name,
+                                PasswordsMeasurement1.prototype.version);
+    let enabled = Services.prefs.getBoolPref("signon.rememberSignons");
+    yield m.setDailyLastNumeric("enabled", enabled ? 1 : 0);
+
+    let loginsCount = Services.logins.countLogins("", "", "");
+    yield m.setDailyLastNumeric("numSavedPasswords", loginsCount);
+
+  },
+});
+
+function PasswordsMeasurement1() {
+  Metrics.Measurement.call(this);
+}
+
+PasswordsMeasurement1.prototype = Object.freeze({
+  __proto__: Metrics.Measurement.prototype,
+  name: "passwordmgr",
+  version: 1,
+  fields: {
+    enabled: {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC},
+    numSavedPasswords: {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC},
+  },
+});
+
+#endif
+#endif
+
 function prefChanged() {
   gDebug = Services.prefs.getBoolPref("signon.debug");
 }
 
 Services.prefs.addObserver("signon.debug", prefChanged, false);
 prefChanged();
 
 var LoginManagerParent = {
--- a/toolkit/components/passwordmgr/moz.build
+++ b/toolkit/components/passwordmgr/moz.build
@@ -26,16 +26,20 @@ EXTRA_COMPONENTS += [
     'nsLoginManagerPrompter.js',
 ]
 
 EXTRA_PP_COMPONENTS += [
     'nsLoginManager.js',
     'passwordmgr.manifest',
 ]
 
+EXTRA_PP_JS_MODULES += [
+    'LoginManagerParent.jsm',
+]
+
 EXTRA_JS_MODULES += [
     'InsecurePasswordUtils.jsm',
     'LoginHelper.jsm',
     'LoginManagerContent.jsm',
     'LoginManagerParent.jsm',
 ]
 
 if CONFIG['OS_TARGET'] == 'Android':
--- a/toolkit/components/passwordmgr/passwordmgr.manifest
+++ b/toolkit/components/passwordmgr/passwordmgr.manifest
@@ -10,8 +10,9 @@ contract @mozilla.org/login-manager/logi
 component {8c2023b9-175c-477e-9761-44ae7b549756} storage-mozStorage.js
 contract @mozilla.org/login-manager/storage/mozStorage;1 {8c2023b9-175c-477e-9761-44ae7b549756}
 #else
 component {c00c432d-a0c9-46d7-bef6-9c45b4d07341} storage-json.js
 contract @mozilla.org/login-manager/storage/json;1 {c00c432d-a0c9-46d7-bef6-9c45b4d07341}
 #endif
 component {dc6c2976-0f73-4f1f-b9ff-3d72b4e28309} crypto-SDR.js
 contract @mozilla.org/login-manager/crypto/SDR;1 {dc6c2976-0f73-4f1f-b9ff-3d72b4e28309}
+category healthreport-js-provider-default PasswordsMetricsProvider resource://gre/modules/LoginManagerParent.jsm
\ No newline at end of file
--- a/toolkit/components/remote/nsXRemoteService.cpp
+++ b/toolkit/components/remote/nsXRemoteService.cpp
@@ -35,16 +35,17 @@
 
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
 
 using namespace mozilla;
 
 #define MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
 #define MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
+#define MOZILLA_COMMAND_PROP   "_MOZILLA_COMMAND"
 #define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
 #define MOZILLA_USER_PROP      "_MOZILLA_USER"
 #define MOZILLA_PROFILE_PROP   "_MOZILLA_PROFILE"
 #define MOZILLA_PROGRAM_PROP   "_MOZILLA_PROGRAM"
 #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
 
 const unsigned char kRemoteVersion[] = "5.1";
 
@@ -55,26 +56,28 @@ const unsigned char kRemoteVersion[] = "
 #else
 #define TO_LITTLE_ENDIAN32(x) (x)
 #endif
 
 // Minimize the roundtrips to the X server by getting all the atoms at once
 static const char *XAtomNames[] = {
   MOZILLA_VERSION_PROP,
   MOZILLA_LOCK_PROP,
+  MOZILLA_COMMAND_PROP,
   MOZILLA_RESPONSE_PROP,
   MOZILLA_USER_PROP,
   MOZILLA_PROFILE_PROP,
   MOZILLA_PROGRAM_PROP,
   MOZILLA_COMMANDLINE_PROP
 };
 static Atom XAtoms[MOZ_ARRAY_LENGTH(XAtomNames)];
 
 Atom nsXRemoteService::sMozVersionAtom;
 Atom nsXRemoteService::sMozLockAtom;
+Atom nsXRemoteService::sMozCommandAtom;
 Atom nsXRemoteService::sMozResponseAtom;
 Atom nsXRemoteService::sMozUserAtom;
 Atom nsXRemoteService::sMozProfileAtom;
 Atom nsXRemoteService::sMozProgramAtom;
 Atom nsXRemoteService::sMozCommandLineAtom;
 
 nsXRemoteService * nsXRemoteService::sRemoteImplementation = 0;
 
@@ -174,17 +177,17 @@ bool
 nsXRemoteService::HandleNewProperty(XID aWindowId, Display* aDisplay,
                                     Time aEventTime,
                                     Atom aChangedAtom,
                                     nsIWeakReference* aDomWindow)
 {
 
   nsCOMPtr<nsIDOMWindow> window (do_QueryReferent(aDomWindow));
 
-  if (aChangedAtom == sMozCommandLineAtom) {
+  if (aChangedAtom == sMozCommandAtom || aChangedAtom == sMozCommandLineAtom) {
     // We got a new command atom.
     int result;
     Atom actual_type;
     int actual_format;
     unsigned long nitems, bytes_after;
     char *data = 0;
 
     result = XGetWindowProperty (aDisplay,
@@ -206,17 +209,21 @@ nsXRemoteService::HandleNewProperty(XID 
     if (result != Success)
       return false;
 
     // Failed to get the data off the window or it was the wrong type?
     if (!data || !TO_LITTLE_ENDIAN32(*reinterpret_cast<int32_t*>(data)))
       return false;
 
     // cool, we got the property data.
-    const char *response = HandleCommandLine(data, window, aEventTime);
+    const char *response = nullptr;
+    if (aChangedAtom == sMozCommandAtom)
+      response = HandleCommand(data, window, aEventTime);
+    else if (aChangedAtom == sMozCommandLineAtom)
+      response = HandleCommandLine(data, window, aEventTime);
 
     // put the property onto the window as the response
     XChangeProperty (aDisplay, aWindowId,
                      sMozResponseAtom, XA_STRING,
                      8, PropModeReplace,
                      (const unsigned char *)response,
                      strlen (response));
     XFree(data);
@@ -232,16 +239,71 @@ nsXRemoteService::HandleNewProperty(XID 
     // someone locked the window
     return true;
   }
 
   return false;
 }
 
 const char*
+nsXRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow,
+                                uint32_t aTimestamp)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsICommandLineRunner> cmdline
+    (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
+  if (NS_FAILED(rv))
+    return "509 internal error";
+
+  // 1) Make sure that it looks remotely valid with parens
+  // 2) Treat ping() immediately and specially
+
+  nsAutoCString command(aCommand);
+  int32_t p1, p2;
+  p1 = command.FindChar('(');
+  p2 = command.FindChar(')');
+
+  if (p1 == kNotFound || p2 == kNotFound || p1 == 0 || p2 < p1) {
+    return "500 command not parseable";
+  }
+
+  command.Truncate(p1);
+  command.Trim(" ", true, true);
+  ToLowerCase(command);
+
+  if (!command.EqualsLiteral("ping")) {
+    nsAutoCString desktopStartupID;
+    nsDependentCString cmd(aCommand);
+    FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
+                                    cmd, '\n',
+                                    &desktopStartupID);
+
+    const char* argv[3] = {"dummyappname", "-remote", aCommand};
+    rv = cmdline->Init(3, argv, nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT);
+    if (NS_FAILED(rv))
+      return "509 internal error";
+
+    if (aWindow)
+      cmdline->SetWindowContext(aWindow);
+
+    if (sRemoteImplementation)
+      sRemoteImplementation->SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
+
+    rv = cmdline->Run();
+    if (NS_ERROR_ABORT == rv)
+      return "500 command not parseable";
+    if (NS_FAILED(rv))
+      return "509 internal error";
+  }
+
+  return "200 executed command";
+}
+
+const char*
 nsXRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
                                     uint32_t aTimestamp)
 {
   nsresult rv;
 
   nsCOMPtr<nsICommandLineRunner> cmdline
     (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
   if (NS_FAILED(rv))
@@ -311,14 +373,15 @@ nsXRemoteService::EnsureAtoms(void)
     return;
 
   XInternAtoms(mozilla::DefaultXDisplay(), const_cast<char**>(XAtomNames),
                ArrayLength(XAtomNames), False, XAtoms);
 
   int i = 0;
   sMozVersionAtom     = XAtoms[i++];
   sMozLockAtom        = XAtoms[i++];
+  sMozCommandAtom     = XAtoms[i++];
   sMozResponseAtom    = XAtoms[i++];
   sMozUserAtom        = XAtoms[i++];
   sMozProfileAtom     = XAtoms[i++];
   sMozProgramAtom     = XAtoms[i++];
   sMozCommandLineAtom = XAtoms[i++];
 }
--- a/toolkit/components/remote/nsXRemoteService.h
+++ b/toolkit/components/remote/nsXRemoteService.h
@@ -36,27 +36,31 @@ protected:
                                     nsIWeakReference* aDomWindow);
     
     void XRemoteBaseStartup(const char *aAppName, const char *aProfileName);
 
     void HandleCommandsFor(Window aWindowId);
     static nsXRemoteService *sRemoteImplementation;
 private:
     void EnsureAtoms();
+    static const char* HandleCommand(char* aCommand, nsIDOMWindow* aWindow,
+                                     uint32_t aTimestamp);
+
     static const char* HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
                                          uint32_t aTimestamp);
 
     virtual void SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID,
                                                 uint32_t aTimestamp) = 0;
 
     nsCString mAppName;
     nsCString mProfileName;
 
     static Atom sMozVersionAtom;
     static Atom sMozLockAtom;
+    static Atom sMozCommandAtom;
     static Atom sMozResponseAtom;
     static Atom sMozUserAtom;
     static Atom sMozProfileAtom;
     static Atom sMozProgramAtom;
     static Atom sMozCommandLineAtom;
 };
 
 #endif // NSXREMOTESERVICE_H
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1604,16 +1604,76 @@ DumpVersion()
     printf("%s ", gAppData->vendor);
   printf("%s %s", gAppData->name, gAppData->version);
   if (gAppData->copyright)
       printf(", %s", gAppData->copyright);
   printf("\n");
 }
 
 #ifdef MOZ_ENABLE_XREMOTE
+// use int here instead of a PR type since it will be returned
+// from main - just to keep types consistent
+static int
+HandleRemoteArgument(const char* remote, const char* aDesktopStartupID)
+{
+  nsresult rv;
+  ArgResult ar;
+
+  const char *profile = 0;
+  nsAutoCString program(gAppData->name);
+  ToLowerCase(program);
+  const char *username = getenv("LOGNAME");
+
+  ar = CheckArg("p", false, &profile);
+  if (ar == ARG_BAD) {
+    PR_fprintf(PR_STDERR, "Error: argument -p requires a profile name\n");
+    return 1;
+  }
+
+  const char *temp = nullptr;
+  ar = CheckArg("a", false, &temp);
+  if (ar == ARG_BAD) {
+    PR_fprintf(PR_STDERR, "Error: argument -a requires an application name\n");
+    return 1;
+  } else if (ar == ARG_FOUND) {
+    program.Assign(temp);
+  }
+
+  ar = CheckArg("u", false, &username);
+  if (ar == ARG_BAD) {
+    PR_fprintf(PR_STDERR, "Error: argument -u requires a username\n");
+    return 1;
+  }
+
+  XRemoteClient client;
+  rv = client.Init();
+  if (NS_FAILED(rv)) {
+    PR_fprintf(PR_STDERR, "Error: Failed to connect to X server.\n");
+    return 1;
+  }
+
+  nsXPIDLCString response;
+  bool success = false;
+  rv = client.SendCommand(program.get(), username, profile, remote,
+                          aDesktopStartupID, getter_Copies(response), &success);
+  // did the command fail?
+  if (NS_FAILED(rv)) {
+    PR_fprintf(PR_STDERR, "Error: Failed to send command: %s\n",
+               response ? response.get() : "No response included");
+    return 1;
+  }
+
+  if (!success) {
+    PR_fprintf(PR_STDERR, "Error: No running window found\n");
+    return 2;
+  }
+
+  return 0;
+}
+
 static RemoteResult
 RemoteCommandLine(const char* aDesktopStartupID)
 {
   nsresult rv;
   ArgResult ar;
 
   nsAutoCString program(gAppData->remotingName);
   ToLowerCase(program);
@@ -3591,21 +3651,31 @@ XREMain::XRE_mainStartup(bool* aExitFlag
     if (mDisableRemote) {
       newInstance = true;
     } else {
       e = PR_GetEnv("MOZ_NEW_INSTANCE");
       newInstance = (e && *e);
     }
   }
 
+  const char* xremotearg;
+  ArgResult ar = CheckArg("remote", true, &xremotearg);
+  if (ar == ARG_BAD) {
+    PR_fprintf(PR_STDERR, "Error: -remote requires an argument\n");
+    return 1;
+  }
+  const char* desktopStartupIDPtr =
+    mDesktopStartupID.IsEmpty() ? nullptr : mDesktopStartupID.get();
+  if (ar) {
+    *aExitFlag = true;
+    return HandleRemoteArgument(xremotearg, desktopStartupIDPtr);
+  }
+
   if (!newInstance) {
     // Try to remote the entire command line. If this fails, start up normally.
-    const char* desktopStartupIDPtr =
-      mDesktopStartupID.IsEmpty() ? nullptr : mDesktopStartupID.get();
-
     RemoteResult rr = RemoteCommandLine(desktopStartupIDPtr);
     if (rr == REMOTE_FOUND) {
       *aExitFlag = true;
       return 0;
     }
     else if (rr == REMOTE_ARG_BAD)
       return 1;
   }
--- a/widget/GfxDriverInfo.h
+++ b/widget/GfxDriverInfo.h
@@ -31,16 +31,17 @@ namespace widget {
 enum OperatingSystem {
   DRIVER_OS_UNKNOWN = 0,
   DRIVER_OS_WINDOWS_XP,
   DRIVER_OS_WINDOWS_SERVER_2003,
   DRIVER_OS_WINDOWS_VISTA,
   DRIVER_OS_WINDOWS_7,
   DRIVER_OS_WINDOWS_8,
   DRIVER_OS_WINDOWS_8_1,
+  DRIVER_OS_WINDOWS_10,
   DRIVER_OS_LINUX,
   DRIVER_OS_OS_X_10_5,
   DRIVER_OS_OS_X_10_6,
   DRIVER_OS_OS_X_10_7,
   DRIVER_OS_OS_X_10_8,
   DRIVER_OS_OS_X_10_9,
   DRIVER_OS_OS_X_10_10,
   DRIVER_OS_ANDROID,
--- a/widget/windows/GfxInfo.cpp
+++ b/widget/windows/GfxInfo.cpp
@@ -842,16 +842,18 @@ WindowsVersionToOperatingSystem(int32_t 
     case kWindowsVista:
       return DRIVER_OS_WINDOWS_VISTA;
     case kWindows7:
       return DRIVER_OS_WINDOWS_7;
     case kWindows8:
       return DRIVER_OS_WINDOWS_8;
     case kWindows8_1:
       return DRIVER_OS_WINDOWS_8_1;
+    case kWindows10:
+      return DRIVER_OS_WINDOWS_10;
     case kWindowsUnknown:
     default:
       return DRIVER_OS_UNKNOWN;
     };
 }
 
 const nsTArray<GfxDriverInfo>&
 GfxInfo::GetGfxDriverInfo()
@@ -1069,16 +1071,22 @@ GfxInfo::GetGfxDriverInfo()
       (nsAString&)GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), (GfxDeviceFamily*)GfxDriverInfo::GetDeviceFamily(Nvidia310M),
       nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
       DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions);
 
     APPEND_TO_DRIVER_BLOCKLIST2(DRIVER_OS_ALL,
       (nsAString&)GfxDriverInfo::GetDeviceVendor(VendorATI), (GfxDeviceFamily*)GfxDriverInfo::GetDeviceFamily(AMDRadeonHD5800),
       nsIGfxInfo::FEATURE_DXVA, nsIGfxInfo::FEATURE_BLOCKED_DEVICE,
       DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions);
+
+    /* Bug 1139503: DXVA crashes with ATI cards on windows 10. */
+    APPEND_TO_DRIVER_BLOCKLIST2(DRIVER_OS_WINDOWS_10,
+      (nsAString&)GfxDriverInfo::GetDeviceVendor(VendorATI), GfxDriverInfo::allDevices,
+      nsIGfxInfo::FEATURE_DXVA, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+      DRIVER_EQUAL, V(15,200,1006,0));
   }
   return *mDriverInfo;
 }
 
 nsresult
 GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
                               int32_t *aStatus, 
                               nsAString & aSuggestedDriverVersion, 
--- a/widget/xremoteclient/XRemoteClient.cpp
+++ b/widget/xremoteclient/XRemoteClient.cpp
@@ -21,16 +21,17 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <limits.h>
 #include <X11/Xatom.h>
 
 #define MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
 #define MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
+#define MOZILLA_COMMAND_PROP   "_MOZILLA_COMMAND"
 #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
 #define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
 #define MOZILLA_USER_PROP      "_MOZILLA_USER"
 #define MOZILLA_PROFILE_PROP   "_MOZILLA_PROFILE"
 #define MOZILLA_PROGRAM_PROP   "_MOZILLA_PROGRAM"
 
 #ifdef IS_BIG_ENDIAN
 #define TO_LITTLE_ENDIAN32(x) \
@@ -56,16 +57,17 @@ static int (*sOldHandler)(Display *, XEr
 static bool sGotBadWindow;
 
 XRemoteClient::XRemoteClient()
 {
   mDisplay = 0;
   mInitialized = false;
   mMozVersionAtom = 0;
   mMozLockAtom = 0;
+  mMozCommandAtom = 0;
   mMozResponseAtom = 0;
   mMozWMStateAtom = 0;
   mMozUserAtom = 0;
   mLockData = 0;
   if (!sRemoteLm)
     sRemoteLm = PR_NewLogModule("XRemoteClient");
   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::XRemoteClient"));
 }
@@ -76,16 +78,17 @@ XRemoteClient::~XRemoteClient()
   if (mInitialized)
     Shutdown();
 }
 
 // Minimize the roundtrips to the X-server
 static const char *XAtomNames[] = {
   MOZILLA_VERSION_PROP,
   MOZILLA_LOCK_PROP,
+  MOZILLA_COMMAND_PROP,
   MOZILLA_RESPONSE_PROP,
   "WM_STATE",
   MOZILLA_USER_PROP,
   MOZILLA_PROFILE_PROP,
   MOZILLA_PROGRAM_PROP,
   MOZILLA_COMMANDLINE_PROP
 };
 static Atom XAtoms[ARRAY_LENGTH(XAtomNames)];
@@ -105,16 +108,17 @@ XRemoteClient::Init()
 
   // get our atoms
   XInternAtoms(mDisplay, const_cast<char**>(XAtomNames),
                ARRAY_LENGTH(XAtomNames), False, XAtoms);
 
   int i = 0;
   mMozVersionAtom  = XAtoms[i++];
   mMozLockAtom     = XAtoms[i++];
+  mMozCommandAtom  = XAtoms[i++];
   mMozResponseAtom = XAtoms[i++];
   mMozWMStateAtom  = XAtoms[i++];
   mMozUserAtom     = XAtoms[i++];
   mMozProfileAtom  = XAtoms[i++];
   mMozProgramAtom  = XAtoms[i++];
   mMozCommandLineAtom = XAtoms[i++];
 
   mInitialized = true;
@@ -135,44 +139,72 @@ XRemoteClient::Shutdown (void)
   mDisplay = 0;
   mInitialized = false;
   if (mLockData) {
     free(mLockData);
     mLockData = 0;
   }
 }
 
+nsresult
+XRemoteClient::SendCommand (const char *aProgram, const char *aUsername,
+                            const char *aProfile, const char *aCommand,
+                            const char* aDesktopStartupID,
+                            char **aResponse, bool *aWindowFound)
+{
+  PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand"));
+
+  return SendCommandInternal(aProgram, aUsername, aProfile,
+                             aCommand, 0, nullptr,
+                             aDesktopStartupID,
+                             aResponse, aWindowFound);
+}
+
+nsresult
+XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername,
+                                const char *aProfile,
+                                int32_t argc, char **argv,
+                                const char* aDesktopStartupID,
+                                char **aResponse, bool *aWindowFound)
+{
+  PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine"));
+
+  return SendCommandInternal(aProgram, aUsername, aProfile,
+                             nullptr, argc, argv,
+                             aDesktopStartupID,
+                             aResponse, aWindowFound);
+}
+
 static int
 HandleBadWindow(Display *display, XErrorEvent *event)
 {
   if (event->error_code == BadWindow) {
     sGotBadWindow = true;
     return 0; // ignored
   }
   else {
     return (*sOldHandler)(display, event);
   }
 }
 
 nsresult
-XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername,
-                                const char *aProfile,
-                                int32_t argc, char **argv,
-                                const char* aDesktopStartupID,
-                                char **aResponse, bool *aWindowFound)
+XRemoteClient::SendCommandInternal(const char *aProgram, const char *aUsername,
+                                   const char *aProfile, const char *aCommand,
+                                   int32_t argc, char **argv,
+                                   const char* aDesktopStartupID,
+                                   char **aResponse, bool *aWindowFound)
 {
-  PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine"));
-
   *aWindowFound = false;
+  bool isCommandLine = !aCommand;
 
   // FindBestWindow() iterates down the window hierarchy, so catch X errors
   // when windows get destroyed before being accessed.
   sOldHandler = XSetErrorHandler(HandleBadWindow);
 
-  Window w = FindBestWindow(aProgram, aUsername, aProfile);
+  Window w = FindBestWindow(aProgram, aUsername, aProfile, isCommandLine);
 
   nsresult rv = NS_OK;
 
   if (w) {
     // ok, let the caller know that we at least found a window.
     *aWindowFound = true;
 
     // Ignore BadWindow errors up to this point.  The last request from
@@ -186,18 +218,24 @@ XRemoteClient::SendCommandLine (const ch
 
     bool destroyed = false;
 
     // get the lock on the window
     rv = GetLock(w, &destroyed);
 
     if (NS_SUCCEEDED(rv)) {
       // send our command
-      rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse,
-                             &destroyed);
+      if (isCommandLine) {
+        rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse,
+                               &destroyed);
+      }
+      else {
+        rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse,
+                           &destroyed);
+      }
 
       // if the window was destroyed, don't bother trying to free the
       // lock.
       if (!destroyed)
           FreeLock(w); // doesn't really matter what this returns
 
     }
   }
@@ -405,17 +443,18 @@ XRemoteClient::GetLock(Window aWindow, b
             (unsigned int) aWindow));
   }
 
   return rv;
 }
 
 Window
 XRemoteClient::FindBestWindow(const char *aProgram, const char *aUsername,
-                              const char *aProfile)
+                              const char *aProfile,
+                              bool aSupportsCommandLine)
 {
   Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mDisplay));
   Window bestWindow = 0;
   Window root2, parent, *kids;
   unsigned int nkids;
 
   // Get a list of the children of the root window, walk the list
   // looking for the best window that fits the criteria.
@@ -450,17 +489,17 @@ XRemoteClient::FindBestWindow(const char
                                     &data_return);
 
     if (!data_return)
       continue;
 
     double version = PR_strtod((char*) data_return, nullptr);
     XFree(data_return);
 
-    if (!(version >= 5.1 && version < 6))
+    if (aSupportsCommandLine && !(version >= 5.1 && version < 6))
       continue;
 
     data_return = 0;
 
     if (status != Success || type == None)
       continue;
 
     // If someone passed in a program name, check it against this one
@@ -594,16 +633,56 @@ XRemoteClient::FreeLock(Window aWindow)
       return NS_ERROR_FAILURE;
   }
 
   if (data)
       XFree(data);
   return NS_OK;
 }
 
+nsresult
+XRemoteClient::DoSendCommand(Window aWindow, const char *aCommand,
+                             const char* aDesktopStartupID,
+                             char **aResponse, bool *aDestroyed)
+{
+  *aDestroyed = false;
+
+  PR_LOG(sRemoteLm, PR_LOG_DEBUG,
+     ("(writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n",
+      aCommand, (unsigned int) aWindow));
+
+  // We add the DESKTOP_STARTUP_ID setting as an extra line of
+  // the command string. Firefox ignores all lines but the first.
+  static char desktopStartupPrefix[] = "\nDESKTOP_STARTUP_ID=";
+
+  int32_t len = strlen(aCommand);
+  if (aDesktopStartupID) {
+    len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID);
+  }
+  char* buffer = (char*)malloc(len + 1);
+  if (!buffer)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  strcpy(buffer, aCommand);
+  if (aDesktopStartupID) {
+    strcat(buffer, desktopStartupPrefix);
+    strcat(buffer, aDesktopStartupID);
+  }
+
+  XChangeProperty (mDisplay, aWindow, mMozCommandAtom, XA_STRING, 8,
+           PropModeReplace, (unsigned char *)buffer, len);
+
+  free(buffer);
+
+  if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandAtom))
+    return NS_ERROR_FAILURE;
+  
+  return NS_OK;
+}
+
 /* like strcpy, but return the char after the final null */
 static char*
 estrcpy(const char* s, char* d)
 {
   while (*s)
     *d++ = *s++;
 
   *d++ = '\0';
@@ -786,16 +865,16 @@ XRemoteClient::WaitForResponse(Window aW
     }
 
     else if (event.xany.type == PropertyNotify &&
              event.xproperty.window == aWindow &&
              event.xproperty.state == PropertyDelete &&
              event.xproperty.atom == aCommandAtom) {
       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
              ("(server 0x%x has accepted "
-              MOZILLA_COMMANDLINE_PROP ".)\n",
+              MOZILLA_COMMAND_PROP ".)\n",
               (unsigned int) aWindow));
     }
     
   }
 
   return accepted;
 }
--- a/widget/xremoteclient/XRemoteClient.h
+++ b/widget/xremoteclient/XRemoteClient.h
@@ -10,44 +10,60 @@
 
 class XRemoteClient : public nsRemoteClient
 {
 public:
   XRemoteClient();
   ~XRemoteClient();
 
   virtual nsresult Init();
+  virtual nsresult SendCommand(const char *aProgram, const char *aUsername,
+                               const char *aProfile, const char *aCommand,
+                               const char* aDesktopStartupID,
+                               char **aResponse, bool *aSucceeded);
   virtual nsresult SendCommandLine(const char *aProgram, const char *aUsername,
                                    const char *aProfile,
                                    int32_t argc, char **argv,
                                    const char* aDesktopStartupID,
                                    char **aResponse, bool *aSucceeded);
   void Shutdown();
 
 private:
 
   Window         CheckWindow      (Window aWindow);
   Window         CheckChildren    (Window aWindow);
   nsresult       GetLock          (Window aWindow, bool *aDestroyed);
   nsresult       FreeLock         (Window aWindow);
   Window         FindBestWindow   (const char *aProgram,
                                    const char *aUsername,
-                                   const char *aProfile);
+                                   const char *aProfile,
+                                   bool aSupportsCommandLine);
+  nsresult     SendCommandInternal(const char *aProgram, const char *aUsername,
+                                   const char *aProfile, const char *aCommand,
+                                   int32_t argc, char **argv,
+                                   const char* aDesktopStartupID,
+                                   char **aResponse, bool *aWindowFound);
+  nsresult       DoSendCommand    (Window aWindow,
+                                   const char *aCommand,
+                                   const char* aDesktopStartupID,
+                                   char **aResponse,
+                                   bool *aDestroyed);
   nsresult       DoSendCommandLine(Window aWindow,
                                    int32_t argc, char **argv,
                                    const char* aDesktopStartupID,
                                    char **aResponse,
                                    bool *aDestroyed);
   bool           WaitForResponse  (Window aWindow, char **aResponse,
                                    bool *aDestroyed, Atom aCommandAtom);
 
   Display       *mDisplay;
 
   Atom           mMozVersionAtom;
   Atom           mMozLockAtom;
+  Atom           mMozCommandAtom;
   Atom           mMozCommandLineAtom;
   Atom           mMozResponseAtom;
   Atom           mMozWMStateAtom;
   Atom           mMozUserAtom;
   Atom           mMozProfileAtom;
   Atom           mMozProgramAtom;
 
   char          *mLockData;
--- a/widget/xremoteclient/nsRemoteClient.h
+++ b/widget/xremoteclient/nsRemoteClient.h
@@ -18,43 +18,62 @@ class nsRemoteClient
 {
 public:
   /**
    * Initializes the client
    */
   virtual nsresult Init() = 0;
 
   /**
-   * Send a complete command line to a running instance.
+   * Sends a command to a running instance.
    *
    * @param aProgram This is the preferred program that we want to use
    * for this particular command.
    *
+   * @param aNoProgramFallback This boolean attribute tells the client
+   * code that if the preferred program isn't found that it should
+   * fail not send the command to another server.
+   *
    * @param aUsername This allows someone to only talk to an instance
    * of the server that's running under a particular username.  If
    * this isn't specified here it's pulled from the LOGNAME
    * environmental variable if it's set.
    *
    * @param aProfile This allows you to specify a particular server
    * running under a named profile.  If it is not specified the
    * profile is not checked.
    *
-   * @param argc The number of command-line arguments.
-   *
-   * @param argv The command-line arguments.
-   *
+   * @param aCommand This is the command that is passed to the server.
+   * Please see the additional information located at:
+   * http://www.mozilla.org/unix/remote.html
+   * 
    * @param aDesktopStartupID the contents of the DESKTOP_STARTUP_ID environment
    * variable defined by the Startup Notification specification
    * http://standards.freedesktop.org/startup-notification-spec/startup-notification-0.1.txt
    *
    * @param aResponse If there is a response, it will be here.  This
    * includes error messages.  The string is allocated using stdlib
    * string functions, so free it with free().
    *
    * @return true if succeeded, false if no running instance was found.
+   */
+  virtual nsresult SendCommand(const char *aProgram, const char *aUsername,
+                               const char *aProfile, const char *aCommand,
+                               const char* aDesktopStartupID,
+                               char **aResponse, bool *aSucceeded) = 0;
+
+  /**
+   * Send a complete command line to a running instance.
+   *
+   * @param aDesktopStartupID the contents of the DESKTOP_STARTUP_ID environment
+   * variable defined by the Startup Notification specification
+   * http://standards.freedesktop.org/startup-notification-spec/startup-notification-0.1.txt
+   *
+   * @see sendCommand
+   * @param argc The number of command-line arguments.
    * 
    */
   virtual nsresult SendCommandLine(const char *aProgram, const char *aUsername,
                                    const char *aProfile,
                                    int32_t argc, char **argv,
                                    const char* aDesktopStartupID,
                                    char **aResponse, bool *aSucceeded) = 0;
 };