Bug 966395 - Correctly propagate the input language's direction on OSX. r=masayuki/mrbkap
authorAlexandru Tifrea <atifrea@mozilla.com>
Fri, 10 Apr 2015 15:18:05 -0700
changeset 268561 25b9ce15d13cbc90e275fd31a7297578f6c0537e
parent 268560 07c01d36c5a287b1913a2d55a00566cad563d8ae
child 268562 52cef178ce3e1dabcdb2f3d71ec6c25f544ed701
push id4830
push userjlund@mozilla.com
push dateMon, 29 Jun 2015 20:18:48 +0000
treeherdermozilla-beta@4c2175bb0420 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki, mrbkap
bugs966395
milestone40.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 966395 - Correctly propagate the input language's direction on OSX. r=masayuki/mrbkap Gtk patch written by mrbkap@mozilla.com, with r=roc
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
widget/PuppetBidiKeyboard.cpp
widget/PuppetBidiKeyboard.h
widget/cocoa/TextInputHandler.h
widget/cocoa/TextInputHandler.mm
widget/cocoa/moz.build
widget/cocoa/nsWidgetFactory.mm
widget/gtk/nsBidiKeyboard.cpp
widget/moz.build
widget/nsContentProcessWidgetFactory.cpp
widget/nsWidgetsCID.h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -174,16 +174,17 @@
 #include "nsDeviceStorage.h"
 #include "AudioChannelService.h"
 #include "DomainPolicy.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/telephony/PTelephonyChild.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/dom/voicemail/VoicemailIPCService.h"
 #include "mozilla/net/NeckoMessageUtils.h"
+#include "mozilla/widget/PuppetBidiKeyboard.h"
 #include "mozilla/RemoteSpellCheckEngineChild.h"
 #include "GMPServiceChild.h"
 
 using namespace mozilla;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::cellbroadcast;
 using namespace mozilla::dom::devicestorage;
@@ -195,16 +196,17 @@ using namespace mozilla::dom::telephony;
 using namespace mozilla::dom::voicemail;
 using namespace mozilla::embedding;
 using namespace mozilla::gmp;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
+using namespace mozilla::widget;
 #if defined(MOZ_WIDGET_GONK)
 using namespace mozilla::system;
 #endif
 using namespace mozilla::widget;
 
 #ifdef MOZ_NUWA_PROCESS
 static bool sNuwaForking = false;
 
@@ -788,22 +790,24 @@ ContentChild::InitXPCOM()
         NS_WARNING("Couldn't acquire console service");
         return;
     }
 
     mConsoleListener = new ConsoleListener(this);
     if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
         NS_WARNING("Couldn't register console listener for child process");
 
-    bool isOffline;
+    bool isOffline, isLangRTL;
     ClipboardCapabilities clipboardCaps;
     DomainPolicyClone domainPolicy;
 
-    SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries, &clipboardCaps, &domainPolicy);
+    SendGetXPCOMProcessAttributes(&isOffline, &isLangRTL, &mAvailableDictionaries,
+                                  &clipboardCaps, &domainPolicy);
     RecvSetOffline(isOffline);
+    RecvBidiKeyboardNotify(isLangRTL);
 
     if (domainPolicy.active()) {
         nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
         MOZ_ASSERT(ssm);
         ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
         if (!mPolicy) {
             MOZ_CRASH("Failed to activate domain policy.");
         }
@@ -1217,16 +1221,28 @@ ContentChild::RecvSpeakerManagerNotify()
     if (service) {
         service->Notify();
     }
     return true;
 #endif
     return false;
 }
 
+bool
+ContentChild::RecvBidiKeyboardNotify(const bool& aIsLangRTL)
+{
+    // bidi is always of type PuppetBidiKeyboard* (because in the child, the only
+    // possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
+    PuppetBidiKeyboard* bidi = static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
+    if (bidi) {
+        bidi->SetIsLangRTL(aIsLangRTL);
+    }
+    return true;
+}
+
 static CancelableTask* sFirstIdleTask;
 
 static void FirstIdle(void)
 {
     MOZ_ASSERT(sFirstIdleTask);
     sFirstIdleTask = nullptr;
     ContentChild::GetSingleton()->SendFirstIdle();
 }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -294,16 +294,18 @@ public:
     virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) override;
     virtual PRemoteSpellcheckEngineChild* AllocPRemoteSpellcheckEngineChild() override;
     virtual bool DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild*) override;
 
     virtual bool RecvSetOffline(const bool& offline) override;
 
     virtual bool RecvSpeakerManagerNotify() override;
 
