1a378bc5e642badae0bcd09f3f677dd76fa36bfd
[WebKit-https.git] / Source / 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 #import "config.h"
27 #import "WebPage.h"
28
29 #import "AccessibilityWebPageObject.h"
30 #import "DataReference.h"
31 #import "PluginView.h"
32 #import "WebCoreArgumentCoders.h"
33 #import "WebEvent.h"
34 #import "WebFrame.h"
35 #import "WebPageProxyMessages.h"
36 #import "WebProcess.h"
37 #import <WebCore/AXObjectCache.h>
38 #import <WebCore/FocusController.h>
39 #import <WebCore/Frame.h>
40 #import <WebCore/FrameView.h>
41 #import <WebCore/HitTestResult.h>
42 #import <WebCore/KeyboardEvent.h>
43 #import <WebCore/Page.h>
44 #import <WebCore/PlatformKeyboardEvent.h>
45 #import <WebCore/ScrollView.h>
46 #import <WebCore/TextIterator.h>
47 #import <WebCore/WindowsKeyboardCodes.h>
48 #import <WebKitSystemInterface.h>
49
50 using namespace WebCore;
51
52 namespace WebKit {
53
54 void WebPage::platformInitialize()
55 {
56     m_page->addSchedulePair(SchedulePair::create([NSRunLoop currentRunLoop], kCFRunLoopCommonModes));
57
58 #if !defined(BUILDING_ON_SNOW_LEOPARD)
59     AccessibilityWebPageObject* mockAccessibilityElement = [[[AccessibilityWebPageObject alloc] init] autorelease];
60
61     // Get the pid for the starting process.
62     pid_t pid = WebProcess::shared().presenterApplicationPid();    
63     WKAXInitializeElementWithPresenterPid(mockAccessibilityElement, pid);
64     [mockAccessibilityElement setWebPage:this];
65     
66     // send data back over
67     NSData* remoteToken = (NSData *)WKAXRemoteTokenForElement(mockAccessibilityElement); 
68     CoreIPC::DataReference dataToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]);
69     send(Messages::WebPageProxy::DidReceiveAccessibilityPageToken(dataToken));
70     m_mockAccessibilityElement = mockAccessibilityElement;
71 #endif
72 }
73
74 void WebPage::platformPreferencesDidChange(const WebPreferencesStore&)
75 {
76 }
77
78 // FIXME: need to add support for input methods
79     
80 bool WebPage::interceptEditingKeyboardEvent(KeyboardEvent* evt, bool shouldSaveCommand)
81 {
82     Node* node = evt->target()->toNode();
83     ASSERT(node);
84     Frame* frame = node->document()->frame();
85     ASSERT(frame);
86     
87     const PlatformKeyboardEvent* keyEvent = evt->keyEvent();
88     if (!keyEvent)
89         return false;
90     const Vector<KeypressCommand>& commands = evt->keypressCommands();
91     bool hasKeypressCommand = !commands.isEmpty();
92     
93     bool eventWasHandled = false;
94     
95     if (shouldSaveCommand || !hasKeypressCommand) {
96         Vector<KeypressCommand> commandsList;  
97         Vector<CompositionUnderline> underlines;
98         unsigned start;
99         unsigned end;
100         if (!WebProcess::shared().connection()->sendSync(Messages::WebPageProxy::InterpretKeyEvent(keyEvent->type()), 
101                                                          Messages::WebPageProxy::InterpretKeyEvent::Reply(commandsList, start, end, underlines),
102                                                          m_pageID, CoreIPC::Connection::NoTimeout))
103             return false;
104         if (commandsList.isEmpty())
105             return eventWasHandled;
106         
107         if (commandsList[0].commandName == "setMarkedText") {
108             frame->editor()->setComposition(commandsList[0].text, underlines, start, end);
109             eventWasHandled = true;
110         } else if (commandsList[0].commandName == "insertText" && frame->editor()->hasComposition()) {
111             frame->editor()->confirmComposition(commandsList[0].text);
112             eventWasHandled = true;
113         } else if (commandsList[0].commandName == "unmarkText") {
114             frame->editor()->confirmComposition();
115             eventWasHandled = true;
116         } else {
117             for (size_t i = 0; i < commandsList.size(); i++)
118                 evt->keypressCommands().append(commandsList[i]);
119         }
120     } else {
121         size_t size = commands.size();
122         // Are there commands that would just cause text insertion if executed via Editor?
123         // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
124         // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
125         // (e.g. Tab that inserts a Tab character, or Enter).
126         bool haveTextInsertionCommands = false;
127         for (size_t i = 0; i < size; ++i) {
128             if (frame->editor()->command(commands[i].commandName).isTextInsertion())
129                 haveTextInsertionCommands = true;
130         }
131         if (!haveTextInsertionCommands || keyEvent->type() == PlatformKeyboardEvent::Char) {
132             for (size_t i = 0; i < size; ++i) {
133                 if (commands[i].commandName == "insertText") {
134                     // Don't insert null or control characters as they can result in unexpected behaviour
135                     if (evt->charCode() < ' ')
136                         return false;
137                     eventWasHandled = frame->editor()->insertText(commands[i].text, evt);
138                 } else
139                     if (frame->editor()->command(commands[i].commandName).isSupported())
140                         eventWasHandled = frame->editor()->command(commands[i].commandName).execute(evt);
141             }
142         }
143     }
144     return eventWasHandled;
145 }
146
147 void WebPage::sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
148 {
149     for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it) {
150         if ((*it)->sendComplexTextInput(pluginComplexTextInputIdentifier, textInput))
151             break;
152     }
153 }
154
155 void WebPage::getMarkedRange(uint64_t& location, uint64_t& length)
156 {
157     location = NSNotFound;
158     length = 0;
159     Frame* frame = m_page->focusController()->focusedOrMainFrame();
160     if (!frame)
161         return;
162     
163     getLocationAndLengthFromRange(frame->editor()->compositionRange().get(), location, length);
164 }
165
166 static Range *characterRangeAtPoint(Frame* frame, const IntPoint point)
167 {
168     VisiblePosition position = frame->visiblePositionForPoint(point);
169     if (position.isNull())
170         return NULL;
171     
172     VisiblePosition previous = position.previous();
173     if (previous.isNotNull()) {
174         Range *previousCharacterRange = makeRange(previous, position).get();
175         NSRect rect = frame->editor()->firstRectForRange(previousCharacterRange);
176         if (NSPointInRect(point, rect))
177             return previousCharacterRange;
178     }
179     
180     VisiblePosition next = position.next();
181     if (next.isNotNull()) {
182         Range *nextCharacterRange = makeRange(position, next).get();
183         NSRect rect = frame->editor()->firstRectForRange(nextCharacterRange);
184         if (NSPointInRect(point, rect))
185             return nextCharacterRange;
186     }
187     
188     return NULL;
189 }
190     
191 void WebPage::characterIndexForPoint(IntPoint point, uint64_t& index)
192 {
193     index = NSNotFound;
194     Frame* frame = m_page->mainFrame();
195     if (!frame)
196         return;
197
198     HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(point, false);
199     frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : m_page->focusController()->focusedOrMainFrame();
200     
201     Range *range = characterRangeAtPoint(frame, result.point());
202     if (!range)
203         return;
204
205     uint64_t length;
206     getLocationAndLengthFromRange(range, index, length);
207 }
208
209 static PassRefPtr<Range> convertToRange(Frame* frame, NSRange nsrange)
210 {
211     if (nsrange.location > INT_MAX)
212         return 0;
213     if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
214         nsrange.length = INT_MAX - nsrange.location;
215         
216     // our critical assumption is that we are only called by input methods that
217     // concentrate on a given area containing the selection
218     // We have to do this because of text fields and textareas. The DOM for those is not
219     // directly in the document DOM, so serialization is problematic. Our solution is
220     // to use the root editable element of the selection start as the positional base.
221     // That fits with AppKit's idea of an input context.
222     Element* selectionRoot = frame->selection()->rootEditableElement();
223     Element* scope = selectionRoot ? selectionRoot : frame->document()->documentElement();
224     return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
225 }
226     
227 void WebPage::firstRectForCharacterRange(uint64_t location, uint64_t length, WebCore::IntRect& resultRect)
228 {
229     Frame* frame = m_page->focusController()->focusedOrMainFrame();
230     resultRect.setLocation(IntPoint(0, 0));
231     resultRect.setSize(IntSize(0, 0));
232     
233     RefPtr<Range> range = convertToRange(frame, NSMakeRange(location, length));
234     if (!range)
235         return;
236     
237     ASSERT(range->startContainer());
238     ASSERT(range->endContainer());
239      
240     IntRect rect = frame->editor()->firstRectForRange(range.get());
241     resultRect = frame->view()->contentsToWindow(rect);
242 }
243
244 static inline void scroll(Page* page, ScrollDirection direction, ScrollGranularity granularity)
245 {
246     page->focusController()->focusedOrMainFrame()->eventHandler()->scrollRecursively(direction, granularity);
247 }
248
249 static inline void logicalScroll(Page* page, ScrollLogicalDirection direction, ScrollGranularity granularity)
250 {
251     page->focusController()->focusedOrMainFrame()->eventHandler()->logicalScrollRecursively(direction, granularity);
252 }
253
254 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent& keyboardEvent)
255 {
256     if (keyboardEvent.type() != WebEvent::KeyDown)
257         return false;
258
259     // FIXME: This should be in WebCore.
260
261     switch (keyboardEvent.windowsVirtualKeyCode()) {
262     case VK_BACK:
263         if (keyboardEvent.shiftKey())
264             m_page->goForward();
265         else
266             m_page->goBack();
267         break;
268     case VK_SPACE:
269         if (keyboardEvent.shiftKey())
270             logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByPage);
271         else
272             logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByPage);
273         break;
274     case VK_PRIOR:
275         logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByPage);
276         break;
277     case VK_NEXT:
278         logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByPage);
279         break;
280     case VK_HOME:
281         logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByDocument);
282         break;
283     case VK_END:
284         logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByDocument);
285         break;
286     case VK_UP:
287         if (keyboardEvent.shiftKey())
288             return false;
289         if (keyboardEvent.metaKey()) {
290             scroll(m_page.get(), ScrollUp, ScrollByDocument);
291             scroll(m_page.get(), ScrollLeft, ScrollByDocument);
292         } else if (keyboardEvent.altKey() || keyboardEvent.controlKey())
293             scroll(m_page.get(), ScrollUp, ScrollByPage);
294         else
295             scroll(m_page.get(), ScrollUp, ScrollByLine);
296         break;
297     case VK_DOWN:
298         if (keyboardEvent.shiftKey())
299             return false;
300         if (keyboardEvent.metaKey()) {
301             scroll(m_page.get(), ScrollDown, ScrollByDocument);
302             scroll(m_page.get(), ScrollLeft, ScrollByDocument);
303         } else if (keyboardEvent.altKey() || keyboardEvent.controlKey())
304             scroll(m_page.get(), ScrollDown, ScrollByPage);
305         else
306             scroll(m_page.get(), ScrollDown, ScrollByLine);
307         break;
308     case VK_LEFT:
309         if (keyboardEvent.shiftKey())
310             return false;
311         if (keyboardEvent.metaKey())
312             m_page->goBack();
313         else {
314             if (keyboardEvent.altKey()  | keyboardEvent.controlKey())
315                 scroll(m_page.get(), ScrollLeft, ScrollByPage);
316             else
317                 scroll(m_page.get(), ScrollLeft, ScrollByLine);
318         }
319         break;
320     case VK_RIGHT:
321         if (keyboardEvent.shiftKey())
322             return false;
323         if (keyboardEvent.metaKey())
324             m_page->goForward();
325         else {
326             if (keyboardEvent.altKey() || keyboardEvent.controlKey())
327                 scroll(m_page.get(), ScrollRight, ScrollByPage);
328             else
329                 scroll(m_page.get(), ScrollRight, ScrollByLine);
330         }
331         break;
332     default:
333         return false;
334     }
335
336     return true;
337 }
338
339 void WebPage::sendAccessibilityPresenterToken(const CoreIPC::DataReference& data)
340 {
341 #if !defined(BUILDING_ON_SNOW_LEOPARD)
342     NSData* tokenData = [NSData dataWithBytes:data.data() length:data.size()];
343     [m_mockAccessibilityElement.get() setRemoteParent:WKAXRemoteElementForToken((CFDataRef)tokenData)];
344 #endif
345 }
346
347 AccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
348 {
349     return m_mockAccessibilityElement.get();
350 }
351          
352 bool WebPage::platformHasLocalDataForURL(const WebCore::KURL& url)
353 {
354     NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
355     [request setValue:(NSString*)userAgent() forHTTPHeaderField:@"User-Agent"];
356     NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
357     [request release];
358     
359     return cachedResponse;
360 }
361
362 String WebPage::cachedResponseMIMETypeForURL(const WebCore::KURL& url)
363 {
364     NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
365     [request setValue:(NSString*)userAgent() forHTTPHeaderField:@"User-Agent"];
366     NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
367     [request release];
368     
369     return [[cachedResponse response] MIMEType];
370 }
371
372 bool WebPage::canHandleRequest(const WebCore::ResourceRequest& request)
373 {
374     if ([NSURLConnection canHandleRequest:request.nsURLRequest()])
375         return YES;
376
377     // FIXME: Return true if this scheme is any one WebKit2 knows how to handle.
378     return request.url().protocolIs("applewebdata");
379 }
380
381 } // namespace WebKit