WebKit2: Use CFNetwork Sessions API.
[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/ResourceHandle.h>
46 #import <WebCore/ScrollView.h>
47 #import <WebCore/TextIterator.h>
48 #import <WebCore/WindowsKeyboardCodes.h>
49 #import <WebKitSystemInterface.h>
50
51 using namespace WebCore;
52
53 namespace WebKit {
54
55 void WebPage::platformInitialize()
56 {
57     m_page->addSchedulePair(SchedulePair::create([NSRunLoop currentRunLoop], kCFRunLoopCommonModes));
58
59 #if !defined(BUILDING_ON_SNOW_LEOPARD)
60     AccessibilityWebPageObject* mockAccessibilityElement = [[[AccessibilityWebPageObject alloc] init] autorelease];
61
62     // Get the pid for the starting process.
63     pid_t pid = WebProcess::shared().presenterApplicationPid();    
64     WKAXInitializeElementWithPresenterPid(mockAccessibilityElement, pid);
65     [mockAccessibilityElement setWebPage:this];
66     
67     // send data back over
68     NSData* remoteToken = (NSData *)WKAXRemoteTokenForElement(mockAccessibilityElement); 
69     CoreIPC::DataReference dataToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]);
70     send(Messages::WebPageProxy::RegisterWebProcessAccessibilityToken(dataToken));
71     m_mockAccessibilityElement = mockAccessibilityElement;
72 #endif
73 }
74
75 void WebPage::platformPreferencesDidChange(const WebPreferencesStore&)
76 {
77 }
78
79 // FIXME: need to add support for input methods
80     
81 bool WebPage::interceptEditingKeyboardEvent(KeyboardEvent* evt, bool shouldSaveCommand)
82 {
83     Node* node = evt->target()->toNode();
84     ASSERT(node);
85     Frame* frame = node->document()->frame();
86     ASSERT(frame);
87     
88     const PlatformKeyboardEvent* keyEvent = evt->keyEvent();
89     if (!keyEvent)
90         return false;
91     const Vector<KeypressCommand>& commands = evt->keypressCommands();
92     bool hasKeypressCommand = !commands.isEmpty();
93     
94     bool eventWasHandled = false;
95     
96     if (shouldSaveCommand || !hasKeypressCommand) {
97         Vector<KeypressCommand> commandsList;  
98         Vector<CompositionUnderline> underlines;
99         unsigned start;
100         unsigned end;
101         if (!WebProcess::shared().connection()->sendSync(Messages::WebPageProxy::InterpretKeyEvent(keyEvent->type()), 
102                                                          Messages::WebPageProxy::InterpretKeyEvent::Reply(commandsList, start, end, underlines),
103                                                          m_pageID, CoreIPC::Connection::NoTimeout))
104             return false;
105         if (commandsList.isEmpty())
106             return eventWasHandled;
107         
108         if (commandsList[0].commandName == "setMarkedText") {
109             frame->editor()->setComposition(commandsList[0].text, underlines, start, end);
110             eventWasHandled = true;
111         } else if (commandsList[0].commandName == "insertText" && frame->editor()->hasComposition()) {
112             frame->editor()->confirmComposition(commandsList[0].text);
113             eventWasHandled = true;
114         } else if (commandsList[0].commandName == "unmarkText") {
115             frame->editor()->confirmComposition();
116             eventWasHandled = true;
117         } else {
118             for (size_t i = 0; i < commandsList.size(); i++)
119                 evt->keypressCommands().append(commandsList[i]);
120         }
121     } else {
122         size_t size = commands.size();
123         // Are there commands that would just cause text insertion if executed via Editor?
124         // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
125         // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
126         // (e.g. Tab that inserts a Tab character, or Enter).
127         bool haveTextInsertionCommands = false;
128         for (size_t i = 0; i < size; ++i) {
129             if (frame->editor()->command(commands[i].commandName).isTextInsertion())
130                 haveTextInsertionCommands = true;
131         }
132         if (!haveTextInsertionCommands || keyEvent->type() == PlatformKeyboardEvent::Char) {
133             for (size_t i = 0; i < size; ++i) {
134                 if (commands[i].commandName == "insertText") {
135                     // Don't insert null or control characters as they can result in unexpected behaviour
136                     if (evt->charCode() < ' ')
137                         return false;
138                     eventWasHandled = frame->editor()->insertText(commands[i].text, evt);
139                 } else
140                     if (frame->editor()->command(commands[i].commandName).isSupported())
141                         eventWasHandled = frame->editor()->command(commands[i].commandName).execute(evt);
142             }
143         }
144     }
145     return eventWasHandled;
146 }
147
148 void WebPage::sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
149 {
150     for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it) {
151         if ((*it)->sendComplexTextInput(pluginComplexTextInputIdentifier, textInput))
152             break;
153     }
154 }
155
156 void WebPage::getMarkedRange(uint64_t& location, uint64_t& length)
157 {
158     location = NSNotFound;
159     length = 0;
160     Frame* frame = m_page->focusController()->focusedOrMainFrame();
161     if (!frame)
162         return;
163     
164     getLocationAndLengthFromRange(frame->editor()->compositionRange().get(), location, length);
165 }
166
167 static PassRefPtr<Range> characterRangeAtPoint(Frame* frame, const IntPoint& point)
168 {
169     VisiblePosition position = frame->visiblePositionForPoint(point);
170     if (position.isNull())
171         return 0;
172     
173     VisiblePosition previous = position.previous();
174     if (previous.isNotNull()) {
175         RefPtr<Range> previousCharacterRange = makeRange(previous, position);
176         IntRect rect = frame->editor()->firstRectForRange(previousCharacterRange.get());
177         if (rect.contains(point))
178             return previousCharacterRange.release();
179     }
180     
181     VisiblePosition next = position.next();
182     if (next.isNotNull()) {
183         RefPtr<Range> nextCharacterRange = makeRange(position, next);
184         IntRect rect = frame->editor()->firstRectForRange(nextCharacterRange.get());
185         if (rect.contains(point))
186             return nextCharacterRange.release();
187     }
188     
189     return 0;
190 }
191     
192 void WebPage::characterIndexForPoint(IntPoint point, uint64_t& index)
193 {
194     index = NSNotFound;
195     Frame* frame = m_page->mainFrame();
196     if (!frame)
197         return;
198
199     HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(point, false);
200     frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : m_page->focusController()->focusedOrMainFrame();
201     
202     RefPtr<Range> range = characterRangeAtPoint(frame, result.point());
203     if (!range)
204         return;
205
206     uint64_t length;
207     getLocationAndLengthFromRange(range.get(), index, length);
208 }
209
210 static PassRefPtr<Range> convertToRange(Frame* frame, NSRange nsrange)
211 {
212     if (nsrange.location > INT_MAX)
213         return 0;
214     if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
215         nsrange.length = INT_MAX - nsrange.location;
216         
217     // our critical assumption is that we are only called by input methods that
218     // concentrate on a given area containing the selection
219     // We have to do this because of text fields and textareas. The DOM for those is not
220     // directly in the document DOM, so serialization is problematic. Our solution is
221     // to use the root editable element of the selection start as the positional base.
222     // That fits with AppKit's idea of an input context.
223     Element* selectionRoot = frame->selection()->rootEditableElement();
224     Element* scope = selectionRoot ? selectionRoot : frame->document()->documentElement();
225     return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
226 }
227     
228 void WebPage::firstRectForCharacterRange(uint64_t location, uint64_t length, WebCore::IntRect& resultRect)
229 {
230     Frame* frame = m_page->focusController()->focusedOrMainFrame();
231     resultRect.setLocation(IntPoint(0, 0));
232     resultRect.setSize(IntSize(0, 0));
233     
234     RefPtr<Range> range = convertToRange(frame, NSMakeRange(location, length));
235     if (!range)
236         return;
237     
238     ASSERT(range->startContainer());
239     ASSERT(range->endContainer());
240      
241     IntRect rect = frame->editor()->firstRectForRange(range.get());
242     resultRect = frame->view()->contentsToWindow(rect);
243 }
244
245 void WebPage::performDictionaryLookupAtLocation(const FloatPoint& floatPoint)
246 {
247     Frame* frame = m_page->mainFrame();
248     if (!frame)
249         return;
250
251     // Find the frame the point is over.
252     IntPoint point = roundedIntPoint(floatPoint);
253
254     HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(point, false);
255     frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : m_page->focusController()->focusedOrMainFrame();
256
257     // Figure out if there are any characters under the point.
258     RefPtr<Range> characterRange = characterRangeAtPoint(frame, frame->view()->windowToContents(point));
259     if (!characterRange)
260         return;
261
262     // Grab the currently selected text.
263     RefPtr<Range> selectedRange = m_page->focusController()->focusedOrMainFrame()->selection()->selection().toNormalizedRange();
264
265     // Use the selected text if the point was anywhere in it. Assertain this by seeing if either character range
266     // the mouse is over is contained by the selection range.
267     if (characterRange && selectedRange) {
268         ExceptionCode ec = 0;
269         selectedRange->compareBoundaryPoints(Range::START_TO_START, characterRange.get(), ec);
270         if (!ec) {
271             if (selectedRange->isPointInRange(characterRange->startContainer(), characterRange->startOffset(), ec)) {
272                 if (!ec)
273                     characterRange = selectedRange;
274             }
275         }
276     }
277
278     if (!characterRange)
279         return;
280
281     // Ensure we have whole words.
282     VisibleSelection selection(characterRange.get());
283     selection.expandUsingGranularity(WordGranularity);
284
285     RefPtr<Range> finalRange = selection.toNormalizedRange();
286     if (!finalRange)
287         return;
288
289     performDictionaryLookupForRange(DictionaryPopupInfo::HotKey, frame, finalRange.get());
290 }
291
292 void WebPage::performDictionaryLookupForRange(DictionaryPopupInfo::Type type, Frame* frame, Range* range)
293 {
294     String rangeText = range->text();
295     if (rangeText.stripWhiteSpace().isEmpty())
296         return;
297     
298     RenderObject* renderer = range->startContainer()->renderer();
299     RenderStyle* style = renderer->style();
300     NSFont *font = style->font().primaryFont()->getNSFont();
301     if (!font)
302         return;
303     
304     CFDictionaryRef fontDescriptorAttributes = (CFDictionaryRef)[[font fontDescriptor] fontAttributes];
305     if (!fontDescriptorAttributes)
306         return;
307
308     Vector<FloatQuad> quads;
309     range->textQuads(quads);
310     if (quads.isEmpty())
311         return;
312     
313     IntRect rangeRect = frame->view()->contentsToWindow(quads[0].enclosingBoundingBox());
314     
315     DictionaryPopupInfo dictionaryPopupInfo;
316     dictionaryPopupInfo.type = type;
317     dictionaryPopupInfo.origin = FloatPoint(rangeRect.x(), rangeRect.y());
318     dictionaryPopupInfo.fontInfo.fontAttributeDictionary = fontDescriptorAttributes;
319
320     send(Messages::WebPageProxy::DidPerformDictionaryLookup(rangeText, dictionaryPopupInfo));
321 }
322
323 static inline void scroll(Page* page, ScrollDirection direction, ScrollGranularity granularity)
324 {
325     page->focusController()->focusedOrMainFrame()->eventHandler()->scrollRecursively(direction, granularity);
326 }
327
328 static inline void logicalScroll(Page* page, ScrollLogicalDirection direction, ScrollGranularity granularity)
329 {
330     page->focusController()->focusedOrMainFrame()->eventHandler()->logicalScrollRecursively(direction, granularity);
331 }
332
333 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent& keyboardEvent)
334 {
335     if (keyboardEvent.type() != WebEvent::KeyDown)
336         return false;
337
338     // FIXME: This should be in WebCore.
339
340     switch (keyboardEvent.windowsVirtualKeyCode()) {
341     case VK_BACK:
342         if (keyboardEvent.shiftKey())
343             m_page->goForward();
344         else
345             m_page->goBack();
346         break;
347     case VK_SPACE:
348         if (keyboardEvent.shiftKey())
349             logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByPage);
350         else
351             logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByPage);
352         break;
353     case VK_PRIOR:
354         logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByPage);
355         break;
356     case VK_NEXT:
357         logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByPage);
358         break;
359     case VK_HOME:
360         logicalScroll(m_page.get(), ScrollBlockDirectionBackward, ScrollByDocument);
361         break;
362     case VK_END:
363         logicalScroll(m_page.get(), ScrollBlockDirectionForward, ScrollByDocument);
364         break;
365     case VK_UP:
366         if (keyboardEvent.shiftKey())
367             return false;
368         if (keyboardEvent.metaKey()) {
369             scroll(m_page.get(), ScrollUp, ScrollByDocument);
370             scroll(m_page.get(), ScrollLeft, ScrollByDocument);
371         } else if (keyboardEvent.altKey() || keyboardEvent.controlKey())
372             scroll(m_page.get(), ScrollUp, ScrollByPage);
373         else
374             scroll(m_page.get(), ScrollUp, ScrollByLine);
375         break;
376     case VK_DOWN:
377         if (keyboardEvent.shiftKey())
378             return false;
379         if (keyboardEvent.metaKey()) {
380             scroll(m_page.get(), ScrollDown, ScrollByDocument);
381             scroll(m_page.get(), ScrollLeft, ScrollByDocument);
382         } else if (keyboardEvent.altKey() || keyboardEvent.controlKey())
383             scroll(m_page.get(), ScrollDown, ScrollByPage);
384         else
385             scroll(m_page.get(), ScrollDown, ScrollByLine);
386         break;
387     case VK_LEFT:
388         if (keyboardEvent.shiftKey())
389             return false;
390         if (keyboardEvent.metaKey())
391             m_page->goBack();
392         else {
393             if (keyboardEvent.altKey()  | keyboardEvent.controlKey())
394                 scroll(m_page.get(), ScrollLeft, ScrollByPage);
395             else
396                 scroll(m_page.get(), ScrollLeft, ScrollByLine);
397         }
398         break;
399     case VK_RIGHT:
400         if (keyboardEvent.shiftKey())
401             return false;
402         if (keyboardEvent.metaKey())
403             m_page->goForward();
404         else {
405             if (keyboardEvent.altKey() || keyboardEvent.controlKey())
406                 scroll(m_page.get(), ScrollRight, ScrollByPage);
407             else
408                 scroll(m_page.get(), ScrollRight, ScrollByLine);
409         }
410         break;
411     default:
412         return false;
413     }
414
415     return true;
416 }
417
418 void WebPage::registerUIProcessAccessibilityTokens(const CoreIPC::DataReference& elementToken, const CoreIPC::DataReference& windowToken)
419 {
420 #if !defined(BUILDING_ON_SNOW_LEOPARD)
421     NSData* elementTokenData = [NSData dataWithBytes:elementToken.data() length:elementToken.size()];
422     NSData* windowTokenData = [NSData dataWithBytes:windowToken.data() length:windowToken.size()];
423     id remoteElement = WKAXRemoteElementForToken(elementTokenData);
424     id remoteWindow = WKAXRemoteElementForToken(windowTokenData);
425     WKAXSetWindowForRemoteElement(remoteWindow, remoteElement);
426     
427     [accessibilityRemoteObject() setRemoteParent:remoteElement];
428 #endif
429 }
430
431 void WebPage::writeSelectionToPasteboard(const String& pasteboardName, const Vector<String>& pasteboardTypes, bool& result)
432 {
433     Frame* frame = m_page->focusController()->focusedOrMainFrame();
434     frame->editor()->writeSelectionToPasteboard(pasteboardName, pasteboardTypes);
435     result = true;
436 }
437
438 AccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
439 {
440     return m_mockAccessibilityElement.get();
441 }
442          
443 bool WebPage::platformHasLocalDataForURL(const WebCore::KURL& url)
444 {
445     NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
446     [request setValue:(NSString*)userAgent() forHTTPHeaderField:@"User-Agent"];
447     NSCachedURLResponse *cachedResponse;
448 #if USE(CFURLSTORAGESESSIONS)
449     if (CFURLStorageSessionRef storageSession = ResourceHandle::privateBrowsingStorageSession())
450         cachedResponse = WKCachedResponseForRequest(storageSession, request);
451     else
452 #endif
453         cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
454     [request release];
455     
456     return cachedResponse;
457 }
458
459 String WebPage::cachedResponseMIMETypeForURL(const WebCore::KURL& url)
460 {
461     NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
462     [request setValue:(NSString*)userAgent() forHTTPHeaderField:@"User-Agent"];
463     NSCachedURLResponse *cachedResponse;
464 #if USE(CFURLSTORAGESESSIONS)
465     if (CFURLStorageSessionRef storageSession = ResourceHandle::privateBrowsingStorageSession())
466         cachedResponse = WKCachedResponseForRequest(storageSession, request);
467     else
468 #endif
469         cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
470     [request release];
471     
472     return [[cachedResponse response] MIMEType];
473 }
474
475 bool WebPage::canHandleRequest(const WebCore::ResourceRequest& request)
476 {
477     if ([NSURLConnection canHandleRequest:request.nsURLRequest()])
478         return YES;
479
480     // FIXME: Return true if this scheme is any one WebKit2 knows how to handle.
481     return request.url().protocolIs("applewebdata");
482 }
483
484 } // namespace WebKit