+    virtual bool RecvBidiKeyboardNotify(const bool& isLangRTL) override;
+
     virtual bool RecvNotifyVisited(const URIParams& aURI) override;
     // auto remove when alertfinished is received.
     nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
 
     virtual bool RecvSystemMemoryAvailable(const uint64_t& aGetterId,
                                            const uint32_t& aMemoryAvailable) override;
 
     virtual bool RecvPreferenceUpdate(const PrefSetting& aPref) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -146,16 +146,18 @@
 #include "nsIWebBrowserChrome.h"
 #include "nsIDocShell.h"
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "gfxPrefs.h"
 #include "prio.h"
 #include "private/pprio.h"
 #include "ContentProcessManager.h"
 
+#include "nsIBidiKeyboard.h"
+
 #if defined(ANDROID) || defined(LINUX)
 #include "nsSystemInfo.h"
 #endif
 
 #if defined(XP_LINUX)
 #include "mozilla/Hal.h"
 #endif
 
@@ -2812,21 +2814,21 @@ ContentParent::RecvAddNewProcess(const u
     size_t numNuwaPrefUpdates = sNuwaPrefUpdates ?
                                 sNuwaPrefUpdates->Length() : 0;
     // Resend pref updates to the forked child.
     for (size_t i = 0; i < numNuwaPrefUpdates; i++) {
         mozilla::unused << content->SendPreferenceUpdate(sNuwaPrefUpdates->ElementAt(i));
     }
 
     // Update offline settings.
-    bool isOffline;
+    bool isOffline, isLangRTL;
     InfallibleTArray<nsString> unusedDictionaries;
     ClipboardCapabilities clipboardCaps;
     DomainPolicyClone domainPolicy;
-    RecvGetXPCOMProcessAttributes(&isOffline, &unusedDictionaries,
+    RecvGetXPCOMProcessAttributes(&isOffline, &isLangRTL, &unusedDictionaries,
                                   &clipboardCaps, &domainPolicy);
     mozilla::unused << content->SendSetOffline(isOffline);
     MOZ_ASSERT(!clipboardCaps.supportsSelectionClipboard() &&
                !clipboardCaps.supportsFindClipboard(),
                "Unexpected values");
 
     PreallocatedProcessManager::PublishSpareProcess(content);
     return true;
@@ -3159,25 +3161,33 @@ ContentParent::RecvGetProcessAttributes(
     *aIsForApp = IsForApp();
     *aIsForBrowser = mIsForBrowser;
 
     return true;
 }
 
 bool
 ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
+                                             bool* aIsLangRTL,
                                              InfallibleTArray<nsString>* dictionaries,
                                              ClipboardCapabilities* clipboardCaps,
                                              DomainPolicyClone* domainPolicy)
 {
     nsCOMPtr<nsIIOService> io(do_GetIOService());
     MOZ_ASSERT(io, "No IO service?");
     DebugOnly<nsresult> rv = io->GetOffline(aIsOffline);
     MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
 
+    nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();
+
+    *aIsLangRTL = false;
+    if (bidi) {
+        bidi->IsLangRTL(aIsLangRTL);
+    }
+
     nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
     MOZ_ASSERT(spellChecker, "No spell checker?");
 
     spellChecker->GetDictionaryList(dictionaries);
 
     nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
     MOZ_ASSERT(clipboard, "No clipboard?");
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -530,16 +530,17 @@ private:
     PProcessHangMonitorParent*
     AllocPProcessHangMonitorParent(Transport* aTransport,
                                    ProcessId aOtherProcess) override;
 
     virtual bool RecvGetProcessAttributes(ContentParentId* aCpId,
                                           bool* aIsForApp,
                                           bool* aIsForBrowser) override;
     virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline,
+                                               bool* aIsLangRTL,
                                                InfallibleTArray<nsString>* dictionaries,
                                                ClipboardCapabilities* clipboardCaps,
                                                DomainPolicyClone* domainPolicy)
         override;
 
     virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;
 
     virtual bool DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -451,16 +451,22 @@ child:
 
     /**
      * Notify the AudioChannelService in the child processes.
      */
     async AudioChannelNotify();
 
     async SpeakerManagerNotify();
 
+    /**
+     * Communication between the PuppetBidiKeyboard and the actual
+     * BidiKeyboard hosted by the parent
+     */
+    async BidiKeyboardNotify(bool isLangRTL);
+
     async DataStoreNotify(uint32_t aAppId, nsString aName,
                           nsString aManifestURL);
 
     /**
      * Dump this process's GC and CC logs to the provided files.
      *
      * For documentation on the other args, see dumpGCAndCCLogsToFile in
      * nsIMemoryInfoDumper.idl
@@ -611,17 +617,17 @@ parent:
      * |isForBrowser|, we're loading <browser>.  When |!isForApp &&
      * !isForBrowser|, we're probably loading <xul:browser remote>.
      *
      * Keep the return values in sync with PBrowser()!
      */
     sync GetProcessAttributes()
         returns (ContentParentId cpId, bool isForApp, bool isForBrowser);
     sync GetXPCOMProcessAttributes()
-        returns (bool isOffline, nsString[] dictionaries,
+        returns (bool isOffline, bool isLangRTL, nsString[] dictionaries,
                  ClipboardCapabilities clipboardCaps,
                  DomainPolicyClone domainPolicy);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority,
                             TabId openerTabId)
         returns (ContentParentId cpId, bool isForApp, bool isForBrowser, TabId tabId);
     sync BridgeToChildProcess(ContentParentId cpId);
new file mode 100644
--- /dev/null
+++ b/widget/PuppetBidiKeyboard.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PuppetBidiKeyboard.h"
+
+using namespace mozilla::widget;
+
+NS_IMPL_ISUPPORTS(PuppetBidiKeyboard, nsIBidiKeyboard)
+
+PuppetBidiKeyboard::PuppetBidiKeyboard() : nsIBidiKeyboard()
+{
+}
+
+PuppetBidiKeyboard::~PuppetBidiKeyboard()
+{
+}
+
+NS_IMETHODIMP
+PuppetBidiKeyboard::Reset()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetBidiKeyboard::IsLangRTL(bool* aIsRTL)
+{
+  *aIsRTL = mIsLangRTL;
+  return NS_OK;
+}
+
+void
+PuppetBidiKeyboard::SetIsLangRTL(bool aIsLangRTL)
+{
+  mIsLangRTL = aIsLangRTL;
+}
+
+NS_IMETHODIMP
+PuppetBidiKeyboard::GetHaveBidiKeyboards(bool* aResult)
+{
+  // not implemented yet
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
new file mode 100644
--- /dev/null
+++ b/widget/PuppetBidiKeyboard.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_widget_PuppetBidiKeyboard_h_
+#define mozilla_widget_PuppetBidiKeyboard_h_
+
+#include "nsIBidiKeyboard.h"
+
+namespace mozilla {
+namespace widget {
+
+class PuppetBidiKeyboard final : public nsIBidiKeyboard
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIBIDIKEYBOARD
+
+  PuppetBidiKeyboard();
+
+  void SetIsLangRTL(bool aIsLangRTL);
+
+private:
+  ~PuppetBidiKeyboard();
+
+  bool mIsLangRTL;
+};
+
+} // namespace widget
+} // namespace mozilla
+
+#endif // mozilla_widget_PuppetBidiKeyboard_h_
--- a/widget/cocoa/TextInputHandler.h
+++ b/widget/cocoa/TextInputHandler.h
@@ -999,16 +999,18 @@ private:
   void OnEndIMEComposition();
 
   // The focused IME handler.  Please note that the handler might lost the
   // actual focus by deactivating the application.  If we are active, this
   // must have the actual focused handle.
   // We cannot access to the NSInputManager during we aren't active, so, the
   // focused handler can have an IME transaction even if we are deactive.
   static IMEInputHandler* sFocusedIMEHandler;
+
+  static bool sCachedIsForRTLLangage;
 };
 
 /**
  * TextInputHandler implements the NSTextInput protocol.
  */
 class TextInputHandler : public IMEInputHandler
 {
 public:
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -16,16 +16,18 @@
 
 #include "nsChildView.h"
 #include "nsObjCExceptions.h"
 #include "nsBidiUtils.h"
 #include "nsToolkit.h"
 #include "nsCocoaUtils.h"
 #include "WidgetUtils.h"
 #include "nsPrintfCString.h"
+#include "mozilla/unused.h"
+#include "mozilla/dom/ContentParent.h"
 #include "ComplexTextInputPanel.h"
 
 using namespace mozilla;
 using namespace mozilla::widget;
 
 #ifdef PR_LOGGING
 
 PRLogModuleInfo* gLog = nullptr;
@@ -2222,16 +2224,17 @@ TextInputHandler::DoCommandBySelector(co
 
 /******************************************************************************
  *
  *  IMEInputHandler implementation (static methods)
  *
  ******************************************************************************/
 
 bool IMEInputHandler::sStaticMembersInitialized = false;
+bool IMEInputHandler::sCachedIsForRTLLangage = false;
 CFStringRef IMEInputHandler::sLatestIMEOpenedModeInputSourceID = nullptr;
 IMEInputHandler* IMEInputHandler::sFocusedIMEHandler = nullptr;
 
 // static
 void
 IMEInputHandler::InitStaticMembers()
 {
   if (sStaticMembersInitialized)
@@ -2310,16 +2313,30 @@ IMEInputHandler::OnCurrentTextInputSourc
          GetCharacters(type0), tis.IsASCIICapable() ? "- ASCII capable " : "",
          GetCharacters(is4), GetCharacters(is5),
          GetCharacters(lang0), GetCharacters(bundleID0),
          GetCharacters(is2), GetCharacters(is1), GetCharacters(is3)));
     }
     sLastTIS = newTIS;
   }
 #endif // #ifdef PR_LOGGING
