WebCore/ChangeLog

 12010-07-02 Erik Arvidsson <arv@chromium.org>
 2
 3 Reviewed by ojan@chromium.org.
 4
 5 Fix issue where a contextmenu event was reporting the wrong target if
 6 the context menu was shown due to pressing the context menu key
 7 (or Shift+F10).
 8
 9 Split sendContextMenuForEvent into one case for keyboard events and use
 10 that when the contextmenu event should be dispatched due to a keypress.
 11
 12 For the keboard case we now use the focused node as the target for the
 13 event and use the clipped rect to determine the position of the menu.
 14
 15 https://bugs.webkit.org/show_bug.cgi?id=38129
 16
 17 Use manual test since DRT does not handle context menu keys.
 18
 19 * manual-tests/win/contextmenu-key.html: Added.
 20 * page/EventHandler.cpp:
 21 (WebCore::EventHandler::sendContextMenuEvent):
 22 (WebCore::EventHandler::sendContextMenuEventForKey):
 23 * page/EventHandler.h:
 24
1252010-07-02 Abhishek Arya <inferno@chromium.org>
226
327 Reviewed by Darin Fisher.
62399

WebCore/manual-tests/win/contextmenu-key.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<style>
 5
 6#outer {
 7 overflow: auto;
 8 width: 200px;
 9 height: 200px;
 10}
 11
 12#inner {
 13 position: relative;
 14 height: 400px;
 15}
 16
 17#inner:focus {
 18 background-color: lightblue;
 19}
 20
 21#inner:active {
 22 background-color: blue;
 23}
 24
 25#h, #h2 {
 26 background: rgba(255, 255, 255, 0);
 27}
 28
 29#h {
 30 position: absolute;
 31 height: 200px;
 32 width: 200px;
 33}
 34
 35#h2 {
 36 position: absolute;
 37 top: 200px;
 38 height: 200px;
 39 width: 100%;
 40}
 41
 42#h:hover,
 43#h2:hover {
 44 background: pink;
 45}
 46
 47#h:active,
 48#h2:active {
 49 background: red;
 50}
 51
 52pre {
 53 position: absolute;
 54 left: 250px;
 55 top: 80px;
 56}
 57
 58</style>
 59</head>
 60<body>
 61
 62<p>Manual test for <a href="https://bugs.webkit.org/show_bug.cgi?id=38129">bug 38129</a></p>
 63
 64<p>Click the div below and press the context menu key on your keyboard (Shift+F10 also works)</p>
 65
 66<div id=outer>
 67 <div id=inner tabindex=0>
 68 <div id=h2></div>
 69 </div>
 70</div>
 71
 72<div id=h></div>
 73
 74<pre></pre>
 75
 76<script>
 77
 78function cs(el)
 79{
 80 if (window.getComputedStyle)
 81 return window.getComputedStyle(el, '');
 82 return el.currentStyle;
 83}
 84
 85document.addEventListener('contextmenu', function(e)
 86{
 87 var inner = document.querySelector('#inner');
 88 var outer = document.querySelector('#outer');
 89 var h = document.querySelector('#h');
 90 var h2 = document.querySelector('#h2');
 91 var result = [];
 92
 93 result.push(e.target, document.querySelector('#inner'));
 94 result.push(cs(inner, '').backgroundColor, 'rgb(0, 0, 255)');
 95 result.push(cs(h, '').backgroundColor, 'rgba(255, 255, 255, 0)');
 96 result.push(cs(h2, '').backgroundColor, 'rgba(255, 255, 255, 0)');
 97
 98 var s = '';
 99 for (var i = 0; i < result.length; i += 2) {
 100 s += result[i] + ' == ' + result[i + 1] + ' - ' +
 101 (result[i] == result[i + 1] ? 'PASS' : 'FAIL') + '<br>';
 102 }
 103
 104 document.querySelector('pre').innerHTML = s;
 105
 106 return true;
 107}, false);
 108
 109</script>
 110
 111</body>
 112</html>
0

WebCore/page/EventHandler.cpp

@@bool EventHandler::sendContextMenuEvent(
20162016 }
20172017#endif
20182018
2019  swallowEvent = dispatchMouseEvent(eventNames().contextmenuEvent, mev.targetNode(), true, 0, event, true);
 2019 swallowEvent = dispatchMouseEvent(eventNames().contextmenuEvent, mev.targetNode(), true, 0, event, false);
