Bug 1174798, propagate keyboard indicator state down to child processes, r=smaug
authorNeil Deakin <neil@mozilla.com>
Thu, 09 Jun 2016 07:59:31 -0400
changeset 301335 435c691340a05280302417b3ba150a317cc16c08
parent 301334 dbb4b83be583eacc401648c5c33091958c29c4c3
child 301336 e7a3ba795e1a1f0547505e2c240baef183243fd3
push id30333
push usercbook@mozilla.com
push dateFri, 10 Jun 2016 13:39:58 +0000
treeherdermozilla-central@52679ce4756c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1174798
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1174798, propagate keyboard indicator state down to child processes, r=smaug
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsFrameLoader.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
dom/base/nsPIWindowRoot.h
dom/base/nsWindowRoot.cpp
dom/base/nsWindowRoot.h
dom/ipc/PBrowser.ipdl
dom/ipc/PTabContext.ipdlh
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabContext.cpp
dom/ipc/TabContext.h
dom/ipc/TabMessageUtils.h
widget/windows/nsWindow.cpp
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7233,16 +7233,46 @@ nsContentUtils::CallOnAllRemoteChildren(
     nsCOMPtr<nsIMessageBroadcaster> windowMM;
     chromeWindow->GetMessageManager(getter_AddRefs(windowMM));
     if (windowMM) {
       CallOnAllRemoteChildren(windowMM, aCallback, aArg);
     }
   }
 }
 
+struct UIStateChangeInfo {
+  UIStateChangeType mShowAccelerators;
+  UIStateChangeType mShowFocusRings;
+
+  UIStateChangeInfo(UIStateChangeType aShowAccelerators,
+                    UIStateChangeType aShowFocusRings)
+    : mShowAccelerators(aShowAccelerators),
+      mShowFocusRings(aShowFocusRings)
+  {}
+};
+
+bool
+SetKeyboardIndicatorsChild(TabParent* aParent, void* aArg)
+{
+  UIStateChangeInfo* stateInfo = static_cast<UIStateChangeInfo*>(aArg);
+  Unused << aParent->SendSetKeyboardIndicators(stateInfo->mShowAccelerators,
+                                               stateInfo->mShowFocusRings);
+  return false;
+}
+
+void
+nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(nsPIDOMWindowOuter* aWindow,
+                                                      UIStateChangeType aShowAccelerators,
+                                                      UIStateChangeType aShowFocusRings)
+{
+  UIStateChangeInfo stateInfo(aShowAccelerators, aShowFocusRings);
+  CallOnAllRemoteChildren(aWindow, SetKeyboardIndicatorsChild,
+                          (void *)&stateInfo);
+}
+
 void
 nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables,
                                                 nsTArray<IPCDataTransfer>& aIPC,
                                                 bool aInSyncMessage,
                                                 mozilla::dom::nsIContentChild* aChild,
                                                 mozilla::dom::nsIContentParent* aParent)
 {
   aIPC.Clear();
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -29,16 +29,17 @@
 #include "Units.h"
 #include "mozilla/dom/AutocompleteInfoBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/Logging.h"
 #include "mozilla/NotNull.h"
 #include "nsIContentPolicy.h"
+#include "nsPIDOMWindow.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
 class imgICache;
 class imgIContainer;
@@ -2387,16 +2388,25 @@ public:
   /*
    * Call the given callback on all remote children of the given top-level
    * window. Return true from the callback to stop calling further children.
    */
   static void CallOnAllRemoteChildren(nsPIDOMWindowOuter* aWindow,
                                       CallOnRemoteChildFunction aCallback,
                                       void* aArg);
 
+  /*
+   * Call nsPIDOMWindow::SetKeyboardIndicators all all remote children. This is
+   * in here rather than nsGlobalWindow because TabParent indirectly includes
+   * Windows headers which aren't allowed there.
+   */
+  static void SetKeyboardIndicatorsOnRemoteChildren(nsPIDOMWindowOuter* aWindow,
+                                                    UIStateChangeType aShowAccelerators,
+                                                    UIStateChangeType aShowFocusRings);
+
   /**
    * Given an nsIFile, attempts to read it into aString.
    *
    * Note: Use sparingly! This causes main-thread I/O, which causes jank and all
    * other bad things.
    */
   static nsresult SlurpFileToString(nsIFile* aFile, nsACString& aString);
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -3428,21 +3428,36 @@ nsFrameLoader::GetNewTabContext(MutableT
   nsAutoString presentationURLStr;
   mOwnerContent->GetAttr(kNameSpaceID_None,
                          nsGkAtoms::mozpresentation,
                          presentationURLStr);
 
   bool isPrivate = mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
   attrs.SyncAttributesWithPrivateBrowsing(isPrivate);
 
+  UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
+  UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
+  nsIDocument* doc = mOwnerContent->OwnerDoc();
+  if (doc) {
+    nsCOMPtr<nsPIWindowRoot> root = nsContentUtils::GetWindowRoot(doc);
+    if (root) {
+      showAccelerators =
+        root->ShowAccelerators() ? UIStateChangeType_Set : UIStateChangeType_Clear;
+      showFocusRings =
+        root->ShowFocusRings() ? UIStateChangeType_Set : UIStateChangeType_Clear;
+    }
+  }
+
   bool tabContextUpdated =
     aTabContext->SetTabContext(OwnerIsMozBrowserFrame(),
                                mIsPrerendered,
                                ownApp,
                                containingApp,
+                               showAccelerators,
+                               showFocusRings,
                                attrs,
                                signedPkgOrigin,
                                presentationURLStr);
   NS_ENSURE_STATE(tabContextUpdated);
 
   return NS_OK;
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1201,23 +1201,16 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     mHasHadSlowScript(false),
     mNotifyIdleObserversIdleOnThaw(false),
     mNotifyIdleObserversActiveOnThaw(false),
     mCreatingInnerWindow(false),
     mIsChrome(false),
     mCleanMessageManager(false),
     mNeedsFocus(true),
     mHasFocus(false),
-#if defined(XP_MACOSX)
-    mShowAccelerators(false),
-    mShowFocusRings(false),
-#else
-    mShowAccelerators(true),
-    mShowFocusRings(true),
-#endif
     mShowFocusRingForContent(false),
     mFocusByKeyOccurred(false),
     mHasGamepad(false),
 #ifdef MOZ_GAMEPAD
     mHasSeenGamepadInput(false),
 #endif
     mNotifiedIDDestroyed(false),
     mAllowScriptsToClose(false),
@@ -1262,18 +1255,16 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
 
         // Watch for dom-storage2-changed so we can fire storage
         // events. Use a strong reference.
         os->AddObserver(mObserver, "dom-storage2-changed", false);
       }
 
       Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
     }
