Add ability for image maps to be focused via tabbing
authorcfleizach@apple.com <cfleizach@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Jan 2010 18:06:41 +0000 (18:06 +0000)
committercfleizach@apple.com <cfleizach@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Jan 2010 18:06:41 +0000 (18:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=17513

Reviewed by Darin Adler.

WebCore:

Test: fast/events/tab-imagemap.html

* accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::focusedImageMapUIElement):
(WebCore::AXObjectCache::focusedUIElementForPage):
* accessibility/AXObjectCache.h:
* accessibility/AccessibilityImageMapLink.h:
(WebCore::AccessibilityImageMapLink::areaElement):
(WebCore::AccessibilityImageMapLink::mapElement):
(WebCore::AccessibilityImageMapLink::isImageMapLink):
* accessibility/AccessibilityObject.h:
(WebCore::AccessibilityObject::isImageMapLink):
* accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::accessibilityParentForImageMap):
* html/HTMLAreaElement.cpp:
(WebCore::HTMLAreaElement::getPath):
(WebCore::HTMLAreaElement::getRect):
(WebCore::HTMLAreaElement::imageElement):
(WebCore::HTMLAreaElement::isKeyboardFocusable):
(WebCore::HTMLAreaElement::isFocusable):
(WebCore::HTMLAreaElement::dispatchBlurEvent):
(WebCore::HTMLAreaElement::updateFocusAppearance):
(WebCore::HTMLAreaElement::supportsFocus):
* html/HTMLAreaElement.h:
* html/HTMLMapElement.cpp:
(WebCore::HTMLMapElement::imageElement):
* html/HTMLMapElement.h:
* platform/graphics/GraphicsContext.h:
* platform/graphics/cairo/GraphicsContextCairo.cpp:
(WebCore::GraphicsContext::drawFocusRing):
* platform/graphics/haiku/GraphicsContextHaiku.cpp:
(WebCore::GraphicsContext::drawFocusRing):
* platform/graphics/mac/GraphicsContextMac.mm:
(WebCore::drawFocusRingToContext):
(WebCore::GraphicsContext::drawFocusRing):
* platform/graphics/qt/GraphicsContextQt.cpp:
(WebCore::GraphicsContext::drawFocusRing):
* platform/graphics/skia/GraphicsContextSkia.cpp:
(WebCore::GraphicsContext::drawFocusRing):
* platform/graphics/win/GraphicsContextCGWin.cpp:
(WebCore::GraphicsContext::drawFocusRing):
* platform/graphics/wince/GraphicsContextWince.cpp:
(WebCore::GraphicsContext::drawFocusRing):
* platform/graphics/wx/GraphicsContextWx.cpp:
(WebCore::GraphicsContext::drawFocusRing):
* rendering/RenderImage.cpp:
(WebCore::RenderImage::paint):
(WebCore::RenderImage::paintFocusRings):
(WebCore::RenderImage::imageMap):
* rendering/RenderImage.h:
* rendering/RenderReplaced.h:

LayoutTests:

* fast/events/resources/tabindex-focus-blur-all.js:
(test):
(testProgrammaticFocus):
* fast/events/tab-imagemap-expected.txt: Added.
* fast/events/tab-imagemap.html: Added.
* fast/events/tabindex-focus-blur-all-expected.txt:

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/resources/tabindex-focus-blur-all.js
LayoutTests/fast/events/tab-imagemap-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/tab-imagemap.html [new file with mode: 0644]
LayoutTests/fast/events/tabindex-focus-blur-all-expected.txt
WebCore/ChangeLog
WebCore/accessibility/AXObjectCache.cpp
WebCore/accessibility/AXObjectCache.h
WebCore/accessibility/AccessibilityImageMapLink.h
WebCore/accessibility/AccessibilityObject.h
WebCore/accessibility/AccessibilityRenderObject.cpp
WebCore/html/HTMLAreaElement.cpp
WebCore/html/HTMLAreaElement.h
WebCore/html/HTMLMapElement.cpp
WebCore/html/HTMLMapElement.h
WebCore/platform/graphics/GraphicsContext.h
WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp
WebCore/platform/graphics/haiku/GraphicsContextHaiku.cpp
WebCore/platform/graphics/mac/GraphicsContextMac.mm
WebCore/platform/graphics/qt/GraphicsContextQt.cpp
WebCore/platform/graphics/skia/GraphicsContextSkia.cpp
WebCore/platform/graphics/win/GraphicsContextCGWin.cpp
WebCore/platform/graphics/wince/GraphicsContextWince.cpp
WebCore/platform/graphics/wx/GraphicsContextWx.cpp
WebCore/rendering/RenderImage.cpp
WebCore/rendering/RenderImage.h
WebCore/rendering/RenderReplaced.h