20202020
20212021 return swallowEvent;
20222022}
 2023
 2024bool EventHandler::sendContextMenuEventForKey()
 2025{
 2026 FrameView* view = m_frame->view();
 2027 if (!view)
 2028 return false;
 2029
 2030 Document* doc = m_frame->document();
 2031 if (!doc)
 2032 return false;
 2033
 2034 static const int kContextMenuMargin = 1;
 2035
 2036#if OS(WINDOWS)
 2037 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
 2038#else
 2039 int rightAligned = 0;
 2040#endif
 2041 IntPoint location;
 2042
 2043 Node* focusedNode = doc->focusedNode();
 2044 SelectionController* selectionController = m_frame->selection();
 2045 Position start = selectionController->selection().start();
 2046
 2047 if (start.node() && (selectionController->rootEditableElement() || selectionController->isRange())) {
 2048 RenderObject* renderer = start.node()->renderer();
 2049 if (!renderer)
 2050 return false;
 2051
 2052 RefPtr<Range> selection = selectionController->toNormalizedRange();
 2053 IntRect firstRect = m_frame->firstRectForRange(selection.get());
 2054
 2055 int x = rightAligned ? firstRect.right() : firstRect.x();
 2056 location = IntPoint(x, firstRect.bottom());
 2057 } else if (focusedNode) {
 2058 RenderBoxModelObject* box = focusedNode->renderBoxModelObject();
 2059 IntRect clippedRect = box->absoluteClippedOverflowRect();
 2060 location = clippedRect.bottomLeft();
 2061 } else {
 2062 location = IntPoint(
 2063 rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
 2064 kContextMenuMargin);
 2065 }
 2066
 2067 m_frame->view()->setCursor(pointerCursor());
 2068
 2069 IntPoint position = view->contentsToWindow(location);
 2070 IntPoint globalPosition = view->contentsToScreen(IntRect(location, IntSize())).location();
 2071
 2072 Node* targetNode = doc->focusedNode();
 2073 if (!targetNode)
 2074 targetNode = doc;
 2075
 2076 // Use the focused node as the target for hover and active.
 2077 HitTestResult result(position);
 2078 result.setInnerNode(targetNode);
 2079 HitTestRequest request(HitTestRequest::Active);
 2080 doc->renderView()->layer()->updateHoverActiveState(request, result);
 2081 doc->updateStyleIfNeeded();
 2082
 2083 // The contextmenu event is a mouse event even when invoked using the keyboard.
 2084 // This is required for web compatibility.
 2085
 2086#if OS(WINDOWS)
 2087 MouseEventType eventType = MouseEventReleased;
 2088#else
 2089 MouseEventType eventType = MouseEventPressed;
 2090#endif
 2091
 2092 PlatformMouseEvent mouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WTF::currentTime());
 2093
 2094 return dispatchMouseEvent(eventNames().contextmenuEvent, targetNode, true, 0, mouseEvent, false);
 2095}
 2096
20232097#endif // ENABLE(CONTEXT_MENUS)
20242098
20252099void EventHandler::scheduleHoverStateUpdate()
61791

WebCore/page/EventHandler.h

@@public:
157157
158158#if ENABLE(CONTEXT_MENUS)
159159 bool sendContextMenuEvent(const PlatformMouseEvent&);
 160 bool sendContextMenuEventForKey();
160161#endif
161162
162163 void setMouseDownMayStartAutoscroll() { m_mouseDownMayStartAutoscroll = true; }
61791

WebKit/chromium/ChangeLog

 12010-07-02 Erik Arvidsson <arv@chromium.org>
 2
 3 Reviewed by ojan@chromium.org.
 4
 5 Fix issue where a contextmenu event was reporting the wrong target
 6 if the context menu was shown due to pressing the context menu key
 7 (or Shift+F10).
 8
 9 https://bugs.webkit.org/show_bug.cgi?id=38129
 10
 11 * src/WebViewImpl.cpp:
 12 (WebKit::WebViewImpl::sendContextMenuEvent):
 13
1142010-07-02 Yury Semikhatsky <yurys@chromium.org>
215
316 Reviewed by Pavel Feldman.
62399