-
-    InitializeShowFocusRings();
   } else {
     // |this| is an outer window. Outer windows start out frozen and
     // remain frozen until they get an inner window, so freeze this
     // outer window here.
     Freeze();
   }
 
   // We could have failed the first time through trying
@@ -10032,96 +10023,74 @@ nsGlobalWindow::SetFocusedNode(nsIConten
 uint32_t
 nsGlobalWindow::GetFocusMethod()
 {
   FORWARD_TO_INNER(GetFocusMethod, (), 0);
 
   return mFocusMethod;
 }
 
-void
-nsGlobalWindow::InitializeShowFocusRings()
-{
-  MOZ_ASSERT(IsInnerWindow());
-
-  // Initialize the focus ring state from the parent window. For root windows,
-  // there is no need to do this as SetKeyboardIndicators will propogate any
-  // non-default value to child windows.
-  nsPIDOMWindowOuter* root = GetPrivateRoot();
-  if (root && GetOuterWindow() != root) {
-    bool showAccelerators = false, showFocusRings = false;
-    root->GetKeyboardIndicators(&showAccelerators, &showFocusRings);
-    mShowFocusRings = showFocusRings;
-  }
-}
-
 bool
 nsGlobalWindow::ShouldShowFocusRing()
 {
   FORWARD_TO_INNER(ShouldShowFocusRing, (), false);
 
-  return mShowFocusRings || mShowFocusRingForContent || mFocusByKeyOccurred;
+  if (mShowFocusRingForContent || mFocusByKeyOccurred) {
+    return true;
+  }
+
+  nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
+  return root ? root->ShowFocusRings() : false;
 }
 
 void
 nsGlobalWindow::SetKeyboardIndicators(UIStateChangeType aShowAccelerators,
                                       UIStateChangeType aShowFocusRings)
 {
-  FORWARD_TO_INNER_VOID(SetKeyboardIndicators, (aShowAccelerators, aShowFocusRings));
+  MOZ_ASSERT(IsOuterWindow());
+
+  nsPIDOMWindowOuter* piWin = GetPrivateRoot();
+  if (!piWin) {
+    return;
+  }
+
+  MOZ_ASSERT(piWin == AsOuter());
 
   bool oldShouldShowFocusRing = ShouldShowFocusRing();
 
   // only change the flags that have been modified
-  if (aShowAccelerators != UIStateChangeType_NoChange)
-    mShowAccelerators = aShowAccelerators == UIStateChangeType_Set;
-  if (aShowFocusRings != UIStateChangeType_NoChange)
-    mShowFocusRings = aShowFocusRings == UIStateChangeType_Set;
-
-  // propagate the indicators to child windows
-  nsCOMPtr<nsIDocShell> docShell = GetDocShell();
-  if (docShell) {
-    int32_t childCount = 0;
-    docShell->GetChildCount(&childCount);
-
-    for (int32_t i = 0; i < childCount; ++i) {
-      nsCOMPtr<nsIDocShellTreeItem> childShell;
-      docShell->GetChildAt(i, getter_AddRefs(childShell));
-      if (!childShell) {
-        continue;
-      }
-
-      if (nsCOMPtr<nsPIDOMWindowOuter> childWindow = childShell->GetWindow()) {
-        childWindow->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings);
-      }
-    }
-  }
+  nsCOMPtr<nsPIWindowRoot> windowRoot = do_QueryInterface(mChromeEventHandler);
+  if (!windowRoot) {
+    return;
+  }
+
+  if (aShowAccelerators != UIStateChangeType_NoChange) {
+    windowRoot->SetShowAccelerators(aShowAccelerators == UIStateChangeType_Set);
+  }
+  if (aShowFocusRings != UIStateChangeType_NoChange) {
+    windowRoot->SetShowFocusRings(aShowFocusRings == UIStateChangeType_Set);
+  }
+
+  nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(GetOuterWindow(),
+                                                        aShowAccelerators,
+                                                        aShowFocusRings);
 
   bool newShouldShowFocusRing = ShouldShowFocusRing();
   if (mHasFocus && mFocusedNode &&
       oldShouldShowFocusRing != newShouldShowFocusRing &&
       mFocusedNode->IsElement()) {
     // Update mFocusedNode's state.
     if (newShouldShowFocusRing) {
       mFocusedNode->AsElement()->AddStates(NS_EVENT_STATE_FOCUSRING);
     } else {
       mFocusedNode->AsElement()->RemoveStates(NS_EVENT_STATE_FOCUSRING);
     }
   }
 }
 
