Trigger activation changes at the right time by listening for WM_NCACTIVATE
authoraroben@apple.com <aroben@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 17 Jan 2008 17:46:02 +0000 (17:46 +0000)
committeraroben@apple.com <aroben@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 17 Jan 2008 17:46:02 +0000 (17:46 +0000)
         This is the final part of <rdar://5006915> Inactive look for Aqua
         controls

         WebView now listens to messages sent to its top-level parent window.
         When the top-level parent receives a WM_NCACTIVATE message, WebView
         recalculates whether it is contained within the current active window.

         Reviewed by Darin.

         * Interfaces/IWebViewPrivate.idl:
         (IWebViewPrivate::windowAncestryDidChange): Added. WebKit clients
         should call this when they change the parent window chain of a
         WebView.
         * WebView.cpp:
         (WebView::WebView): Initialize new member.
         (findTopLevelParent): Added.
         (WebViewWndProc):
             - Separated activation handling from focus handling, now that
               activation handling is taken care of by updateActiveState
               exclusively.
             - Compare top-level parents in our WM_KILLFOCUS handler so that we
               really know if focus is staying inside our top-level window.
             - Update our active state on WM_WINDOWPOSCHANGED.
             - Added a WM_TIMER handler.
         (WebView::initWithFrame): Call windowAncestryDidChange after setting
         up our HWND as a child of the host window.
         (WebView::windowReceivedMessage): Added. Update our active state when
         our top-level parent receives a WM_NCACTIVATE message.
         (WebView::updateActiveStateSoon): Added.
         (WebView::setHostWindow): Call windowAncestryDidChange after changing
         the host window.
         (WebView::updateActiveState): We are active if our top-level parent is
         the same as the top-level parent of the active window.
         (WebView::windowAncestryDidChange): Added. Recalculates our top-level
         parent and registers as a listener for the new top-level parent's
         messages.
         * WebView.h: Made WebView and WindowMessageListener so that it can
         utilize WindowMessageBroadcaster to listen to its top-level parent's
         messages.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@29583 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebKit/win/ChangeLog
WebKit/win/Interfaces/IWebViewPrivate.idl
WebKit/win/WebView.cpp
WebKit/win/WebView.h

index 5dc0fe0..ef26615 100644 (file)
@@ -1,5 +1,49 @@
 2008-01-16  Adam Roben  <aroben@apple.com>
 
+        Trigger activation changes at the right time by listening for WM_NCACTIVATE
+
+        This is the final part of <rdar://5006915> Inactive look for Aqua
+        controls
+
+        WebView now listens to messages sent to its top-level parent window.
+        When the top-level parent receives a WM_NCACTIVATE message, WebView
+        recalculates whether it is contained within the current active window.
+
+        Reviewed by Darin.
+
+        * Interfaces/IWebViewPrivate.idl:
+        (IWebViewPrivate::windowAncestryDidChange): Added. WebKit clients
+        should call this when they change the parent window chain of a
+        WebView.
+        * WebView.cpp:
+        (WebView::WebView): Initialize new member.
+        (findTopLevelParent): Added.
+        (WebViewWndProc):
+            - Separated activation handling from focus handling, now that
+              activation handling is taken care of by updateActiveState
+              exclusively.
+            - Compare top-level parents in our WM_KILLFOCUS handler so that we
+              really know if focus is staying inside our top-level window.
+            - Update our active state on WM_WINDOWPOSCHANGED.
+            - Added a WM_TIMER handler.
+        (WebView::initWithFrame): Call windowAncestryDidChange after setting
+        up our HWND as a child of the host window.
+        (WebView::windowReceivedMessage): Added. Update our active state when
+        our top-level parent receives a WM_NCACTIVATE message.
+        (WebView::updateActiveStateSoon): Added.
+        (WebView::setHostWindow): Call windowAncestryDidChange after changing
+        the host window.
+        (WebView::updateActiveState): We are active if our top-level parent is
+        the same as the top-level parent of the active window.
+        (WebView::windowAncestryDidChange): Added. Recalculates our top-level
+        parent and registers as a listener for the new top-level parent's
+        messages.
+        * WebView.h: Made WebView and WindowMessageListener so that it can
+        utilize WindowMessageBroadcaster to listen to its top-level parent's
+        messages.
+
+2008-01-16  Adam Roben  <aroben@apple.com>
+
         Updated for ScrollBarClient changes
 
         Reviewed by Darin.
index e5c0726..e091440 100644 (file)
@@ -138,4 +138,6 @@ interface IWebViewPrivate : IUnknown
 
     // SPI for DumpRenderTree
     HRESULT executeCoreCommandByName([in] BSTR name, [in] BSTR value);
+
+    HRESULT windowAncestryDidChange();
 }
