OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // The Mac interface forwards most of these commands to the application layer, | 5 // The Mac interface forwards most of these commands to the application layer, |
6 // and I'm not really sure what to do about most of them. | 6 // and I'm not really sure what to do about most of them. |
7 | 7 |
8 #include "config.h" | 8 #include "config.h" |
9 | 9 |
10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
11 | 11 |
12 MSVC_PUSH_WARNING_LEVEL(0); | 12 MSVC_PUSH_WARNING_LEVEL(0); |
13 #include "Document.h" | 13 #include "Document.h" |
14 #include "EditCommand.h" | 14 #include "EditCommand.h" |
15 #include "Editor.h" | 15 #include "Editor.h" |
16 #include "EventHandler.h" | 16 #include "EventHandler.h" |
17 #include "EventNames.h" | 17 #include "EventNames.h" |
18 #include "KeyboardCodes.h" | 18 #include "KeyboardCodes.h" |
19 #include "HTMLInputElement.h" | 19 #include "HTMLInputElement.h" |
| 20 #include "HTMLNames.h" |
20 #include "Frame.h" | 21 #include "Frame.h" |
21 #include "KeyboardEvent.h" | 22 #include "KeyboardEvent.h" |
22 #include "PlatformKeyboardEvent.h" | 23 #include "PlatformKeyboardEvent.h" |
23 #include "PlatformString.h" | 24 #include "PlatformString.h" |
24 MSVC_POP_WARNING(); | 25 MSVC_POP_WARNING(); |
25 | 26 |
26 #undef LOG | 27 #undef LOG |
| 28 #include "base/message_loop.h" |
27 #include "base/string_util.h" | 29 #include "base/string_util.h" |
28 #include "webkit/glue/editor_client_impl.h" | 30 #include "webkit/glue/editor_client_impl.h" |
29 #include "webkit/glue/glue_util.h" | 31 #include "webkit/glue/glue_util.h" |
30 #include "webkit/glue/webkit_glue.h" | 32 #include "webkit/glue/webkit_glue.h" |
31 #include "webkit/glue/webview.h" | 33 #include "webkit/glue/webview.h" |
32 #include "webkit/glue/webview_impl.h" | 34 #include "webkit/glue/webview_impl.h" |
33 | 35 |
34 // Arbitrary depth limit for the undo stack, to keep it from using | 36 // Arbitrary depth limit for the undo stack, to keep it from using |
35 // unbounded memory. This is the maximum number of distinct undoable | 37 // unbounded memory. This is the maximum number of distinct undoable |
36 // actions -- unbroken stretches of typed characters are coalesced | 38 // actions -- unbroken stretches of typed characters are coalesced |
37 // into a single action. | 39 // into a single action. |
38 static const size_t kMaximumUndoStackDepth = 1000; | 40 static const size_t kMaximumUndoStackDepth = 1000; |
39 | 41 |
| 42 // The size above which we stop triggering autofill for an input text field |
| 43 // (so to avoid sending long strings through IPC). |
| 44 static const size_t kMaximumTextSizeForAutofill = 1000; |
| 45 |
40 namespace { | 46 namespace { |
41 | 47 |
42 // Record an editor command from the keyDownEntries[] below. We ignore the | 48 // Record an editor command from the keyDownEntries[] below. We ignore the |
43 // Move* and Insert* commands because they're not that interesting. | 49 // Move* and Insert* commands because they're not that interesting. |
44 void MaybeRecordCommand(WebViewDelegate* d, const char* command_name) { | 50 void MaybeRecordCommand(WebViewDelegate* d, const char* command_name) { |
45 if (!d) | 51 if (!d) |
46 return; | 52 return; |
47 | 53 |
48 const char* move_prefix = "Move"; | 54 const char* move_prefix = "Move"; |
49 const char* insert_prefix = "Insert"; | 55 const char* insert_prefix = "Insert"; |
50 const char* delete_prefix = "Delete"; | 56 const char* delete_prefix = "Delete"; |
51 // Ignore all the Move*, Insert*, and Delete* commands. | 57 // Ignore all the Move*, Insert*, and Delete* commands. |
52 if (0 == strncmp(command_name, move_prefix, sizeof(move_prefix)) || | 58 if (0 == strncmp(command_name, move_prefix, sizeof(move_prefix)) || |
53 0 == strncmp(command_name, insert_prefix, sizeof(insert_prefix)) || | 59 0 == strncmp(command_name, insert_prefix, sizeof(insert_prefix)) || |
54 0 == strncmp(command_name, delete_prefix, sizeof(delete_prefix))) { | 60 0 == strncmp(command_name, delete_prefix, sizeof(delete_prefix))) { |
55 return; | 61 return; |
56 } | 62 } |
57 d->UserMetricsRecordComputedAction(UTF8ToWide(command_name)); | 63 d->UserMetricsRecordComputedAction(UTF8ToWide(command_name)); |
58 } | 64 } |
59 | 65 |
60 } | 66 } |
61 | 67 |
62 EditorClientImpl::EditorClientImpl(WebView* web_view) | 68 EditorClientImpl::EditorClientImpl(WebView* web_view) |
63 : web_view_(static_cast<WebViewImpl*>(web_view)), | 69 : web_view_(static_cast<WebViewImpl*>(web_view)), |
64 use_editor_delegate_(false), | 70 use_editor_delegate_(false), |
65 in_redo_(false), | 71 in_redo_(false), |
66 preserve_(false), | 72 backspace_pressed_(false), |
67 pending_inline_autocompleted_element_(NULL) { | 73 // Don't complain about using "this" in initializer list. |
| 74 MSVC_PUSH_DISABLE_WARNING(4355) |
| 75 autofill_factory_(this) { |
| 76 MSVC_POP_WARNING() |
68 } | 77 } |
69 | 78 |
70 EditorClientImpl::~EditorClientImpl() { | 79 EditorClientImpl::~EditorClientImpl() { |
71 } | 80 } |
72 | 81 |
73 void EditorClientImpl::pageDestroyed() { | 82 void EditorClientImpl::pageDestroyed() { |
74 // Called by the Page (which owns the editor client) when the page is going | 83 // Called by the Page (which owns the editor client) when the page is going |
75 // away. This should cause us to delete ourselves, which is stupid. The page | 84 // away. This should cause us to delete ourselves, which is stupid. The page |
76 // should just delete us when it's going away. Oh well. | 85 // should just delete us when it's going away. Oh well. |
77 delete this; | 86 delete this; |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 | 184 |
176 bool EditorClientImpl::shouldDeleteRange(WebCore::Range* range) { | 185 bool EditorClientImpl::shouldDeleteRange(WebCore::Range* range) { |
177 if (use_editor_delegate_) { | 186 if (use_editor_delegate_) { |
178 WebViewDelegate* d = web_view_->delegate(); | 187 WebViewDelegate* d = web_view_->delegate(); |
179 if (d) | 188 if (d) |
180 return d->ShouldDeleteRange(web_view_, Describe(range)); | 189 return d->ShouldDeleteRange(web_view_, Describe(range)); |
181 } | 190 } |
182 return true; | 191 return true; |
183 } | 192 } |
184 | 193 |
185 void EditorClientImpl::PreserveSelection() { | |
186 preserve_ = true; | |
187 } | |
188 | |
189 bool EditorClientImpl::shouldChangeSelectedRange(WebCore::Range* fromRange, | 194 bool EditorClientImpl::shouldChangeSelectedRange(WebCore::Range* fromRange, |
190 WebCore::Range* toRange, | 195 WebCore::Range* toRange, |
191 WebCore::EAffinity affinity, | 196 WebCore::EAffinity affinity, |
192 bool stillSelecting) { | 197 bool stillSelecting) { |
193 if (use_editor_delegate_) { | 198 if (use_editor_delegate_) { |
194 WebViewDelegate* d = web_view_->delegate(); | 199 WebViewDelegate* d = web_view_->delegate(); |
195 if (d) { | 200 if (d) { |
196 return d->ShouldChangeSelectedRange(web_view_, | 201 return d->ShouldChangeSelectedRange(web_view_, |
197 Describe(fromRange), | 202 Describe(fromRange), |
198 Describe(toRange), | 203 Describe(toRange), |
199 Describe(affinity), | 204 Describe(affinity), |
200 stillSelecting); | 205 stillSelecting); |
201 } | 206 } |
202 } | 207 } |
203 // Have we been told to preserve the selection? | |
204 // (See comments for PreserveSelection in header). | |
205 if (preserve_) { | |
206 preserve_ = false; | |
207 return false; | |
208 } | |
209 return true; | 208 return true; |
210 } | 209 } |
211 | 210 |
212 bool EditorClientImpl::shouldApplyStyle(WebCore::CSSStyleDeclaration* style, | 211 bool EditorClientImpl::shouldApplyStyle(WebCore::CSSStyleDeclaration* style, |
213 WebCore::Range* range) { | 212 WebCore::Range* range) { |
214 if (use_editor_delegate_) { | 213 if (use_editor_delegate_) { |
215 WebViewDelegate* d = web_view_->delegate(); | 214 WebViewDelegate* d = web_view_->delegate(); |
216 if (d) | 215 if (d) |
217 return d->ShouldApplyStyle(web_view_, Describe(style), Describe(range)); | 216 return d->ShouldApplyStyle(web_view_, Describe(style), Describe(range)); |
218 } | 217 } |
(...skipping 16 matching lines...) Expand all Loading... |
235 | 234 |
236 void EditorClientImpl::respondToChangedSelection() { | 235 void EditorClientImpl::respondToChangedSelection() { |
237 if (use_editor_delegate_) { | 236 if (use_editor_delegate_) { |
238 WebViewDelegate* d = web_view_->delegate(); | 237 WebViewDelegate* d = web_view_->delegate(); |
239 if (d) | 238 if (d) |
240 d->DidChangeSelection(); | 239 d->DidChangeSelection(); |
241 } | 240 } |
242 } | 241 } |
243 | 242 |
244 void EditorClientImpl::respondToChangedContents() { | 243 void EditorClientImpl::respondToChangedContents() { |
245 // Ugly Hack. (See also webkit bug #16976). | |
246 // Something is wrong with webcore's focusController in that when selection | |
247 // is set to a region within a text element when handling an input event, if | |
248 // you don't re-focus the node then it only _APPEARS_ to have successfully | |
249 // changed the selection (the UI "looks" right) but in reality there is no | |
250 // selection of text. And to make matters worse, you can't just re-focus it, | |
251 // you have to re-focus it in code executed after the entire event listener | |
252 // loop has finished; and hence here we are. Oh, and to make matters worse, | |
253 // this sequence of events _doesn't_ happen when you debug through the code | |
254 // -- in that case it works perfectly fine -- because swapping to the debugger
| |
255 // causes the refocusing we artificially reproduce here. | |
256 // TODO (timsteele): Clean this up once root webkit problem is identified and | |
257 // the bug is patched. | |
258 if (pending_inline_autocompleted_element_) { | |
259 pending_inline_autocompleted_element_->blur(); | |
260 pending_inline_autocompleted_element_->focus(); | |
261 pending_inline_autocompleted_element_ = NULL; | |
262 } | |
263 | |
264 if (use_editor_delegate_) { | 244 if (use_editor_delegate_) { |
265 WebViewDelegate* d = web_view_->delegate(); | 245 WebViewDelegate* d = web_view_->delegate(); |
266 if (d) | 246 if (d) |
267 d->DidChangeContents(); | 247 d->DidChangeContents(); |
268 } | 248 } |
269 } | 249 } |
270 | 250 |
271 void EditorClientImpl::didEndEditing() { | 251 void EditorClientImpl::didEndEditing() { |
272 if (use_editor_delegate_) { | 252 if (use_editor_delegate_) { |
273 WebViewDelegate* d = web_view_->delegate(); | 253 WebViewDelegate* d = web_view_->delegate(); |
(...skipping 340 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
614 void EditorClientImpl::handleInputMethodKeydown(WebCore::KeyboardEvent* keyEvent
) { | 594 void EditorClientImpl::handleInputMethodKeydown(WebCore::KeyboardEvent* keyEvent
) { |
615 NOTIMPLEMENTED(); | 595 NOTIMPLEMENTED(); |
616 } | 596 } |
617 | 597 |
618 void EditorClientImpl::textFieldDidBeginEditing(WebCore::Element*) { | 598 void EditorClientImpl::textFieldDidBeginEditing(WebCore::Element*) { |
619 } | 599 } |
620 | 600 |
621 void EditorClientImpl::textFieldDidEndEditing(WebCore::Element*) { | 601 void EditorClientImpl::textFieldDidEndEditing(WebCore::Element*) { |
622 // Notification that focus was lost. Be careful with this, it's also sent | 602 // Notification that focus was lost. Be careful with this, it's also sent |
623 // when the page is being closed. | 603 // when the page is being closed. |
| 604 |
| 605 // Cancel any pending DoAutofill calls. |
| 606 autofill_factory_.RevokeAll(); |
624 } | 607 } |
625 | 608 |
626 void EditorClientImpl::textDidChangeInTextField(WebCore::Element* element) { | 609 void EditorClientImpl::textDidChangeInTextField(WebCore::Element* element) { |
627 // Track the element so we can blur/focus it in respondToChangedContents | 610 DCHECK(element->hasLocalName(WebCore::HTMLNames::inputTag)); |
628 // so that the selected range is properly set. (See respondToChangedContents). | 611 |
629 if (static_cast<WebCore::HTMLInputElement*>(element)->autofilled()) | 612 // Cancel any pending DoAutofill calls. |
630 pending_inline_autocompleted_element_ = element; | 613 autofill_factory_.RevokeAll(); |
| 614 |
| 615 // Let's try to trigger autofill for that field, if applicable. |
| 616 WebCore::HTMLInputElement* input_element = |
| 617 static_cast<WebCore::HTMLInputElement*>(element); |
| 618 if (!input_element->isEnabled() || !input_element->isTextField() || |
| 619 input_element->isPasswordField() || !input_element->autoComplete()) { |
| 620 return; |
| 621 } |
| 622 |
| 623 std::wstring name = webkit_glue::StringToStdWString(input_element->name()); |
| 624 if (name.empty()) // If the field has no name, then we won't have values. |
| 625 return; |
| 626 |
| 627 // Don't attempt to autofill with values that are too large. |
| 628 if (input_element->value().length() > kMaximumTextSizeForAutofill) |
| 629 return; |
| 630 |
| 631 // We post a task for doing the autofill as the caret position is not set |
| 632 // properly at this point ( http://bugs.webkit.org/show_bug.cgi?id=16976) |
| 633 // and we need it to determine whether or not to trigger autofill. |
| 634 std::wstring value = webkit_glue::StringToStdWString(input_element->value()); |
| 635 MessageLoop::current()->PostTask( |
| 636 FROM_HERE, |
| 637 autofill_factory_.NewRunnableMethod(&EditorClientImpl::DoAutofill, |
| 638 input_element, |
| 639 backspace_pressed_)); |
631 } | 640 } |
632 | 641 |
633 bool EditorClientImpl::doTextFieldCommandFromEvent(WebCore::Element*, | 642 void EditorClientImpl::DoAutofill(WebCore::HTMLInputElement* input_element, |
634 WebCore::KeyboardEvent*) { | 643 bool backspace) { |
| 644 std::wstring value = webkit_glue::StringToStdWString(input_element->value()); |
| 645 |
| 646 // Only autofill when there is some text and the caret is at the end. |
| 647 bool caret_at_end = |
| 648 input_element->selectionStart() == input_element->selectionEnd() && |
| 649 input_element->selectionEnd() == value.length(); |
| 650 if (value.empty() || !caret_at_end) |
| 651 return; |
| 652 |
| 653 // First let's see if there is a password listener for that element. |
| 654 WebFrameImpl* webframe = |
| 655 WebFrameImpl::FromFrame(input_element->form()->document()->frame()); |
| 656 webkit_glue::PasswordAutocompleteListener* listener = |
| 657 webframe->GetPasswordListener(input_element); |
| 658 if (listener) { |
| 659 if (backspace) // No autocomplete for password on backspace. |
| 660 return; |
| 661 |
| 662 listener->OnInlineAutocompleteNeeded(input_element, value); |
| 663 return; |
| 664 } |
| 665 |
| 666 // Then trigger form autofill. |
| 667 std::wstring name = webkit_glue::StringToStdWString(input_element-> |
| 668 name().string()); |
| 669 web_view_->delegate()->QueryFormFieldAutofill(name, value, |
| 670 reinterpret_cast<int64>(input_element)); |
| 671 } |
| 672 |
| 673 bool EditorClientImpl::doTextFieldCommandFromEvent( |
| 674 WebCore::Element* element, |
| 675 WebCore::KeyboardEvent* event) { |
| 676 // Remember if backspace was pressed for the autofill. It is not clear how to |
| 677 // find if backspace was pressed from textFieldDidBeginEditing and |
| 678 // textDidChangeInTextField as when these methods are called the value of the |
| 679 // input element already contains the type character. |
| 680 backspace_pressed_ = (event->keyCode() == WebCore::VKEY_BACK); |
| 681 |
635 // The Mac code appears to use this method as a hook to implement special | 682 // The Mac code appears to use this method as a hook to implement special |
636 // keyboard commands specific to Safari's auto-fill implementation. We | 683 // keyboard commands specific to Safari's auto-fill implementation. We |
637 // just return false to allow the default action. | 684 // just return false to allow the default action. |
638 return false; | 685 return false; |
639 } | 686 } |
640 | 687 |
641 void EditorClientImpl::textWillBeDeletedInTextField(WebCore::Element*) { | 688 void EditorClientImpl::textWillBeDeletedInTextField(WebCore::Element*) { |
642 } | 689 } |
643 | 690 |
644 void EditorClientImpl::textDidChangeInTextArea(WebCore::Element*) { | 691 void EditorClientImpl::textDidChangeInTextArea(WebCore::Element*) { |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
793 return L"(UNKNOWN AFFINITY)"; | 840 return L"(UNKNOWN AFFINITY)"; |
794 } | 841 } |
795 | 842 |
796 std::wstring EditorClientImpl::Describe(WebCore::CSSStyleDeclaration* style) { | 843 std::wstring EditorClientImpl::Describe(WebCore::CSSStyleDeclaration* style) { |
797 // TODO(pamg): Implement me. It's not clear what WebKit produces for this | 844 // TODO(pamg): Implement me. It's not clear what WebKit produces for this |
798 // (their [style description] method), and none of the layout tests provide | 845 // (their [style description] method), and none of the layout tests provide |
799 // an example. But because none of them use it, it's not yet important. | 846 // an example. But because none of them use it, it's not yet important. |
800 return std::wstring(); | 847 return std::wstring(); |
801 } | 848 } |
802 | 849 |
OLD | NEW |