+
+  /**
+   * When the direction is changed, all the children are notified.
+   * No need to treat the initial case separately because it is covered
+   * by the general case (sCachedIsForRTLLangage is initially false)
+   */
+  if (sCachedIsForRTLLangage != tis.IsForRTLLanguage()) {
+    nsTArray<dom::ContentParent*> children;
+    dom::ContentParent::GetAll(children);
+    for (uint32_t i = 0; i < children.Length(); i++) {
+      unused << children[i]->SendBidiKeyboardNotify(tis.IsForRTLLanguage());
+    }
+    sCachedIsForRTLLangage = tis.IsForRTLLanguage();
+  }
 }
 
 // static
 void
 IMEInputHandler::FlushPendingMethods(nsITimer* aTimer, void* aClosure)
 {
   NS_ASSERTION(aClosure, "aClosure is null");
   static_cast<IMEInputHandler*>(aClosure)->ExecutePendingMethods();
--- a/widget/cocoa/moz.build
+++ b/widget/cocoa/moz.build
@@ -7,16 +7,17 @@
 XPIDL_SOURCES += [
     'nsPIWidgetCocoa.idl',
 ]
 
 XPIDL_MODULE = 'widget_cocoa'
 
 EXPORTS += [
     'mozView.h',
+    'nsBidiKeyboard.h',
     'nsChangeObserver.h',
     'nsCocoaFeatures.h',
     'nsCocoaUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'ComplexTextInputPanel.mm',
     'GfxInfo.mm',
--- a/widget/cocoa/nsWidgetFactory.mm
+++ b/widget/cocoa/nsWidgetFactory.mm
@@ -130,17 +130,18 @@ static const mozilla::Module::CIDEntry k
     mozilla::Module::MAIN_PROCESS_ONLY },
   { &kNS_TRANSFERABLE_CID, false, NULL, nsTransferableConstructor },
   { &kNS_HTMLFORMATCONVERTER_CID, false, NULL, nsHTMLFormatConverterConstructor },
   { &kNS_CLIPBOARD_CID, false, NULL, nsClipboardConstructor,
     mozilla::Module::MAIN_PROCESS_ONLY },
   { &kNS_CLIPBOARDHELPER_CID, false, NULL, nsClipboardHelperConstructor },
   { &kNS_DRAGSERVICE_CID, false, NULL, nsDragServiceConstructor,
     mozilla::Module::MAIN_PROCESS_ONLY },
-  { &kNS_BIDIKEYBOARD_CID, false, NULL, nsBidiKeyboardConstructor },
+  { &kNS_BIDIKEYBOARD_CID, false, NULL, nsBidiKeyboardConstructor,
+    mozilla::Module::MAIN_PROCESS_ONLY },
   { &kNS_THEMERENDERER_CID, false, NULL, nsNativeThemeCocoaConstructor },
   { &kNS_SCREENMANAGER_CID, false, NULL, nsScreenManagerCocoaConstructor,
     mozilla::Module::MAIN_PROCESS_ONLY },
   { &kNS_DEVICE_CONTEXT_SPEC_CID, false, NULL, nsDeviceContextSpecXConstructor },
   { &kNS_PRINTSESSION_CID, false, NULL, nsPrintSessionConstructor },
   { &kNS_PRINTSETTINGSSERVICE_CID, false, NULL, nsPrintOptionsXConstructor },
   { &kNS_PRINTDIALOGSERVICE_CID, false, NULL, nsPrintDialogServiceXConstructor },
   { &kNS_IDLE_SERVICE_CID, false, NULL, nsIdleServiceXConstructor },
@@ -167,17 +168,18 @@ static const mozilla::Module::ContractID
     mozilla::Module::MAIN_PROCESS_ONLY },
   { "@mozilla.org/widget/transferable;1", &kNS_TRANSFERABLE_CID },
   { "@mozilla.org/widget/htmlformatconverter;1", &kNS_HTMLFORMATCONVERTER_CID },
   { "@mozilla.org/widget/clipboard;1", &kNS_CLIPBOARD_CID,
     mozilla::Module::MAIN_PROCESS_ONLY },
   { "@mozilla.org/widget/clipboardhelper;1", &kNS_CLIPBOARDHELPER_CID },
   { "@mozilla.org/widget/dragservice;1", &kNS_DRAGSERVICE_CID,
     mozilla::Module::MAIN_PROCESS_ONLY },
