MSAA: Crash when posting a notification for a detached object
authorjhoneycutt@apple.com <jhoneycutt@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 29 Jan 2010 21:55:58 +0000 (21:55 +0000)
committerjhoneycutt@apple.com <jhoneycutt@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 29 Jan 2010 21:55:58 +0000 (21:55 +0000)
https://bugs.webkit.org/show_bug.cgi?id=34309
<rdar://problem/7409759>

Reviewed by Darin Adler.

WebCore:

Test: platform/win/accessibility/detached-object-notification-crash.html

* accessibility/AccessibilityRenderObject.cpp:
(AccessibilityRenderObject::document):
Null check m_renderer. This is the bug fix; the other changes in the
patch are for the test.

* accessibility/win/AXObjectCacheWin.cpp:
(WebCore::AXObjectCache::postPlatformNotification):
Map AXValueChanged to EVENT_OBJECT_VALUECHANGED, so we'll post a
notification when AXValueChanged is posted. Receiving an event of this
type tells us that the test passed.

WebKit/win:

* AccessibleBase.cpp:
(AccessibleBase::QueryService):
If an unrecognized service ID is passed, return early. Otherwise, return
the result of QueryInterface.
(AccessibleBase::QueryInterface):
Add static_casts. Check for new UUIDs.
(AccessibleBase::isSameObject):
Query the object for AccessibleBase. Return whether the pointers or the
wrapped objects match.

* AccessibleBase.h:
Give the class a UUID so we can query for it in isSameObject(). Inherit
from IAccessibleComparable; inherit from IServiceProvider so clients can
use QueryService to query for a custom interface.

* Interfaces/AccessibleComparable.idl: Added. Declares a function that
can be called to compare to accessible objects.

* Interfaces/WebKit.idl:
Include the new IDL.

* WebKit.vcproj/Interfaces.vcproj:
Add the new IDL to the project.

WebKitTools:

* DumpRenderTree/AccessibilityController.h:
Declare new functions. Add new members to store the event hook and the
mapping of accessibility elements to their JS callbacks.

* DumpRenderTree/gtk/AccessibilityControllerGtk.cpp:
(AccessibilityController::notificationReceived):
Stubbed.
(AccessibilityController::addNotificationListener):
Stubbed.

* DumpRenderTree/mac/AccessibilityControllerMac.mm:
(AccessibilityController::notificationReceived):
Stubbed.
(AccessibilityController::addNotificationListener):
Stubbed.

* DumpRenderTree/win/AccessibilityControllerWin.cpp:
(AccessibilityController::AccessibilityController):
Initialize the event hook.
(AccessibilityController::~AccessibilityController):
Remove the event hook. Unprotect all of the JS functions that are stored
in the map.
(logEventProc):
Clean-up a variable.
(stringEvent):
Return a string description of the MSAA event code.
(notificationListenerProc):
Get the accessible object from the event, and query it for IAccessible.
Call the AccessibilityController's notificationReceived().
(comparableObject):
Use QueryService to obtain the IAccessibleComparable for the
IServiceProvider.
(AccessibilityController::notificationReceived):
Iterate the map of objects that have registered for notification
callbacks. Query each for IServiceProvider, then use comparableObject()
to get an IAccessibleComparable. If we find an object matching the
notified object, call its callback, passing the event that was received.
(AccessibilityController::addNotificationListener):
If we have not created the event hook, create it. Protect the JS
callback function object, and add the object and its callback to our
map.

* DumpRenderTree/win/AccessibilityUIElementWin.cpp:
(AccessibilityUIElement::addNotificationListener):
Call through to the AccessibilityController's addNotificationListener().

* DumpRenderTree/win/DumpRenderTreeWin.h:
Add an extern declaration for the shared FrameLoadDelegate extern, so we
can access it from AccessibilityController.

* DumpRenderTree/win/FrameLoadDelegate.h:
(FrameLoadDelegate::accessibilityController):
Return the AccessibilityController.

LayoutTests:

* platform/win/accessibility/detached-object-notification-crash-expected.txt: Added.
* platform/win/accessibility/detached-object-notification-crash.html: Added.

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