WebKit/chromium/src/WebViewImpl.cpp

@@bool WebViewImpl::touchEvent(const WebTo
661661}
662662#endif
663663
664 // The WebViewImpl::SendContextMenuEvent function is based on the Webkit
665 // function
666 // bool WebView::handleContextMenuEvent(WPARAM wParam, LPARAM lParam) in
667 // webkit\webkit\win\WebView.cpp. The only significant change in this
668 // function is the code to convert from a Keyboard event to the Right
669 // Mouse button up event.
670 //
671 // This function is an ugly copy/paste and should be cleaned up when the
672 // WebKitWin version is cleaned: https://bugs.webkit.org/show_bug.cgi?id=20438
673664#if OS(WINDOWS) || OS(LINUX)
674 // FIXME: implement on Mac
 665// Mac has no way to open a context menu based on a keyboard event.
675666bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event)
676667{
677  static const int kContextMenuMargin = 1;
678  Frame* mainFrameImpl = page()->mainFrame();
679  FrameView* view = mainFrameImpl->view();
680  if (!view)
681  return false;
682 
683  IntPoint coords(-1, -1);
684 #if OS(WINDOWS)
685  int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
686 #else
687  int rightAligned = 0;
688 #endif
689  IntPoint location;
690 
691 
692  Frame* focusedFrame = page()->focusController()->focusedOrMainFrame();
693  Node* focusedNode = focusedFrame->document()->focusedNode();
694  Position start = mainFrameImpl->selection()->selection().start();
695 
696  if (start.node()) {
697  RenderObject* renderer = start.node()->renderer();
698  if (!renderer)
699  return false;
700 
701  RefPtr<Range> selection = mainFrameImpl->selection()->toNormalizedRange();
702  IntRect firstRect = mainFrameImpl->firstRectForRange(selection.get());
703 
704  int x = rightAligned ? firstRect.right() : firstRect.x();
705  location = IntPoint(x, firstRect.bottom());
706  } else if (focusedNode)
707  location = focusedNode->getRect().bottomLeft();
708  else {
709  location = IntPoint(
710  rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
711  kContextMenuMargin);
712  }
713 
714  location = view->contentsToWindow(location);
715  // FIXME: The IntSize(0, -1) is a hack to get the hit-testing to result in
716  // the selected element. Ideally we'd have the position of a context menu
717  // event be separate from its target node.
718  coords = location + IntSize(0, -1);
719 
720668 // The contextMenuController() holds onto the last context menu that was
721669 // popped up on the page until a new one is created. We need to clear
722670 // this menu before propagating the event through the DOM so that we can

@@bool WebViewImpl::sendContextMenuEvent(c
725673 // not run.
726674 page()->contextMenuController()->clearContextMenu();
727675
728  focusedFrame->view()->setCursor(pointerCursor());
729  WebMouseEvent mouseEvent;
730  mouseEvent.button = WebMouseEvent::ButtonRight;
731  mouseEvent.x = coords.x();
732  mouseEvent.y = coords.y();
733  mouseEvent.type = WebInputEvent::MouseUp;
734 
735  PlatformMouseEventBuilder platformEvent(view, mouseEvent);
736 
737676 m_contextMenuAllowed = true;
738  bool handled = focusedFrame->eventHandler()->sendContextMenuEvent(platformEvent);
 677 Frame* focusedFrame = page()->focusController()->focusedOrMainFrame();
 678 bool handled = focusedFrame->eventHandler()->sendContextMenuEventForKey();
739679 m_contextMenuAllowed = false;
740680 return handled;
741681}
61791

WebKit/win/ChangeLog

 12010-07-02 Erik Arvidsson <arv@chromium.org>
 2
 3 Reviewed by ojan@chromium.org.
 4
 5 Fix issue where a contextmenu event was reporting the wrong target
 6 if the context menu was shown due to pressing the context menu key
 7 (or Shift+F10).
 8
 9 * WebView.cpp:
 10 (WebView::handleContextMenuEvent):
 11
1122010-07-01 Steve Falkenburg <sfalken@apple.com>
213
314 Reviewed by Adele Peterson.
62399

WebKit/win/WebView.cpp

@@Page* WebView::page()
11881188
11891189bool WebView::handleContextMenuEvent(WPARAM wParam, LPARAM lParam)
11901190{
1191  static const int contextMenuMargin = 1;
1192 
11931191 // Translate the screen coordinates into window coordinates
11941192 POINT coords = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
11951193 if (coords.x == -1 || coords.y == -1) {
1196  FrameView* view = m_page->mainFrame()->view();
1197  if (!view)
1198  return false;
 1194 // The contextMenuController() holds onto the last context menu that was popped up on the
 1195 // page until a new one is created. We need to clear this menu before propagating the event
 1196 // through the DOM so that we can detect if we create a new menu for this event, since we
 1197 // won't create a new menu if the DOM swallows the event and the defaultEventHandler does
 1198 // not run.
 1199 m_page->contextMenuController()->clearContextMenu();
11991200
1200  int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
1201  IntPoint location;
 1201 Frame* focusedFrame = m_page->focusController()->focusedOrMainFrame();
 1202 return focusedFrame->eventHandler()->sendContextMenuEventForKey();
12021203
1203  // The context menu event was generated from the keyboard, so show the context menu by the current selection.
1204  Position start = m_page->mainFrame()->selection()->selection().start();
1205  Position end = m_page->mainFrame()->selection()->selection().end();
1206 
1207  if (!start.node() || !end.node())
1208  location = IntPoint(rightAligned ? view->contentsWidth() - contextMenuMargin : contextMenuMargin, contextMenuMargin);
1209  else {
1210  RenderObject* renderer = start.node()->renderer();
1211  if (!renderer)
1212  return false;
1213 
1214  // Calculate the rect of the first line of the selection (cribbed from -[WebCoreFrameBridge firstRectForDOMRange:],
1215  // now Frame::firstRectForRange(), which perhaps this should call).
1216  int extraWidthToEndOfLine = 0;
1217 
1218  InlineBox* startInlineBox;
1219  int startCaretOffset;
1220  start.getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset);
1221  IntRect startCaretRect = renderer->localCaretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine);
1222  if (startCaretRect != IntRect())
1223  startCaretRect = renderer->localToAbsoluteQuad(FloatRect(startCaretRect)).enclosingBoundingBox();
1224 
1225  InlineBox* endInlineBox;
1226  int endCaretOffset;
1227  end.getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset);
1228  IntRect endCaretRect = renderer->localCaretRect(endInlineBox, endCaretOffset);
1229  if (endCaretRect != IntRect())
1230  endCaretRect = renderer->localToAbsoluteQuad(FloatRect(endCaretRect)).enclosingBoundingBox();
1231 
1232  IntRect firstRect;
1233  if (startCaretRect.y() == endCaretRect.y())
1234  firstRect = IntRect(min(startCaretRect.x(), endCaretRect.x()), startCaretRect.y(), abs(endCaretRect.x() - startCaretRect.x()), max(startCaretRect.height(), endCaretRect.height()));
1235  else
1236  firstRect = IntRect(startCaretRect.x(), startCaretRect.y(), startCaretRect.width() + extraWidthToEndOfLine, startCaretRect.height());
1237 
1238  location = IntPoint(rightAligned ? firstRect.right() : firstRect.x(), firstRect.bottom());
1239  }
1240 
1241  location = view->contentsToWindow(location);
1242  // FIXME: The IntSize(0, -1) is a hack to get the hit-testing to result in the selected element.
1243  // Ideally we'd have the position of a context menu event be separate from its target node.
1244  coords = location + IntSize(0, -1);
12451204 } else {
12461205 if (!::ScreenToClient(m_viewWindow, &coords))
12471206 return false;

@@bool WebView::handleContextMenuEvent(WPA
12491208
12501209 lParam = MAKELPARAM(coords.x, coords.y);
12511210
1252  // The contextMenuController() holds onto the last context menu that was popped up on the
1253  // page until a new one is created. We need to clear this menu before propagating the event
1254  // through the DOM so that we can detect if we create a new menu for this event, since we
1255  // won't create a new menu if the DOM swallows the event and the defaultEventHandler does
1256  // not run.
12571211 m_page->contextMenuController()->clearContextMenu();
12581212
12591213 IntPoint documentPoint(m_page->mainFrame()->view()->windowToContents(coords));
61791