diff --git a/browser/app/profile/palemoon.js b/browser/app/profile/palemoon.js index 4be883a2cf..5cbf03d55f 100644 --- a/browser/app/profile/palemoon.js +++ b/browser/app/profile/palemoon.js @@ -918,6 +918,8 @@ pref("browser.tabs.remote", false); // This will require a restart. pref("security.sandbox.windows.log", false); +pref("dom.ipc.plugins.sandbox-level.flash", 0); + #if defined(MOZ_CONTENT_SANDBOX) // This controls the strength of the Windows content process sandbox for testing // purposes. This will require a restart. diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 405605a184..4a919fa6fc 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -4807,6 +4807,7 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x, if (!sw || !sh) { return; } + nsRefPtr thebes; RefPtr drawDT; // Rendering directly is faster and can be done if mTarget supports Azure @@ -4961,6 +4962,51 @@ CanvasRenderingContext2D::AsyncDrawXULElement(nsXULElement& elem, #endif } +void +CanvasRenderingContext2D::DrawWidgetAsOnScreen(nsGlobalWindow& aWindow, + mozilla::ErrorResult& error) +{ + EnsureTarget(); + + // This is an internal API. + if (!nsContentUtils::IsCallerChrome()) { + error.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + + nsRefPtr presContext; + nsIDocShell* docshell = aWindow.GetDocShell(); + if (docshell) { + docshell->GetPresContext(getter_AddRefs(presContext)); + } + if (!presContext) { + error.Throw(NS_ERROR_FAILURE); + return; + } + + nsIWidget* widget = presContext->GetRootWidget(); + if (!widget) { + error.Throw(NS_ERROR_FAILURE); + return; + } + RefPtr snapshot = widget->SnapshotWidgetOnScreen(); + if (!snapshot) { + error.Throw(NS_ERROR_FAILURE); + return; + } + + mgfx::Rect sourceRect(mgfx::Point(0, 0), mgfx::Size(snapshot->GetSize())); + mTarget->DrawSurface(snapshot, sourceRect, sourceRect, + DrawSurfaceOptions(mgfx::Filter::POINT), + DrawOptions(GlobalAlpha(), CompositionOp::OP_OVER, + AntialiasMode::NONE)); + mTarget->Flush(); + + RedrawUser(gfxRect(0, 0, + std::min(mWidth, snapshot->GetSize().width), + std::min(mHeight, snapshot->GetSize().height))); +} + // // device pixel getting/setting // diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index 466fa50ba7..4be8a8445f 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -471,6 +471,7 @@ public: void DrawWindow(nsGlobalWindow& window, double x, double y, double w, double h, const nsAString& bgColor, uint32_t flags, mozilla::ErrorResult& error); + void DrawWidgetAsOnScreen(nsGlobalWindow& aWindow, mozilla::ErrorResult& error); void AsyncDrawXULElement(nsXULElement& elem, double x, double y, double w, double h, const nsAString& bgColor, uint32_t flags, mozilla::ErrorResult& error); diff --git a/dom/ipc/PPluginWidget.ipdl b/dom/ipc/PPluginWidget.ipdl index 174e83cdf2..4be20e4b61 100644 --- a/dom/ipc/PPluginWidget.ipdl +++ b/dom/ipc/PPluginWidget.ipdl @@ -41,6 +41,12 @@ parent: * native HWND of the plugin widget. */ sync GetNativePluginPort() returns (uintptr_t value); + + /** + * Sends an NS_NATIVE_CHILD_WINDOW to be adopted by the widget's native window + * on the chrome side. This is only currently used on Windows. + */ + sync SetNativeChildWindow(uintptr_t childWindow); }; } diff --git a/dom/plugins/base/nsPluginNativeWindow.h b/dom/plugins/base/nsPluginNativeWindow.h index 451094b2da..4c00ffd1e4 100644 --- a/dom/plugins/base/nsPluginNativeWindow.h +++ b/dom/plugins/base/nsPluginNativeWindow.h @@ -49,7 +49,7 @@ public: return NS_OK; } - nsresult GetPluginWidget(nsIWidget **aWidget) { + nsresult GetPluginWidget(nsIWidget **aWidget) const { NS_IF_ADDREF(*aWidget = mWidget); return NS_OK; } diff --git a/dom/plugins/ipc/PPluginInstance.ipdl b/dom/plugins/ipc/PPluginInstance.ipdl index cd608eca20..3a4a3d521d 100644 --- a/dom/plugins/ipc/PPluginInstance.ipdl +++ b/dom/plugins/ipc/PPluginInstance.ipdl @@ -68,7 +68,10 @@ intr protocol PPluginInstance child: intr __delete__(); - intr NPP_SetWindow(NPRemoteWindow window); + // Return value is only used on Windows and only when the window needs its + // parent set to the chrome widget native window. + intr NPP_SetWindow(NPRemoteWindow window) + returns (NPRemoteWindow childWindowToBeAdopted); intr NPP_GetValue_NPPVpluginWantsAllNetworkStreams() returns (bool value, NPError result); diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index 1a42ba183b..6d28027f12 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -1122,7 +1122,8 @@ void PluginInstanceChild::DeleteWindow() #endif bool -PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow) +PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow, + NPRemoteWindow* aChildWindowToBeAdopted) { PLUGIN_LOG_DEBUG(("%s (aWindow=)", FULLFUNCTION, @@ -1213,9 +1214,32 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow) if (!CreatePluginWindow()) return false; - ReparentPluginWindow(reinterpret_cast(aWindow.window)); SizePluginWindow(aWindow.width, aWindow.height); + // If the window is not our parent set the return child window so that + // it can be re-parented in the chrome process. Re-parenting now + // happens there as we might not have sufficient permission. + // Also, this needs to be after SizePluginWindow because SetWindowPos + // relies on things that it sets. + HWND parentWindow = reinterpret_cast(aWindow.window); + if (mPluginParentHWND != parentWindow && IsWindow(parentWindow)) { + mPluginParentHWND = parentWindow; + aChildWindowToBeAdopted->window = + reinterpret_cast(mPluginWindowHWND); + } else { + // Now we know that the window has the correct parent we can show + // it. The actual visibility is controlled by its parent. + // First time round, these calls are made by our caller after the + // parent is set. + ShowWindow(mPluginWindowHWND, SW_SHOWNA); + + // This used to be called in SizePluginWindow, but we need to make + // sure that mPluginWindowHWND has had it's parent set correctly, + // otherwise it can cause a focus issue. + SetWindowPos(mPluginWindowHWND, nullptr, 0, 0, aWindow.width, + aWindow.height, SWP_NOZORDER | SWP_NOREPOSITION); + } + mWindow.window = (void*)mPluginWindowHWND; mWindow.x = aWindow.x; mWindow.y = aWindow.y; @@ -1401,25 +1425,6 @@ PluginInstanceChild::DestroyPluginWindow() } } -void -PluginInstanceChild::ReparentPluginWindow(HWND hWndParent) -{ - if (hWndParent != mPluginParentHWND && IsWindow(hWndParent)) { - // Fix the child window's style to be a child window. - LONG_PTR style = GetWindowLongPtr(mPluginWindowHWND, GWL_STYLE); - style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - style &= ~WS_POPUP; - SetWindowLongPtr(mPluginWindowHWND, GWL_STYLE, style); - - // Do the reparenting. - SetParent(mPluginWindowHWND, hWndParent); - - // Make sure we're visible. - ShowWindow(mPluginWindowHWND, SW_SHOWNA); - } - mPluginParentHWND = hWndParent; -} - void PluginInstanceChild::SizePluginWindow(int width, int height) @@ -1427,8 +1432,6 @@ PluginInstanceChild::SizePluginWindow(int width, if (mPluginWindowHWND) { mPluginSize.x = width; mPluginSize.y = height; - SetWindowPos(mPluginWindowHWND, nullptr, 0, 0, width, height, - SWP_NOZORDER | SWP_NOREPOSITION); } } diff --git a/dom/plugins/ipc/PluginInstanceChild.h b/dom/plugins/ipc/PluginInstanceChild.h index 7b1dab1e4d..b4dc88779b 100644 --- a/dom/plugins/ipc/PluginInstanceChild.h +++ b/dom/plugins/ipc/PluginInstanceChild.h @@ -65,7 +65,8 @@ class PluginInstanceChild : public PPluginInstanceChild #endif protected: - virtual bool AnswerNPP_SetWindow(const NPRemoteWindow& window) override; + bool AnswerNPP_SetWindow(const NPRemoteWindow& window, + NPRemoteWindow* aChildWindowToBeAdopted) override; virtual bool AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(bool* wantsAllStreams, NPError* rv) override; @@ -276,7 +277,6 @@ private: static bool RegisterWindowClass(); bool CreatePluginWindow(); void DestroyPluginWindow(); - void ReparentPluginWindow(HWND hWndParent); void SizePluginWindow(int width, int height); int16_t WinlessHandleEvent(NPEvent& event); void CreateWinlessPopupSurrogate(); diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index a2935e85ed..142c0190b6 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -49,6 +49,8 @@ #include "mozilla/plugins/PluginSurfaceParent.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" +#include "nsIWidget.h" +#include "nsPluginNativeWindow.h" extern const wchar_t* kFlashFullscreenClass; #elif defined(MOZ_WIDGET_GTK) #include @@ -1025,8 +1027,30 @@ PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow) window.colormap = ws_info->colormap; #endif - if (!CallNPP_SetWindow(window)) + NPRemoteWindow childWindow; + if (!CallNPP_SetWindow(window, &childWindow)) { return NPERR_GENERIC_ERROR; + } + +#if defined(XP_WIN) + // If a child window is returned it means that we need to re-parent it. + if (childWindow.window) { + nsCOMPtr widget; + static_cast(aWindow)-> + GetPluginWidget(getter_AddRefs(widget)); + if (widget) { + widget->SetNativeData(NS_NATIVE_CHILD_WINDOW, + static_cast(childWindow.window)); + } + + // Now it has got the correct parent, make sure it is visible. + // In subsequent calls to SetWindow these calls happen in the Child. + HWND childHWND = reinterpret_cast(childWindow.window); + ShowWindow(childHWND, SW_SHOWNA); + SetWindowPos(childHWND, nullptr, 0, 0, window.width, window.height, + SWP_NOZORDER | SWP_NOREPOSITION); + } +#endif return NPERR_NO_ERROR; } diff --git a/dom/plugins/ipc/PluginWidgetParent.cpp b/dom/plugins/ipc/PluginWidgetParent.cpp index 7ba0e4be76..e3f1298378 100644 --- a/dom/plugins/ipc/PluginWidgetParent.cpp +++ b/dom/plugins/ipc/PluginWidgetParent.cpp @@ -210,5 +210,20 @@ PluginWidgetParent::RecvGetNativePluginPort(uintptr_t* value) return true; } +bool +PluginWidgetParent::RecvSetNativeChildWindow(const uintptr_t& aChildWindow) +{ +#if defined(XP_WIN) + ENSURE_CHANNEL; + PWLOG("PluginWidgetParent::RecvSetNativeChildWindow(%p)\n", + static_cast(aChildWindow)); + mWidget->SetNativeData(NS_NATIVE_CHILD_WINDOW, aChildWindow); + return true; +#else + NS_NOTREACHED("PluginWidgetParent::RecvSetNativeChildWindow not implemented!"); + return false; +#endif +} + } // namespace plugins } // namespace mozilla diff --git a/dom/plugins/ipc/PluginWidgetParent.h b/dom/plugins/ipc/PluginWidgetParent.h index 21adc56cb7..121e886876 100644 --- a/dom/plugins/ipc/PluginWidgetParent.h +++ b/dom/plugins/ipc/PluginWidgetParent.h @@ -31,6 +31,7 @@ public: virtual bool RecvCreate(nsresult* aResult) override; virtual bool RecvSetFocus(const bool& aRaise) override; virtual bool RecvGetNativePluginPort(uintptr_t* value) override; + bool RecvSetNativeChildWindow(const uintptr_t& aChildWindow) override; // Helper for compositor checks on the channel bool ActorDestroyed() { return !mWidget; } diff --git a/dom/webidl/CanvasRenderingContext2D.webidl b/dom/webidl/CanvasRenderingContext2D.webidl index b933cd3b9b..781124a432 100644 --- a/dom/webidl/CanvasRenderingContext2D.webidl +++ b/dom/webidl/CanvasRenderingContext2D.webidl @@ -223,6 +223,18 @@ interface CanvasRenderingContext2D { void asyncDrawXULElement(XULElement elem, double x, double y, double w, double h, DOMString bgColor, optional unsigned long flags = 0); + + /** + * Render the root widget of a window into the canvas. Unlike drawWindow, + * this uses the operating system to snapshot the widget on-screen, rather + * than reading from our own compositor. + * + * Currently, this is only supported on Windows, and only on widgets that + * use OMTC, and only from within the chrome process. + */ + [Throws, ChromeOnly] + void drawWidgetAsOnScreen(Window window); + /** * This causes a context that is currently using a hardware-accelerated * backend to fallback to a software one. All state should be preserved. diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index f9d078fe47..713388ae40 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -804,6 +804,18 @@ CompositorParent::RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, return true; } +bool +CompositorParent::RecvMakeWidgetSnapshot(const SurfaceDescriptor& aInSnapshot) +{ + if (!mCompositor || !mCompositor->GetWidget()) { + return false; + } + + RefPtr target = GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO); + mCompositor->GetWidget()->CaptureWidgetOnScreen(target); + return true; +} + bool CompositorParent::RecvFlushRendering() { @@ -1700,6 +1712,8 @@ public: virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, const gfx::IntRect& aRect) override { return true; } + virtual bool RecvMakeWidgetSnapshot(const SurfaceDescriptor& aInSnapshot) override + { return true; } virtual bool RecvFlushRendering() override { return true; } virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override { return true; } virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override { return true; } diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index 09c4ce4986..801c4b3739 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -241,6 +241,7 @@ public: virtual bool RecvAdoptChild(const uint64_t& child) override; virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, const gfx::IntRect& aRect) override; + virtual bool RecvMakeWidgetSnapshot(const SurfaceDescriptor& aInSnapshot) override; virtual bool RecvFlushRendering() override; virtual bool RecvGetTileSize(int32_t* aWidth, int32_t* aHeight) override; diff --git a/gfx/layers/ipc/PCompositor.ipdl b/gfx/layers/ipc/PCompositor.ipdl index 6029391788..c89e88a97e 100644 --- a/gfx/layers/ipc/PCompositor.ipdl +++ b/gfx/layers/ipc/PCompositor.ipdl @@ -115,6 +115,14 @@ parent: // and so forth being interpolated. That's what we want to happen. sync MakeSnapshot(SurfaceDescriptor inSnapshot, IntRect dirtyRect); + // Same as Makesnapshot(), except the snapshot is read from the underlying + // operating system desktop rather than the compositor's backbuffer. This + // is intended for testing whether hardware acceleration works. + // + // This call is part of IPDL, even though it simply wraps an nsIWidget + // call, to make sure it does not occur in the middle of a composite. + sync MakeWidgetSnapshot(SurfaceDescriptor inSnapshot); + // Make sure any pending composites are started immediately and // block until they are completed. sync FlushRendering(); diff --git a/layout/generic/test/chrome.ini b/layout/generic/test/chrome.ini index 2494d1d288..25af1ee2b9 100644 --- a/layout/generic/test/chrome.ini +++ b/layout/generic/test/chrome.ini @@ -7,6 +7,7 @@ support-files = frame_selection_underline.xhtml [test_backspace_delete.xul] +skip-if = true # Bug 1163311 [test_bug348681.html] [test_bug469613.xul] [test_bug469774.xul] diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index ce5831217c..a100462336 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3403,6 +3403,13 @@ pref("intl.imm.composition_font.japanist_2003", "MS PGothic"); // FYI: Changing this pref requires to restart. pref("intl.imm.vertical_writing.always_assume_not_supported", false); +// We cannot retrieve active IME name with IMM32 API if a TIP of TSF is active. +// This pref can specify active IME name when Japanese TIP is active. +// For example: +// Google Japanese Input: "Google 日本語入力 IMM32 モジュール" +// ATOK 2011: "ATOK 2011" (similarly, e.g., ATOK 2013 is "ATOK 2013") +pref("intl.imm.japanese.assume_active_tip_name_as", ""); + // See bug 448927, on topmost panel, some IMEs are not usable on Windows. pref("ui.panel.default_level_parent", false); diff --git a/widget/PluginWidgetProxy.cpp b/widget/PluginWidgetProxy.cpp index a2c1009884..dd17eeda34 100644 --- a/widget/PluginWidgetProxy.cpp +++ b/widget/PluginWidgetProxy.cpp @@ -141,6 +141,29 @@ PluginWidgetProxy::GetNativeData(uint32_t aDataType) return (void*)value; } +#if defined(XP_WIN) +void +PluginWidgetProxy::SetNativeData(uint32_t aDataType, uintptr_t aVal) +{ + if (!mActor) { + return; + } + + auto tab = static_cast(mActor->Manager()); + if (tab && tab->IsDestroyed()) { + return; + } + + switch (aDataType) { + case NS_NATIVE_CHILD_WINDOW: + mActor->SendSetNativeChildWindow(aVal); + break; + default: + NS_ERROR("SetNativeData called with unsupported data type."); + } +} +#endif + NS_IMETHODIMP PluginWidgetProxy::SetFocus(bool aRaise) { diff --git a/widget/PluginWidgetProxy.h b/widget/PluginWidgetProxy.h index 428b75bd0f..5935cce0dc 100644 --- a/widget/PluginWidgetProxy.h +++ b/widget/PluginWidgetProxy.h @@ -43,6 +43,9 @@ public: virtual nsIWidget* GetParent(void) override; virtual void* GetNativeData(uint32_t aDataType) override; +#if defined(XP_WIN) + void SetNativeData(uint32_t aDataType, uintptr_t aVal) override; +#endif virtual nsTransparencyMode GetTransparencyMode() override { return eTransparencyOpaque; } virtual void GetWindowClipRegion(nsTArray* aRects) override; diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index dda3bcad19..563bad2415 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -227,7 +227,7 @@ PuppetWidget::ConfigureChildren(const nsTArray& aConfigurations) { for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { const Configuration& configuration = aConfigurations[i]; - PuppetWidget* w = static_cast(configuration.mChild); + PuppetWidget* w = static_cast(configuration.mChild.get()); NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); w->SetWindowClipRegion(configuration.mClipRegion, true); diff --git a/widget/gtk/nsGtkIMModule.cpp b/widget/gtk/nsGtkIMModule.cpp index 3003316ef9..a8479a182c 100644 --- a/widget/gtk/nsGtkIMModule.cpp +++ b/widget/gtk/nsGtkIMModule.cpp @@ -56,8 +56,30 @@ GetEnabledStateName(uint32_t aState) } } +class GetWritingModeName : public nsAutoCString +{ +public: + explicit GetWritingModeName(const WritingMode& aWritingMode) + { + if (!aWritingMode.IsVertical()) { + AssignLiteral("Horizontal"); + return; + } + if (aWritingMode.IsVerticalLR()) { + AssignLiteral("Vertical (LTR)"); + return; + } + AssignLiteral("Vertical (RTL)"); + } + virtual ~GetWritingModeName() {} +}; + const static bool kUseSimpleContextDefault = MOZ_WIDGET_GTK == 2; +/****************************************************************************** + * nsGtkIMModule + ******************************************************************************/ + nsGtkIMModule* nsGtkIMModule::sLastFocusedModule = nullptr; bool nsGtkIMModule::sUseSimpleContext; @@ -74,6 +96,7 @@ nsGtkIMModule::nsGtkIMModule(nsWindow* aOwnerWindow) , mCompositionState(eCompositionState_NotComposing) , mIsIMFocused(false) , mIsDeletingSurrounding(false) + , mLayoutChanged(false) { if (!gGtkIMLog) { gGtkIMLog = PR_NewLogModule("nsGtkIMModuleWidgets"); @@ -396,6 +419,7 @@ nsGtkIMModule::OnFocusChangeInGecko(bool aFocus) // We shouldn't carry over the removed string to another editor. mSelectedString.Truncate(); + mSelection.Clear(); } void @@ -452,13 +476,28 @@ nsGtkIMModule::EndIMEComposition(nsWindow* aCaller) } void -nsGtkIMModule::OnUpdateComposition() +nsGtkIMModule::OnLayoutChange() { if (MOZ_UNLIKELY(IsDestroyed())) { return; } SetCursorPosition(GetActiveContext(), mCompositionTargetOffset); + mLayoutChanged = true; +} + +void +nsGtkIMModule::OnUpdateComposition() +{ + if (MOZ_UNLIKELY(IsDestroyed())) { + return; + } + + // If we've already set candidate window position, we don't need to update + // the position with update composition notification. + if (!mLayoutChanged) { + SetCursorPosition(GetActiveContext(), mCompositionTargetOffset); + } } void @@ -660,8 +699,11 @@ nsGtkIMModule::Blur() } void -nsGtkIMModule::OnSelectionChange(nsWindow* aCaller) +nsGtkIMModule::OnSelectionChange(nsWindow* aCaller, + const IMENotification& aIMENotification) { + mSelection.Assign(aIMENotification); + if (MOZ_UNLIKELY(IsDestroyed())) { return; } @@ -685,42 +727,13 @@ nsGtkIMModule::OnSelectionChange(nsWindow* aCaller) // event handler. So, we're dispatching NS_COMPOSITION_START, // we should ignore selection change notification. if (mCompositionState == eCompositionState_CompositionStartDispatched) { - nsCOMPtr focusedWindow(mLastFocusedWindow); - nsEventStatus status; - WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, - focusedWindow); - InitEvent(selection); - mLastFocusedWindow->DispatchEvent(&selection, status); - - bool cannotContinueComposition = false; - if (MOZ_UNLIKELY(IsDestroyed())) { - PR_LOG(gGtkIMLog, PR_LOG_ALWAYS, - (" ERROR: nsGtkIMModule instance is destroyed during " - "querying selection offset")); - return; - } else if (NS_WARN_IF(!selection.mSucceeded)) { - cannotContinueComposition = true; - PR_LOG(gGtkIMLog, PR_LOG_ALWAYS, - (" ERROR: failed to retrieve new caret offset")); - } else if (selection.mReply.mOffset == UINT32_MAX) { - cannotContinueComposition = true; + if (NS_WARN_IF(!mSelection.IsValid())) { PR_LOG(gGtkIMLog, PR_LOG_ALWAYS, (" ERROR: new offset is too large, cannot keep composing")); - } else if (!mLastFocusedWindow || focusedWindow != mLastFocusedWindow) { - cannotContinueComposition = true; - PR_LOG(gGtkIMLog, PR_LOG_ALWAYS, - (" ERROR: focus is changed during querying selection " - "offset")); - } else if (focusedWindow->Destroyed()) { - cannotContinueComposition = true; - PR_LOG(gGtkIMLog, PR_LOG_ALWAYS, - (" ERROR: focused window started to be being destroyed " - "during querying selection offset")); - } - - if (!cannotContinueComposition) { + } else { // Modify the selection start offset with new offset. - mCompositionStart = selection.mReply.mOffset; + mCompositionStart = mSelection.mOffset; + // XXX We should modify mSelectedString? But how? PR_LOG(gGtkIMLog, PR_LOG_ALWAYS, (" NOTE: mCompositionStart is updated to %u, " "the selection change doesn't cause resetting IM context", @@ -1023,13 +1036,7 @@ nsGtkIMModule::DispatchCompositionStart(GtkIMContext* aContext) return false; } - nsEventStatus status; - WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, - mLastFocusedWindow); - InitEvent(selection); - mLastFocusedWindow->DispatchEvent(&selection, status); - - if (!selection.mSucceeded || selection.mReply.mOffset == UINT32_MAX) { + if (NS_WARN_IF(!EnsureToCacheSelection())) { PR_LOG(gGtkIMLog, PR_LOG_ALWAYS, (" FAILED, cannot query the selection offset")); return false; @@ -1039,7 +1046,7 @@ nsGtkIMModule::DispatchCompositionStart(GtkIMContext* aContext) // even though we strongly hope it doesn't happen. // Every composition event should have the start offset for the result // because it may high cost if we query the offset every time. - mCompositionStart = selection.mReply.mOffset; + mCompositionStart = mSelection.mOffset; mDispatchedCompositionString.Truncate(); if (mProcessingKeyEvent && !mKeyDownEventWasSent && @@ -1067,6 +1074,7 @@ nsGtkIMModule::DispatchCompositionStart(GtkIMContext* aContext) mLastFocusedWindow); InitEvent(compEvent); nsCOMPtr kungFuDeathGrip = mLastFocusedWindow; + nsEventStatus status; mLastFocusedWindow->DispatchEvent(&compEvent, status); if (static_cast(kungFuDeathGrip.get())->IsDestroyed() || kungFuDeathGrip != mLastFocusedWindow) { @@ -1108,15 +1116,12 @@ nsGtkIMModule::DispatchCompositionChangeEvent( // Store the selected string which will be removed by following // compositionchange event. if (mCompositionState == eCompositionState_CompositionStartDispatched) { - // XXX We should assume, for now, any web applications don't change - // selection at handling this compositionchange event. - WidgetQueryContentEvent querySelectedTextEvent(true, - NS_QUERY_SELECTED_TEXT, - mLastFocusedWindow); - mLastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status); - if (querySelectedTextEvent.mSucceeded) { - mSelectedString = querySelectedTextEvent.mReply.mString; - mCompositionStart = querySelectedTextEvent.mReply.mOffset; + if (NS_WARN_IF(!EnsureToCacheSelection(&mSelectedString))) { + // XXX How should we behave in this case?? + } else { + // XXX We should assume, for now, any web applications don't change + // selection at handling this compositionchange event. + mCompositionStart = mSelection.mOffset; } } @@ -1135,6 +1140,14 @@ nsGtkIMModule::DispatchCompositionChangeEvent( mCompositionState = eCompositionState_CompositionChangeEventDispatched; + // We cannot call SetCursorPosition for e10s-aware. + // DispatchEvent is async on e10s, so composition rect isn't updated now + // on tab parent. + mLayoutChanged = false; + mCompositionTargetRange.mOffset = targetOffset; + mCompositionTargetRange.mLength = + compositionChangeEvent.mRanges->TargetClauseLength(); + mLastFocusedWindow->DispatchEvent(&compositionChangeEvent, status); if (lastFocusedWindow->IsDestroyed() || lastFocusedWindow != mLastFocusedWindow) { @@ -1143,12 +1156,6 @@ nsGtkIMModule::DispatchCompositionChangeEvent( "compositionchange event")); return false; } - - // We cannot call SetCursorPosition for e10s-aware. - // DispatchEvent is async on e10s, so composition rect isn't updated now - // on tab parent. - mCompositionTargetOffset = targetOffset; - return true; } @@ -1424,14 +1431,14 @@ nsGtkIMModule::GetCurrentParagraph(nsAString& aText, uint32_t& aCursorPos) // current selection. if (!EditorHasCompositionString()) { // Query cursor position & selection - WidgetQueryContentEvent querySelectedTextEvent(true, - NS_QUERY_SELECTED_TEXT, - mLastFocusedWindow); - mLastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status); - NS_ENSURE_TRUE(querySelectedTextEvent.mSucceeded, NS_ERROR_FAILURE); + if (NS_WARN_IF(!EnsureToCacheSelection())) { + PR_LOG(gGtkIMLog, PR_LOG_ALWAYS, + (" FAILED, due to no valid selection information")); + return NS_ERROR_FAILURE; + } - selOffset = querySelectedTextEvent.mReply.mOffset; - selLength = querySelectedTextEvent.mReply.mString.Length(); + selOffset = mSelection.mOffset; + selLength = mSelection.mLength; } PR_LOG(gGtkIMLog, PR_LOG_ALWAYS, @@ -1529,14 +1536,12 @@ nsGtkIMModule::DeleteText(GtkIMContext* aContext, return NS_ERROR_FAILURE; } } else { - // Query cursor position & selection - WidgetQueryContentEvent querySelectedTextEvent(true, - NS_QUERY_SELECTED_TEXT, - mLastFocusedWindow); - lastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status); - NS_ENSURE_TRUE(querySelectedTextEvent.mSucceeded, NS_ERROR_FAILURE); - - selOffset = querySelectedTextEvent.mReply.mOffset; + if (NS_WARN_IF(!EnsureToCacheSelection())) { + PR_LOG(gGtkIMLog, PR_LOG_ALWAYS, + (" FAILED, due to no valid selection information")); + return NS_ERROR_FAILURE; + } + selOffset = mSelection.mOffset; } // Get all text contents of the focused editor @@ -1659,3 +1664,77 @@ nsGtkIMModule::InitEvent(WidgetGUIEvent& aEvent) { aEvent.time = PR_Now() / 1000; } + +bool +nsGtkIMModule::EnsureToCacheSelection(nsAString* aSelectedString) +{ + if (aSelectedString) { + aSelectedString->Truncate(); + } + + if (mSelection.IsValid() && + (!mSelection.Collapsed() || !aSelectedString)) { + return true; + } + + if (NS_WARN_IF(!mLastFocusedWindow)) { + PR_LOG(gGtkIMLog, PR_LOG_ERROR, + ("GtkIMModule(%p): EnsureToCacheSelection(), FAILED, due to " + "no focused window", this)); + return false; + } + + nsEventStatus status; + WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, + mLastFocusedWindow); + InitEvent(selection); + mLastFocusedWindow->DispatchEvent(&selection, status); + if (NS_WARN_IF(!selection.mSucceeded)) { + PR_LOG(gGtkIMLog, PR_LOG_ERROR, + ("GtkIMModule(%p): EnsureToCacheSelection(), FAILED, due to " + "failure of query selection event", this)); + return false; + } + + mSelection.Assign(selection); + if (!mSelection.IsValid()) { + PR_LOG(gGtkIMLog, PR_LOG_ERROR, + ("GtkIMModule(%p): EnsureToCacheSelection(), FAILED, due to " + "failure of query selection event (invalid result)", this)); + return false; + } + + if (!mSelection.Collapsed() && aSelectedString) { + aSelectedString->Assign(selection.mReply.mString); + } + + PR_LOG(gGtkIMLog, PR_LOG_DEBUG, + ("GtkIMModule(%p): EnsureToCacheSelection(), Succeeded, mSelection=" + "{ mOffset=%u, mLength=%u, mWritingMode=%s }", + this, mSelection.mOffset, mSelection.mLength, + GetWritingModeName(mSelection.mWritingMode).get())); + return true; +} + +/****************************************************************************** + * nsGtkIMModule::Selection + ******************************************************************************/ + +void +nsGtkIMModule::Selection::Assign(const IMENotification& aIMENotification) +{ + MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE); + mOffset = aIMENotification.mSelectionChangeData.mOffset; + mLength = aIMENotification.mSelectionChangeData.mLength; + mWritingMode = aIMENotification.mSelectionChangeData.GetWritingMode(); +} + +void +nsGtkIMModule::Selection::Assign(const WidgetQueryContentEvent& aEvent) +{ + MOZ_ASSERT(aEvent.message == NS_QUERY_SELECTED_TEXT); + MOZ_ASSERT(aEvent.mSucceeded); + mOffset = aEvent.mReply.mOffset; + mLength = aEvent.mReply.mString.Length(); + mWritingMode = aEvent.GetWritingMode(); +} diff --git a/widget/gtk/nsGtkIMModule.h b/widget/gtk/nsGtkIMModule.h index c3a688f1df..1636ef227d 100644 --- a/widget/gtk/nsGtkIMModule.h +++ b/widget/gtk/nsGtkIMModule.h @@ -16,13 +16,16 @@ #include "nsCOMPtr.h" #include "nsTArray.h" #include "nsIWidget.h" +#include "mozilla/CheckedInt.h" #include "mozilla/EventForwards.h" +#include "WritingModes.h" class nsWindow; class nsGtkIMModule { protected: + typedef mozilla::widget::IMENotification IMENotification; typedef mozilla::widget::InputContext InputContext; typedef mozilla::widget::InputContextAction InputContextAction; @@ -48,7 +51,8 @@ public: void OnFocusChangeInGecko(bool aFocus); // OnSelectionChange is a notification that selection (caret) is changed // in the focused editor. - void OnSelectionChange(nsWindow* aCaller); + void OnSelectionChange(nsWindow* aCaller, + const IMENotification& aIMENotification); // OnKeyEvent is called when aWindow gets a native key press event or a // native key release event. If this returns TRUE, the key event was @@ -65,6 +69,7 @@ public: const InputContextAction* aAction); InputContext GetInputContext(); void OnUpdateComposition(); + void OnLayoutChange(); protected: ~nsGtkIMModule(); @@ -167,6 +172,44 @@ protected: } } + struct Selection final + { + uint32_t mOffset; + uint32_t mLength; + mozilla::WritingMode mWritingMode; + + Selection() + : mOffset(UINT32_MAX) + , mLength(UINT32_MAX) + { + } + + void Clear() + { + mOffset = UINT32_MAX; + mLength = UINT32_MAX; + mWritingMode = mozilla::WritingMode(); + } + + void Assign(const IMENotification& aIMENotification); + void Assign(const mozilla::WidgetQueryContentEvent& aSelectedTextEvent); + + bool IsValid() const { return mOffset != UINT32_MAX; } + bool Collapsed() const { return !mLength; } + uint32_t EndOffset() const + { + if (NS_WARN_IF(!IsValid())) { + return UINT32_MAX; + } + mozilla::CheckedInt endOffset = + mozilla::CheckedInt(mOffset) + mLength; + if (NS_WARN_IF(!endOffset.isValid())) { + return UINT32_MAX; + } + return endOffset.value(); + } + } mSelection; + bool EnsureToCacheSelection(nsAString* aSelectedString = nullptr); // mIsIMFocused is set to TRUE when we call gtk_im_context_focus_in(). And // it's set to FALSE when we call gtk_im_context_focus_out(). @@ -184,6 +227,9 @@ protected: // mIsDeletingSurrounding is true while OnDeleteSurroundingNative() is // trying to delete the surrounding text. bool mIsDeletingSurrounding; + // mLayoutChanged is true after OnLayoutChange() is called. This is reset + // when NS_COMPOSITION_CHANGE is being dispatched. + bool mLayoutChanged; // sLastFocusedModule is a pointer to the last focused instance of this // class. When a instance is destroyed and sLastFocusedModule refers it, diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 2b250342b2..9541069887 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -4159,7 +4159,7 @@ nsWindow::ConfigureChildren(const nsTArray& aConfigurations) for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { const Configuration& configuration = aConfigurations[i]; - nsWindow* w = static_cast(configuration.mChild); + nsWindow* w = static_cast(configuration.mChild.get()); NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); w->SetWindowClipRegion(configuration.mClipRegion, true); @@ -6027,11 +6027,14 @@ nsWindow::NotifyIMEInternal(const IMENotification& aIMENotification) case NOTIFY_IME_OF_BLUR: mIMModule->OnFocusChangeInGecko(false); return NS_OK; + case NOTIFY_IME_OF_POSITION_CHANGE: + mIMModule->OnLayoutChange(); + return NS_OK; case NOTIFY_IME_OF_COMPOSITION_UPDATE: mIMModule->OnUpdateComposition(); return NS_OK; case NOTIFY_IME_OF_SELECTION_CHANGE: - mIMModule->OnSelectionChange(this); + mIMModule->OnSelectionChange(this, aIMENotification); return NS_OK; default: return NS_ERROR_NOT_IMPLEMENTED; @@ -6070,7 +6073,8 @@ nsIMEUpdatePreference nsWindow::GetIMEUpdatePreference() { nsIMEUpdatePreference updatePreference( - nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE); + nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE | + nsIMEUpdatePreference::NOTIFY_POSITION_CHANGE); // We shouldn't notify IME of selection change caused by changes of // composition string. Therefore, we don't need to be notified selection // changes which are caused by compositionchange events handled. diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index d25ed7be79..65b7d4d0af 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -1930,6 +1930,60 @@ nsIWidget::UpdateRegisteredPluginWindowVisibility(nsTArray& aVisibleL #endif } +already_AddRefed +nsIWidget::SnapshotWidgetOnScreen() +{ + // This is only supported on a widget with a compositor. + LayerManager* layerManager = GetLayerManager(); + if (!layerManager) { + return nullptr; + } + + ClientLayerManager* lm = layerManager->AsClientLayerManager(); + if (!lm) { + return nullptr; + } + + CompositorChild* cc = lm->GetRemoteRenderer(); + if (!cc) { + return nullptr; + } + + nsIntRect bounds; + GetBounds(bounds); + if (bounds.IsEmpty()) { + return nullptr; + } + + gfx::IntSize size(bounds.width, bounds.height); + + ShadowLayerForwarder* forwarder = lm->AsShadowForwarder(); + SurfaceDescriptor surface; + if (!forwarder->AllocSurfaceDescriptor(size, gfxContentType::COLOR_ALPHA, &surface)) { + return nullptr; + } + + if (!cc->SendMakeWidgetSnapshot(surface)) { + return nullptr; + } + + RefPtr snapshot = GetSurfaceForDescriptor(surface); + RefPtr dt = + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(size, gfx::SurfaceFormat::B8G8R8A8); + if (!snapshot || !dt) { + forwarder->DestroySharedSurface(&surface); + return nullptr; + } + + dt->DrawSurface(snapshot, + gfx::Rect(gfx::Point(), gfx::Size(size)), + gfx::Rect(gfx::Point(), gfx::Size(size)), + gfx::DrawSurfaceOptions(gfx::Filter::POINT)); + + forwarder->DestroySharedSurface(&surface); + return dt->Snapshot(); +} + #ifdef DEBUG ////////////////////////////////////////////////////////////// // diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index 06e66d9962..dfb4a184b7 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -294,6 +294,10 @@ public: virtual const SizeConstraints& GetSizeConstraints() const override; virtual void SetSizeConstraints(const SizeConstraints& aConstraints) override; + virtual bool CaptureWidgetOnScreen(mozilla::RefPtr aDT) override { + return false; + } + /** * Use this when GetLayerManager() returns a BasicLayerManager * (nsBaseWidget::GetLayerManager() does). This sets up the widget's diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 21ec42625f..512aec7dc9 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -58,6 +58,7 @@ struct ScrollableLayerGuid; } namespace gfx { class DrawTarget; +class SourceSurface; } namespace widget { class TextEventDispatcher; @@ -109,6 +110,7 @@ typedef void* nsNativeWidget; #define NS_NATIVE_TSF_CATEGORY_MGR 101 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102 #define NS_NATIVE_ICOREWINDOW 103 // winrt specific +#define NS_NATIVE_CHILD_WINDOW 104 #endif #if defined(MOZ_WIDGET_GTK) // set/get nsPluginNativeWindowGtk, e10s specific @@ -1464,7 +1466,7 @@ class nsIWidget : public nsISupports { * a child widget. */ struct Configuration { - nsIWidget* mChild; + nsCOMPtr mChild; uintptr_t mWindowID; // e10s specific, the unique plugin port id bool mVisible; // e10s specific, widget visibility nsIntRect mBounds; @@ -2118,6 +2120,23 @@ class nsIWidget : public nsISupports { */ virtual nsresult ClearNativeTouchSequence(nsIObserver* aObserver); + /* + * Snapshot the contents of the widget by reading pixels back from the + * Operating System. Unlike RenderDocument(), this does not read from our + * own backbuffers, so that we can test if there is a difference in how + * our buffers are being presented. + * + * This is only supported for widgets using OMTC. + */ + already_AddRefed SnapshotWidgetOnScreen(); + + /* + * Implementation of SnapshotWidgetOnScreen. This is invoked by the + * compositor for SnapshotWidgetOnScreen(), and should not be called + * otherwise. + */ + virtual bool CaptureWidgetOnScreen(mozilla::RefPtr aDT) = 0; + private: class LongTapInfo { diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 554a390388..283c14ccf8 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -180,6 +180,7 @@ IMEHandler::NotifyIME(nsWindow* aWindow, // composition window position. if (IsIMMActive()) { nsIMM32Handler::OnUpdateComposition(aWindow); + nsIMM32Handler::OnSelectionChange(aWindow, aIMENotification); } return rv; } @@ -238,6 +239,9 @@ IMEHandler::NotifyIME(nsWindow* aWindow, case NOTIFY_IME_OF_COMPOSITION_UPDATE: nsIMM32Handler::OnUpdateComposition(aWindow); return NS_OK; + case NOTIFY_IME_OF_SELECTION_CHANGE: + nsIMM32Handler::OnSelectionChange(aWindow, aIMENotification); + return NS_OK; case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT: return nsIMM32Handler::OnMouseButtonEvent(aWindow, aIMENotification); #ifdef NS_ENABLE_TSF diff --git a/widget/windows/nsIMM32Handler.cpp b/widget/windows/nsIMM32Handler.cpp index bfcef126e4..23e0b5e941 100644 --- a/widget/windows/nsIMM32Handler.cpp +++ b/widget/windows/nsIMM32Handler.cpp @@ -11,7 +11,6 @@ #include "nsWindowDefs.h" #include "WinUtils.h" #include "KeyboardLayout.h" -#include "WritingModes.h" #include #include "mozilla/MiscEvents.h" @@ -132,6 +131,7 @@ static UINT sWM_MSIME_MOUSE = 0; // mouse message for MSIME 98/2000 #define IMEMOUSE_WUP 0x10 // wheel up #define IMEMOUSE_WDOWN 0x20 // wheel down +WritingMode nsIMM32Handler::sWritingModeOfCompositionFont; nsString nsIMM32Handler::sIMEName; UINT nsIMM32Handler::sCodePage = 0; DWORD nsIMM32Handler::sIMEProperty = 0; @@ -158,7 +158,7 @@ nsIMM32Handler::Initialize() sAssumeVerticalWritingModeNotSupported = Preferences::GetBool( "intl.imm.vertical_writing.always_assume_not_supported", false); - InitKeyboardLayout(::GetKeyboardLayout(0)); + InitKeyboardLayout(nullptr, ::GetKeyboardLayout(0)); } /* static */ void @@ -205,6 +205,15 @@ nsIMM32Handler::IsJapanist2003Active() return sIMEName.EqualsLiteral("Japanist 2003"); } +/* static */ bool +nsIMM32Handler::IsGoogleJapaneseInputActive() +{ + // NOTE: Even on Windows for en-US, the name of Google Japanese Input is + // written in Japanese. + return sIMEName.Equals(L"Google \x65E5\x672C\x8A9E\x5165\x529B " + L"IMM32 \x30E2\x30B8\x30E5\x30FC\x30EB"); +} + /* static */ bool nsIMM32Handler::ShouldDrawCompositionStringOurselves() { @@ -223,18 +232,24 @@ nsIMM32Handler::IsVerticalWritingSupported() if (sAssumeVerticalWritingModeNotSupported) { return false; } + // Google Japanese Input doesn't support vertical writing mode. We should + // return false if it's active IME. + if (IsGoogleJapaneseInputActive()) { + return false; + } return !!(sIMEUIProperty & (UI_CAP_2700 | UI_CAP_ROT90 | UI_CAP_ROTANY)); } /* static */ void -nsIMM32Handler::InitKeyboardLayout(HKL aKeyboardLayout) +nsIMM32Handler::InitKeyboardLayout(nsWindow* aWindow, + HKL aKeyboardLayout) { UINT IMENameLength = ::ImmGetDescriptionW(aKeyboardLayout, nullptr, 0); if (IMENameLength) { // Add room for the terminating null character sIMEName.SetLength(++IMENameLength); IMENameLength = - ::ImmGetDescriptionW(aKeyboardLayout, sIMEName.BeginWriting(), + ::ImmGetDescriptionW(aKeyboardLayout, wwc(sIMEName.BeginWriting()), IMENameLength); // Adjust the length to ignore the terminating null character sIMEName.SetLength(IMENameLength); @@ -248,6 +263,22 @@ nsIMM32Handler::InitKeyboardLayout(HKL aKeyboardLayout) (PWSTR)&sCodePage, sizeof(sCodePage) / sizeof(WCHAR)); sIMEProperty = ::ImmGetProperty(aKeyboardLayout, IGP_PROPERTY); sIMEUIProperty = ::ImmGetProperty(aKeyboardLayout, IGP_UI); + + // If active IME is a TIP of TSF, we cannot retrieve the name with IMM32 API. + // For hacking some bugs of some TIP, we should set an IME name from the + // pref. + if (sCodePage == 932 && sIMEName.IsEmpty()) { + sIMEName = + Preferences::GetString("intl.imm.japanese.assume_active_tip_name_as"); + } + + // Whether the IME supports vertical writing mode might be changed or + // some IMEs may need specific font for their UI. Therefore, we should + // update composition font forcibly here. + if (aWindow) { + MaybeAdjustCompositionFont(aWindow, sWritingModeOfCompositionFont, true); + } + PR_LOG(gIMM32Log, PR_LOG_ALWAYS, ("IMM32: InitKeyboardLayout, aKeyboardLayout=%08x (\"%s\"), sCodePage=%lu, " "sIMEProperty=%s, sIMEUIProperty=%s", @@ -268,6 +299,7 @@ nsIMM32Handler::GetIMEUpdatePreference() { return nsIMEUpdatePreference( nsIMEUpdatePreference::NOTIFY_POSITION_CHANGE | + nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE | nsIMEUpdatePreference::NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR); } @@ -389,6 +421,43 @@ nsIMM32Handler::OnUpdateComposition(nsWindow* aWindow) gIMM32Handler->SetIMERelatedWindowsPos(aWindow, IMEContext); } +// static +void +nsIMM32Handler::OnSelectionChange(nsWindow* aWindow, + const IMENotification& aIMENotification) +{ + if (aIMENotification.mSelectionChangeData.mCausedByComposition) { + return; + } + MaybeAdjustCompositionFont(aWindow, + aIMENotification.mSelectionChangeData.GetWritingMode()); +} + +// static +void +nsIMM32Handler::MaybeAdjustCompositionFont(nsWindow* aWindow, + const WritingMode& aWritingMode, + bool aForceUpdate) +{ + switch (sCodePage) { + case 932: // Japanese Shift-JIS + case 936: // Simlified Chinese GBK + case 949: // Korean + case 950: // Traditional Chinese Big5 + EnsureHandlerInstance(); + break; + default: + // If there is no instance of nsIMM32Hander, we shouldn't waste footprint. + if (!gIMM32Handler) { + return; + } + } + + // Like Navi-Bar of ATOK, some IMEs may require proper composition font even + // before sending WM_IME_STARTCOMPOSITION. + nsIMEContext IMEContext(aWindow->GetWindowHandle()); + gIMM32Handler->AdjustCompositionFont(IMEContext, aWritingMode, aForceUpdate); +} /* static */ bool nsIMM32Handler::ProcessInputLangChangeMessage(nsWindow* aWindow, @@ -402,7 +471,7 @@ nsIMM32Handler::ProcessInputLangChangeMessage(nsWindow* aWindow, if (gIMM32Handler) { gIMM32Handler->OnInputLangChange(aWindow, wParam, lParam, aResult); } - InitKeyboardLayout(reinterpret_cast(lParam)); + InitKeyboardLayout(aWindow, reinterpret_cast(lParam)); // We can release the instance here, because the instance may be never // used. E.g., the new keyboard layout may not use IME, or it may use TSF. Terminate(); @@ -1487,18 +1556,46 @@ nsIMM32Handler::HandleQueryCharPosition(nsWindow* aWindow, // even if the content of the popup window has focus. ResolveIMECaretPos(aWindow->GetTopLevelWindow(false), r, nullptr, screenRect); + + // XXX This might need to check writing mode. However, MSDN doesn't explain + // how to set the values in vertical writing mode. Additionally, IME + // doesn't work well with top-left of the character (this is explicitly + // documented) and its horizontal width. So, it might be better to set + // top-right corner of the character and horizontal width, but we're not + // sure if it doesn't cause any problems with a lot of IMEs... pCharPosition->pt.x = screenRect.x; pCharPosition->pt.y = screenRect.y; pCharPosition->cLineHeight = r.height; - // XXX we should use NS_QUERY_EDITOR_RECT event here. - ::GetWindowRect(aWindow->GetWindowHandle(), &pCharPosition->rcDocument); + WidgetQueryContentEvent editorRect(true, NS_QUERY_EDITOR_RECT, aWindow); + aWindow->InitEvent(editorRect); + aWindow->DispatchWindowEvent(&editorRect); + if (NS_WARN_IF(!editorRect.mSucceeded)) { + PR_LOG(gIMM32Log, PR_LOG_ERROR, + ("IMM32: HandleQueryCharPosition, NS_QUERY_EDITOR_RECT failed")); + ::GetWindowRect(aWindow->GetWindowHandle(), &pCharPosition->rcDocument); + } else { + nsIntRect editorRectInWindow = + LayoutDevicePixel::ToUntyped(editorRect.mReply.mRect); + nsWindow* window = editorRect.mReply.mFocusedWidget ? + static_cast(editorRect.mReply.mFocusedWidget) : aWindow; + nsIntRect editorRectInScreen; + ResolveIMECaretPos(window, editorRectInWindow, nullptr, editorRectInScreen); + ::SetRect(&pCharPosition->rcDocument, + editorRectInScreen.x, editorRectInScreen.y, + editorRectInScreen.XMost(), editorRectInScreen.YMost()); + } *oResult = TRUE; PR_LOG(gIMM32Log, PR_LOG_ALWAYS, - ("IMM32: HandleQueryCharPosition, SUCCEEDED\n")); + ("IMM32: HandleQueryCharPosition, SUCCEEDED, pCharPosition={ pt={ x=%d, " + "y=%d }, cLineHeight=%d, rcDocument={ left=%d, top=%d, right=%d, " + "bottom=%d } }", + pCharPosition->pt.x, pCharPosition->pt.y, pCharPosition->cLineHeight, + pCharPosition->rcDocument.left, pCharPosition->rcDocument.top, + pCharPosition->rcDocument.right, pCharPosition->rcDocument.bottom)); return true; } @@ -2252,7 +2349,8 @@ SetVerticalFontToLogFont(const nsAString& aFontFace, void nsIMM32Handler::AdjustCompositionFont(const nsIMEContext& aIMEContext, - const WritingMode& aWritingMode) + const WritingMode& aWritingMode, + bool aForceUpdate) { // An instance of nsIMM32Handler is destroyed when active IME is changed. // Therefore, we need to store the information which are set to the IM @@ -2264,13 +2362,13 @@ nsIMM32Handler::AdjustCompositionFont(const nsIMEContext& aIMEContext, // If composition font is customized by pref, we need to modify the // composition font of the IME context at first time even if the writing mode // is horizontal. - bool setCompositionFontForcibly = - !sCompositionFontsInitialized && !sCompositionFont.IsEmpty(); + bool setCompositionFontForcibly = aForceUpdate || + (!sCompositionFontsInitialized && !sCompositionFont.IsEmpty()); static WritingMode sCurrentWritingMode; static nsString sCurrentIMEName; if (!setCompositionFontForcibly && - sCurrentWritingMode == aWritingMode && + sWritingModeOfCompositionFont == aWritingMode && sCurrentIMEName == sIMEName) { // Nothing to do if writing mode isn't being changed. return; @@ -2319,7 +2417,7 @@ nsIMM32Handler::AdjustCompositionFont(const nsIMEContext& aIMEContext, } } - sCurrentWritingMode = aWritingMode; + sWritingModeOfCompositionFont = aWritingMode; sCurrentIMEName = sIMEName; LOGFONTW logFont; diff --git a/widget/windows/nsIMM32Handler.h b/widget/windows/nsIMM32Handler.h index 7cf11c409d..d58f5cf4e3 100644 --- a/widget/windows/nsIMM32Handler.h +++ b/widget/windows/nsIMM32Handler.h @@ -14,13 +14,11 @@ #include "nsIWidget.h" #include "mozilla/EventForwards.h" #include "nsRect.h" +#include "WritingModes.h" class nsWindow; namespace mozilla { - -class WritingMode; - namespace widget { struct MSGResult; @@ -146,6 +144,8 @@ public: static void CommitComposition(nsWindow* aWindow, bool aForce = false); static void CancelComposition(nsWindow* aWindow, bool aForce = false); static void OnUpdateComposition(nsWindow* aWindow); + static void OnSelectionChange(nsWindow* aWindow, + const IMENotification& aIMENotification); static nsIMEUpdatePreference GetIMEUpdatePreference(); @@ -162,10 +162,12 @@ protected: static bool IsComposingWindow(nsWindow* aWindow); static bool IsJapanist2003Active(); + static bool IsGoogleJapaneseInputActive(); static bool ShouldDrawCompositionStringOurselves(); static bool IsVerticalWritingSupported(); - static void InitKeyboardLayout(HKL aKeyboardLayout); + // aWindow can be nullptr if it's called without receiving WM_INPUTLANGCHANGE. + static void InitKeyboardLayout(nsWindow* aWindow, HKL aKeyboardLayout); static UINT GetKeyboardCodePage(); /** @@ -295,9 +297,23 @@ protected: /** * AdjustCompositionFont() makes IME vertical writing mode if it's supported. + * If aForceUpdate is true, it will update composition font even if writing + * mode isn't being changed. */ void AdjustCompositionFont(const nsIMEContext& aIMEContext, - const mozilla::WritingMode& aWritingMode); + const mozilla::WritingMode& aWritingMode, + bool aForceUpdate = false); + + /** + * MaybeAdjustCompositionFont() calls AdjustCompositionFont() when the + * locale of active IME is CJK. Note that this creates an instance even + * when there is no composition but the locale is CJK. + */ + static void MaybeAdjustCompositionFont( + nsWindow* aWindow, + const mozilla::WritingMode& aWritingMode, + bool aForceUpdate = false); + /** * Get the current target clause of composition string. * If there are one or more characters whose attribute is ATTR_TARGET_*, @@ -375,6 +391,7 @@ protected: bool mIsComposingOnPlugin; bool mNativeCaretIsCreated; + static mozilla::WritingMode sWritingModeOfCompositionFont; static nsString sIMEName; static UINT sCodePage; static DWORD sIMEProperty; diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index a5fde29c4f..ebf200944c 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -2939,6 +2939,29 @@ void* nsWindow::GetNativeData(uint32_t aDataType) return nullptr; } +void +nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal) +{ + switch (aDataType) { + case NS_NATIVE_CHILD_WINDOW: + { + HWND childWindow = reinterpret_cast(aVal); + + // Make sure the window is styled to be a child window. + LONG_PTR style = GetWindowLongPtr(childWindow, GWL_STYLE); + style |= WS_CHILD; + style &= ~WS_POPUP; + SetWindowLongPtr(childWindow, GWL_STYLE, style); + + // Do the reparenting. + ::SetParent(childWindow, mWnd); + break; + } + default: + NS_ERROR("SetNativeData called with unsupported data type."); + } +} + // Free some native data according to aDataType void nsWindow::FreeNativeData(void * data, uint32_t aDataType) { @@ -6388,7 +6411,7 @@ nsWindow::ConfigureChildren(const nsTArray& aConfigurations) // need. for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { const Configuration& configuration = aConfigurations[i]; - nsWindow* w = static_cast(configuration.mChild); + nsWindow* w = static_cast(configuration.mChild.get()); NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); nsresult rv = w->SetWindowClipRegion(configuration.mClipRegion, true); @@ -7599,6 +7622,47 @@ void nsWindow::PickerClosed() } } +bool nsWindow::CaptureWidgetOnScreen(RefPtr aDT) +{ + BOOL dwmEnabled = false; + if (WinUtils::dwmIsCompositionEnabledPtr && + WinUtils::dwmFlushProcPtr && + WinUtils::dwmIsCompositionEnabledPtr(&dwmEnabled) && + dwmEnabled) + { + WinUtils::dwmFlushProcPtr(); + } + + HDC dc = ::GetDC(mWnd); + uint32_t flags = (mTransparencyMode == eTransparencyOpaque) + ? 0 + : gfxWindowsSurface::FLAG_IS_TRANSPARENT; + + nsRefPtr surf = new gfxWindowsSurface(dc, flags); + IntSize size(surf->GetSize().width, surf->GetSize().height); + if (size.width < 0 || size.height < 0) { + ::ReleaseDC(mWnd, dc); + return false; + } + + RefPtr source = Factory::CreateDrawTargetForCairoSurface(surf->CairoSurface(), size); + if (!source) { + ::ReleaseDC(mWnd, dc); + return false; + } + RefPtr snapshot = source->Snapshot(); + if (!snapshot) { + ::ReleaseDC(mWnd, dc); + return false; + } + + aDT->DrawSurface(snapshot, + Rect(0, 0, size.width, size.height), + Rect(0, 0, size.width, size.height)); + ::ReleaseDC(mWnd, dc); + return true; +} + bool nsWindow::PreRender(LayerManagerComposite*) { // This can block waiting for WM_SETTEXT to finish diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 5a52f356da..33f53b49d6 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -133,6 +133,7 @@ public: bool aIncludeChildren = false); NS_IMETHOD Invalidate(const nsIntRect & aRect); virtual void* GetNativeData(uint32_t aDataType); + void SetNativeData(uint32_t aDataType, uintptr_t aVal) override; virtual void FreeNativeData(void * data, uint32_t aDataType); NS_IMETHOD SetTitle(const nsAString& aTitle); NS_IMETHOD SetIcon(const nsAString& aIconSpec); @@ -281,6 +282,8 @@ public: bool IsPopup(); virtual bool ShouldUseOffMainThreadCompositing(); + bool CaptureWidgetOnScreen(mozilla::RefPtr aDT); + protected: virtual ~nsWindow();