20 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/win/accessibility/detached-object-notification-crash-expected.txt [new file with mode: 0644]
LayoutTests/platform/win/accessibility/detached-object-notification-crash.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/accessibility/AccessibilityRenderObject.cpp
WebCore/accessibility/win/AXObjectCacheWin.cpp
WebKit/win/AccessibleBase.cpp
WebKit/win/AccessibleBase.h
WebKit/win/ChangeLog
WebKit/win/Interfaces/AccessibleComparable.idl [new file with mode: 0644]
WebKit/win/Interfaces/WebKit.idl
WebKit/win/WebKit.vcproj/Interfaces.vcproj
WebKitTools/ChangeLog
WebKitTools/DumpRenderTree/AccessibilityController.h
WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp
WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm
WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp
WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp
WebKitTools/DumpRenderTree/win/DumpRenderTreeWin.h
WebKitTools/DumpRenderTree/win/FrameLoadDelegate.h

index ba72390..9ae4669 100644 (file)
@@ -1,3 +1,15 @@
+2010-01-28  Jon Honeycutt  <jhoneycutt@apple.com>
+
+        MSAA: Crash when posting a notification for a detached object
+
+        https://bugs.webkit.org/show_bug.cgi?id=34309
+        <rdar://problem/7409759>
+
+        Reviewed by Darin Adler.
+
+        * platform/win/accessibility/detached-object-notification-crash-expected.txt: Added.
+        * platform/win/accessibility/detached-object-notification-crash.html: Added.
+
 2010-01-29  Darin Fisher  <darin@chromium.org>
 
         Okayed by Oliver Hunt.
diff --git a/LayoutTests/platform/win/accessibility/detached-object-notification-crash-expected.txt b/LayoutTests/platform/win/accessibility/detached-object-notification-crash-expected.txt
new file mode 100644 (file)
index 0000000..de08737
--- /dev/null
@@ -0,0 +1,6 @@
+This tests that posting a notification for a deleted element does not cause a crash.
+
+
+PASS: Didn't crash.
+
diff --git a/LayoutTests/platform/win/accessibility/detached-object-notification-crash.html b/LayoutTests/platform/win/accessibility/detached-object-notification-crash.html
new file mode 100644 (file)
index 0000000..2ed3c4c
--- /dev/null
@@ -0,0 +1,49 @@
+<html>
+<head>
+    <link rel="stylesheet" href="../../../fast/js/resources/js-test-style.css">
+    <script src="../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+
+<body id="body">
+
+<input id="avnElement">
+<input id="avnElement2">
+
+<p>This tests that posting a notification for a deleted element does
+not cause a crash.</p>
+
+<p id="notDRT">This test should only be run inside of DumpRenderTree.</p>
+
+<p id="console"></p>
+
+<script>
+    function notificationReceived(event)
+    {
+        if (event != "value change event")
+            return;
+
+        debug("PASS: Didn't crash.");
+        layoutTestController.notifyDone();
+    }
+    if (window.layoutTestController && window.accessibilityController) {
+        document.getElementById("notDRT").style.visibility = "hidden";
+
+        layoutTestController.dumpAsText();
+        layoutTestController.waitUntilDone();
+
+        var element = document.getElementById("avnElement");
+        element.focus();
+
+        element.setAttribute("aria-valuenow", 2);
+        document.getElementById("body").removeChild(element);
+
+        var element2 = document.getElementById("avnElement2");
+        element2.focus();
+
+        accessibilityController.focusedElement.addNotificationListener(notificationReceived);
+
+        element2.setAttribute("aria-valuenow", 2);
+    }
+</script>
+</body>
+</html>
index f851d88..a4388b3 100644 (file)
@@ -1,3 +1,25 @@
+2010-01-28  Jon Honeycutt  <jhoneycutt@apple.com>
+
+        MSAA: Crash when posting a notification for a detached object
+
+        https://bugs.webkit.org/show_bug.cgi?id=34309
+        <rdar://problem/7409759>
+
+        Reviewed by Darin Adler.
+
+        Test: platform/win/accessibility/detached-object-notification-crash.html
+
+        * accessibility/AccessibilityRenderObject.cpp:
+        (AccessibilityRenderObject::document):
+        Null check m_renderer. This is the bug fix; the other changes in the
+        patch are for the test.
+
+        * accessibility/win/AXObjectCacheWin.cpp:
+        (WebCore::AXObjectCache::postPlatformNotification):
+        Map AXValueChanged to EVENT_OBJECT_VALUECHANGED, so we'll post a
+        notification when AXValueChanged is posted. Receiving an event of this
+        type tells us that the test passed.
+
 2010-01-29  Darin Fisher  <darin@chromium.org>
 
         Okayed by Oliver Hunt.