index 443d438ff75fe21157407040521cd8614297672c..9e10ff799e18d86a32ba787859ac67405fca66ba 100644 (file)
@@ -1,3 +1,17 @@
+2010-01-26  Chris Fleizach  <cfleizach@apple.com>
+
+        Reviewed by Darin Adler.
+
+        Add ability for image maps to be focused via tabbing
+        https://bugs.webkit.org/show_bug.cgi?id=17513
+
+        * fast/events/resources/tabindex-focus-blur-all.js:
+        (test):
+        (testProgrammaticFocus):
+        * fast/events/tab-imagemap-expected.txt: Added.
+        * fast/events/tab-imagemap.html: Added.
+        * fast/events/tabindex-focus-blur-all-expected.txt:
+
 2010-01-26  Simon Hausmann  <simon.hausmann@nokia.com>
 
         Reviewed by Holger Freyther.
index 1a6cc8152dba597e5e968d0fde3122e80b3f69e4..3355630ee679a0ecbeff2328e6ed42d7b0f501b8 100644 (file)
@@ -11,6 +11,7 @@ var focusedElem = null;
 var failedTestCount = 0;
 
 var tagNamesAlwaysFocused = ["A",
+                             "AREA",
                              "BUTTON",
                              "IFRAME",
                              "INPUT",
@@ -20,6 +21,8 @@ var tagNamesAlwaysFocused = ["A",
 
 var tagNamesTransferFocused = ["LABEL"]; // labels always transfer focus to the labeled element
 
+var noDisplayTagNamesWithFocus = ["AREA"];  // AREA elements can get focus, but are not displayed.
+
 function printToConsole(str)
 {
     if (!consoleOutput) {
@@ -49,7 +52,7 @@ function test()
     var homeBase = window.frames[1].document.getElementsByClassName('homebase');
     homeBase[0].focus();
 
-    var resultSummary = focusCount+" focus / "+blurCount+" blur events dispatched, and should be 329 / 329 ";
+    var resultSummary = focusCount+" focus / "+blurCount+" blur events dispatched, and should be 337 / 337 ";
     resultSummary += (focusCount==blurCount) ? "<span style='color:green'>PASSED</span><br>" : "<span style='color:red'>FAILED</span><br>";
     resultSummary += "Total of "+failedTestCount+" focus test(s) failed.";
     if (failedTestCount)
@@ -102,9 +105,9 @@ function testProgrammaticFocus(elem)
         elemThatShouldFocus = elem;
     else if (tagNamesAlwaysFocused.find(elem.tagName)) // special case form elements and other controls that are always focusable
         elemThatShouldFocus = elem;
-    
+
     // Hidden elements should not be focusable. https://bugs.webkit.org/show_bug.cgi?id=27099
-    if (document.defaultView.getComputedStyle(elem).display == "none")
+    if (document.defaultView.getComputedStyle(elem).display == "none" && !noDisplayTagNamesWithFocus.find(elem.tagName))
         elemThatShouldFocus = null;
 
     if (tagNamesTransferFocused.find(elem.tagName)) {
diff --git a/LayoutTests/fast/events/tab-imagemap-expected.txt b/LayoutTests/fast/events/tab-imagemap-expected.txt
new file mode 100644 (file)
index 0000000..9bcb5bc
--- /dev/null
@@ -0,0 +1,13 @@
+This tests that links in a image map are able to be reached through keyboard access and tabbing.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.activeElement.id is 'area1'
+PASS document.activeElement.id is 'area2'
+PASS document.activeElement.id is 'area3'
+PASS document.activeElement.id is 'area5'
+PASS document.activeElement.id is 'area6'
+PASS document.activeElement.id is 'area1'
+
diff --git a/LayoutTests/fast/events/tab-imagemap.html b/LayoutTests/fast/events/tab-imagemap.html
new file mode 100644 (file)
index 0000000..a19bbda
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../js/resources/js-test-style.css">
+<script>
+var successfullyParsed = false;
+</script>
+<script src="../js/resources/js-test-pre.js"></script>
+<body id="body">
+
+<map name="mymap">
+<area id="area1" shape="circle" coords="70,84,51" href="1">
+<area id="area2" shape="rect" coords="25,180,125,280" href="2">
+<area id="area3" shape="poly" coords="153,106,186,225,340,193,315,81,304,167" href="3">
+<area id="area4" shape="rect" coords="420,19,478,278" nohref>
+<area id="area5" shape="circle" coords="220,150,100" href="4">
+<area id="area6" shape="default" coords="0,0,500,300" href="5">
+<area id="area7" shape="rect" coords="1, 1, 10, 10" tabindex=-1 href="6">
+</map>
+
+<img src="resources/abe.png" width="500" height="300" alt="Image Map" usemap="#mymap" ismap>
+
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+
+    description("This tests that links in a image map are able to be reached through keyboard access and tabbing.");
+
+    eventSender.keyDown('\t');
+    shouldBe("document.activeElement.id", "'area1'");
+
+    eventSender.keyDown('\t');
+    shouldBe("document.activeElement.id", "'area2'");
+
+    eventSender.keyDown('\t');
+    shouldBe("document.activeElement.id", "'area3'");
+
+    // We'll skip over area4 because its nohref
+    eventSender.keyDown('\t');
+    shouldBe("document.activeElement.id", "'area5'");
+
+    eventSender.keyDown('\t');
+    shouldBe("document.activeElement.id", "'area6'");
+
+    // We'll skip over area7 because its tabindex=-1
+    eventSender.keyDown('\t');
+    shouldBe("document.activeElement.id", "'area1'");
+
+    successfullyParsed = true;
+</script>
+
+<script src="../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
index 2c5bf626db7f163946412825ef31b578f56a35d7..e26093ca934510bd5daa18f51ed7f6fe3faa7813 100644 (file)
@@ -1,2 +1,2 @@
-329 focus / 329 blur events dispatched, and should be 329 / 329 PASSED
+337 focus / 337 blur events dispatched, and should be 337 / 337 PASSED
 Total of 0 focus test(s) failed. PASSED
index f0767eec99c2134bba1a1f72b85b6324780df7d9..ef8dba15d8a1586ef3c3ec9545d37bbcd6dcff6e 100644 (file)
@@ -1,3 +1,62 @@
+2010-01-26  Chris Fleizach  <cfleizach@apple.com>
+
+        Reviewed by Darin Adler.
+
+        Add ability for image maps to be focused via tabbing
+        https://bugs.webkit.org/show_bug.cgi?id=17513
+
+        Test: fast/events/tab-imagemap.html
+
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::AXObjectCache::focusedImageMapUIElement):
+        (WebCore::AXObjectCache::focusedUIElementForPage):
+        * accessibility/AXObjectCache.h:
+        * accessibility/AccessibilityImageMapLink.h:
+        (WebCore::AccessibilityImageMapLink::areaElement):
+        (WebCore::AccessibilityImageMapLink::mapElement):
+        (WebCore::AccessibilityImageMapLink::isImageMapLink):
+        * accessibility/AccessibilityObject.h:
+        (WebCore::AccessibilityObject::isImageMapLink):
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore::AccessibilityRenderObject::accessibilityParentForImageMap):
+        * html/HTMLAreaElement.cpp:
+        (WebCore::HTMLAreaElement::getPath):
+        (WebCore::HTMLAreaElement::getRect):
+        (WebCore::HTMLAreaElement::imageElement):
+        (WebCore::HTMLAreaElement::isKeyboardFocusable):
+        (WebCore::HTMLAreaElement::isFocusable):
+        (WebCore::HTMLAreaElement::dispatchBlurEvent):
+        (WebCore::HTMLAreaElement::updateFocusAppearance):
+        (WebCore::HTMLAreaElement::supportsFocus):
+        * html/HTMLAreaElement.h:
+        * html/HTMLMapElement.cpp:
+        (WebCore::HTMLMapElement::imageElement):
+        * html/HTMLMapElement.h:
+        * platform/graphics/GraphicsContext.h:
+        * platform/graphics/cairo/GraphicsContextCairo.cpp:
+        (WebCore::GraphicsContext::drawFocusRing):
+        * platform/graphics/haiku/GraphicsContextHaiku.cpp:
+        (WebCore::GraphicsContext::drawFocusRing):
+        * platform/graphics/mac/GraphicsContextMac.mm:
+        (WebCore::drawFocusRingToContext):
+        (WebCore::GraphicsContext::drawFocusRing):
+        * platform/graphics/qt/GraphicsContextQt.cpp:
+        (WebCore::GraphicsContext::drawFocusRing):
+        * platform/graphics/skia/GraphicsContextSkia.cpp:
+        (WebCore::GraphicsContext::drawFocusRing):
+        * platform/graphics/win/GraphicsContextCGWin.cpp:
+        (WebCore::GraphicsContext::drawFocusRing):
+        * platform/graphics/wince/GraphicsContextWince.cpp:
+        (WebCore::GraphicsContext::drawFocusRing):
+        * platform/graphics/wx/GraphicsContextWx.cpp:
+        (WebCore::GraphicsContext::drawFocusRing):
+        * rendering/RenderImage.cpp:
+        (WebCore::RenderImage::paint):
+        (WebCore::RenderImage::paintFocusRings):
+        (WebCore::RenderImage::imageMap):
+        * rendering/RenderImage.h:
+        * rendering/RenderReplaced.h:
+
 2010-01-26  Pavel Feldman  <pfeldman@chromium.org>
 
         Reviewed by Timothy Hatcher.
index 1cd917f9cce8232570413d02f31b06211898d089..60ac97a7fda3b831075bbb5c0747ef88e9063f1c 100644 (file)
@@ -50,6 +50,8 @@
 #include "AccessibilityTableRow.h"
 #include "FocusController.h"
 #include "Frame.h"
+#include "HTMLAreaElement.h"
+#include "HTMLImageElement.h"
 #include "HTMLNames.h"
 #if ENABLE(VIDEO)
 #include "MediaControlElements.h"
@@ -84,6 +86,35 @@ AXObjectCache::~AXObjectCache()
     }
 }
 
+AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
+{
+    // Find the corresponding accessibility object for the HTMLAreaElement. This should be
+    // in the list of children for its corresponding image.
+    if (!areaElement)
+        return 0;
+    
+    HTMLImageElement* imageElement = areaElement->imageElement();
+    if (!imageElement)
+        return 0;
+    
+    AccessibilityObject* axRenderImage = areaElement->document()->axObjectCache()->getOrCreate(imageElement->renderer());
+    if (!axRenderImage)
+        return 0;
+    
+    AccessibilityObject::AccessibilityChildrenVector imageChildren = axRenderImage->children();
+    unsigned count = imageChildren.size();
+    for (unsigned k = 0; k < count; ++k) {
+        AccessibilityObject* child = imageChildren[k].get();
+        if (!child->isImageMapLink())
+            continue;
+        
+        if (static_cast<AccessibilityImageMapLink*>(child)->areaElement() == areaElement)
+            return child;
+    }    
+    
+    return 0;
+}
+    
 AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
 {
     // get the focused node in the page
@@ -92,6 +123,9 @@ AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
     if (!focusedNode)
         focusedNode = focusedDocument;
 
+    if (focusedNode->hasTagName(areaTag))
+        return focusedImageMapUIElement(static_cast<HTMLAreaElement*>(focusedNode));
+    
     RenderObject* focusedNodeRenderer = focusedNode->renderer();
     if (!focusedNodeRenderer)
         return 0;
index ee51ff57a5b1f104b5ed25065ac4a5b31f86d8f5..450396fce88ad57cd170b02e91d40e85df8a4173 100644 (file)
@@ -42,6 +42,7 @@ class WebCoreTextMarker;
 
 namespace WebCore {
 
+class HTMLAreaElement;
 class Node;
 class Page;
 class RenderObject;
@@ -135,6 +136,8 @@ private:
     Vector<pair<RefPtr<AccessibilityObject>, AXNotification> > m_notificationsToPost;
     void notificationPostTimerFired(Timer<AXObjectCache>*);
     
+    static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
+    
     AXID getAXID(AccessibilityObject*);
     bool nodeIsAriaType(Node*, String role);
 };
index 3024f379e2bfeb711bc6874abc88415226924852..ca4c62c71be3916d74cbf1993c3f5714424304d9 100644 (file)
@@ -44,13 +44,17 @@ public:
     virtual ~AccessibilityImageMapLink();
     
     void setHTMLAreaElement(HTMLAreaElement* element) { m_areaElement = element; }
+    HTMLAreaElement* areaElement() const { return m_areaElement; }
+    
     void setHTMLMapElement(HTMLMapElement* element) { m_mapElement = element; }    
+    HTMLMapElement* mapElement() const { return m_mapElement; }
+    
     void setParent(AccessibilityObject* parent) { m_parent = parent; }
         
     virtual AccessibilityRole roleValue() const;
     virtual bool accessibilityIsIgnored() const { return false; }
     virtual bool isEnabled() const { return true; }
-
+    
     virtual AccessibilityObject* parentObject() const;
     virtual Element* anchorElement() const;
     virtual Element* actionElement() const;
@@ -70,6 +74,8 @@ private:
     HTMLAreaElement* m_areaElement;
     HTMLMapElement* m_mapElement;
     AccessibilityObject* m_parent;
+    
+    virtual bool isImageMapLink() const { return true; }
 };
     
 } // namespace WebCore
