25b032651039c0ae82b12c14184cb5fb5339ba85
[WebKit-https.git] / WebKit2 / WebProcess / WebPage / mac / WebPageMac.mm
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "WebPage.h"
27
28 #include "PluginView.h"
29 #include "WebCoreArgumentCoders.h"
30 #include "WebEvent.h"
31 #include "WebPageProxyMessages.h"
32 #include "WebProcess.h"
33 #include <WebCore/FocusController.h>
34 #include <WebCore/Frame.h>
35 #include <WebCore/FrameView.h>
36 #include <WebCore/HitTestResult.h>
37 #include <WebCore/KeyboardEvent.h>
38 #include <WebCore/Page.h>
39 #include <WebCore/PlatformKeyboardEvent.h>
40 #include <WebCore/ScrollView.h>
41 #include <WebCore/TextIterator.h>
42 #include <WebCore/WindowsKeyboardCodes.h>
43
44 using namespace WebCore;
45
46 namespace WebKit {
47
48 void WebPage::platformInitialize()
49 {
50     m_page->addSchedulePair(SchedulePair::create([NSRunLoop currentRunLoop], kCFRunLoopCommonModes));
51 }
52
53 void WebPage::platformPreferencesDidChange(const WebPreferencesStore&)
54 {
55 }
56
57 // FIXME: need to add support for input methods
58     
59 bool WebPage::interceptEditingKeyboardEvent(KeyboardEvent* evt, bool shouldSaveCommand)
60 {
61     Node* node = evt->target()->toNode();
62     ASSERT(node);
63     Frame* frame = node->document()->frame();
64     ASSERT(frame);
65     
66     const PlatformKeyboardEvent* keyEvent = evt->keyEvent();
67     if (!keyEvent)
68         return false;
69     const Vector<KeypressCommand>& commands = evt->keypressCommands();
70     bool hasKeypressCommand = !commands.isEmpty();
71     
72     bool eventWasHandled = false;
73     
74     if (shouldSaveCommand || !hasKeypressCommand) {
75         Vector<KeypressCommand> commandsList;  
76         Vector<CompositionUnderline> underlines;
77         unsigned start;
78         unsigned end;
79         if (!WebProcess::shared().connection()->sendSync(Messages::WebPageProxy::InterpretKeyEvent(keyEvent->type()), 
80                                                          Messages::WebPageProxy::InterpretKeyEvent::Reply(commandsList, start, end, underlines),
81                                                          m_pageID, CoreIPC::Connection::NoTimeout))
82             return false;
83         if (commandsList.isEmpty())
84             return eventWasHandled;
85         
86         if (commandsList[0].commandName == "setMarkedText") {
87             frame->editor()->setComposition(commandsList[0].text, underlines, start, end);
88             eventWasHandled = true;
89         } else if (commandsList[0].commandName == "insertText" && frame->editor()->hasComposition()) {
90             frame->editor()->confirmComposition(commandsList[0].text);
91             eventWasHandled = true;
92         } else if (commandsList[0].commandName == "unmarkText") {
93             frame->editor()->confirmComposition();
94             eventWasHandled = true;
95         } else {
96             for (size_t i = 0; i < commandsList.size(); i++)
97                 evt->keypressCommands().append(commandsList[i]);
98         }
99     } else {
100         size_t size = commands.size();
101         // Are there commands that would just cause text insertion if executed via Editor?
102         // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
103         // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
104         // (e.g. Tab that inserts a Tab character, or Enter).
105         bool haveTextInsertionCommands = false;
106         for (size_t i = 0; i < size; ++i) {
107             if (frame->editor()->command(commands[i].commandName).isTextInsertion())
108                 haveTextInsertionCommands = true;
109         }
110         if (!haveTextInsertionCommands || keyEvent->type() == PlatformKeyboardEvent::Char) {
111             for (size_t i = 0; i < size; ++i) {
112                 if (commands[i].commandName == "insertText") {
113                     // Don't insert null or control characters as they can result in unexpected behaviour
114                     if (evt->charCode() < ' ')
115                         return false;
116                     eventWasHandled = frame->editor()->insertText(commands[i].text, evt);
117                 } else
118                     if (frame->editor()->command(commands[i].commandName).isSupported())
119                         eventWasHandled = frame->editor()->command(commands[i].commandName).execute(evt);
120             }
121         }
122     }
123     return eventWasHandled;
124 }
125
126 void WebPage::sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
127 {
128     for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it) {
129         if ((*it)->sendComplexTextInput(pluginComplexTextInputIdentifier, textInput))
130             break;
131     }
132 }
133
134 void WebPage::getMarkedRange(uint64_t& location, uint64_t& length)
135 {
136     location = NSNotFound;
137     length = 0;
138     Frame* frame = m_page->focusController()->focusedOrMainFrame();
139     if (!frame)
140         return;
141     
142     getLocationAndLengthFromRange(frame->editor()->compositionRange().get(), location, length);
143 }
144
145 static Range *characterRangeAtPoint(Frame* frame, const IntPoint point)
146 {
147     VisiblePosition position = frame->visiblePositionForPoint(point);
148     if (position.isNull())
149         return NULL;
150     
151     VisiblePosition previous = position.previous();
152     if (previous.isNotNull()) {
153         Range *previousCharacterRange = makeRange(previous, position).get();
154         NSRect rect = frame->editor()->firstRectForRange(previousCharacterRange);
155         if (NSPointInRect(point, rect))
156             return previousCharacterRange;
157     }
158     
159     VisiblePosition next = position.next();
160     if (next.isNotNull()) {
161         Range *nextCharacterRange = makeRange(position, next).get();
162         NSRect rect = frame->editor()->firstRectForRange(nextCharacterRange);
163         if (NSPointInRect(point, rect))
164             return nextCharacterRange;
165     }
166     
167     return NULL;
168 }
169     
170 void WebPage::characterIndexForPoint(IntPoint point, uint64_t& index)
171 {
172     index = NSNotFound;
173     Frame* frame = m_page->mainFrame();
174     if (!frame)
175         return;
176
177     HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(point, false);
178     frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : m_page->focusController()->focusedOrMainFrame();
179     
180     Range *range = characterRangeAtPoint(frame, result.point());
181     if (!range)
182         return;
183
184     uint64_t length;
185     getLocationAndLengthFromRange(range, index, length);
186 }
187
188 static PassRefPtr<Range> convertToRange(Frame* frame, NSRange nsrange)
189 {
190     if (nsrange.location > INT_MAX)
191         return 0;
192     if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
193         nsrange.length = INT_MAX - nsrange.location;
194         
195     // our critical assumption is that we are only called by input methods that
196     // concentrate on a given area containing the selection
197     // We have to do this because of text fields and textareas. The DOM for those is not
198     // directly in the document DOM, so serialization is problematic. Our solution is
199     // to use the root editable element of the selection start as the positional base.
200     // That fits with AppKit's idea of an input context.
201     Element* selectionRoot = frame->selection()->rootEditableElement();
202     Element* scope = selectionRoot ? selectionRoot : frame->document()->documentElement();
203     return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
204 }
205     
206 void WebPage::firstRectForCharacterRange(uint64_t location, uint64_t length, WebCore::IntRect& resultRect)
207 {
208     Frame* frame = m_page->focusController()->focusedOrMainFrame();
209     resultRect.setLocation(IntPoint(0, 0));
210     resultRect.setSize(IntSize(0, 0));
211     
212     RefPtr<Range> range = convertToRange(frame, NSMakeRange(location, length));
213     if (range) {
214         ASSERT(range->startContainer());
215         ASSERT(range->endContainer());
216     }
217      
218     IntRect rect = frame->editor()->firstRectForRange(range.get());
219     resultRect = frame->view()->contentsToWindow(rect);
220 }
221
222 static inline void scroll(Page* page, ScrollDirection direction, ScrollGranularity granularity)
223 {
224     page->focusController()->focusedOrMainFrame()->eventHandler()->scrollRecursively(direction, granularity);
225 }
226
227 static inline void logicalScroll(Page* page, ScrollLogicalDirection direction, ScrollGranularity granularity)
228 {
229     page->focusController()->focusedOrMainFrame()->eventHandler()->logicalScrollRecursively(direction, granularity);
230 }
231
232 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent& keyboardEvent)
233 {
234     if (keyboardEvent.type() != WebEvent::KeyDown)
235         return false;
236
237     // FIXME: This should be in WebCore.
238
239     switch (keyboardEvent.windowsVirtualKeyCode()) {
240     case VK_BACK:
241         if (keyboardEvent.shiftKey())
242             m_page->goForward();
243         else
244             m_page->goBack();
245         break;
246     case VK_SPACE:
247         if (keyboardEvent.shiftKey())
248             logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByPage);
249         else
250             logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByPage);
251         break;
252     case VK_PRIOR:
253         logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByPage);
254         break;
255     case VK_NEXT:
256         logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByPage);
257         break;
258     case VK_HOME:
259         logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByDocument);
260         break;
261     case VK_END:
262         logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByDocument);
263         break;
264     case VK_UP:
265         if (keyboardEvent.shiftKey())
266             return false;
267         if (keyboardEvent.metaKey()) {
268             scroll(m_page.get(), ScrollUp, ScrollByDocument);
269             scroll(m_page.get(), ScrollLeft, ScrollByDocument);
270         } else if (keyboardEvent.altKey() || keyboardEvent.controlKey())
271             scroll(m_page.get(), ScrollUp, ScrollByPage);
272         else
273             scroll(m_page.get(), ScrollUp, ScrollByLine);
274         break;
275     case VK_DOWN:
276         if (keyboardEvent.shiftKey())
277             return false;
278         if (keyboardEvent.metaKey()) {
279             scroll(m_page.get(), ScrollDown, ScrollByDocument);
280             scroll(m_page.get(), ScrollLeft, ScrollByDocument);
281         } else if (keyboardEvent.altKey() || keyboardEvent.controlKey())
282             scroll(m_page.get(), ScrollDown, ScrollByPage);
283         else
284             scroll(m_page.get(), ScrollDown, ScrollByLine);
285         break;
286     case VK_LEFT:
287         if (keyboardEvent.shiftKey())
288             return false;
289         if (keyboardEvent.metaKey())
290             m_page->goBack();
291         else {
292             if (keyboardEvent.altKey()  | keyboardEvent.controlKey())
293                 scroll(m_page.get(), ScrollLeft, ScrollByPage);
294             else
295                 scroll(m_page.get(), ScrollLeft, ScrollByLine);
296         }
297         break;
298     case VK_RIGHT:
299         if (keyboardEvent.shiftKey())
300             return false;
301         if (keyboardEvent.metaKey())
302             m_page->goForward();
303         else {
304             if (keyboardEvent.altKey() || keyboardEvent.controlKey())
305                 scroll(m_page.get(), ScrollRight, ScrollByPage);
306             else
307                 scroll(m_page.get(), ScrollRight, ScrollByLine);
308         }
309         break;
310     default:
311         return false;
312     }
313
314     return true;
315 }
316
317 bool WebPage::platformHasLocalDataForURL(const WebCore::KURL& url)
318 {
319     NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
320     [request setValue:(NSString*)userAgent() forHTTPHeaderField:@"User-Agent"];
321     NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
322     [request release];
323     
324     return cachedResponse;
325 }
326
327 bool WebPage::canHandleRequest(const WebCore::ResourceRequest& request)
328 {
329     if ([NSURLConnection canHandleRequest:request.nsURLRequest()])
330         return YES;
331
332     // FIXME: Return true if this scheme is any one WebKit2 knows how to handle.
333     return request.url().protocolIs("applewebdata");
334 }
335
336 } // namespace WebKit