index 8cd51ae..d738ca8 100644 (file)
@@ -2026,6 +2026,8 @@ RenderView* AccessibilityRenderObject::topRenderer() const
 
 Document* AccessibilityRenderObject::document() const
 {
+    if (!m_renderer)
+        return 0;
     return m_renderer->document();
 }
 
index 8b7d99e..21e61d9 100644 (file)
@@ -83,6 +83,7 @@ void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotific
             msaaEvent = EVENT_SYSTEM_SCROLLINGSTART;
             break;
 
+        case AXValueChanged:
         case AXMenuListValueChanged:
             msaaEvent = EVENT_OBJECT_VALUECHANGE;
             break;
index 978a339..0704771 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -77,15 +77,30 @@ AccessibleBase* AccessibleBase::createInstance(AccessibilityObject* obj)
     return new AccessibleBase(obj);
 }
 
+HRESULT AccessibleBase::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
+{
+    if (!IsEqualGUID(guidService, SID_AccessibleComparable)) {
+        *ppvObject = 0;
+        return E_INVALIDARG;
+    }
+    return QueryInterface(riid, ppvObject);
+}
+
 // IUnknown
 HRESULT STDMETHODCALLTYPE AccessibleBase::QueryInterface(REFIID riid, void** ppvObject)
 {
     if (IsEqualGUID(riid, __uuidof(IAccessible)))
-        *ppvObject = this;
+        *ppvObject = static_cast<IAccessible*>(this);
     else if (IsEqualGUID(riid, __uuidof(IDispatch)))
-        *ppvObject = this;
+        *ppvObject = static_cast<IAccessible*>(this);
     else if (IsEqualGUID(riid, __uuidof(IUnknown)))
-        *ppvObject = this;
+        *ppvObject = static_cast<IAccessible*>(this);
+    else if (IsEqualGUID(riid, __uuidof(IAccessibleComparable)))
+        *ppvObject = static_cast<IAccessibleComparable*>(this);
+    else if (IsEqualGUID(riid, __uuidof(IServiceProvider)))
+        *ppvObject = static_cast<IServiceProvider*>(this);
+    else if (IsEqualGUID(riid, __uuidof(AccessibleBase)))
+        *ppvObject = static_cast<AccessibleBase*>(this);
     else {
         *ppvObject = 0;
         return E_NOINTERFACE;
@@ -699,3 +714,10 @@ AccessibleBase* AccessibleBase::wrapper(AccessibilityObject* obj)
         result = createInstance(obj);
     return result;
 }
+
+HRESULT AccessibleBase::isSameObject(IAccessibleComparable* other, BOOL* result)
+{
+    COMPtr<AccessibleBase> otherAccessibleBase(Query, other);
+    *result = (otherAccessibleBase == this || otherAccessibleBase->m_object == m_object);
+    return S_OK;
+}
index 3b6bce8..ca1703f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #ifndef AccessibleBase_h
 #define AccessibleBase_h
 
-#include <oleacc.h>
 #include <WebCore/AccessibilityObject.h>
 #include <WebCore/AccessibilityObjectWrapperWin.h>
+#include <WebKit/WebKit.h>
+#include <oleacc.h>
 
-class AccessibleBase : public IAccessible, public WebCore::AccessibilityObjectWrapper {
+class DECLSPEC_UUID("3dbd565b-db22-4d88-8e0e-778bde54524a") AccessibleBase : public IAccessibleComparable, public IServiceProvider, public WebCore::AccessibilityObjectWrapper {
 public:
     static AccessibleBase* createInstance(WebCore::AccessibilityObject*);
 
+    // IServiceProvider
+    virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void **ppv);
+
     // IUnknown
     virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
     virtual ULONG STDMETHODCALLTYPE AddRef(void) { return ++m_refCount; }
@@ -89,6 +93,9 @@ public:
         m_object = 0;
     }
 
+    // IAccessibleComparable
+    virtual HRESULT STDMETHODCALLTYPE isSameObject(IAccessibleComparable* other, BOOL* result);
+
 protected:
     AccessibleBase(WebCore::AccessibilityObject*);
     virtual ~AccessibleBase();
index e1895be..f3a2d05 100644 (file)
@@ -1,3 +1,36 @@
+2010-01-28  Jon Honeycutt  <jhoneycutt@apple.com>
+
+        MSAA: Crash when posting a notification for a detached object
+
+        https://bugs.webkit.org/show_bug.cgi?id=34309
+        <rdar://problem/7409759>
+
+        Reviewed by Darin Adler.
+
+        * AccessibleBase.cpp:
+        (AccessibleBase::QueryService):
+        If an unrecognized service ID is passed, return early. Otherwise, return
+        the result of QueryInterface.
+        (AccessibleBase::QueryInterface):
+        Add static_casts. Check for new UUIDs.
+        (AccessibleBase::isSameObject):
+        Query the object for AccessibleBase. Return whether the pointers or the
+        wrapped objects match.
+
+        * AccessibleBase.h:
+        Give the class a UUID so we can query for it in isSameObject(). Inherit
+        from IAccessibleComparable; inherit from IServiceProvider so clients can
+        use QueryService to query for a custom interface.
+
+        * Interfaces/AccessibleComparable.idl: Added. Declares a function that
+        can be called to compare to accessible objects.
+
+        * Interfaces/WebKit.idl:
+        Include the new IDL.
+
+        * WebKit.vcproj/Interfaces.vcproj:
+        Add the new IDL to the project.
+
 2010-01-27  Aaron Boodman  <aa@chromium.org>
 
         Expand NotificationCenter::checkPermission() interface.
diff --git a/WebKit/win/Interfaces/AccessibleComparable.idl b/WebKit/win/Interfaces/AccessibleComparable.idl
new file mode 100644 (file)
index 0000000..710e0d1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef DO_NO_IMPORTS
+import "oaidl.idl";
+import "ocidl.idl";
+#endif
+
+import "oleacc.idl";
+
+cpp_quote("extern const GUID  __declspec(selectany) SID_AccessibleComparable = { 0x62b8cb5f, 0xfb7a, 0x4faf, 0x81, 0xe8, 0x52, 0xb6, 0x5f, 0x12, 0x8b, 0x31 };")
+
+[
+    object,
+    uuid(4f0381ad-dab3-42ad-9ca2-a85b0ae041c0),
+    hidden,
+    dual,
+    pointer_default(unique)
+]
+interface IAccessibleComparable : IAccessible
+{
+    HRESULT isSameObject([in] IAccessibleComparable* other, [out, retval] BOOL* result);
+}
index 5ae1ad9..3cd748c 100644 (file)
@@ -55,6 +55,7 @@ cpp_quote(" */")
 import "oaidl.idl";
 import "ocidl.idl";
 
+#include "AccessibleComparable.idl"
 #include "WebScrollbarTypes.idl"
 #include "JavaScriptCoreAPITypes.idl"
 #include "IWebScriptObject.idl"
index 5d87acd..c6a0add 100644 (file)
        </References>\r
        <Files>\r
                <File\r
+                       RelativePath="..\Interfaces\AccessibleComparable.idl"\r
+                       >\r
+                       <FileConfiguration\r
+                               Name="Debug|Win32"\r
+                               ExcludedFromBuild="true"\r
+                               >\r
+                               <Tool\r
+                                       Name="VCMIDLTool"\r
+                               />\r
+                       </FileConfiguration>\r
+                       <FileConfiguration\r
+                               Name="Release|Win32"\r
+                               ExcludedFromBuild="true"\r
+                               >\r
+                               <Tool\r
+                                       Name="VCMIDLTool"\r
+                               />\r
+                       </FileConfiguration>\r
+               </File>\r
+               <File\r
                        RelativePath="..\Interfaces\DOMCore.idl"\r
                        >\r
                        <FileConfiguration\r
index d5dcb06..945462c 100644 (file)
@@ -1,3 +1,66 @@
+2010-01-28  Jon Honeycutt  <jhoneycutt@apple.com>
+
+        MSAA: Crash when posting a notification for a detached object
+
+        https://bugs.webkit.org/show_bug.cgi?id=34309
+        <rdar://problem/7409759>
+
+        Reviewed by Darin Adler.
+
+        * DumpRenderTree/AccessibilityController.h:
+        Declare new functions. Add new members to store the event hook and the
+        mapping of accessibility elements to their JS callbacks.
+
+        * DumpRenderTree/gtk/AccessibilityControllerGtk.cpp:
+        (AccessibilityController::notificationReceived):
+        Stubbed.
+        (AccessibilityController::addNotificationListener):
+        Stubbed.
+
+        * DumpRenderTree/mac/AccessibilityControllerMac.mm:
+        (AccessibilityController::notificationReceived):
+        Stubbed.
+        (AccessibilityController::addNotificationListener):
+        Stubbed.
+
+        * DumpRenderTree/win/AccessibilityControllerWin.cpp:
+        (AccessibilityController::AccessibilityController):
+        Initialize the event hook.
+        (AccessibilityController::~AccessibilityController):
+        Remove the event hook. Unprotect all of the JS functions that are stored
+        in the map.
+        (logEventProc):
+        Clean-up a variable.
+        (stringEvent):
+        Return a string description of the MSAA event code.
+        (notificationListenerProc):
+        Get the accessible object from the event, and query it for IAccessible.
+        Call the AccessibilityController's notificationReceived().
+        (comparableObject):
+        Use QueryService to obtain the IAccessibleComparable for the
+        IServiceProvider.
+        (AccessibilityController::notificationReceived):
+        Iterate the map of objects that have registered for notification
+        callbacks. Query each for IServiceProvider, then use comparableObject()
+        to get an IAccessibleComparable. If we find an object matching the
+        notified object, call its callback, passing the event that was received.
+        (AccessibilityController::addNotificationListener):
+        If we have not created the event hook, create it. Protect the JS
+        callback function object, and add the object and its callback to our
+        map.
+
+        * DumpRenderTree/win/AccessibilityUIElementWin.cpp:
+        (AccessibilityUIElement::addNotificationListener):
+        Call through to the AccessibilityController's addNotificationListener().
+
+        * DumpRenderTree/win/DumpRenderTreeWin.h:
+        Add an extern declaration for the shared FrameLoadDelegate extern, so we
+        can access it from AccessibilityController.
+
+        * DumpRenderTree/win/FrameLoadDelegate.h:
+        (FrameLoadDelegate::accessibilityController):
+        Return the AccessibilityController.
+
 2010-01-29  Shinichiro Hamaji  <hamaji@chromium.org>
 
         Reviewed by Darin Adler.
index 5a75c2c..de58f84 100644 (file)
 #ifndef AccessibilityController_h
 #define AccessibilityController_h
 
+#include "AccessibilityUIElement.h"
 #include <JavaScriptCore/JSObjectRef.h>
+#include <string>
+#include <wtf/HashMap.h>
 #include <wtf/Platform.h>
 #if PLATFORM(WIN)
 #include <windows.h>
 #endif
 
-class AccessibilityUIElement;
-
 class AccessibilityController {
 public:
     AccessibilityController();
@@ -51,6 +52,9 @@ public:
 
     void resetToConsistentState();
 
+    void addNotificationListener(PlatformUIElement, JSObjectRef functionCallback);
+    void notificationReceived(PlatformUIElement, const std::string& eventName);
+
 private:
     static JSClassRef getJSClass();
 
@@ -58,6 +62,9 @@ private:
     HWINEVENTHOOK m_focusEventHook;
     HWINEVENTHOOK m_valueChangeEventHook;
     HWINEVENTHOOK m_scrollingStartEventHook;
+
+    HWINEVENTHOOK m_allEventsHook;
+    HashMap<PlatformUIElement, JSObjectRef> m_notificationListeners;
 #endif
 };
 
index d95f854..12653fc 100644 (file)
@@ -77,3 +77,11 @@ void AccessibilityController::setLogScrollingStartEvents(bool)
 void AccessibilityController::setLogValueChangeEvents(bool)
 {
 }
+
+void AccessibilityController::addNotificationListener(PlatformUIElement, JSObjectRef)
+{
+}
+
+void AccessibilityController::notificationReceived(PlatformUIElement, const std::string&)
+{
+}
index 4895a1a..4d2da6e 100644 (file)
@@ -65,3 +65,11 @@ void AccessibilityController::setLogScrollingStartEvents(bool)
 void AccessibilityController::setLogValueChangeEvents(bool)
 {
 }
+
+void AccessibilityController::addNotificationListener(PlatformUIElement, JSObjectRef)
+{
+}
+
+void AccessibilityController::notificationReceived(PlatformUIElement, const std::string&)
+{
+}
index 1abb71e..6b35948 100644 (file)
 
 #include "AccessibilityUIElement.h"
 #include "DumpRenderTree.h"
+#include "FrameLoadDelegate.h"
 #include <JavaScriptCore/Assertions.h>
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <JavaScriptCore/JSStringRef.h>
 #include <WebCore/COMPtr.h>
 #include <WebKit/WebKit.h>
 #include <oleacc.h>
@@ -40,6 +43,7 @@ AccessibilityController::AccessibilityController()
     : m_focusEventHook(0)
     , m_scrollingStartEventHook(0)
     , m_valueChangeEventHook(0)
+    , m_allEventsHook(0)
 {
 }
 
@@ -47,6 +51,12 @@ AccessibilityController::~AccessibilityController()
 {
     setLogFocusEvents(false);
     setLogValueChangeEvents(false);
+
+    if (m_allEventsHook)
+        UnhookWinEvent(m_allEventsHook);
+
+    for (HashMap<PlatformUIElement, JSObjectRef>::iterator it = m_notificationListeners.begin(); it != m_notificationListeners.end(); ++it)
+        JSValueUnprotect(frame->globalContext(), it->second);
 }
 
 AccessibilityUIElement AccessibilityController::focusedElement()
@@ -91,7 +101,7 @@ AccessibilityUIElement AccessibilityController::rootElement()
     return rootAccessible;
 }
 