index 363681dede3d81ea68b7b2d12f55a5a2ca28ce2e..e4b1d991f840c2a28644f9acfacb0a181b441d77 100644 (file)
@@ -285,6 +285,7 @@ public:
     virtual bool isFieldset() const { return false; }
     virtual bool isGroup() const { return false; }
     virtual bool isARIATreeGridRow() const { return false; }
+    virtual bool isImageMapLink() const { return false; }
     virtual bool isMenuList() const { return false; }
     virtual bool isMenuListPopup() const { return false; }
     virtual bool isMenuListOption() const { return false; }
index 45228ec10d6566c248762b3cda14d5e00cff3af6..8cd51aef4e8cc6337e85aa049a36a3a623edeadc 100644 (file)
@@ -2049,24 +2049,14 @@ AXObjectCache* AccessibilityRenderObject::axObjectCache() const
 AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const
 {
     // find an image that is using this map
-    if (!m_renderer || !map)
+    if (!map)
         return 0;
 
-    String mapName = map->getName().string().lower();
-    RefPtr<HTMLCollection> coll = m_renderer->document()->images();
-    for (Node* curr = coll->firstItem(); curr; curr = coll->nextItem()) {
-        RenderObject* obj = curr->renderer();
-        if (!obj || !curr->hasTagName(imgTag))
-            continue;
-        
-        // The HTMLImageElement's useMap() value includes the '#' symbol at the beginning,
-        // which has to be stripped off
-        String useMapName = static_cast<HTMLImageElement*>(curr)->getAttribute(usemapAttr).string().substring(1).lower();
-        if (useMapName == mapName)
-            return axObjectCache()->getOrCreate(obj);
-    }
+    HTMLImageElement* imageElement = map->imageElement();
+    if (!imageElement)
+        return 0;
     
-    return 0;
+    return axObjectCache()->getOrCreate(imageElement->renderer());
 }
     
 void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& result)
