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