-void
-nsGlobalWindow::GetKeyboardIndicators(bool* aShowAccelerators,
-                                      bool* aShowFocusRings)
-{
-  FORWARD_TO_INNER_VOID(GetKeyboardIndicators, (aShowAccelerators, aShowFocusRings));
-
-  *aShowAccelerators = mShowAccelerators;
-  *aShowFocusRings = mShowFocusRings;
-}
-
 bool
 nsGlobalWindow::TakeFocus(bool aFocus, uint32_t aFocusMethod)
 {
   FORWARD_TO_INNER(TakeFocus, (aFocus, aFocusMethod), false);
 
   if (mCleanedUp) {
     return false;
   }
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1602,18 +1602,16 @@ public:
                               bool aNeedsFocus = false) override;
 
   virtual uint32_t GetFocusMethod() override;
 
   virtual bool ShouldShowFocusRing() override;
 
   virtual void SetKeyboardIndicators(UIStateChangeType aShowAccelerators,
                                      UIStateChangeType aShowFocusRings) override;
-  virtual void GetKeyboardIndicators(bool* aShowAccelerators,
-                                     bool* aShowFocusRings) override;
 
   // Inner windows only.
   void UpdateCanvasFocus(bool aFocusChanged, nsIContent* aNewContent);
 
 public:
   virtual already_AddRefed<nsPIWindowRoot> GetTopWindowRoot() override;
 
 protected:
@@ -1754,22 +1752,16 @@ protected:
   // window superclass.
   bool                          mCleanMessageManager : 1;
 
   // Indicates that the current document has never received a document focus
   // event.
   bool                   mNeedsFocus : 1;
   bool                   mHasFocus : 1;
 