index b202caea46eee7f54e25fbda1acb42016dd1b0f6..b862f6943264083904f789edd3f4c0727cdbb18b 100644 (file)
@@ -22,6 +22,8 @@
 #include "config.h"
 #include "HTMLAreaElement.h"
 
+#include "HTMLImageElement.h"
+#include "HTMLMapElement.h"
 #include "HTMLNames.h"
 #include "HitTestResult.h"
 #include "MappedAttribute.h"
@@ -82,13 +84,27 @@ bool HTMLAreaElement::mapMouseEvent(int x, int y, const IntSize& size, HitTestRe
     return true;
 }
 
-IntRect HTMLAreaElement::getRect(RenderObject* obj) const
+Path HTMLAreaElement::getPath(RenderObject* obj) const
 {
+    if (!obj)
+        return Path();
+    
     // FIXME: This doesn't work correctly with transforms.
     FloatPoint absPos = obj->localToAbsolute();
-    Path p = getRegion(m_lastSize);
+
+    // Default should default to the size of the containing object.
+    IntSize size = m_lastSize;
+    if (m_shape == Default)
+        size = obj->absoluteOutlineBounds().size();
+    
+    Path p = getRegion(size);
     p.translate(absPos - FloatPoint());
-    return enclosingIntRect(p.boundingRect());
+    return p;
+}
+    
+IntRect HTMLAreaElement::getRect(RenderObject* obj) const
+{
+    return enclosingIntRect(getPath(obj).boundingRect());
 }
 
 Path HTMLAreaElement::getRegion(const IntSize& size) const
