| 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 |