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