@@ -161,11 +177,58 @@ void HTMLAreaElement::setNoHref(bool noHref)
 {
     setAttribute(nohrefAttr, noHref ? "" : 0);
 }
+    
+HTMLImageElement* HTMLAreaElement::imageElement() const
+{
+    Node* mapElement = parent();
+    if (!mapElement->hasTagName(mapTag))
+        return 0;
+    
+    return static_cast<HTMLMapElement*>(mapElement)->imageElement();
+}
 
+bool HTMLAreaElement::isKeyboardFocusable(KeyboardEvent*) const
+{
+    return supportsFocus();
+}
+
+bool HTMLAreaElement::isFocusable() const
+{
+    return supportsFocus();
+}
+    
+void HTMLAreaElement::dispatchBlurEvent()
+{
+    HTMLAnchorElement::dispatchBlurEvent();
+    
+    // On a blur, we might need to remove our focus rings by repainting.
+    updateFocusAppearance(false);
+}
+    
+void HTMLAreaElement::updateFocusAppearance(bool restorePreviousSelection)
+{
+    Node* parent = parentNode();
+    if (!parent || !parent->hasTagName(mapTag))
+        return;
+    
+    HTMLImageElement* imageElement = static_cast<HTMLMapElement*>(parent)->imageElement();
+    if (!imageElement)
+        return;
+    
+    // This will handle scrolling to the image if necessary.
+    imageElement->updateFocusAppearance(restorePreviousSelection);
+    
+    RenderObject* imageRenderer = imageElement->renderer();
+    if (imageRenderer)
+        imageRenderer->setNeedsLayout(true);
+}
+    
 bool HTMLAreaElement::supportsFocus() const
 {
-    // Skip HTMLAnchorElements isLink() check.
-    return HTMLElement::supportsFocus();
+    // If the AREA element was a link, it should support focus.
+    // The inherited method is not used because it assumes that a render object must exist 
+    // for the element to support focus. AREA elements do not have render objects.
+    return isLink();
 }
 
 String HTMLAreaElement::target() const
