Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(221)

Side by Side Diff: webkit/api/src/WebViewImpl.cpp

Issue 385057: Deleted webkit/api which now lives in webkit.org (Closed)
Patch Set: Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « webkit/api/src/WebViewImpl.h ('k') | webkit/api/src/WebWorkerBase.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "WebViewImpl.h"
33
34 #include "AutocompletePopupMenuClient.h"
35 #include "AXObjectCache.h"
36 #include "CSSStyleSelector.h"
37 #include "CSSValueKeywords.h"
38 #include "Cursor.h"
39 #include "Document.h"
40 #include "DocumentLoader.h"
41 #include "DOMUtilitiesPrivate.h"
42 #include "DragController.h"
43 #include "DragData.h"
44 #include "Editor.h"
45 #include "EventHandler.h"
46 #include "FocusController.h"
47 #include "FontDescription.h"
48 #include "FrameLoader.h"
49 #include "FrameTree.h"
50 #include "FrameView.h"
51 #include "GraphicsContext.h"
52 #include "HitTestResult.h"
53 #include "HTMLInputElement.h"
54 #include "HTMLMediaElement.h"
55 #include "HTMLNames.h"
56 #include "Image.h"
57 #include "InspectorController.h"
58 #include "IntRect.h"
59 #include "KeyboardCodes.h"
60 #include "KeyboardEvent.h"
61 #include "MIMETypeRegistry.h"
62 #include "NodeRenderStyle.h"
63 #include "Page.h"
64 #include "PageGroup.h"
65 #include "Pasteboard.h"
66 #include "PlatformContextSkia.h"
67 #include "PlatformKeyboardEvent.h"
68 #include "PlatformMouseEvent.h"
69 #include "PlatformWheelEvent.h"
70 #include "PluginInfoStore.h"
71 #include "PopupMenuChromium.h"
72 #include "PopupMenuClient.h"
73 #include "RenderView.h"
74 #include "ResourceHandle.h"
75 #include "SecurityOrigin.h"
76 #include "SelectionController.h"
77 #include "Settings.h"
78 #include "TypingCommand.h"
79 #include "WebAccessibilityObject.h"
80 #include "WebDevToolsAgentPrivate.h"
81 #include "WebDragData.h"
82 #include "WebFrameImpl.h"
83 #include "WebInputEvent.h"
84 #include "WebInputEventConversion.h"
85 #include "WebMediaPlayerAction.h"
86 #include "WebNode.h"
87 #include "WebPoint.h"
88 #include "WebPopupMenuImpl.h"
89 #include "WebRect.h"
90 #include "WebSettingsImpl.h"
91 #include "WebString.h"
92 #include "WebVector.h"
93 #include "WebViewClient.h"
94
95 #if PLATFORM(WIN_OS)
96 #include "KeyboardCodesWin.h"
97 #include "RenderThemeChromiumWin.h"
98 #else
99 #include "KeyboardCodesPosix.h"
100 #include "RenderTheme.h"
101 #endif
102
103 // Get rid of WTF's pow define so we can use std::pow.
104 #undef pow
105 #include <cmath> // for std::pow
106
107 using namespace WebCore;
108
109 namespace WebKit {
110
111 // Change the text zoom level by kTextSizeMultiplierRatio each time the user
112 // zooms text in or out (ie., change by 20%). The min and max values limit
113 // text zoom to half and 3x the original text size. These three values match
114 // those in Apple's port in WebKit/WebKit/WebView/WebView.mm
115 static const double textSizeMultiplierRatio = 1.2;
116 static const double minTextSizeMultiplier = 0.5;
117 static const double maxTextSizeMultiplier = 3.0;
118
119 // The group name identifies a namespace of pages. Page group is used on OSX
120 // for some programs that use HTML views to display things that don't seem like
121 // web pages to the user (so shouldn't have visited link coloring). We only use
122 // one page group.
123 const char* pageGroupName = "default";
124
125 // Ensure that the WebDragOperation enum values stay in sync with the original
126 // DragOperation constants.
127 #define COMPILE_ASSERT_MATCHING_ENUM(coreName) \
128 COMPILE_ASSERT(int(coreName) == int(Web##coreName), dummy##coreName)
129 COMPILE_ASSERT_MATCHING_ENUM(DragOperationNone);
130 COMPILE_ASSERT_MATCHING_ENUM(DragOperationCopy);
131 COMPILE_ASSERT_MATCHING_ENUM(DragOperationLink);
132 COMPILE_ASSERT_MATCHING_ENUM(DragOperationGeneric);
133 COMPILE_ASSERT_MATCHING_ENUM(DragOperationPrivate);
134 COMPILE_ASSERT_MATCHING_ENUM(DragOperationMove);
135 COMPILE_ASSERT_MATCHING_ENUM(DragOperationDelete);
136 COMPILE_ASSERT_MATCHING_ENUM(DragOperationEvery);
137
138 // Note that focusOnShow is false so that the autocomplete popup is shown not
139 // activated. We need the page to still have focus so the user can keep typing
140 // while the popup is showing.
141 static const PopupContainerSettings autocompletePopupSettings = {
142 false, // focusOnShow
143 false, // setTextOnIndexChange
144 false, // acceptOnAbandon
145 true, // loopSelectionNavigation
146 true, // restrictWidthOfListBox. Same as other browser (Fx, IE, and safari)
147 // For autocomplete, we use the direction of the input field as the direction
148 // of the popup items. The main reason is to keep the display of items in
149 // drop-down the same as the items in the input field.
150 PopupContainerSettings::DOMElementDirection,
151 };
152
153 // WebView ----------------------------------------------------------------
154
155 WebView* WebView::create(WebViewClient* client)
156 {
157 return new WebViewImpl(client);
158 }
159
160 void WebView::updateVisitedLinkState(unsigned long long linkHash)
161 {
162 Page::visitedStateChanged(PageGroup::pageGroup(pageGroupName), linkHash);
163 }
164
165 void WebView::resetVisitedLinkState()
166 {
167 Page::allVisitedStateChanged(PageGroup::pageGroup(pageGroupName));
168 }
169
170 void WebViewImpl::initializeMainFrame(WebFrameClient* frameClient)
171 {
172 // NOTE: The WebFrameImpl takes a reference to itself within InitMainFrame
173 // and releases that reference once the corresponding Frame is destroyed.
174 RefPtr<WebFrameImpl> frame = WebFrameImpl::create(frameClient);
175
176 frame->initializeAsMainFrame(this);
177
178 // Restrict the access to the local file system
179 // (see WebView.mm WebView::_commonInitializationWithFrameName).
180 SecurityOrigin::setLocalLoadPolicy(SecurityOrigin::AllowLocalLoadsForLocalOnly);
181 }
182
183 WebViewImpl::WebViewImpl(WebViewClient* client)
184 : m_client(client)
185 , m_backForwardListClientImpl(this)
186 , m_chromeClientImpl(this)
187 , m_contextMenuClientImpl(this)
188 , m_dragClientImpl(this)
189 , m_editorClientImpl(this)
190 , m_inspectorClientImpl(this)
191 , m_observedNewNavigation(false)
192 #ifndef NDEBUG
193 , m_newNavigationLoader(0)
194 #endif
195 , m_zoomLevel(0)
196 , m_contextMenuAllowed(false)
197 , m_doingDragAndDrop(false)
198 , m_ignoreInputEvents(false)
199 , m_suppressNextKeypressEvent(false)
200 , m_initialNavigationPolicy(WebNavigationPolicyIgnore)
201 , m_imeAcceptEvents(true)
202 , m_dragTargetDispatch(false)
203 , m_dragIdentity(0)
204 , m_dropEffect(DropEffectDefault)
205 , m_operationsAllowed(WebDragOperationNone)
206 , m_dragOperation(WebDragOperationNone)
207 , m_autocompletePopupShowing(false)
208 , m_isTransparent(false)
209 , m_tabsToLinks(false)
210 {
211 // WebKit/win/WebView.cpp does the same thing, except they call the
212 // KJS specific wrapper around this method. We need to have threading
213 // initialized because CollatorICU requires it.
214 WTF::initializeThreading();
215
216 // set to impossible point so we always get the first mouse pos
217 m_lastMousePosition = WebPoint(-1, -1);
218
219 // the page will take ownership of the various clients
220 m_page.set(new Page(&m_chromeClientImpl,
221 &m_contextMenuClientImpl,
222 &m_editorClientImpl,
223 &m_dragClientImpl,
224 &m_inspectorClientImpl,
225 0));
226
227 m_page->backForwardList()->setClient(&m_backForwardListClientImpl);
228 m_page->setGroupName(pageGroupName);
229 }
230
231 WebViewImpl::~WebViewImpl()
232 {
233 ASSERT(!m_page);
234 }
235
236 RenderTheme* WebViewImpl::theme() const
237 {
238 return m_page.get() ? m_page->theme() : RenderTheme::defaultTheme().get();
239 }
240
241 WebFrameImpl* WebViewImpl::mainFrameImpl()
242 {
243 return m_page.get() ? WebFrameImpl::fromFrame(m_page->mainFrame()) : 0;
244 }
245
246 bool WebViewImpl::tabKeyCyclesThroughElements() const
247 {
248 ASSERT(m_page.get());
249 return m_page->tabKeyCyclesThroughElements();
250 }
251
252 void WebViewImpl::setTabKeyCyclesThroughElements(bool value)
253 {
254 if (m_page)
255 m_page->setTabKeyCyclesThroughElements(value);
256 }
257
258 void WebViewImpl::mouseMove(const WebMouseEvent& event)
259 {
260 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
261 return;
262
263 m_lastMousePosition = WebPoint(event.x, event.y);
264
265 // We call mouseMoved here instead of handleMouseMovedEvent because we need
266 // our ChromeClientImpl to receive changes to the mouse position and
267 // tooltip text, and mouseMoved handles all of that.
268 mainFrameImpl()->frame()->eventHandler()->mouseMoved(
269 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event));
270 }
271
272 void WebViewImpl::mouseLeave(const WebMouseEvent& event)
273 {
274 // This event gets sent as the main frame is closing. In that case, just
275 // ignore it.
276 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
277 return;
278
279 m_client->setMouseOverURL(WebURL());
280
281 mainFrameImpl()->frame()->eventHandler()->handleMouseMoveEvent(
282 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event));
283 }
284
285 void WebViewImpl::mouseDown(const WebMouseEvent& event)
286 {
287 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
288 return;
289
290 m_lastMouseDownPoint = WebPoint(event.x, event.y);
291
292 // If a text field that has focus is clicked again, we should display the
293 // autocomplete popup.
294 RefPtr<Node> clickedNode;
295 if (event.button == WebMouseEvent::ButtonLeft) {
296 RefPtr<Node> focusedNode = focusedWebCoreNode();
297 if (focusedNode.get() && toHTMLInputElement(focusedNode.get())) {
298 IntPoint point(event.x, event.y);
299 point = m_page->mainFrame()->view()->windowToContents(point);
300 HitTestResult result(point);
301 result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, false);
302 if (result.innerNonSharedNode() == focusedNode) {
303 // Already focused text field was clicked, let's remember this. If
304 // focus has not changed after the mouse event is processed, we'll
305 // trigger the autocomplete.
306 clickedNode = focusedNode;
307 }
308 }
309 }
310
311 mainFrameImpl()->frame()->eventHandler()->handleMousePressEvent(
312 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event));
313
314 if (clickedNode.get() && clickedNode == focusedWebCoreNode()) {
315 // Focus has not changed, show the autocomplete popup.
316 static_cast<EditorClientImpl*>(m_page->editorClient())->
317 showFormAutofillForNode(clickedNode.get());
318 }
319
320 // Dispatch the contextmenu event regardless of if the click was swallowed.
321 // On Windows, we handle it on mouse up, not down.
322 #if PLATFORM(DARWIN)
323 if (event.button == WebMouseEvent::ButtonRight
324 || (event.button == WebMouseEvent::ButtonLeft
325 && event.modifiers & WebMouseEvent::ControlKey))
326 mouseContextMenu(event);
327 #elif PLATFORM(LINUX)
328 if (event.button == WebMouseEvent::ButtonRight)
329 mouseContextMenu(event);
330 #endif
331 }
332
333 void WebViewImpl::mouseContextMenu(const WebMouseEvent& event)
334 {
335 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
336 return;
337
338 m_page->contextMenuController()->clearContextMenu();
339
340 PlatformMouseEventBuilder pme(mainFrameImpl()->frameView(), event);
341
342 // Find the right target frame. See issue 1186900.
343 HitTestResult result = hitTestResultForWindowPos(pme.pos());
344 Frame* targetFrame;
345 if (result.innerNonSharedNode())
346 targetFrame = result.innerNonSharedNode()->document()->frame();
347 else
348 targetFrame = m_page->focusController()->focusedOrMainFrame();
349
350 #if PLATFORM(WIN_OS)
351 targetFrame->view()->setCursor(pointerCursor());
352 #endif
353
354 m_contextMenuAllowed = true;
355 targetFrame->eventHandler()->sendContextMenuEvent(pme);
356 m_contextMenuAllowed = false;
357 // Actually showing the context menu is handled by the ContextMenuClient
358 // implementation...
359 }
360
361 void WebViewImpl::mouseUp(const WebMouseEvent& event)
362 {
363 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
364 return;
365
366 #if PLATFORM(LINUX)
367 // If the event was a middle click, attempt to copy text into the focused
368 // frame. We execute this before we let the page have a go at the event
369 // because the page may change what is focused during in its event handler.
370 //
371 // This code is in the mouse up handler. There is some debate about putting
372 // this here, as opposed to the mouse down handler.
373 // xterm: pastes on up.
374 // GTK: pastes on down.
375 // Firefox: pastes on up.
376 // Midori: couldn't paste at all with 0.1.2
377 //
378 // There is something of a webcompat angle to this well, as highlighted by
379 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on
380 // down then the text is pasted just before the onclick handler runs and
381 // clears the text box. So it's important this happens after the
382 // handleMouseReleaseEvent() earlier in this function
383 if (event.button == WebMouseEvent::ButtonMiddle) {
384 Frame* focused = focusedWebCoreFrame();
385 IntPoint clickPoint(m_lastMouseDownPoint.x, m_lastMouseDownPoint.y);
386 clickPoint = m_page->mainFrame()->view()->windowToContents(clickPoint);
387 HitTestResult hitTestResult =
388 focused->eventHandler()->hitTestResultAtPoint(clickPoint, false, false,
389 ShouldHitTestScrollbars);
390 // We don't want to send a paste when middle clicking a scroll bar or a
391 // link (which will navigate later in the code).
392 if (!hitTestResult.scrollbar() && !hitTestResult.isLiveLink() && focused) {
393 Editor* editor = focused->editor();
394 Pasteboard* pasteboard = Pasteboard::generalPasteboard();
395 bool oldSelectionMode = pasteboard->isSelectionMode();
396 pasteboard->setSelectionMode(true);
397 editor->command(AtomicString("Paste")).execute();
398 pasteboard->setSelectionMode(oldSelectionMode);
399 }
400 }
401 #endif
402
403 mouseCaptureLost();
404 mainFrameImpl()->frame()->eventHandler()->handleMouseReleaseEvent(
405 PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event));
406
407 #if PLATFORM(WIN_OS)
408 // Dispatch the contextmenu event regardless of if the click was swallowed.
409 // On Mac/Linux, we handle it on mouse down, not up.
410 if (event.button == WebMouseEvent::ButtonRight)
411 mouseContextMenu(event);
412 #endif
413 }
414
415 void WebViewImpl::mouseWheel(const WebMouseWheelEvent& event)
416 {
417 PlatformWheelEventBuilder platformEvent(mainFrameImpl()->frameView(), event);
418 mainFrameImpl()->frame()->eventHandler()->handleWheelEvent(platformEvent);
419 }
420
421 bool WebViewImpl::keyEvent(const WebKeyboardEvent& event)
422 {
423 ASSERT((event.type == WebInputEvent::RawKeyDown)
424 || (event.type == WebInputEvent::KeyDown)
425 || (event.type == WebInputEvent::KeyUp));
426
427 // Please refer to the comments explaining the m_suppressNextKeypressEvent
428 // member.
429 // The m_suppressNextKeypressEvent is set if the KeyDown is handled by
430 // Webkit. A keyDown event is typically associated with a keyPress(char)
431 // event and a keyUp event. We reset this flag here as this is a new keyDown
432 // event.
433 m_suppressNextKeypressEvent = false;
434
435 // Give autocomplete a chance to consume the key events it is interested in.
436 if (autocompleteHandleKeyEvent(event))
437 return true;
438
439 Frame* frame = focusedWebCoreFrame();
440 if (!frame)
441 return false;
442
443 EventHandler* handler = frame->eventHandler();
444 if (!handler)
445 return keyEventDefault(event);
446
447 #if PLATFORM(WIN_OS) || PLATFORM(LINUX)
448 if ((!event.modifiers && (event.windowsKeyCode == VKEY_APPS))
449 || ((event.modifiers == WebInputEvent::ShiftKey) && (event.windowsKeyCode == VKEY_F10))) {
450 sendContextMenuEvent(event);
451 return true;
452 }
453 #endif
454
455 // It's not clear if we should continue after detecting a capslock keypress.
456 // I'll err on the side of continuing, which is the pre-existing behaviour.
457 if (event.windowsKeyCode == VKEY_CAPITAL)
458 handler->capsLockStateMayHaveChanged();
459
460 PlatformKeyboardEventBuilder evt(event);
461
462 if (handler->keyEvent(evt)) {
463 if (WebInputEvent::RawKeyDown == event.type && !evt.isSystemKey())
464 m_suppressNextKeypressEvent = true;
465 return true;
466 }
467
468 return keyEventDefault(event);
469 }
470
471 bool WebViewImpl::autocompleteHandleKeyEvent(const WebKeyboardEvent& event)
472 {
473 if (!m_autocompletePopupShowing
474 // Home and End should be left to the text field to process.
475 || event.windowsKeyCode == VKEY_HOME
476 || event.windowsKeyCode == VKEY_END)
477 return false;
478
479 // Pressing delete triggers the removal of the selected suggestion from the DB.
480 if (event.windowsKeyCode == VKEY_DELETE
481 && m_autocompletePopup->selectedIndex() != -1) {
482 Node* node = focusedWebCoreNode();
483 if (!node || (node->nodeType() != Node::ELEMENT_NODE)) {
484 ASSERT_NOT_REACHED();
485 return false;
486 }
487 Element* element = static_cast<Element*>(node);
488 if (!element->hasLocalName(HTMLNames::inputTag)) {
489 ASSERT_NOT_REACHED();
490 return false;
491 }
492
493 int selectedIndex = m_autocompletePopup->selectedIndex();
494 HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element);
495 WebString name = inputElement->name();
496 WebString value = m_autocompletePopupClient->itemText(selectedIndex);
497 m_client->removeAutofillSuggestions(name, value);
498 // Update the entries in the currently showing popup to reflect the
499 // deletion.
500 m_autocompletePopupClient->removeItemAtIndex(selectedIndex);
501 refreshAutofillPopup();
502 return false;
503 }
504
505 if (!m_autocompletePopup->isInterestedInEventForKey(event.windowsKeyCode))
506 return false;
507
508 if (m_autocompletePopup->handleKeyEvent(PlatformKeyboardEventBuilder(event))) {
509 // We need to ignore the next Char event after this otherwise pressing
510 // enter when selecting an item in the menu will go to the page.
511 if (WebInputEvent::RawKeyDown == event.type)
512 m_suppressNextKeypressEvent = true;
513 return true;
514 }
515
516 return false;
517 }
518
519 bool WebViewImpl::charEvent(const WebKeyboardEvent& event)
520 {
521 ASSERT(event.type == WebInputEvent::Char);
522
523 // Please refer to the comments explaining the m_suppressNextKeypressEvent
524 // member. The m_suppressNextKeypressEvent is set if the KeyDown is
525 // handled by Webkit. A keyDown event is typically associated with a
526 // keyPress(char) event and a keyUp event. We reset this flag here as it
527 // only applies to the current keyPress event.
528 if (m_suppressNextKeypressEvent) {
529 m_suppressNextKeypressEvent = false;
530 return true;
531 }
532
533 Frame* frame = focusedWebCoreFrame();
534 if (!frame)
535 return false;
536
537 EventHandler* handler = frame->eventHandler();
538 if (!handler)
539 return keyEventDefault(event);
540
541 PlatformKeyboardEventBuilder evt(event);
542 if (!evt.isCharacterKey())
543 return true;
544
545 // Safari 3.1 does not pass off windows system key messages (WM_SYSCHAR) to
546 // the eventHandler::keyEvent. We mimic this behavior on all platforms since
547 // for now we are converting other platform's key events to windows key
548 // events.
549 if (evt.isSystemKey())
550 return handler->handleAccessKey(evt);
551
552 if (!handler->keyEvent(evt))
553 return keyEventDefault(event);
554
555 return true;
556 }
557
558 // The WebViewImpl::SendContextMenuEvent function is based on the Webkit
559 // function
560 // bool WebView::handleContextMenuEvent(WPARAM wParam, LPARAM lParam) in
561 // webkit\webkit\win\WebView.cpp. The only significant change in this
562 // function is the code to convert from a Keyboard event to the Right
563 // Mouse button up event.
564 //
565 // This function is an ugly copy/paste and should be cleaned up when the
566 // WebKitWin version is cleaned: https://bugs.webkit.org/show_bug.cgi?id=20438
567 #if PLATFORM(WIN_OS) || PLATFORM(LINUX)
568 // FIXME: implement on Mac
569 bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event)
570 {
571 static const int kContextMenuMargin = 1;
572 Frame* mainFrameImpl = page()->mainFrame();
573 FrameView* view = mainFrameImpl->view();
574 if (!view)
575 return false;
576
577 IntPoint coords(-1, -1);
578 #if PLATFORM(WIN_OS)
579 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
580 #else
581 int rightAligned = 0;
582 #endif
583 IntPoint location;
584
585 // The context menu event was generated from the keyboard, so show the
586 // context menu by the current selection.
587 Position start = mainFrameImpl->selection()->selection().start();
588 Position end = mainFrameImpl->selection()->selection().end();
589
590 if (!start.node() || !end.node()) {
591 location = IntPoint(
592 rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
593 kContextMenuMargin);
594 } else {
595 RenderObject* renderer = start.node()->renderer();
596 if (!renderer)
597 return false;
598
599 RefPtr<Range> selection = mainFrameImpl->selection()->toNormalizedRange();
600 IntRect firstRect = mainFrameImpl->firstRectForRange(selection.get());
601
602 int x = rightAligned ? firstRect.right() : firstRect.x();
603 location = IntPoint(x, firstRect.bottom());
604 }
605
606 location = view->contentsToWindow(location);
607 // FIXME: The IntSize(0, -1) is a hack to get the hit-testing to result in
608 // the selected element. Ideally we'd have the position of a context menu
609 // event be separate from its target node.
610 coords = location + IntSize(0, -1);
611
612 // The contextMenuController() holds onto the last context menu that was
613 // popped up on the page until a new one is created. We need to clear
614 // this menu before propagating the event through the DOM so that we can
615 // detect if we create a new menu for this event, since we won't create
616 // a new menu if the DOM swallows the event and the defaultEventHandler does
617 // not run.
618 page()->contextMenuController()->clearContextMenu();
619
620 Frame* focusedFrame = page()->focusController()->focusedOrMainFrame();
621 focusedFrame->view()->setCursor(pointerCursor());
622 WebMouseEvent mouseEvent;
623 mouseEvent.button = WebMouseEvent::ButtonRight;
624 mouseEvent.x = coords.x();
625 mouseEvent.y = coords.y();
626 mouseEvent.type = WebInputEvent::MouseUp;
627
628 PlatformMouseEventBuilder platformEvent(view, mouseEvent);
629
630 m_contextMenuAllowed = true;
631 bool handled = focusedFrame->eventHandler()->sendContextMenuEvent(platformEvent);
632 m_contextMenuAllowed = false;
633 return handled;
634 }
635 #endif
636
637 bool WebViewImpl::keyEventDefault(const WebKeyboardEvent& event)
638 {
639 Frame* frame = focusedWebCoreFrame();
640 if (!frame)
641 return false;
642
643 switch (event.type) {
644 case WebInputEvent::Char:
645 if (event.windowsKeyCode == VKEY_SPACE) {
646 int keyCode = ((event.modifiers & WebInputEvent::ShiftKey) ? VKEY_PRIOR : VKEY_NEXT);
647 return scrollViewWithKeyboard(keyCode, event.modifiers);
648 }
649 break;
650 case WebInputEvent::RawKeyDown:
651 if (event.modifiers == WebInputEvent::ControlKey) {
652 switch (event.windowsKeyCode) {
653 case 'A':
654 focusedFrame()->executeCommand(WebString::fromUTF8("SelectAll"));
655 return true;
656 case VKEY_INSERT:
657 case 'C':
658 focusedFrame()->executeCommand(WebString::fromUTF8("Copy"));
659 return true;
660 // Match FF behavior in the sense that Ctrl+home/end are the only Ctrl
661 // key combinations which affect scrolling. Safari is buggy in the
662 // sense that it scrolls the page for all Ctrl+scrolling key
663 // combinations. For e.g. Ctrl+pgup/pgdn/up/down, etc.
664 case VKEY_HOME:
665 case VKEY_END:
666 break;
667 default:
668 return false;
669 }
670 }
671 if (!event.isSystemKey && !(event.modifiers & WebInputEvent::ShiftKey))
672 return scrollViewWithKeyboard(event.windowsKeyCode, event.modifiers);
673 break;
674 default:
675 break;
676 }
677 return false;
678 }
679
680 bool WebViewImpl::scrollViewWithKeyboard(int keyCode, int modifiers)
681 {
682 ScrollDirection scrollDirection;
683 ScrollGranularity scrollGranularity;
684
685 switch (keyCode) {
686 case VKEY_LEFT:
687 scrollDirection = ScrollLeft;
688 scrollGranularity = ScrollByLine;
689 break;
690 case VKEY_RIGHT:
691 scrollDirection = ScrollRight;
692 scrollGranularity = ScrollByLine;
693 break;
694 case VKEY_UP:
695 scrollDirection = ScrollUp;
696 scrollGranularity = ScrollByLine;
697 break;
698 case VKEY_DOWN:
699 scrollDirection = ScrollDown;
700 scrollGranularity = ScrollByLine;
701 break;
702 case VKEY_HOME:
703 scrollDirection = ScrollUp;
704 scrollGranularity = ScrollByDocument;
705 break;
706 case VKEY_END:
707 scrollDirection = ScrollDown;
708 scrollGranularity = ScrollByDocument;
709 break;
710 case VKEY_PRIOR: // page up
711 scrollDirection = ScrollUp;
712 scrollGranularity = ScrollByPage;
713 break;
714 case VKEY_NEXT: // page down
715 scrollDirection = ScrollDown;
716 scrollGranularity = ScrollByPage;
717 break;
718 default:
719 return false;
720 }
721
722 return propagateScroll(scrollDirection, scrollGranularity);
723 }
724
725 bool WebViewImpl::propagateScroll(ScrollDirection scrollDirection,
726 ScrollGranularity scrollGranularity)
727 {
728 Frame* frame = focusedWebCoreFrame();
729 if (!frame)
730 return false;
731
732 bool scrollHandled =
733 frame->eventHandler()->scrollOverflow(scrollDirection,
734 scrollGranularity);
735 Frame* currentFrame = frame;
736 while (!scrollHandled && currentFrame) {
737 scrollHandled = currentFrame->view()->scroll(scrollDirection,
738 scrollGranularity);
739 currentFrame = currentFrame->tree()->parent();
740 }
741 return scrollHandled;
742 }
743
744 Frame* WebViewImpl::focusedWebCoreFrame()
745 {
746 return m_page.get() ? m_page->focusController()->focusedOrMainFrame() : 0;
747 }
748
749 WebViewImpl* WebViewImpl::fromPage(Page* page)
750 {
751 if (!page)
752 return 0;
753
754 return static_cast<ChromeClientImpl*>(page->chrome()->client())->webView();
755 }
756
757 // WebWidget ------------------------------------------------------------------
758
759 void WebViewImpl::close()
760 {
761 RefPtr<WebFrameImpl> mainFrameImpl;
762
763 if (m_page.get()) {
764 // Initiate shutdown for the entire frameset. This will cause a lot of
765 // notifications to be sent.
766 if (m_page->mainFrame()) {
767 mainFrameImpl = WebFrameImpl::fromFrame(m_page->mainFrame());
768 m_page->mainFrame()->loader()->frameDetached();
769 }
770 m_page.clear();
771 }
772
773 // Should happen after m_page.clear().
774 if (m_devToolsAgent.get())
775 m_devToolsAgent.clear();
776
777 // We drop the client after the page has been destroyed to support the
778 // WebFrameClient::didDestroyScriptContext method.
779 if (mainFrameImpl)
780 mainFrameImpl->dropClient();
781
782 // Reset the delegate to prevent notifications being sent as we're being
783 // deleted.
784 m_client = 0;
785
786 deref(); // Balances ref() acquired in WebView::create
787 }
788
789 void WebViewImpl::resize(const WebSize& newSize)
790 {
791 if (m_size == newSize)
792 return;
793 m_size = newSize;
794
795 if (mainFrameImpl()->frameView()) {
796 mainFrameImpl()->frameView()->resize(m_size.width, m_size.height);
797 mainFrameImpl()->frame()->eventHandler()->sendResizeEvent();
798 }
799
800 if (m_client) {
801 WebRect damagedRect(0, 0, m_size.width, m_size.height);
802 m_client->didInvalidateRect(damagedRect);
803 }
804 }
805
806 void WebViewImpl::layout()
807 {
808 WebFrameImpl* webframe = mainFrameImpl();
809 if (webframe) {
810 // In order for our child HWNDs (NativeWindowWidgets) to update properly,
811 // they need to be told that we are updating the screen. The problem is
812 // that the native widgets need to recalculate their clip region and not
813 // overlap any of our non-native widgets. To force the resizing, call
814 // setFrameRect(). This will be a quick operation for most frames, but
815 // the NativeWindowWidgets will update a proper clipping region.
816 FrameView* view = webframe->frameView();
817 if (view)
818 view->setFrameRect(view->frameRect());
819
820 // setFrameRect may have the side-effect of causing existing page
821 // layout to be invalidated, so layout needs to be called last.
822
823 webframe->layout();
824 }
825 }
826
827 void WebViewImpl::paint(WebCanvas* canvas, const WebRect& rect)
828 {
829 WebFrameImpl* webframe = mainFrameImpl();
830 if (webframe)
831 webframe->paint(canvas, rect);
832 }
833
834 // FIXME: m_currentInputEvent should be removed once ChromeClient::show() can
835 // get the current-event information from WebCore.
836 const WebInputEvent* WebViewImpl::m_currentInputEvent = 0;
837
838 bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent)
839 {
840 // If we've started a drag and drop operation, ignore input events until
841 // we're done.
842 if (m_doingDragAndDrop)
843 return true;
844
845 if (m_ignoreInputEvents)
846 return true;
847
848 // FIXME: Remove m_currentInputEvent.
849 // This only exists to allow ChromeClient::show() to know which mouse button
850 // triggered a window.open event.
851 // Safari must perform a similar hack, ours is in our WebKit glue layer
852 // theirs is in the application. This should go when WebCore can be fixed
853 // to pass more event information to ChromeClient::show()
854 m_currentInputEvent = &inputEvent;
855
856 bool handled = true;
857
858 // FIXME: WebKit seems to always return false on mouse events processing
859 // methods. For now we'll assume it has processed them (as we are only
860 // interested in whether keyboard events are processed).
861 switch (inputEvent.type) {
862 case WebInputEvent::MouseMove:
863 mouseMove(*static_cast<const WebMouseEvent*>(&inputEvent));
864 break;
865
866 case WebInputEvent::MouseLeave:
867 mouseLeave(*static_cast<const WebMouseEvent*>(&inputEvent));
868 break;
869
870 case WebInputEvent::MouseWheel:
871 mouseWheel(*static_cast<const WebMouseWheelEvent*>(&inputEvent));
872 break;
873
874 case WebInputEvent::MouseDown:
875 mouseDown(*static_cast<const WebMouseEvent*>(&inputEvent));
876 break;
877
878 case WebInputEvent::MouseUp:
879 mouseUp(*static_cast<const WebMouseEvent*>(&inputEvent));
880 break;
881
882 case WebInputEvent::RawKeyDown:
883 case WebInputEvent::KeyDown:
884 case WebInputEvent::KeyUp:
885 handled = keyEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent));
886 break;
887
888 case WebInputEvent::Char:
889 handled = charEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent));
890 break;
891
892 default:
893 handled = false;
894 }
895
896 m_currentInputEvent = 0;
897
898 return handled;
899 }
900
901 void WebViewImpl::mouseCaptureLost()
902 {
903 }
904
905 void WebViewImpl::setFocus(bool enable)
906 {
907 m_page->focusController()->setFocused(enable);
908 if (enable) {
909 // Note that we don't call setActive() when disabled as this cause extra
910 // focus/blur events to be dispatched.
911 m_page->focusController()->setActive(true);
912 RefPtr<Frame> focusedFrame = m_page->focusController()->focusedFrame();
913 if (focusedFrame) {
914 Node* focusedNode = focusedFrame->document()->focusedNode();
915 if (focusedNode && focusedNode->isElementNode()
916 && focusedFrame->selection()->selection().isNone()) {
917 // If the selection was cleared while the WebView was not
918 // focused, then the focus element shows with a focus ring but
919 // no caret and does respond to keyboard inputs.
920 Element* element = static_cast<Element*>(focusedNode);
921 if (element->isTextFormControl())
922 element->updateFocusAppearance(true);
923 else {
924 // updateFocusAppearance() selects all the text of
925 // contentseditable DIVs. So we set the selection explicitly
926 // instead. Note that this has the side effect of moving the
927 // caret back to the begining of the text.
928 Position position(focusedNode, 0,
929 Position::PositionIsOffsetInAnchor);
930 focusedFrame->selection()->setSelection(
931 VisibleSelection(position, SEL_DEFAULT_AFFINITY));
932 }
933 }
934 }
935 m_imeAcceptEvents = true;
936 } else {
937 hideAutoCompletePopup();
938
939 // Clear focus on the currently focused frame if any.
940 if (!m_page.get())
941 return;
942
943 Frame* frame = m_page->mainFrame();
944 if (!frame)
945 return;
946
947 RefPtr<Frame> focusedFrame = m_page->focusController()->focusedFrame();
948 if (focusedFrame.get()) {
949 // Finish an ongoing composition to delete the composition node.
950 Editor* editor = focusedFrame->editor();
951 if (editor && editor->hasComposition())
952 editor->confirmComposition();
953 m_imeAcceptEvents = false;
954 }
955 }
956 }
957
958 bool WebViewImpl::handleCompositionEvent(WebCompositionCommand command,
959 int cursorPosition,
960 int targetStart,
961 int targetEnd,
962 const WebString& imeString)
963 {
964 Frame* focused = focusedWebCoreFrame();
965 if (!focused || !m_imeAcceptEvents)
966 return false;
967 Editor* editor = focused->editor();
968 if (!editor)
969 return false;
970 if (!editor->canEdit()) {
971 // The input focus has been moved to another WebWidget object.
972 // We should use this |editor| object only to complete the ongoing
973 // composition.
974 if (!editor->hasComposition())
975 return false;
976 }
977
978 // We should verify the parent node of this IME composition node are
979 // editable because JavaScript may delete a parent node of the composition
980 // node. In this case, WebKit crashes while deleting texts from the parent
981 // node, which doesn't exist any longer.
982 PassRefPtr<Range> range = editor->compositionRange();
983 if (range) {
984 const Node* node = range->startPosition().node();
985 if (!node || !node->isContentEditable())
986 return false;
987 }
988
989 if (command == WebCompositionCommandDiscard) {
990 // A browser process sent an IPC message which does not contain a valid
991 // string, which means an ongoing composition has been canceled.
992 // If the ongoing composition has been canceled, replace the ongoing
993 // composition string with an empty string and complete it.
994 String emptyString;
995 Vector<CompositionUnderline> emptyUnderlines;
996 editor->setComposition(emptyString, emptyUnderlines, 0, 0);
997 } else {
998 // A browser process sent an IPC message which contains a string to be
999 // displayed in this Editor object.
1000 // To display the given string, set the given string to the
1001 // m_compositionNode member of this Editor object and display it.
1002 if (targetStart < 0)
1003 targetStart = 0;
1004 if (targetEnd < 0)
1005 targetEnd = static_cast<int>(imeString.length());
1006 String compositionString(imeString);
1007 // Create custom underlines.
1008 // To emphasize the selection, the selected region uses a solid black
1009 // for its underline while other regions uses a pale gray for theirs.
1010 Vector<CompositionUnderline> underlines(3);
1011 underlines[0].startOffset = 0;
1012 underlines[0].endOffset = targetStart;
1013 underlines[0].thick = true;
1014 underlines[0].color.setRGB(0xd3, 0xd3, 0xd3);
1015 underlines[1].startOffset = targetStart;
1016 underlines[1].endOffset = targetEnd;
1017 underlines[1].thick = true;
1018 underlines[1].color.setRGB(0x00, 0x00, 0x00);
1019 underlines[2].startOffset = targetEnd;
1020 underlines[2].endOffset = static_cast<int>(imeString.length());
1021 underlines[2].thick = true;
1022 underlines[2].color.setRGB(0xd3, 0xd3, 0xd3);
1023 // When we use custom underlines, WebKit ("InlineTextBox.cpp" Line 282)
1024 // prevents from writing a text in between 'selectionStart' and
1025 // 'selectionEnd' somehow.
1026 // Therefore, we use the 'cursorPosition' for these arguments so that
1027 // there are not any characters in the above region.
1028 editor->setComposition(compositionString, underlines,
1029 cursorPosition, cursorPosition);
1030 // The given string is a result string, which means the ongoing
1031 // composition has been completed. I have to call the
1032 // Editor::confirmCompletion() and complete this composition.
1033 if (command == WebCompositionCommandConfirm)
1034 editor->confirmComposition();
1035 }
1036
1037 return editor->hasComposition();
1038 }
1039
1040 bool WebViewImpl::queryCompositionStatus(bool* enableIME, WebRect* caretRect)
1041 {
1042 // Store whether the selected node needs IME and the caret rectangle.
1043 // This process consists of the following four steps:
1044 // 1. Retrieve the selection controller of the focused frame;
1045 // 2. Retrieve the caret rectangle from the controller;
1046 // 3. Convert the rectangle, which is relative to the parent view, to the
1047 // one relative to the client window, and;
1048 // 4. Store the converted rectangle.
1049 const Frame* focused = focusedWebCoreFrame();
1050 if (!focused)
1051 return false;
1052
1053 const Editor* editor = focused->editor();
1054 if (!editor || !editor->canEdit())
1055 return false;
1056
1057 SelectionController* controller = focused->selection();
1058 if (!controller)
1059 return false;
1060
1061 const Node* node = controller->start().node();
1062 if (!node)
1063 return false;
1064
1065 *enableIME = node->shouldUseInputMethod() && !controller->isInPasswordField();
1066 const FrameView* view = node->document()->view();
1067 if (!view)
1068 return false;
1069
1070 *caretRect = view->contentsToWindow(controller->absoluteCaretBounds());
1071 return true;
1072 }
1073
1074 void WebViewImpl::setTextDirection(WebTextDirection direction)
1075 {
1076 // The Editor::setBaseWritingDirection() function checks if we can change
1077 // the text direction of the selected node and updates its DOM "dir"
1078 // attribute and its CSS "direction" property.
1079 // So, we just call the function as Safari does.
1080 const Frame* focused = focusedWebCoreFrame();
1081 if (!focused)
1082 return;
1083
1084 Editor* editor = focused->editor();
1085 if (!editor || !editor->canEdit())
1086 return;
1087
1088 switch (direction) {
1089 case WebTextDirectionDefault:
1090 editor->setBaseWritingDirection(NaturalWritingDirection);
1091 break;
1092
1093 case WebTextDirectionLeftToRight:
1094 editor->setBaseWritingDirection(LeftToRightWritingDirection);
1095 break;
1096
1097 case WebTextDirectionRightToLeft:
1098 editor->setBaseWritingDirection(RightToLeftWritingDirection);
1099 break;
1100
1101 default:
1102 notImplemented();
1103 break;
1104 }
1105 }
1106
1107 // WebView --------------------------------------------------------------------
1108
1109 WebSettings* WebViewImpl::settings()
1110 {
1111 if (!m_webSettings.get())
1112 m_webSettings.set(new WebSettingsImpl(m_page->settings()));
1113 ASSERT(m_webSettings.get());
1114 return m_webSettings.get();
1115 }
1116
1117 WebString WebViewImpl::pageEncoding() const
1118 {
1119 if (!m_page.get())
1120 return WebString();
1121
1122 return m_page->mainFrame()->loader()->encoding();
1123 }
1124
1125 void WebViewImpl::setPageEncoding(const WebString& encodingName)
1126 {
1127 if (!m_page.get())
1128 return;
1129
1130 // Only change override encoding, don't change default encoding.
1131 // Note that the new encoding must be 0 if it isn't supposed to be set.
1132 String newEncodingName;
1133 if (!encodingName.isEmpty())
1134 newEncodingName = encodingName;
1135 m_page->mainFrame()->loader()->reloadWithOverrideEncoding(newEncodingName);
1136 }
1137
1138 bool WebViewImpl::dispatchBeforeUnloadEvent()
1139 {
1140 // FIXME: This should really cause a recursive depth-first walk of all
1141 // frames in the tree, calling each frame's onbeforeunload. At the moment,
1142 // we're consistent with Safari 3.1, not IE/FF.
1143 Frame* frame = m_page->focusController()->focusedOrMainFrame();
1144 if (!frame)
1145 return true;
1146
1147 return frame->shouldClose();
1148 }
1149
1150 void WebViewImpl::dispatchUnloadEvent()
1151 {
1152 // Run unload handlers.
1153 m_page->mainFrame()->loader()->closeURL();
1154 }
1155
1156 WebFrame* WebViewImpl::mainFrame()
1157 {
1158 return mainFrameImpl();
1159 }
1160
1161 WebFrame* WebViewImpl::findFrameByName(
1162 const WebString& name, WebFrame* relativeToFrame)
1163 {
1164 if (!relativeToFrame)
1165 relativeToFrame = mainFrame();
1166 Frame* frame = static_cast<WebFrameImpl*>(relativeToFrame)->frame();
1167 frame = frame->tree()->find(name);
1168 return WebFrameImpl::fromFrame(frame);
1169 }
1170
1171 WebFrame* WebViewImpl::focusedFrame()
1172 {
1173 return WebFrameImpl::fromFrame(focusedWebCoreFrame());
1174 }
1175
1176 void WebViewImpl::setFocusedFrame(WebFrame* frame)
1177 {
1178 if (!frame) {
1179 // Clears the focused frame if any.
1180 Frame* frame = focusedWebCoreFrame();
1181 if (frame)
1182 frame->selection()->setFocused(false);
1183 return;
1184 }
1185 WebFrameImpl* frameImpl = static_cast<WebFrameImpl*>(frame);
1186 Frame* webcoreFrame = frameImpl->frame();
1187 webcoreFrame->page()->focusController()->setFocusedFrame(webcoreFrame);
1188 }
1189
1190 void WebViewImpl::setInitialFocus(bool reverse)
1191 {
1192 if (!m_page.get())
1193 return;
1194
1195 // Since we don't have a keyboard event, we'll create one.
1196 WebKeyboardEvent keyboardEvent;
1197 keyboardEvent.type = WebInputEvent::RawKeyDown;
1198 if (reverse)
1199 keyboardEvent.modifiers = WebInputEvent::ShiftKey;
1200
1201 // VK_TAB which is only defined on Windows.
1202 keyboardEvent.windowsKeyCode = 0x09;
1203 PlatformKeyboardEventBuilder platformEvent(keyboardEvent);
1204 RefPtr<KeyboardEvent> webkitEvent = KeyboardEvent::create(platformEvent, 0);
1205 page()->focusController()->setInitialFocus(
1206 reverse ? FocusDirectionBackward : FocusDirectionForward,
1207 webkitEvent.get());
1208 }
1209
1210 void WebViewImpl::clearFocusedNode()
1211 {
1212 if (!m_page.get())
1213 return;
1214
1215 RefPtr<Frame> frame = m_page->mainFrame();
1216 if (!frame.get())
1217 return;
1218
1219 RefPtr<Document> document = frame->document();
1220 if (!document.get())
1221 return;
1222
1223 RefPtr<Node> oldFocusedNode = document->focusedNode();
1224
1225 // Clear the focused node.
1226 document->setFocusedNode(0);
1227
1228 if (!oldFocusedNode.get())
1229 return;
1230
1231 // If a text field has focus, we need to make sure the selection controller
1232 // knows to remove selection from it. Otherwise, the text field is still
1233 // processing keyboard events even though focus has been moved to the page and
1234 // keystrokes get eaten as a result.
1235 if (oldFocusedNode->hasTagName(HTMLNames::textareaTag)
1236 || (oldFocusedNode->hasTagName(HTMLNames::inputTag)
1237 && static_cast<HTMLInputElement*>(oldFocusedNode.get())->isTextField())) {
1238 // Clear the selection.
1239 SelectionController* selection = frame->selection();
1240 selection->clear();
1241 }
1242 }
1243
1244 void WebViewImpl::zoomIn(bool textOnly)
1245 {
1246 Frame* frame = mainFrameImpl()->frame();
1247 double multiplier = std::min(std::pow(textSizeMultiplierRatio, m_zoomLevel + 1),
1248 maxTextSizeMultiplier);
1249 float zoomFactor = static_cast<float>(multiplier);
1250 if (zoomFactor != frame->zoomFactor()) {
1251 ++m_zoomLevel;
1252 frame->setZoomFactor(zoomFactor, textOnly);
1253 }
1254 }
1255
1256 void WebViewImpl::zoomOut(bool textOnly)
1257 {
1258 Frame* frame = mainFrameImpl()->frame();
1259 double multiplier = std::max(std::pow(textSizeMultiplierRatio, m_zoomLevel - 1),
1260 minTextSizeMultiplier);
1261 float zoomFactor = static_cast<float>(multiplier);
1262 if (zoomFactor != frame->zoomFactor()) {
1263 --m_zoomLevel;
1264 frame->setZoomFactor(zoomFactor, textOnly);
1265 }
1266 }
1267
1268 void WebViewImpl::zoomDefault()
1269 {
1270 // We don't change the zoom mode (text only vs. full page) here. We just want
1271 // to reset whatever is already set.
1272 m_zoomLevel = 0;
1273 mainFrameImpl()->frame()->setZoomFactor(
1274 1.0f, mainFrameImpl()->frame()->isZoomFactorTextOnly());
1275 }
1276
1277 void WebViewImpl::performMediaPlayerAction(const WebMediaPlayerAction& action,
1278 const WebPoint& location)
1279 {
1280 HitTestResult result =
1281 hitTestResultForWindowPos(location);
1282 RefPtr<Node> node = result.innerNonSharedNode();
1283 if (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))
1284 return;
1285
1286 RefPtr<HTMLMediaElement> mediaElement =
1287 static_pointer_cast<HTMLMediaElement>(node);
1288 switch (action.type) {
1289 case WebMediaPlayerAction::Play:
1290 if (action.enable)
1291 mediaElement->play();
1292 else
1293 mediaElement->pause();
1294 break;
1295 case WebMediaPlayerAction::Mute:
1296 mediaElement->setMuted(action.enable);
1297 break;
1298 case WebMediaPlayerAction::Loop:
1299 mediaElement->setLoop(action.enable);
1300 break;
1301 default:
1302 ASSERT_NOT_REACHED();
1303 }
1304 }
1305
1306 void WebViewImpl::copyImageAt(const WebPoint& point)
1307 {
1308 if (!m_page.get())
1309 return;
1310
1311 HitTestResult result = hitTestResultForWindowPos(point);
1312
1313 if (result.absoluteImageURL().isEmpty()) {
1314 // There isn't actually an image at these coordinates. Might be because
1315 // the window scrolled while the context menu was open or because the page
1316 // changed itself between when we thought there was an image here and when
1317 // we actually tried to retreive the image.
1318 //
1319 // FIXME: implement a cache of the most recent HitTestResult to avoid having
1320 // to do two hit tests.
1321 return;
1322 }
1323
1324 m_page->mainFrame()->editor()->copyImage(result);
1325 }
1326
1327 void WebViewImpl::dragSourceEndedAt(
1328 const WebPoint& clientPoint,
1329 const WebPoint& screenPoint,
1330 WebDragOperation operation)
1331 {
1332 PlatformMouseEvent pme(clientPoint,
1333 screenPoint,
1334 LeftButton, MouseEventMoved, 0, false, false, false,
1335 false, 0);
1336 m_page->mainFrame()->eventHandler()->dragSourceEndedAt(pme,
1337 static_cast<DragOperation>(operation));
1338 }
1339
1340 void WebViewImpl::dragSourceSystemDragEnded()
1341 {
1342 // It's possible for us to get this callback while not doing a drag if
1343 // it's from a previous page that got unloaded.
1344 if (m_doingDragAndDrop) {
1345 m_page->dragController()->dragEnded();
1346 m_doingDragAndDrop = false;
1347 }
1348 }
1349
1350 WebDragOperation WebViewImpl::dragTargetDragEnter(
1351 const WebDragData& webDragData, int identity,
1352 const WebPoint& clientPoint,
1353 const WebPoint& screenPoint,
1354 WebDragOperationsMask operationsAllowed)
1355 {
1356 ASSERT(!m_currentDragData.get());
1357
1358 m_currentDragData = webDragData;
1359 m_dragIdentity = identity;
1360 m_operationsAllowed = operationsAllowed;
1361
1362 DragData dragData(
1363 m_currentDragData.get(),
1364 clientPoint,
1365 screenPoint,
1366 static_cast<DragOperation>(operationsAllowed));
1367
1368 m_dropEffect = DropEffectDefault;
1369 m_dragTargetDispatch = true;
1370 DragOperation effect = m_page->dragController()->dragEntered(&dragData);
1371 // Mask the operation against the drag source's allowed operations.
1372 if ((effect & dragData.draggingSourceOperationMask()) != effect)
1373 effect = DragOperationNone;
1374 m_dragTargetDispatch = false;
1375
1376 if (m_dropEffect != DropEffectDefault) {
1377 m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy
1378 : WebDragOperationNone;
1379 } else
1380 m_dragOperation = static_cast<WebDragOperation>(effect);
1381 return m_dragOperation;
1382 }
1383
1384 WebDragOperation WebViewImpl::dragTargetDragOver(
1385 const WebPoint& clientPoint,
1386 const WebPoint& screenPoint,
1387 WebDragOperationsMask operationsAllowed)
1388 {
1389 ASSERT(m_currentDragData.get());
1390
1391 m_operationsAllowed = operationsAllowed;
1392 DragData dragData(
1393 m_currentDragData.get(),
1394 clientPoint,
1395 screenPoint,
1396 static_cast<DragOperation>(operationsAllowed));
1397
1398 m_dropEffect = DropEffectDefault;
1399 m_dragTargetDispatch = true;
1400 DragOperation effect = m_page->dragController()->dragUpdated(&dragData);
1401 // Mask the operation against the drag source's allowed operations.
1402 if ((effect & dragData.draggingSourceOperationMask()) != effect)
1403 effect = DragOperationNone;
1404 m_dragTargetDispatch = false;
1405
1406 if (m_dropEffect != DropEffectDefault) {
1407 m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy
1408 : WebDragOperationNone;
1409 } else
1410 m_dragOperation = static_cast<WebDragOperation>(effect);
1411 return m_dragOperation;
1412 }
1413
1414 void WebViewImpl::dragTargetDragLeave()
1415 {
1416 ASSERT(m_currentDragData.get());
1417
1418 DragData dragData(
1419 m_currentDragData.get(),
1420 IntPoint(),
1421 IntPoint(),
1422 static_cast<DragOperation>(m_operationsAllowed));
1423
1424 m_dragTargetDispatch = true;
1425 m_page->dragController()->dragExited(&dragData);
1426 m_dragTargetDispatch = false;
1427
1428 m_currentDragData = 0;
1429 m_dropEffect = DropEffectDefault;
1430 m_dragOperation = WebDragOperationNone;
1431 m_dragIdentity = 0;
1432 }
1433
1434 void WebViewImpl::dragTargetDrop(const WebPoint& clientPoint,
1435 const WebPoint& screenPoint)
1436 {
1437 ASSERT(m_currentDragData.get());
1438
1439 // If this webview transitions from the "drop accepting" state to the "not
1440 // accepting" state, then our IPC message reply indicating that may be in-
1441 // flight, or else delayed by javascript processing in this webview. If a
1442 // drop happens before our IPC reply has reached the browser process, then
1443 // the browser forwards the drop to this webview. So only allow a drop to
1444 // proceed if our webview m_dragOperation state is not DragOperationNone.
1445
1446 if (m_dragOperation == WebDragOperationNone) { // IPC RACE CONDITION: do not allow this drop.
1447 dragTargetDragLeave();
1448 return;
1449 }
1450
1451 DragData dragData(
1452 m_currentDragData.get(),
1453 clientPoint,
1454 screenPoint,
1455 static_cast<DragOperation>(m_operationsAllowed));
1456
1457 m_dragTargetDispatch = true;
1458 m_page->dragController()->performDrag(&dragData);
1459 m_dragTargetDispatch = false;
1460
1461 m_currentDragData = 0;
1462 m_dropEffect = DropEffectDefault;
1463 m_dragOperation = WebDragOperationNone;
1464 m_dragIdentity = 0;
1465 }
1466
1467 int WebViewImpl::dragIdentity()
1468 {
1469 if (m_dragTargetDispatch)
1470 return m_dragIdentity;
1471 return 0;
1472 }
1473
1474 void WebViewImpl::inspectElementAt(const WebPoint& point)
1475 {
1476 if (!m_page.get())
1477 return;
1478
1479 if (point.x == -1 || point.y == -1)
1480 m_page->inspectorController()->inspect(0);
1481 else {
1482 HitTestResult result = hitTestResultForWindowPos(point);
1483
1484 if (!result.innerNonSharedNode())
1485 return;
1486
1487 m_page->inspectorController()->inspect(result.innerNonSharedNode());
1488 }
1489 }
1490
1491 WebString WebViewImpl::inspectorSettings() const
1492 {
1493 return m_inspectorSettings;
1494 }
1495
1496 void WebViewImpl::setInspectorSettings(const WebString& settings)
1497 {
1498 m_inspectorSettings = settings;
1499 }
1500
1501 WebDevToolsAgent* WebViewImpl::devToolsAgent()
1502 {
1503 return m_devToolsAgent.get();
1504 }
1505
1506 void WebViewImpl::setDevToolsAgent(WebDevToolsAgent* devToolsAgent)
1507 {
1508 ASSERT(!m_devToolsAgent.get()); // May only set once!
1509 m_devToolsAgent.set(static_cast<WebDevToolsAgentPrivate*>(devToolsAgent));
1510 }
1511
1512 WebAccessibilityObject WebViewImpl::accessibilityObject()
1513 {
1514 if (!mainFrameImpl())
1515 return WebAccessibilityObject();
1516
1517 Document* document = mainFrameImpl()->frame()->document();
1518 return WebAccessibilityObject(
1519 document->axObjectCache()->getOrCreate(document->renderer()));
1520 }
1521
1522 void WebViewImpl::applyAutofillSuggestions(
1523 const WebNode& node,
1524 const WebVector<WebString>& suggestions,
1525 int defaultSuggestionIndex)
1526 {
1527 if (!m_page.get() || suggestions.isEmpty()) {
1528 hideAutoCompletePopup();
1529 return;
1530 }
1531
1532 ASSERT(defaultSuggestionIndex < static_cast<int>(suggestions.size()));
1533
1534 if (RefPtr<Frame> focused = m_page->focusController()->focusedFrame()) {
1535 RefPtr<Document> document = focused->document();
1536 if (!document.get()) {
1537 hideAutoCompletePopup();
1538 return;
1539 }
1540
1541 RefPtr<Node> focusedNode = document->focusedNode();
1542 // If the node for which we queried the autofill suggestions is not the
1543 // focused node, then we have nothing to do. FIXME: also check the
1544 // carret is at the end and that the text has not changed.
1545 if (!focusedNode.get() || focusedNode != PassRefPtr<Node>(node)) {
1546 hideAutoCompletePopup();
1547 return;
1548 }
1549
1550 if (!focusedNode->hasTagName(HTMLNames::inputTag)) {
1551 ASSERT_NOT_REACHED();
1552 return;
1553 }
1554
1555 HTMLInputElement* inputElem =
1556 static_cast<HTMLInputElement*>(focusedNode.get());
1557
1558 // The first time the autocomplete is shown we'll create the client and the
1559 // popup.
1560 if (!m_autocompletePopupClient.get())
1561 m_autocompletePopupClient.set(new AutocompletePopupMenuClient(this));
1562 m_autocompletePopupClient->initialize(inputElem,
1563 suggestions,
1564 defaultSuggestionIndex);
1565 if (!m_autocompletePopup.get()) {
1566 m_autocompletePopup =
1567 PopupContainer::create(m_autocompletePopupClient.get(),
1568 autocompletePopupSettings);
1569 }
1570
1571 if (m_autocompletePopupShowing) {
1572 m_autocompletePopupClient->setSuggestions(suggestions);
1573 refreshAutofillPopup();
1574 } else {
1575 m_autocompletePopup->show(focusedNode->getRect(),
1576 focusedNode->ownerDocument()->view(), 0);
1577 m_autocompletePopupShowing = true;
1578 }
1579 }
1580 }
1581
1582 void WebViewImpl::hideAutofillPopup()
1583 {
1584 hideAutoCompletePopup();
1585 }
1586
1587 // WebView --------------------------------------------------------------------
1588
1589 bool WebViewImpl::setDropEffect(bool accept)
1590 {
1591 if (m_dragTargetDispatch) {
1592 m_dropEffect = accept ? DropEffectCopy : DropEffectNone;
1593 return true;
1594 }
1595 return false;
1596 }
1597
1598 void WebViewImpl::setIsTransparent(bool isTransparent)
1599 {
1600 // Set any existing frames to be transparent.
1601 Frame* frame = m_page->mainFrame();
1602 while (frame) {
1603 frame->view()->setTransparent(isTransparent);
1604 frame = frame->tree()->traverseNext();
1605 }
1606
1607 // Future frames check this to know whether to be transparent.
1608 m_isTransparent = isTransparent;
1609 }
1610
1611 bool WebViewImpl::isTransparent() const
1612 {
1613 return m_isTransparent;
1614 }
1615
1616 void WebViewImpl::setIsActive(bool active)
1617 {
1618 if (page() && page()->focusController())
1619 page()->focusController()->setActive(active);
1620 }
1621
1622 bool WebViewImpl::isActive() const
1623 {
1624 return (page() && page()->focusController()) ? page()->focusController()->isActive() : false;
1625 }
1626
1627 void WebViewImpl::didCommitLoad(bool* isNewNavigation)
1628 {
1629 if (isNewNavigation)
1630 *isNewNavigation = m_observedNewNavigation;
1631
1632 #ifndef NDEBUG
1633 ASSERT(!m_observedNewNavigation
1634 || m_page->mainFrame()->loader()->documentLoader() == m_newNavigationLoader);
1635 m_newNavigationLoader = 0;
1636 #endif
1637 m_observedNewNavigation = false;
1638 }
1639
1640 bool WebViewImpl::navigationPolicyFromMouseEvent(unsigned short button,
1641 bool ctrl, bool shift,
1642 bool alt, bool meta,
1643 WebNavigationPolicy* policy)
1644 {
1645 #if PLATFORM(WIN_OS) || PLATFORM(LINUX) || PLATFORM(FREEBSD)
1646 const bool newTabModifier = (button == 1) || ctrl;
1647 #elif PLATFORM(DARWIN)
1648 const bool newTabModifier = (button == 1) || meta;
1649 #endif
1650 if (!newTabModifier && !shift && !alt)
1651 return false;
1652
1653 ASSERT(policy);
1654 if (newTabModifier) {
1655 if (shift)
1656 *policy = WebNavigationPolicyNewForegroundTab;
1657 else
1658 *policy = WebNavigationPolicyNewBackgroundTab;
1659 } else {
1660 if (shift)
1661 *policy = WebNavigationPolicyNewWindow;
1662 else
1663 *policy = WebNavigationPolicyDownload;
1664 }
1665 return true;
1666 }
1667
1668 void WebViewImpl::startDragging(const WebPoint& eventPos,
1669 const WebDragData& dragData,
1670 WebDragOperationsMask mask)
1671 {
1672 if (!m_client)
1673 return;
1674 ASSERT(!m_doingDragAndDrop);
1675 m_doingDragAndDrop = true;
1676 m_client->startDragging(eventPos, dragData, mask);
1677 }
1678
1679 void WebViewImpl::setCurrentHistoryItem(HistoryItem* item)
1680 {
1681 m_backForwardListClientImpl.setCurrentHistoryItem(item);
1682 }
1683
1684 HistoryItem* WebViewImpl::previousHistoryItem()
1685 {
1686 return m_backForwardListClientImpl.previousHistoryItem();
1687 }
1688
1689 void WebViewImpl::observeNewNavigation()
1690 {
1691 m_observedNewNavigation = true;
1692 #ifndef NDEBUG
1693 m_newNavigationLoader = m_page->mainFrame()->loader()->documentLoader();
1694 #endif
1695 }
1696
1697 void WebViewImpl::hideAutoCompletePopup()
1698 {
1699 if (m_autocompletePopupShowing) {
1700 m_autocompletePopup->hidePopup();
1701 autoCompletePopupDidHide();
1702 }
1703 }
1704
1705 void WebViewImpl::autoCompletePopupDidHide()
1706 {
1707 m_autocompletePopupShowing = false;
1708 }
1709
1710 void WebViewImpl::setIgnoreInputEvents(bool newValue)
1711 {
1712 ASSERT(m_ignoreInputEvents != newValue);
1713 m_ignoreInputEvents = newValue;
1714 }
1715
1716 #if ENABLE(NOTIFICATIONS)
1717 NotificationPresenterImpl* WebViewImpl::notificationPresenterImpl()
1718 {
1719 if (!m_notificationPresenter.isInitialized() && m_client)
1720 m_notificationPresenter.initialize(m_client->notificationPresenter());
1721 return &m_notificationPresenter;
1722 }
1723 #endif
1724
1725 void WebViewImpl::refreshAutofillPopup()
1726 {
1727 ASSERT(m_autocompletePopupShowing);
1728
1729 // Hide the popup if it has become empty.
1730 if (!m_autocompletePopupClient->listSize()) {
1731 hideAutoCompletePopup();
1732 return;
1733 }
1734
1735 IntRect oldBounds = m_autocompletePopup->boundsRect();
1736 m_autocompletePopup->refresh();
1737 IntRect newBounds = m_autocompletePopup->boundsRect();
1738 // Let's resize the backing window if necessary.
1739 if (oldBounds != newBounds) {
1740 WebPopupMenuImpl* popupMenu =
1741 static_cast<WebPopupMenuImpl*>(m_autocompletePopup->client());
1742 popupMenu->client()->setWindowRect(newBounds);
1743 }
1744 }
1745
1746 Node* WebViewImpl::focusedWebCoreNode()
1747 {
1748 Frame* frame = m_page->focusController()->focusedFrame();
1749 if (!frame)
1750 return 0;
1751
1752 Document* document = frame->document();
1753 if (!document)
1754 return 0;
1755
1756 return document->focusedNode();
1757 }
1758
1759 HitTestResult WebViewImpl::hitTestResultForWindowPos(const IntPoint& pos)
1760 {
1761 IntPoint docPoint(m_page->mainFrame()->view()->windowToContents(pos));
1762 return m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(docPoint, false);
1763 }
1764
1765 void WebViewImpl::setTabsToLinks(bool enable)
1766 {
1767 m_tabsToLinks = enable;
1768 }
1769
1770 bool WebViewImpl::tabsToLinks() const
1771 {
1772 return m_tabsToLinks;
1773 }
1774
1775 } // namespace WebKit
OLDNEW
« no previous file with comments | « webkit/api/src/WebViewImpl.h ('k') | webkit/api/src/WebWorkerBase.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698