-static void CALLBACK logEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD, DWORD)
+static void CALLBACK logEventProc(HWINEVENTHOOK, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD, DWORD)
 {
     // Get the accessible object for this event.
     COMPtr<IAccessible> parentObject;
@@ -133,6 +143,8 @@ static void CALLBACK logEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND
             printf("Received unknown event for object '%S'.\n", name.c_str());
             break;
     }
+
+    VariantClear(&vChild);
 }
 
 void AccessibilityController::setLogFocusEvents(bool logFocusEvents)
@@ -194,3 +206,83 @@ void AccessibilityController::setLogScrollingStartEvents(bool logScrollingStartE
 
     ASSERT(m_scrollingStartEventHook);
 }
+
+static string stringEvent(DWORD event)
+{
+    switch(event) {
+        case EVENT_OBJECT_VALUECHANGE:
+            return "value change event";
+        default:
+            return "unknown event";
+    }
+}
+
+static void CALLBACK notificationListenerProc(HWINEVENTHOOK, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD, DWORD)
+{
+    // Get the accessible object for this event.
+    COMPtr<IAccessible> parentObject;
+
+    VARIANT vChild;
+    VariantInit(&vChild);
+
+    HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &parentObject, &vChild);
+    ASSERT(SUCCEEDED(hr));
+
+    COMPtr<IDispatch> childDispatch;
+    if (FAILED(parentObject->get_accChild(vChild, &childDispatch))) {
+        VariantClear(&vChild);
+        return;
+    }
+
+    COMPtr<IAccessible> childAccessible(Query, childDispatch);
+
+    sharedFrameLoadDelegate->accessibilityController()->notificationReceived(childAccessible, stringEvent(event));
+
+    VariantClear(&vChild);
+}
+
+static COMPtr<IAccessibleComparable> comparableObject(const COMPtr<IServiceProvider>& serviceProvider)
+{
+    COMPtr<IAccessibleComparable> comparable;
+    serviceProvider->QueryService(SID_AccessibleComparable, __uuidof(IAccessibleComparable), reinterpret_cast<void**>(&comparable));
+    return comparable;
+}
+
+void AccessibilityController::notificationReceived(PlatformUIElement element, const string& eventName)
+{
+    for (HashMap<PlatformUIElement, JSObjectRef>::iterator it = m_notificationListeners.begin(); it != m_notificationListeners.end(); ++it) {
+        COMPtr<IServiceProvider> thisServiceProvider(Query, it->first);
+        if (!thisServiceProvider)
+            continue;
+
+        COMPtr<IAccessibleComparable> thisComparable = comparableObject(thisServiceProvider);
+        if (!thisComparable)
+            continue;
+
+        COMPtr<IServiceProvider> elementServiceProvider(Query, element);
+        if (!elementServiceProvider)
+            continue;
+
+        COMPtr<IAccessibleComparable> elementComparable = comparableObject(elementServiceProvider);
+        if (!elementComparable)
+            continue;
+
+        BOOL isSame = FALSE;
+        thisComparable->isSameObject(elementComparable.get(), &isSame);
+        if (!isSame)
+            continue;
+
+        JSRetainPtr<JSStringRef> jsNotification(Adopt, JSStringCreateWithUTF8CString(eventName.c_str()));
+        JSValueRef argument = JSValueMakeString(frame->globalContext(), jsNotification.get());
+        JSObjectCallAsFunction(frame->globalContext(), it->second, NULL, 1, &argument, NULL);
+    }
+}
+
+void AccessibilityController::addNotificationListener(PlatformUIElement element, JSObjectRef functionCallback)
+{
+    if (!m_allEventsHook)
+        m_allEventsHook = SetWinEventHook(EVENT_MIN, EVENT_MAX, GetModuleHandle(0), notificationListenerProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
+
+    JSValueProtect(frame->globalContext(), functionCallback);
+    m_notificationListeners.add(element, functionCallback);
+}
index 9aff5e6..301112f 100644 (file)
@@ -26,6 +26,9 @@
 #include "config.h"
 #include "AccessibilityUIElement.h"
 
+#include "AccessibilityController.h"
+#include "DumpRenderTree.h"
+#include "FrameLoadDelegate.h"
 #include <JavaScriptCore/JSStringRef.h>
 #include <tchar.h>
 #include <string>
@@ -530,8 +533,11 @@ JSStringRef AccessibilityUIElement::url()
 
 bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallback)
 {
-    // FIXME: implement
-    return false;
+    if (!functionCallback)
+        return false;
+
+    sharedFrameLoadDelegate->accessibilityController()->addNotificationListener(m_element, functionCallback);
+    return true;
 }
 
 bool AccessibilityUIElement::isSelectable() const
index 54ec87b..499c57b 100644 (file)
@@ -32,6 +32,7 @@
 struct IWebFrame;
 struct IWebScriptWorld;
 struct IWebView;
+struct FrameLoadDelegate;
 struct PolicyDelegate;
 typedef const struct __CFString* CFStringRef;
 typedef struct HWND__* HWND;
@@ -60,4 +61,6 @@ unsigned worldIDForWorld(IWebScriptWorld*);
 
 extern UINT_PTR waitToDumpWatchdog;
 
+extern COMPtr<FrameLoadDelegate> sharedFrameLoadDelegate;
+
 #endif // DumpRenderTreeWin_h
index cc6653b..329c17f 100644 (file)
@@ -44,6 +44,8 @@ public:
 
     void resetToConsistentState();
 
+    AccessibilityController* accessibilityController() const { return m_accessibilityController.get(); }
+
     // IUnknown
     virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
     virtual ULONG STDMETHODCALLTYPE AddRef(void);