index 7b2497c3d5a236cbc7b277144584e641510dbb8e..f8e25649ccc3bd79d6339069d7385ba6ea39d8cc 100644 (file)
@@ -30,6 +30,7 @@
 namespace WebCore {
 
 class HitTestResult;
+class HTMLImageElement;
 class Path;
 
 class HTMLAreaElement : public HTMLAnchorElement {
@@ -41,7 +42,11 @@ public:
     bool mapMouseEvent(int x, int y, const IntSize&, HitTestResult&);
 
     IntRect getRect(RenderObject*) const;
-
+    Path getPath(RenderObject*) const;
+    
+    // Convenience method to get the parent map's image.
+    HTMLImageElement* imageElement() const;
+    
     KURL href() const;
 
     bool noHref() const;
@@ -55,7 +60,11 @@ private:
     virtual void parseMappedAttribute(MappedAttribute*);
     virtual bool supportsFocus() const;
     virtual String target() const;
-
+    virtual bool isKeyboardFocusable(KeyboardEvent*) const;
+    virtual bool isFocusable() const;
+    virtual void updateFocusAppearance(bool /*restorePreviousSelection*/);
+    virtual void dispatchBlurEvent();
+    
     enum Shape { Default, Poly, Rect, Circle, Unknown };
     Path getRegion(const IntSize&) const;
 
index 2d623b268eedc095228170d70a0c3033d77db1e4..9617afc7c4d5ee076d6f4f35b6aeb1fe07cb8c60 100644 (file)
 #include "Document.h"
 #include "HTMLAreaElement.h"
 #include "HTMLCollection.h"
+#include "HTMLImageElement.h"
 #include "HTMLNames.h"
 #include "HitTestResult.h"
 #include "IntSize.h"
 #include "MappedAttribute.h"
+#include "RenderObject.h"
 
 using namespace std;
 
@@ -75,6 +77,24 @@ bool HTMLMapElement::mapMouseEvent(int x, int y, const IntSize& size, HitTestRes
     return defaultArea;
 }
 
+HTMLImageElement* HTMLMapElement::imageElement() const
+{
+    RefPtr<HTMLCollection> coll = renderer()->document()->images();
+    for (Node* curr = coll->firstItem(); curr; curr = coll->nextItem()) {
+        if (!curr->hasTagName(imgTag))
+            continue;
+        
+        // The HTMLImageElement's useMap() value includes the '#' symbol at the beginning,
+        // which has to be stripped off.
+        HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(curr);
+        String useMapName = imageElement->getAttribute(usemapAttr).string().substring(1);
+        if (equalIgnoringCase(useMapName, m_name))
+            return imageElement;
+    }
+    
+    return 0;    
+}
+    
 void HTMLMapElement::parseMappedAttribute(MappedAttribute* attr)
 {
     const QualifiedName& attrName = attr->name();
index 2a6eb8daec50667474e567640ab0e9f6b3913a93..f40e78ec18def260039a68d930ff633c1b5e904c 100644 (file)
@@ -29,7 +29,8 @@ namespace WebCore {
 
 class IntSize;
 class HitTestResult;
-
+class HTMLImageElement;
+    
 class HTMLMapElement : public HTMLElement {
 public:
     HTMLMapElement(const QualifiedName&, Document*);
@@ -44,7 +45,8 @@ public:
     virtual void parseMappedAttribute(MappedAttribute*);
 
     bool mapMouseEvent(int x, int y, const IntSize&, HitTestResult&);
-
+    
+    HTMLImageElement* imageElement() const;
     PassRefPtr<HTMLCollection> areas();
 
     String name() const;
index 9d7bac3cab192307df83cbfde53c3cfa6ac3ac8d..1946b979a8ffea0f074120cfb1ab3b3a78ef909c 100644 (file)
@@ -263,6 +263,7 @@ namespace WebCore {
         void clearShadow();
 
         void drawFocusRing(const Vector<IntRect>&, int width, int offset, const Color&);
+        void drawFocusRing(const Vector<Path>&, int width, int offset, const Color&);
 
         void setLineCap(LineCap);
         void setLineDash(const DashArray&, float dashOffset);
index 9e1cd1ed3198a6de5431296ecae8be13825f1cd2..d866b6cdd6925378edbe68cb457908f33b8ac868 100644 (file)
@@ -606,6 +606,11 @@ void GraphicsContext::clipPath(WindRule clipRule)
     cairo_clip(cr);
 }
 
+void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
+{
+    // FIXME: implement
+}
+
 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color)
 {
     if (paintingDisabled())
index 64d117942a1fb7439d7cd5cf7b3dba417a9dbc11..6038c6aa0de19546d751e0d2847f9e656c0b5e41 100644 (file)
@@ -204,6 +204,11 @@ void GraphicsContext::clip(const FloatRect& rect)
     m_data->m_view->ConstrainClippingRegion(&region);
 }
 
+void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
+{
+    // FIXME: implement
+}
+
 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color)
 {
     if (paintingDisabled())
index 5cf127b3d8b42ffd5b44efc473ce9b3e64ee15d8..5f111f6e2f1b6920cf1260a622fa6007b8c77cf1 100644 (file)
@@ -43,6 +43,38 @@ namespace WebCore {
 // calls in this file are all exception-safe, so we don't block
 // exceptions for those.
 
+static void drawFocusRingToContext(CGContextRef context, RetainPtr<CGPathRef> focusRingPath, RetainPtr<CGColorRef> colorRef, int radius)
+{
+#ifdef BUILDING_ON_TIGER
+    CGContextBeginTransparencyLayer(context, 0);
+#endif
+    CGContextBeginPath(context);
+    CGContextAddPath(context, focusRingPath.get());
+    wkDrawFocusRing(context, colorRef.get(), radius);
+#ifdef BUILDING_ON_TIGER
+    CGContextEndTransparencyLayer(context);
+#endif
+}
+
+void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
+{
+    if (paintingDisabled())
+        return;
+    
+    int radius = (width - 1) / 2;
+    offset += radius;
+    RetainPtr<CGColorRef> colorRef;
+    if (color.isValid())
+        colorRef.adoptCF(createCGColor(color));
+    
+    RetainPtr<CGMutablePathRef> focusRingPath(AdoptCF, CGPathCreateMutable());
+    unsigned pathCount = paths.size();
+    for (unsigned i = 0; i < pathCount; i++)
+        CGPathAddPath(focusRingPath.get(), 0, paths[i].platformPath());
+    
+    drawFocusRingToContext(platformContext(), focusRingPath, colorRef, radius);
+}    
+    
 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
 {
     if (paintingDisabled())
@@ -59,16 +91,7 @@ void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int
     for (unsigned i = 0; i < rectCount; i++)
         CGPathAddRect(focusRingPath.get(), 0, CGRectInset(rects[i], -offset, -offset));
 
-    CGContextRef context = platformContext();
-#ifdef BUILDING_ON_TIGER
-    CGContextBeginTransparencyLayer(context, NULL);
-#endif
-    CGContextBeginPath(context);
-    CGContextAddPath(context, focusRingPath.get());
-    wkDrawFocusRing(context, colorRef.get(), radius);
-#ifdef BUILDING_ON_TIGER
-    CGContextEndTransparencyLayer(context);
-#endif
+    drawFocusRingToContext(platformContext(), focusRingPath, colorRef, radius);
 }
 
 #ifdef BUILDING_ON_TIGER // Post-Tiger's setCompositeOperation() is defined in GraphicsContextCG.cpp.
index 043ab0b8249e5eb056025d94ff5900de70cd6848..b78a6e8ac5e62f71aad02ae7fd7e8aa48974b79f 100644 (file)
@@ -795,6 +795,11 @@ void GraphicsContext::clipPath(WindRule clipRule)
     p->setClipPath(newPath);
 }
 
+void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
+{
+    // FIXME: implement
+}
+
 /**
  * Focus ring handling is not handled here. Qt style in 
  * RenderTheme handles drawing focus on widgets which 
index 7d4c01a047619c60cfefc357344c380cf723600e..985442c781f8295c49de342386c692bce9bf95ea 100644 (file)
@@ -501,6 +501,11 @@ void GraphicsContext::drawEllipse(const IntRect& elipseRect)
     }
 }
 
+void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
+{
+    // FIXME: implement
+}
+
 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color)
 {
     if (paintingDisabled())
index e5ebaf0cec542a1f97239bdca0224051dae54db8..47a51de886d14263c201277be0738bc946ad34b2 100644 (file)
@@ -124,6 +124,11 @@ void GraphicsContext::drawWindowsBitmap(WindowsBitmap* image, const IntPoint& po
     CGContextDrawImage(m_data->m_cgContext.get(), CGRectMake(point.x(), point.y(), image->size().width(), image->size().height()), cgImage.get());   
 }
 
+void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
+{
+    // FIXME: implement
+}
+
 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
 {
     if (paintingDisabled())
index 608cdd1c7bd8a34e6cc001c4fe8789fa1594251b..0e387f5e4cfb8c77bff82710ccd2129115e6967c 100644 (file)
@@ -1002,6 +1002,11 @@ void GraphicsContext::clipOut(const IntRect& rect)
     ExcludeClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom());
 }
 
+void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
+{
+    // FIXME: implement
+}
+
 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
 {
     if (!m_data->m_opacity || paintingDisabled())
index f5dfe00194d515a275e70dca090ddc2d569c3c6b..839bc59de34d0b9c205695a8b3789960ec27cc2f 100644 (file)
@@ -270,6 +270,11 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef
     notImplemented();
 }
 
+void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
+{
+    // FIXME: implement
+}
+
 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
 {
     if (paintingDisabled())
index c10ab8e0006a8d1d2039bf50098882284d56b8eb..a14ab2753c58e6e9903484165b43227173571712 100644 (file)
 #include "config.h"
 #include "RenderImage.h"
 
+#include "Frame.h"
 #include "GraphicsContext.h"
+#include "HTMLAreaElement.h"
+#include "HTMLCollection.h"
 #include "HTMLImageElement.h"
 #include "HTMLInputElement.h"
 #include "HTMLMapElement.h"
 #include "HTMLNames.h"
 #include "HitTestResult.h"
 #include "Page.h"
+#include "RenderTheme.h"
 #include "RenderView.h"
+#include "SelectionController.h"
 #include <wtf/CurrentTime.h>
 #include <wtf/UnusedParam.h>
 
@@ -425,6 +430,51 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, int tx, int ty)
     }
 }
 
+void RenderImage::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+    RenderReplaced::paint(paintInfo, tx, ty);
+    
+    if (paintInfo.phase == PaintPhaseOutline)
+        paintFocusRings(paintInfo, style());
+}
+    
+void RenderImage::paintFocusRings(PaintInfo& paintInfo, const RenderStyle* style)
+{
+    // Don't draw focus rings if printing.
+    if (document()->printing() || !document()->frame()->selection()->isFocusedAndActive())
+        return;
+    
+    if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints())
+        return;
+
+    HTMLMapElement* mapElement = imageMap();
+    if (!mapElement)
+        return;
+    
+    Document* document = mapElement->document();
+    if (!document)
+        return;
+    
+    Node* focusedNode = document->focusedNode();
+    if (!focusedNode)
+        return;
+    
+    RefPtr<HTMLCollection> areas = mapElement->areas();
+    unsigned numAreas = areas->length();
+    
+    // FIXME: Clip the paths to the image bounding box.
+    for (unsigned k = 0; k < numAreas; ++k) {
+        HTMLAreaElement* areaElement = static_cast<HTMLAreaElement*>(areas->item(k));
+        if (focusedNode != areaElement)
+            continue;
+        
+        Vector<Path> focusRingPaths;
+        focusRingPaths.append(areaElement->getPath(this));
+        paintInfo.context->drawFocusRing(focusRingPaths, style->outlineWidth(), style->outlineOffset(), style->outlineColor());
+        break;
+    }
+}
+    
 void RenderImage::paintIntoRect(GraphicsContext* context, const IntRect& rect)
 {
     if (!hasImage() || errorOccurred() || rect.width() <= 0 || rect.height() <= 0)
@@ -445,7 +495,7 @@ int RenderImage::minimumReplacedHeight() const
     return errorOccurred() ? intrinsicSize().height() : 0;
 }
 
-HTMLMapElement* RenderImage::imageMap()
+HTMLMapElement* RenderImage::imageMap() const
 {
     HTMLImageElement* i = node() && node()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(node()) : 0;
     return i ? i->document()->getImageMap(i->getAttribute(usemapAttr)) : 0;
index ab94667a2e6cdf0a217b376cdb0ed18bd5d337e6..bc5e2d81f8c00ac030c803107cf03b5524bee929 100644 (file)
@@ -43,7 +43,7 @@ public:
     void setCachedImage(CachedImage*);
     CachedImage* cachedImage() const { return m_cachedImage.get(); }
 
-    HTMLMapElement* imageMap();
+    HTMLMapElement* imageMap() const;
 
     void resetAnimation();
 
@@ -58,6 +58,8 @@ protected:
     virtual void imageChanged(WrappedImagePtr, const IntRect* = 0);
 
     virtual void paintIntoRect(GraphicsContext*, const IntRect&);
+    void paintFocusRings(PaintInfo&, const RenderStyle*);
+    virtual void paint(PaintInfo&, int tx, int ty);
 
     bool isWidthSpecified() const;
     bool isHeightSpecified() const;
index 0ba6b8abc278f476bfbaeb62c49486d5eb076289..bcf565da2056b1964e1a0ee6015fd25885ccd701 100644 (file)
@@ -46,6 +46,7 @@ protected:
     void setIntrinsicSize(const IntSize&);
     virtual void intrinsicSizeChanged();
 
+    virtual void paint(PaintInfo&, int tx, int ty);
     bool shouldPaint(PaintInfo&, int& tx, int& ty);
     void adjustOverflowForBoxShadowAndReflect();
     IntRect localSelectionRect(bool checkWhetherSelected = true) const;
@@ -62,7 +63,6 @@ private:
 
     virtual int minimumReplacedHeight() const { return 0; }
 
-    virtual void paint(PaintInfo&, int tx, int ty);
     virtual void paintReplaced(PaintInfo&, int /*tx*/, int /*ty*/) { }
 
     virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);