-  { "@mozilla.org/widget/bidikeyboard;1", &kNS_BIDIKEYBOARD_CID },
+  { "@mozilla.org/widget/bidikeyboard;1", &kNS_BIDIKEYBOARD_CID,
+    mozilla::Module::MAIN_PROCESS_ONLY },
   { "@mozilla.org/chrome/chrome-native-theme;1", &kNS_THEMERENDERER_CID },
   { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID,
     mozilla::Module::MAIN_PROCESS_ONLY },
   { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID },
   { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID },
   { "@mozilla.org/gfx/printsettings-service;1", &kNS_PRINTSETTINGSSERVICE_CID },
   { NS_PRINTDIALOGSERVICE_CONTRACTID, &kNS_PRINTDIALOGSERVICE_CID },
   { "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID },
--- a/widget/gtk/nsBidiKeyboard.cpp
+++ b/widget/gtk/nsBidiKeyboard.cpp
@@ -15,17 +15,20 @@ NS_IMPL_ISUPPORTS(nsBidiKeyboard, nsIBid
 nsBidiKeyboard::nsBidiKeyboard()
 {
     Reset();
 }
 
 NS_IMETHODIMP
 nsBidiKeyboard::Reset()
 {
-    mHaveBidiKeyboards = gdk_keymap_have_bidi_layouts(gdk_keymap_get_default());
+    // NB: The default keymap can be null (e.g. in xpcshell). In that case,
+    // simply assume that we don't have bidi keyboards.
+    GdkKeymap *keymap = gdk_keymap_get_default();
+    mHaveBidiKeyboards = keymap && gdk_keymap_have_bidi_layouts(keymap);
     return NS_OK;
 }
 
 nsBidiKeyboard::~nsBidiKeyboard()
 {
 }
 
 NS_IMETHODIMP
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -126,16 +126,20 @@ EXPORTS.mozilla += [
     'TextEventDispatcherListener.h',
     'TextEvents.h',
     'TextRange.h',
     'TouchEvents.h',
     'VsyncDispatcher.h',
     'WidgetUtils.h',
 ]
 
+EXPORTS.mozilla.widget += [
+    'PuppetBidiKeyboard.h',
+]
+
 UNIFIED_SOURCES += [
     'ContentHelper.cpp',
     'GfxDriverInfo.cpp',
     'GfxInfoBase.cpp',
     'GfxInfoCollector.cpp',
     'GfxInfoWebGL.cpp',
     'InputData.cpp',
     'nsBaseAppShell.cpp',
@@ -153,16 +157,17 @@ UNIFIED_SOURCES += [
     'nsPrimitiveHelpers.cpp',
     'nsPrintSession.cpp',
     'nsPrintSettingsImpl.cpp',
     'nsScreenManagerProxy.cpp',
     'nsShmImage.cpp',
     'nsTransferable.cpp',
     'nsXPLookAndFeel.cpp',
     'PluginWidgetProxy.cpp',
+    'PuppetBidiKeyboard.cpp',
     'PuppetWidget.cpp',
     'ScreenProxy.cpp',
     'SharedWidgetUtils.cpp',
     'TextEventDispatcher.cpp',
     'VsyncDispatcher.cpp',
     'WidgetEventImpl.cpp',
     'WidgetUtils.cpp',
 ]
--- a/widget/nsContentProcessWidgetFactory.cpp
+++ b/widget/nsContentProcessWidgetFactory.cpp
@@ -7,44 +7,50 @@
 
 #include "mozilla/ModuleUtils.h"
 #include "nsWidgetsCID.h"
 #include "nsClipboardProxy.h"
 #include "nsColorPickerProxy.h"
 #include "nsDragServiceProxy.h"
 #include "nsFilePickerProxy.h"
 #include "nsScreenManagerProxy.h"
+#include "mozilla/widget/PuppetBidiKeyboard.h"
 
 using namespace mozilla;
+using namespace mozilla::widget;
 
 #ifndef MOZ_B2G
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardProxy)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsColorPickerProxy)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragServiceProxy)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFilePickerProxy)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerProxy)
+NS_GENERIC_FACTORY_CONSTRUCTOR(PuppetBidiKeyboard)
 
 NS_DEFINE_NAMED_CID(NS_CLIPBOARD_CID);
 NS_DEFINE_NAMED_CID(NS_COLORPICKER_CID);
 NS_DEFINE_NAMED_CID(NS_DRAGSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_FILEPICKER_CID);