-  // whether to show keyboard accelerators
-  bool                   mShowAccelerators : 1;
-
-  // whether to show focus rings
-  bool                   mShowFocusRings : 1;
-
   // when true, show focus rings for the current focused content only.
   // This will be reset when another element is focused
   bool                   mShowFocusRingForContent : 1;
 
   // true if tab navigation has occurred for this window. Focus rings
   // should be displayed.
   bool                   mFocusByKeyOccurred : 1;
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -62,17 +62,18 @@ enum PopupControlState {
   openAbused,       // it's a popup. disallow it, but allow domain override.
   openOverridden    // disallow window open
 };
 
 enum UIStateChangeType
 {
   UIStateChangeType_NoChange,
   UIStateChangeType_Set,
-  UIStateChangeType_Clear
+  UIStateChangeType_Clear,
+  UIStateChangeType_Invalid // used for serialization only
 };
 
 enum class FullscreenReason
 {
   // Toggling the fullscreen mode requires trusted context.
   ForFullscreenMode,
   // Fullscreen API is the API provided to untrusted content.
   ForFullscreenAPI,
@@ -406,22 +407,16 @@ public:
 
   /**
    * Set the keyboard indicator state for accelerators and focus rings.
    */
   virtual void SetKeyboardIndicators(UIStateChangeType aShowAccelerators,
                                      UIStateChangeType aShowFocusRings) = 0;
 
   /**
-   * Get the keyboard indicator state for accelerators and focus rings.
-   */
-  virtual void GetKeyboardIndicators(bool* aShowAccelerators,
-                                     bool* aShowFocusRings) = 0;
-
-  /**
    * Indicates that the page in the window has been hidden. This is used to
    * reset the focus state.
    */
   virtual void PageHidden() = 0;
 
   /**
    * Instructs this window to asynchronously dispatch a hashchange event.  This
    * method must be called on an inner window.
--- a/dom/base/nsPIWindowRoot.h
+++ b/dom/base/nsPIWindowRoot.h
@@ -49,13 +49,18 @@ public:
   // Stores a weak reference to the browser.
   virtual void AddBrowser(mozilla::dom::TabParent* aBrowser) = 0;
   virtual void RemoveBrowser(mozilla::dom::TabParent* aBrowser) = 0;
 
   typedef void (*BrowserEnumerator)(mozilla::dom::TabParent* aTab, void* aArg);
 
   // Enumerate all stored browsers that for which the weak reference is valid.
   virtual void EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg) = 0;
+
+  virtual bool ShowAccelerators() = 0;
+  virtual bool ShowFocusRings() = 0;
+  virtual void SetShowAccelerators(bool aEnable) = 0;
+  virtual void SetShowFocusRings(bool aEnable) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIWindowRoot, NS_IWINDOWROOT_IID)
 
 #endif // nsPIWindowRoot_h__
--- a/dom/base/nsWindowRoot.cpp
+++ b/dom/base/nsWindowRoot.cpp
@@ -32,16 +32,25 @@
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsWindowRoot::nsWindowRoot(nsPIDOMWindowOuter* aWindow)
 {
   mWindow = aWindow;
   MOZ_ASSERT(mWindow->IsOuterWindow());
+
+  // Keyboard indicators are not shown on Mac by default.
+#if defined(XP_MACOSX)
+  mShowAccelerators = false;
+  mShowFocusRings = false;
+#else
+  mShowAccelerators = true;
+  mShowFocusRings = true;
+#endif
 }
 
 nsWindowRoot::~nsWindowRoot()
 {
   if (mListenerManager) {
     mListenerManager->Disconnect();
   }
 }
--- a/dom/base/nsWindowRoot.h
+++ b/dom/base/nsWindowRoot.h
@@ -67,30 +67,55 @@ public:
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsWindowRoot,
                                                          nsIDOMEventTarget)
 
   virtual void AddBrowser(mozilla::dom::TabParent* aBrowser) override;
   virtual void RemoveBrowser(mozilla::dom::TabParent* aBrowser) override;
   virtual void EnumerateBrowsers(BrowserEnumerator aEnumFunc, void *aArg) override;
 
+  virtual bool ShowAccelerators() override
+  {
+    return mShowAccelerators;
+  }
+
+  virtual bool ShowFocusRings() override
+  {
+    return mShowFocusRings;
+  }
+
+  virtual void SetShowAccelerators(bool aEnable) override
+  {
+    mShowAccelerators = aEnable;
+  }
+
+  virtual void SetShowFocusRings(bool aEnable) override
+  {
+    mShowFocusRings = aEnable;
+  }
+
 protected:
   virtual ~nsWindowRoot();
 
   void GetEnabledDisabledCommandsForControllers(nsIControllers* aControllers,
                                                 nsTHashtable<nsCharPtrHashKey>& aCommandsHandled,
                                                 nsTArray<nsCString>& aEnabledCommands,
                                                 nsTArray<nsCString>& aDisabledCommands);
 
   // Members
   nsCOMPtr<nsPIDOMWindowOuter> mWindow;
   // We own the manager, which owns event listeners attached to us.
   RefPtr<mozilla::EventListenerManager> mListenerManager; // [Strong]
   nsCOMPtr<nsIDOMNode> mPopupNode; // [OWNER]
 
+  // True if focus rings and accelerators are enabled for this
+  // window hierarchy.
+  bool mShowAccelerators;
+  bool mShowFocusRings;
+
   nsCOMPtr<mozilla::dom::EventTarget> mParent;
 
   // The TabParents that are currently registered with this top-level window.
   typedef nsTHashtable<nsRefPtrHashKey<nsIWeakReference>> WeakBrowserTable;
   WeakBrowserTable mWeakBrowsers;
 };
 
 extern already_AddRefed<mozilla::dom::EventTarget>
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -588,16 +588,19 @@ child:
      * Sending an activate message moves focus to the child.
      */
     async Activate();
 
     async Deactivate();
 
     async ParentActivated(bool aActivated);
 
+    async SetKeyboardIndicators(UIStateChangeType showAccelerators,
+                                UIStateChangeType showFocusRings);
+
     /**
      * StopIMEStateManagement() is called when the process loses focus and
      * should stop managing IME state.
      */
     async StopIMEStateManagement();
 
     /**
      * MenuKeyboardListenerInstalled() is called when menu keyboard listener
--- a/dom/ipc/PTabContext.ipdlh
+++ b/dom/ipc/PTabContext.ipdlh
@@ -2,16 +2,18 @@
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 /* 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 protocol PBrowser;
 include PBrowserOrId;
 
+using UIStateChangeType from "nsPIDOMWindow.h";
+
 namespace mozilla {
 namespace dom {
 
 // An IPCTabContext which corresponds to a PBrowser opened by a child when it
 // receives window.open().
 //
 // If isMozBrowserElement is false, this PopupIPCTabContext is either a
 // <xul:browser> or an app frame.  The frame's app-id and app-frame-owner-app-id
@@ -50,16 +52,20 @@ struct FrameIPCTabContext
 
   // Whether this TabContext should work in prerender mode.
   bool isPrerendered;
 
   // The requested presentation URL.
   // This value would be empty if the TabContext isn't created for
   // presented content.
   nsString presentationURL;
+
+  // Keyboard indicator state inherited from the parent.
+  UIStateChangeType showAccelerators;
+  UIStateChangeType showFocusRings;
 };
 
 // XXXcatalinb: This is only used by ServiceWorkerClients::OpenWindow.
 // Because service workers don't have an associated TabChild
 // we can't satisfy the security constraints on b2g. As such, the parent
 // process will accept this tab context only on desktop.
 struct UnsafeIPCTabContext
 { };
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -849,16 +849,18 @@ TabChild::Init()
   // XXX: ideally, we would set a chrome event handler earlier,
   // and all windows, even the root one, will use the docshell one.
   nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   nsCOMPtr<EventTarget> chromeHandler =
     do_QueryInterface(window->GetChromeEventHandler());
   docShell->SetChromeEventHandler(chromeHandler);
 
+  window->SetKeyboardIndicators(ShowAccelerators(), ShowFocusRings());
+
   // Set prerender flag if necessary.
   if (mIsPrerendered) {
     docShell->SetIsPrerendered();
   }
 
   nsContentUtils::SetScrollbarsVisibility(window->GetDocShell(),
     !!(mChromeFlags & nsIWebBrowserChrome::CHROME_SCROLLBARS));
 
@@ -1855,16 +1857,26 @@ bool TabChild::RecvParentActivated(const
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   NS_ENSURE_TRUE(fm, true);
 
   nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
   fm->ParentActivated(window, aActivated);
   return true;
 }
 
+bool TabChild::RecvSetKeyboardIndicators(const UIStateChangeType& aShowAccelerators,
+                                         const UIStateChangeType& aShowFocusRings)
+{
+  nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+  NS_ENSURE_TRUE(window, true);
+
+  window->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings);
+  return true;
+}
+
 bool
 TabChild::RecvStopIMEStateManagement()
 {
   IMEStateManager::StopIMEStateManagement();
   return true;
 }
 
 bool
@@ -2907,17 +2919,16 @@ void
 TabChild::EnableDisableCommands(const nsAString& aAction,
                                 nsTArray<nsCString>& aEnabledCommands,
                                 nsTArray<nsCString>& aDisabledCommands)
 {
   PBrowserChild::SendEnableDisableCommands(PromiseFlatString(aAction),
                                            aEnabledCommands, aDisabledCommands);
 }
 
-
 NS_IMETHODIMP
 TabChild::GetTabId(uint64_t* aId)
 {
   *aId = GetTabId();
   return NS_OK;
 }
 
 void
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -664,16 +664,19 @@ protected:
                                  const bool& aForDocumentNavigation) override;
 
   virtual bool RecvRequestNotifyAfterRemotePaint() override;
 
   virtual bool RecvSuppressDisplayport(const bool& aEnabled) override;
 
   virtual bool RecvParentActivated(const bool& aActivated) override;
 
+  virtual bool RecvSetKeyboardIndicators(const UIStateChangeType& aShowAccelerators,
+                                         const UIStateChangeType& aShowFocusRings) override;
+
   virtual bool RecvStopIMEStateManagement() override;
 
   virtual bool RecvMenuKeyboardListenerInstalled(
                  const bool& aInstalled) override;
 
 #ifdef MOZ_WIDGET_GONK
   void MaybeRequestPreinitCamera();
 #endif
--- a/dom/ipc/TabContext.cpp
+++ b/dom/ipc/TabContext.cpp
@@ -21,16 +21,18 @@ namespace mozilla {
 namespace dom {
 
 TabContext::TabContext()
   : mIsPrerendered(false)
   , mInitialized(false)
   , mIsMozBrowserElement(false)
   , mContainingAppId(NO_APP_ID)
   , mOriginAttributes()
+  , mShowAccelerators(UIStateChangeType_NoChange)
+  , mShowFocusRings(UIStateChangeType_NoChange)
 {
 }
 
 bool
 TabContext::IsMozBrowserElement() const
 {
   return mIsMozBrowserElement;
 }
@@ -198,21 +200,35 @@ TabContext::SignedPkgOriginNoSuffix() co
 }
 
 const nsAString&
 TabContext::PresentationURL() const
 {
   return mPresentationURL;
 }
 
+UIStateChangeType
+TabContext::ShowAccelerators() const
+{
+  return mShowAccelerators;
+}
+
+UIStateChangeType
+TabContext::ShowFocusRings() const
+{
+  return mShowFocusRings;
+}
+
 bool
 TabContext::SetTabContext(bool aIsMozBrowserElement,
                           bool aIsPrerendered,
                           mozIApplication* aOwnApp,
                           mozIApplication* aAppFrameOwnerApp,
+                          UIStateChangeType aShowAccelerators,
+                          UIStateChangeType aShowFocusRings,
                           const DocShellOriginAttributes& aOriginAttributes,
                           const nsACString& aSignedPkgOriginNoSuffix,
                           const nsAString& aPresentationURL)
 {
   NS_ENSURE_FALSE(mInitialized, false);
 
   // Get ids for both apps and only write to our member variables after we've
   // verified that this worked.
@@ -239,30 +255,34 @@ TabContext::SetTabContext(bool aIsMozBro
   mIsMozBrowserElement = aIsMozBrowserElement;
   mIsPrerendered = aIsPrerendered;
   mOriginAttributes = aOriginAttributes;
   mContainingAppId = containingAppId;
   mOwnApp = aOwnApp;
   mContainingApp = aAppFrameOwnerApp;
   mSignedPkgOriginNoSuffix = aSignedPkgOriginNoSuffix;
   mPresentationURL = aPresentationURL;
+  mShowAccelerators = aShowAccelerators;
+  mShowFocusRings = aShowFocusRings;
   return true;
 }
 
 IPCTabContext
 TabContext::AsIPCTabContext() const
 {
   nsAutoCString originSuffix;
   mOriginAttributes.CreateSuffix(originSuffix);
   return IPCTabContext(FrameIPCTabContext(originSuffix,
                                           mContainingAppId,
                                           mSignedPkgOriginNoSuffix,
                                           mIsMozBrowserElement,
                                           mIsPrerendered,
-                                          mPresentationURL));
+                                          mPresentationURL,
+                                          mShowAccelerators,
+                                          mShowFocusRings));
 }
 
 static already_AddRefed<mozIApplication>
 GetAppForId(uint32_t aAppId)
 {
   nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(appsService, nullptr);
 
@@ -277,16 +297,18 @@ MaybeInvalidTabContext::MaybeInvalidTabC
 {
   bool isMozBrowserElement = false;
   bool isPrerendered = false;
   uint32_t containingAppId = NO_APP_ID;
   DocShellOriginAttributes originAttributes;
   nsAutoCString originSuffix;
   nsAutoCString signedPkgOriginNoSuffix;
   nsAutoString presentationURL;
+  UIStateChangeType showAccelerators;
+  UIStateChangeType showFocusRings;
 
   switch(aParams.type()) {
     case IPCTabContext::TPopupIPCTabContext: {
       const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext();
 
       TabContext *context;
       if (ipcContext.opener().type() == PBrowserOrId::TPBrowserParent) {
         context = TabParent::GetFrom(ipcContext.opener().get_PBrowserParent());
@@ -339,16 +361,18 @@ MaybeInvalidTabContext::MaybeInvalidTabC
       const FrameIPCTabContext &ipcContext =
         aParams.get_FrameIPCTabContext();
 
       isMozBrowserElement = ipcContext.isMozBrowserElement();
       isPrerendered = ipcContext.isPrerendered();
       containingAppId = ipcContext.frameOwnerAppId();
       signedPkgOriginNoSuffix = ipcContext.signedPkgOriginNoSuffix();
       presentationURL = ipcContext.presentationURL();
+      showAccelerators = ipcContext.showAccelerators();
+      showFocusRings = ipcContext.showFocusRings();
       originSuffix = ipcContext.originSuffix();
       originAttributes.PopulateFromSuffix(originSuffix);
       break;
     }
     case IPCTabContext::TUnsafeIPCTabContext: {
       // XXXcatalinb: This used *only* by ServiceWorkerClients::OpenWindow.
       // It is meant as a temporary solution until service workers can
       // provide a TabChild equivalent. Don't allow this on b2g since
@@ -387,16 +411,18 @@ MaybeInvalidTabContext::MaybeInvalidTabC
     return;
   }
 
   bool rv;
   rv = mTabContext.SetTabContext(isMozBrowserElement,
                                  isPrerendered,
                                  ownApp,
                                  containingApp,
+                                 showAccelerators,
+                                 showFocusRings,
                                  originAttributes,
                                  signedPkgOriginNoSuffix,
                                  presentationURL);
   if (!rv) {
     mInvalidReason = "Couldn't initialize TabContext.";
   }
 }
 
--- a/dom/ipc/TabContext.h
+++ b/dom/ipc/TabContext.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_TabContext_h
 #define mozilla_dom_TabContext_h
 
 #include "mozIApplication.h"
 #include "nsCOMPtr.h"
 #include "mozilla/BasePrincipal.h"
+#include "nsPIWindowRoot.h"
 
 namespace mozilla {
 namespace dom {
 
 class IPCTabContext;
 
 /**
  * TabContext encapsulates information about an iframe that may be a mozbrowser
@@ -131,16 +132,19 @@ public:
   const nsACString& SignedPkgOriginNoSuffix() const;
 
   /**
    * Returns the presentation URL associated with the tab if this tab is
    * created for presented content
    */
   const nsAString& PresentationURL() const;
 
+  UIStateChangeType ShowAccelerators() const;
+  UIStateChangeType ShowFocusRings() const;
+
 protected:
   friend class MaybeInvalidTabContext;
 
   /**
    * These protected mutator methods let you modify a TabContext once.  Further
    * attempts to modify a given TabContext will fail (the method will return
    * false).
    *
@@ -164,16 +168,18 @@ protected:
    *    apps can be null.
    *  - a browser frame inside the given owner app (which may be null).
    *  - a non-browser, non-app frame. Both own app and owner app should be null.
    */
   bool SetTabContext(bool aIsMozBrowserElement,
                      bool aIsPrerendered,
                      mozIApplication* aOwnApp,
                      mozIApplication* aAppFrameOwnerApp,
+                     UIStateChangeType aShowAccelerators,
+                     UIStateChangeType aShowFocusRings,
                      const DocShellOriginAttributes& aOriginAttributes,
                      const nsACString& aSignedPkgOriginNoSuffix,
                      const nsAString& aPresentationURL);
 
   /**
    * Modify this TabContext to match the given TabContext.  This is a special
    * case triggered by nsFrameLoader::SwapWithOtherRemoteLoader which may have
    * caused the owner content to change.
@@ -233,16 +239,22 @@ private:
    * doesn't own a signed package, this value would be empty.
    */
   nsCString mSignedPkgOriginNoSuffix;
 
   /**
    * The requested presentation URL.
    */
   nsString mPresentationURL;
+
+  /**
+   * Keyboard indicator state (focus rings, accelerators).
+   */
+  UIStateChangeType mShowAccelerators;
+  UIStateChangeType mShowFocusRings;
 };
 
 /**
  * MutableTabContext is the same as MaybeInvalidTabContext, except the mutation
  * methods are public instead of protected.  You can still only call these
  * mutation methods once on a given object.
  */
 class MutableTabContext : public TabContext