index a2bc399..2abbd58 100644 (file)
@@ -93,6 +93,7 @@
 #include <WebCore/Settings.h>
 #include <WebCore/SimpleFontData.h>
 #include <WebCore/TypingCommand.h>
+#include <WebCore/WindowMessageBroadcaster.h>
 #pragma warning(pop)
 #include <JavaScriptCore/collector.h>
 #include <JavaScriptCore/value.h>
@@ -230,6 +231,10 @@ static bool grammarCheckingEnabled;
 static bool s_didSetCacheModel;
 static WebCacheModel s_cacheModel = WebCacheModelDocumentViewer;
 
+enum {
+    UpdateActiveStateTimer = 1,
+};
+
 // WebView ----------------------------------------------------------------
 
 bool WebView::s_allowSiteSpecificHacks = false;
@@ -255,6 +260,7 @@ WebView::WebView()
 , m_inIMEComposition(0)
 , m_toolTipHwnd(0)
 , m_closeWindowTimer(this, &WebView::closeWindowTimerFired)
+, m_topLevelParent(0)
 {
     KJS::Collector::registerAsMainThread();
 
@@ -1558,6 +1564,19 @@ namespace WebCore {
     extern HCURSOR lastSetCursor;
 }
 
+static HWND findTopLevelParent(HWND window)
+{
+    if (!window)
+        return 0;
+
+    HWND current = window;
+    for (HWND parent = GetParent(current); current; current = parent, parent = GetParent(parent))
+        if (!parent || !(GetWindowLongPtr(current, GWL_STYLE) & (WS_POPUP | WS_CHILD)))
+            return current;
+    ASSERT_NOT_REACHED();
+    return 0;
+}
+
 static LRESULT CALLBACK WebViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
     LRESULT lResult = 0;
@@ -1661,12 +1680,6 @@ static LRESULT CALLBACK WebViewWndProc(HWND hWnd, UINT message, WPARAM wParam, L
                 uiDelegatePrivate->webViewReceivedFocus(webView);
             // FIXME: Merge this logic with updateActiveState, and switch this over to use updateActiveState
 
-            webView->page()->focusController()->setActive(true);
-
-            // It's ok to just always do setWindowHasFocus, since we won't fire the focus event on the DOM
-            // window unless the value changes.  It's also ok to do setIsActive inside focus,
-            // because Windows has no concept of having to update control tints (e.g., graphite vs. aqua)
-            // and therefore only needs to update the selection (which is limited to the focused frame).
             FocusController* focusController = webView->page()->focusController();
             if (Frame* frame = focusController->focusedFrame()) {
                 // If the previously focused window is a child of ours (for example a plugin), don't send any
@@ -1692,8 +1705,7 @@ static LRESULT CALLBACK WebViewWndProc(HWND hWnd, UINT message, WPARAM wParam, L
             // and we need to clear out the focused target.
             FocusController* focusController = webView->page()->focusController();
             webView->resetIME(focusController->focusedOrMainFrame());
-            if (GetAncestor(hWnd, GA_ROOT) != newFocusWnd) {
-                webView->page()->focusController()->setActive(false);
+            if (webView->topLevelParent() != findTopLevelParent(newFocusWnd)) {
                 if (Frame* frame = focusController->focusedOrMainFrame()) {
                     // If we're losing focus to a child of ours, don't send blur events.
                     if (!IsChild(hWnd, newFocusWnd))
@@ -1703,6 +1715,11 @@ static LRESULT CALLBACK WebViewWndProc(HWND hWnd, UINT message, WPARAM wParam, L
                 focusController->setFocusedFrame(0);
             break;
         }
+        case WM_WINDOWPOSCHANGED:
+            if (reinterpret_cast<WINDOWPOS*>(lParam)->flags & SWP_SHOWWINDOW)
+                webView->updateActiveStateSoon();
+            handled = false;
+            break;
         case WM_CUT:
             webView->cut(0);
             break;
@@ -1790,6 +1807,14 @@ static LRESULT CALLBACK WebViewWndProc(HWND hWnd, UINT message, WPARAM wParam, L
         case WM_IME_SETCONTEXT:
             handled = webView->onIMESetContext(wParam, lParam);
             break;
+        case WM_TIMER:
+            switch (wParam) {
+                case UpdateActiveStateTimer:
+                    KillTimer(hWnd, UpdateActiveStateTimer);
+                    webView->updateActiveState();
+                    break;
+            }
+            break;
         case WM_SETCURSOR:
             if (lastSetCursor) {
                 SetCursor(lastSetCursor);
@@ -2062,6 +2087,7 @@ HRESULT STDMETHODCALLTYPE WebView::initWithFrame(
     ShowWindow(m_viewWindow, SW_SHOW);
 
     initializeToolTipWindow();
+    windowAncestryDidChange();
 
     IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal();
     notifyCenter->addObserver(this, WebPreferences::webPreferencesChangedNotification(), static_cast<IWebPreferences*>(m_preferences.get()));
@@ -2592,6 +2618,20 @@ HRESULT STDMETHODCALLTYPE WebView::preferencesIdentifier(
     return E_NOTIMPL;
 }
 
+void WebView::windowReceivedMessage(HWND, UINT message, WPARAM, LPARAM)
+{
+    switch (message) {
+    case WM_NCACTIVATE:
+        updateActiveStateSoon();
+        break;
+    }
+}
+
+void WebView::updateActiveStateSoon() const
+{
+    SetTimer(m_viewWindow, UpdateActiveStateTimer, 0, 0);
+}
+
 HRESULT STDMETHODCALLTYPE WebView::setHostWindow( 
     /* [in] */ OLE_HANDLE oleWindow)
 {
@@ -2601,6 +2641,9 @@ HRESULT STDMETHODCALLTYPE WebView::setHostWindow(
 
     m_hostWindow = window;
 
+    if (m_viewWindow)
+        windowAncestryDidChange();
+
     return S_OK;
 }
 
@@ -2663,14 +2706,9 @@ HRESULT STDMETHODCALLTYPE WebView::searchFor(
 
 HRESULT STDMETHODCALLTYPE WebView::updateActiveState()
 {
-    HWND window = ::GetAncestor(m_viewWindow, GA_ROOT);
-    HWND activeWindow = ::GetActiveWindow();
-    bool windowIsKey = window == activeWindow;
-    activeWindow = ::GetAncestor(activeWindow, GA_ROOTOWNER);
-
-    bool windowOrSheetIsKey = windowIsKey || (window == activeWindow);
+    HWND activeWindow = GetActiveWindow();
 
-    m_page->focusController()->setActive(windowOrSheetIsKey);
+    m_page->focusController()->setActive(activeWindow && m_topLevelParent == findTopLevelParent(activeWindow));
 
     return S_OK;
 }
@@ -4562,6 +4600,25 @@ HRESULT STDMETHODCALLTYPE WebView::inspector(IWebInspector** inspector)
     return m_webInspector.copyRefTo(inspector);
 }
 
+HRESULT STDMETHODCALLTYPE WebView::windowAncestryDidChange()
+{
+    HWND newParent = findTopLevelParent(m_hostWindow);
+    if (newParent == m_topLevelParent)
+        return S_OK;
+
+    if (m_topLevelParent)
+        WindowMessageBroadcaster::removeListener(m_topLevelParent, this);
+
+    m_topLevelParent = newParent;
+
+    if (m_topLevelParent)
+        WindowMessageBroadcaster::addListener(m_topLevelParent, this);
+
+    updateActiveState();
+
+    return S_OK;
+}
+
 class EnumTextMatches : public IEnumTextMatches
 {
     long m_ref;
index 4d15cb4..4772bc6 100644 (file)
@@ -36,6 +36,7 @@
 
 #include <WebCore/IntRect.h>
 #include <WebCore/Timer.h>
+#include <WebCore/WindowMessageListener.h>
 #include <wtf/OwnPtr.h>
 
 class WebFrame;
@@ -57,6 +58,7 @@ class WebView
     , public IWebViewEditingActions
     , public IWebNotificationObserver
     , public IDropTarget
+    , WebCore::WindowMessageListener
 {
 public:
     static WebView* createInstance();
@@ -630,6 +632,8 @@ public:
     virtual HRESULT STDMETHODCALLTYPE setProhibitsMainFrameScrolling(BOOL);
     virtual HRESULT STDMETHODCALLTYPE setShouldApplyMacFontAscentHack(BOOL);
 
+    virtual HRESULT STDMETHODCALLTYPE windowAncestryDidChange();
+
     // WebView
     WebCore::Page* page();
     bool handleMouseEvent(UINT, WPARAM, LPARAM);
@@ -703,6 +707,10 @@ public:
     static bool didSetCacheModel();
     static WebCacheModel maxCacheModelInAnyInstance();
 
+    void updateActiveStateSoon() const;
+
+    HWND topLevelParent() const { return m_topLevelParent; }
+
 protected:
     HIMC getIMMContext();
     void releaseIMMContext(HIMC);
@@ -721,6 +729,8 @@ protected:
     void addToAllWebViewsSet();
     void removeFromAllWebViewsSet();
 
+    virtual void windowReceivedMessage(HWND, UINT message, WPARAM, LPARAM);
+
     ULONG m_refCount;
     WebCore::String m_groupName;
     HWND m_hostWindow;
@@ -771,6 +781,8 @@ protected:
 
     WebCore::Timer<WebView> m_closeWindowTimer;
     OwnPtr<TRACKMOUSEEVENT> m_mouseOutTracker;
+
+    HWND m_topLevelParent;
 };
 
 #endif