+NS_DEFINE_NAMED_CID(PUPPETBIDIKEYBOARD_CID);
 NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID);
 
 static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
     { &kNS_CLIPBOARD_CID, false, nullptr, nsClipboardProxyConstructor,
       Module::CONTENT_PROCESS_ONLY },
     { &kNS_COLORPICKER_CID, false, nullptr, nsColorPickerProxyConstructor,
       Module::CONTENT_PROCESS_ONLY },
     { &kNS_DRAGSERVICE_CID, false, nullptr, nsDragServiceProxyConstructor,
       Module::CONTENT_PROCESS_ONLY },
     { &kNS_FILEPICKER_CID, false, nullptr, nsFilePickerProxyConstructor,
       Module::CONTENT_PROCESS_ONLY },
     { &kNS_SCREENMANAGER_CID, false, nullptr, nsScreenManagerProxyConstructor,
       Module::CONTENT_PROCESS_ONLY },
+    { &kPUPPETBIDIKEYBOARD_CID, false, NULL, PuppetBidiKeyboardConstructor,
+      mozilla::Module::CONTENT_PROCESS_ONLY },
     { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
     { "@mozilla.org/widget/clipboard;1", &kNS_CLIPBOARD_CID, Module::CONTENT_PROCESS_ONLY },
     { "@mozilla.org/colorpicker;1", &kNS_COLORPICKER_CID, Module::CONTENT_PROCESS_ONLY },
     { "@mozilla.org/filepicker;1", &kNS_FILEPICKER_CID, Module::CONTENT_PROCESS_ONLY },
     { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID, Module::CONTENT_PROCESS_ONLY },
--- a/widget/nsWidgetsCID.h
+++ b/widget/nsWidgetsCID.h
@@ -104,16 +104,19 @@
 // {B148EED2-236D-11d3-B35C-00A0CC3C1CDE}
 #define NS_SOUND_CID \
 { 0xb148eed2, 0x236d, 0x11d3, { 0xb3, 0x5c, 0x0, 0xa0, 0xcc, 0x3c, 0x1c, 0xde } }
 
 // {9f1800ab-f428-4207-b40c-e832e77b01fc}
 #define NS_BIDIKEYBOARD_CID \
 { 0x9f1800ab, 0xf428, 0x4207, { 0xb4, 0x0c, 0xe8, 0x32, 0xe7, 0x7b, 0x01, 0xfc } }
 
+#define PUPPETBIDIKEYBOARD_CID \
+{ 0x689e2586, 0x0344, 0x40b2, {0x83, 0x75, 0x13, 0x67, 0x2d, 0x3b, 0x71, 0x9a } }
+
 #define NS_SCREENMANAGER_CID \
 { 0xc401eb80, 0xf9ea, 0x11d3, { 0xbb, 0x6f, 0xe7, 0x32, 0xb7, 0x3e, 0xbe, 0x7c } }
 
 // {6987230e-0089-4e78-bc5f-1493ee7519fa}
 #define NS_IDLE_SERVICE_CID \
 { 0x6987230e, 0x0098, 0x4e78, { 0xbc, 0x5f, 0x14, 0x93, 0xee, 0x75, 0x19, 0xfa } }
 
 #define NS_WIN_TASKBAR_CID \