@@ -253,24 +265,28 @@ public:
     return TabContext::SetTabContext(aContext);
   }
 
   bool
   SetTabContext(bool aIsMozBrowserElement,
                 bool aIsPrerendered,
                 mozIApplication* aOwnApp,
                 mozIApplication* aAppFrameOwnerApp,
+                UIStateChangeType aShowAccelerators,
+                UIStateChangeType aShowFocusRings,
                 const DocShellOriginAttributes& aOriginAttributes,
                 const nsACString& aSignedPkgOriginNoSuffix = EmptyCString(),
                 const nsAString& aPresentationURL = EmptyString())
   {
     return TabContext::SetTabContext(aIsMozBrowserElement,
                                      aIsPrerendered,
                                      aOwnApp,
                                      aAppFrameOwnerApp,
+                                     aShowAccelerators,
+                                     aShowFocusRings,
                                      aOriginAttributes,
                                      aSignedPkgOriginNoSuffix,
                                      aPresentationURL);
   }
 };
 
 /**
  * MaybeInvalidTabContext is a simple class that lets you transform an
--- a/dom/ipc/TabMessageUtils.h
+++ b/dom/ipc/TabMessageUtils.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TABMESSAGE_UTILS_H
 #define TABMESSAGE_UTILS_H
 
 #include "ipc/IPCMessageUtils.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "nsIDOMEvent.h"
+#include "nsPIDOMWindow.h"
 #include "nsCOMPtr.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 namespace mozilla {
 namespace dom {
@@ -99,11 +100,18 @@ struct ParamTraits<nsEventStatus>
 
 template<>
 struct ParamTraits<nsSizeMode>
   : public ContiguousEnumSerializer<nsSizeMode,
                                     nsSizeMode_Normal,
                                     nsSizeMode_Invalid>
 {};
 
+template<>
+struct ParamTraits<UIStateChangeType>
+  : public ContiguousEnumSerializer<UIStateChangeType,
+                                    UIStateChangeType_NoChange,
+                                    UIStateChangeType_Invalid>
+{ };
+
 } // namespace IPC
 
 #endif // TABMESSAGE_UTILS_H
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -5574,26 +5574,30 @@ nsWindow::ProcessMessage(UINT msg, WPARA
 
   case WM_UPDATEUISTATE:
   {
     // If the UI state has changed, fire an event so the UI updates the
     // keyboard cues based on the system setting and how the window was
     // opened. For example, a dialog opened via a keyboard press on a button
     // should enable cues, whereas the same dialog opened via a mouse click of
     // the button should not.
-    int32_t action = LOWORD(wParam);
-    if (action == UIS_SET || action == UIS_CLEAR) {
-      int32_t flags = HIWORD(wParam);
-      UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
-      UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
-      if (flags & UISF_HIDEACCEL)
-        showAccelerators = (action == UIS_SET) ? UIStateChangeType_Clear : UIStateChangeType_Set;
-      if (flags & UISF_HIDEFOCUS)
-        showFocusRings = (action == UIS_SET) ? UIStateChangeType_Clear : UIStateChangeType_Set;
-      NotifyUIStateChanged(showAccelerators, showFocusRings);
+    if (mWindowType == eWindowType_toplevel ||
+        mWindowType == eWindowType_dialog) {
+      int32_t action = LOWORD(wParam);
+      if (action == UIS_SET || action == UIS_CLEAR) {
+        int32_t flags = HIWORD(wParam);
+        UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
+        UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
+        if (flags & UISF_HIDEACCEL)
+          showAccelerators = (action == UIS_SET) ? UIStateChangeType_Clear : UIStateChangeType_Set;
+        if (flags & UISF_HIDEFOCUS)
+          showFocusRings = (action == UIS_SET) ? UIStateChangeType_Clear : UIStateChangeType_Set;
+
+        NotifyUIStateChanged(showAccelerators, showFocusRings);
+      }
     }
 
     break;
   }
 
   /* Gesture support events */
   case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
     // According to MS samples, this must be handled to enable