diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index c624ff0be2e3..6f1de1895e68 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -37,6 +37,7 @@ #endif #ifdef XP_WIN +#include "mozilla/a11y/Compatibility.h" #include "HTMLWin32ObjectAccessible.h" #endif @@ -63,6 +64,7 @@ #include "mozilla/dom/Element.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" #include "mozilla/Util.h" #include "nsDeckFrame.h" @@ -210,6 +212,45 @@ nsAccessibilityService::GetRootDocumentAccessible(nsIPresShell* aPresShell, return nullptr; } +#ifdef XP_WIN +static StaticAutoPtr > > sPendingPlugins; +static StaticAutoPtr > > sPluginTimers; + +class PluginTimerCallBack MOZ_FINAL : public nsITimerCallback +{ +public: + PluginTimerCallBack(nsIContent* aContent) : mContent(aContent) {} + + NS_DECL_ISUPPORTS + + NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL + { + nsIPresShell* ps = mContent->OwnerDoc()->GetShell(); + if (ps) { + DocAccessible* doc = ps->GetDocAccessible(); + if (doc) { + // Make sure that if we created an accessible for the plugin that wasn't + // a plugin accessible we remove it before creating the right accessible. + doc->RecreateAccessible(mContent); + sPluginTimers->RemoveElement(aTimer); + return NS_OK; + } + } + + // We couldn't get a doc accessible so presumably the document went away. + // In this case don't leak our ref to the content or timer. + sPendingPlugins->RemoveElement(mContent); + sPluginTimers->RemoveElement(aTimer); + return NS_OK; + } + +private: + nsCOMPtr mContent; +}; + +NS_IMPL_ISUPPORTS1(PluginTimerCallBack, nsITimerCallback) +#endif + already_AddRefed nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame, nsIContent* aContent, @@ -225,6 +266,23 @@ nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame, if (NS_SUCCEEDED(aFrame->GetPluginInstance(getter_AddRefs(pluginInstance))) && pluginInstance) { #ifdef XP_WIN + if (!sPendingPlugins->Contains(aContent) && + (Preferences::GetBool("accessibility.delay_plugins") || + Compatibility::IsJAWS() || Compatibility::IsWE())) { + nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID); + nsRefPtr cb = new PluginTimerCallBack(aContent); + timer->InitWithCallback(cb, Preferences::GetUint("accessibility.delay_plugin_time"), + nsITimer::TYPE_ONE_SHOT); + sPluginTimers->AppendElement(timer); + sPendingPlugins->AppendElement(aContent); + return nullptr; + } + + // We need to remove aContent from the pending plugins here to avoid + // reentrancy. When the timer fires it calls + // DocAccessible::ContentInserted() which does the work async. + sPendingPlugins->RemoveElement(aContent); + // Note: pluginPort will be null if windowless. HWND pluginPort = nullptr; aFrame->GetPluginPort(&pluginPort); @@ -1004,6 +1062,11 @@ nsAccessibilityService::Init() NS_LITERAL_CSTRING("Active")); #endif +#ifdef XP_WIN + sPendingPlugins = new nsTArray >; + sPluginTimers = new nsTArray >; +#endif + gIsShutdown = false; // Now its safe to start platform accessibility. @@ -1030,6 +1093,16 @@ nsAccessibilityService::Shutdown() SelectionManager::Shutdown(); +#ifdef XP_WIN + sPendingPlugins = nullptr; + + uint32_t timerCount = sPluginTimers->Length(); + for (uint32_t i = 0; i < timerCount; i++) + sPluginTimers->ElementAt(i)->Cancel(); + + sPluginTimers = nullptr; +#endif + // Application is going to be closed, shutdown accessibility and mark // accessibility service as shutdown to prevent calls of its methods. // Don't null accessibility service static member at this point to be safe diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index eb4f6bbce408..7971affdfb17 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -331,6 +331,16 @@ pref("accessibility.tabfocus_applies_to_xul", true); // further checks. pref("accessibility.force_disabled", 0); +#ifdef XP_WIN +// Some accessibility tools poke at windows in the plugin process during setup +// which can cause hangs. To hack around this set accessibility.delay_plugins +// to true, you can also try increasing accessibility.delay_plugin_time if your +// machine is slow and you still experience hangs. +// See bug 781791. +pref("accessibility.delay_plugins", false); +pref("accessibility.delay_plugin_time", 10000); +#endif + pref("focusmanager.testmode", false); pref("accessibility.usetexttospeech", "");