<http://webkit.org/b/91015> Remove BUILDING_ON / TARGETING macros in favor of system...
[WebKit-https.git] / Source / WebKit2 / UIProcess / mac / WebPageProxyMac.mm
1 /*
2  * Copyright (C) 2010, 2011 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 "WebPageProxy.h"
28
29 #import "AttributedString.h"
30 #import "ColorSpaceData.h"
31 #import "DataReference.h"
32 #import "DictionaryPopupInfo.h"
33 #import "EditorState.h"
34 #import "NativeWebKeyboardEvent.h"
35 #import "PluginComplexTextInputState.h"
36 #import "PageClient.h"
37 #import "PageClientImpl.h"
38 #import "StringUtilities.h"
39 #import "TextChecker.h"
40 #import "WebPageMessages.h"
41 #import "WebProcessProxy.h"
42 #import <WebCore/DictationAlternative.h>
43 #import <WebCore/SharedBuffer.h>
44 #import <WebCore/TextAlternativeWithRange.h>
45 #import <WebKitSystemInterface.h>
46 #import <wtf/text/StringConcatenate.h>
47
48 @interface NSApplication (Details)
49 - (void)speakString:(NSString *)string;
50 @end
51
52 #define MESSAGE_CHECK(assertion) MESSAGE_CHECK_BASE(assertion, process()->connection())
53
54 using namespace WebCore;
55
56 namespace WebKit {
57
58 #if defined(__ppc__) || defined(__ppc64__)
59 #define PROCESSOR "PPC"
60 #elif defined(__i386__) || defined(__x86_64__)
61 #define PROCESSOR "Intel"
62 #else
63 #error Unknown architecture
64 #endif
65
66 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
67
68 static String macOSXVersionString()
69 {
70     // Use underscores instead of dots because when we first added the Mac OS X version to the user agent string
71     // we were concerned about old DHTML libraries interpreting "4." as Netscape 4. That's no longer a concern for us
72     // but we're sticking with the underscores for compatibility with the format used by older versions of Safari.
73     return [WKGetMacOSXVersionString() stringByReplacingOccurrencesOfString:@"." withString:@"_"];
74 }
75
76 #else
77
78 static inline int callGestalt(OSType selector)
79 {
80     SInt32 value = 0;
81     Gestalt(selector, &value);
82     return value;
83 }
84
85 // Uses underscores instead of dots because if "4." ever appears in a user agent string, old DHTML libraries treat it as Netscape 4.
86 static String macOSXVersionString()
87 {
88     // Can't use -[NSProcessInfo operatingSystemVersionString] because it has too much stuff we don't want.
89     int major = callGestalt(gestaltSystemVersionMajor);
90     ASSERT(major);
91
92     int minor = callGestalt(gestaltSystemVersionMinor);
93     int bugFix = callGestalt(gestaltSystemVersionBugFix);
94     if (bugFix)
95         return String::format("%d_%d_%d", major, minor, bugFix);
96     if (minor)
97         return String::format("%d_%d", major, minor);
98     return String::format("%d", major);
99 }
100
101 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
102
103 static String userVisibleWebKitVersionString()
104 {
105     // If the version is 4 digits long or longer, then the first digit represents
106     // the version of the OS. Our user agent string should not include this first digit,
107     // so strip it off and report the rest as the version. <rdar://problem/4997547>
108     NSString *fullVersion = [[NSBundle bundleForClass:NSClassFromString(@"WKView")] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey];
109     NSRange nonDigitRange = [fullVersion rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]];
110     if (nonDigitRange.location == NSNotFound && [fullVersion length] >= 4)
111         return [fullVersion substringFromIndex:1];
112     if (nonDigitRange.location != NSNotFound && nonDigitRange.location >= 4)
113         return [fullVersion substringFromIndex:1];
114     return fullVersion;
115 }
116
117 String WebPageProxy::standardUserAgent(const String& applicationNameForUserAgent)
118 {
119     DEFINE_STATIC_LOCAL(String, osVersion, (macOSXVersionString()));
120     DEFINE_STATIC_LOCAL(String, webKitVersion, (userVisibleWebKitVersionString()));
121
122     if (applicationNameForUserAgent.isEmpty())
123         return makeString("Mozilla/5.0 (Macintosh; " PROCESSOR " Mac OS X ", osVersion, ") AppleWebKit/", webKitVersion, " (KHTML, like Gecko)");
124     return makeString("Mozilla/5.0 (Macintosh; " PROCESSOR " Mac OS X ", osVersion, ") AppleWebKit/", webKitVersion, " (KHTML, like Gecko) ", applicationNameForUserAgent);
125 }
126
127 void WebPageProxy::getIsSpeaking(bool& isSpeaking)
128 {
129     isSpeaking = [NSApp isSpeaking];
130 }
131
132 void WebPageProxy::speak(const String& string)
133 {
134     [NSApp speakString:nsStringFromWebCoreString(string)];
135 }
136
137 void WebPageProxy::stopSpeaking()
138 {
139     [NSApp stopSpeaking:nil];
140 }
141
142 void WebPageProxy::searchWithSpotlight(const String& string)
143 {
144     [[NSWorkspace sharedWorkspace] showSearchResultsForQueryString:nsStringFromWebCoreString(string)];
145 }
146     
147 CGContextRef WebPageProxy::containingWindowGraphicsContext()
148 {
149     return m_pageClient->containingWindowGraphicsContext();
150 }
151
152 void WebPageProxy::updateWindowIsVisible(bool windowIsVisible)
153 {
154     if (!isValid())
155         return;
156     process()->send(Messages::WebPage::SetWindowIsVisible(windowIsVisible), m_pageID);
157 }
158
159 void WebPageProxy::windowAndViewFramesChanged(const IntRect& windowFrameInScreenCoordinates, const IntRect& viewFrameInWindowCoordinates, const IntPoint& accessibilityViewCoordinates)
160 {
161     if (!isValid())
162         return;
163
164     process()->send(Messages::WebPage::WindowAndViewFramesChanged(windowFrameInScreenCoordinates, viewFrameInWindowCoordinates, accessibilityViewCoordinates), m_pageID);
165 }
166
167 void WebPageProxy::setComposition(const String& text, Vector<CompositionUnderline> underlines, uint64_t selectionStart, uint64_t selectionEnd, uint64_t replacementRangeStart, uint64_t replacementRangeEnd)
168 {
169     if (!isValid()) {
170         // If this fails, we should call -discardMarkedText on input context to notify the input method.
171         // This will happen naturally later, as part of reloading the page.
172         return;
173     }
174
175     process()->sendSync(Messages::WebPage::SetComposition(text, underlines, selectionStart, selectionEnd, replacementRangeStart, replacementRangeEnd), Messages::WebPage::SetComposition::Reply(m_editorState), m_pageID);
176 }
177
178 void WebPageProxy::confirmComposition()
179 {
180     if (!isValid())
181         return;
182
183     process()->sendSync(Messages::WebPage::ConfirmComposition(), Messages::WebPage::ConfirmComposition::Reply(m_editorState), m_pageID);
184 }
185
186 void WebPageProxy::cancelComposition()
187 {
188     if (!isValid())
189         return;
190
191     process()->sendSync(Messages::WebPage::CancelComposition(), Messages::WebPage::ConfirmComposition::Reply(m_editorState), m_pageID);
192 }
193
194 bool WebPageProxy::insertText(const String& text, uint64_t replacementRangeStart, uint64_t replacementRangeEnd)
195 {
196     if (!isValid())
197         return true;
198
199     bool handled = true;
200     process()->sendSync(Messages::WebPage::InsertText(text, replacementRangeStart, replacementRangeEnd), Messages::WebPage::InsertText::Reply(handled, m_editorState), m_pageID);
201     return handled;
202 }
203
204 bool WebPageProxy::insertDictatedText(const String& text, uint64_t replacementRangeStart, uint64_t replacementRangeEnd, const Vector<TextAlternativeWithRange>& dictationAlternativesWithRange)
205 {
206 #if USE(DICTATION_ALTERNATIVES)
207     if (dictationAlternativesWithRange.isEmpty())
208         return insertText(text, replacementRangeStart, replacementRangeEnd);
209
210     if (!isValid())
211         return true;
212
213     Vector<DictationAlternative> dictationAlternatives;
214
215     for (size_t i = 0; i < dictationAlternativesWithRange.size(); ++i) {
216         const TextAlternativeWithRange& alternativeWithRange = dictationAlternativesWithRange[i];
217         uint64_t dictationContext = m_pageClient->addDictationAlternatives(alternativeWithRange.alternatives);
218         if (dictationContext)
219             dictationAlternatives.append(DictationAlternative(alternativeWithRange.range.location, alternativeWithRange.range.length, dictationContext));
220     }
221
222     if (dictationAlternatives.isEmpty())
223         return insertText(text, replacementRangeStart, replacementRangeEnd);
224
225     bool handled = true;
226     process()->sendSync(Messages::WebPage::InsertDictatedText(text, replacementRangeStart, replacementRangeEnd, dictationAlternatives), Messages::WebPage::InsertDictatedText::Reply(handled, m_editorState), m_pageID);
227     return handled;
228 #else
229     return insertText(text, replacementRangeStart, replacementRangeEnd);
230 #endif
231 }
232
233 void WebPageProxy::getMarkedRange(uint64_t& location, uint64_t& length)
234 {
235     location = NSNotFound;
236     length = 0;
237
238     if (!isValid())
239         return;
240
241     process()->sendSync(Messages::WebPage::GetMarkedRange(), Messages::WebPage::GetMarkedRange::Reply(location, length), m_pageID);
242 }
243
244 void WebPageProxy::getSelectedRange(uint64_t& location, uint64_t& length)
245 {
246     location = NSNotFound;
247     length = 0;
248
249     if (!isValid())
250         return;
251
252     process()->sendSync(Messages::WebPage::GetSelectedRange(), Messages::WebPage::GetSelectedRange::Reply(location, length), m_pageID);
253 }
254
255 void WebPageProxy::getAttributedSubstringFromRange(uint64_t location, uint64_t length, AttributedString& result)
256 {
257     if (!isValid())
258         return;
259     process()->sendSync(Messages::WebPage::GetAttributedSubstringFromRange(location, length), Messages::WebPage::GetAttributedSubstringFromRange::Reply(result), m_pageID);
260 }
261
262 uint64_t WebPageProxy::characterIndexForPoint(const IntPoint point)
263 {
264     if (!isValid())
265         return 0;
266
267     uint64_t result = 0;
268     process()->sendSync(Messages::WebPage::CharacterIndexForPoint(point), Messages::WebPage::CharacterIndexForPoint::Reply(result), m_pageID);
269     return result;
270 }
271
272 IntRect WebPageProxy::firstRectForCharacterRange(uint64_t location, uint64_t length)
273 {
274     if (!isValid())
275         return IntRect();
276
277     IntRect resultRect;
278     process()->sendSync(Messages::WebPage::FirstRectForCharacterRange(location, length), Messages::WebPage::FirstRectForCharacterRange::Reply(resultRect), m_pageID);
279     return resultRect;
280 }
281
282 bool WebPageProxy::executeKeypressCommands(const Vector<WebCore::KeypressCommand>& commands)
283 {
284     if (!isValid())
285         return false;
286
287     bool result = false;
288     process()->sendSync(Messages::WebPage::ExecuteKeypressCommands(commands), Messages::WebPage::ExecuteKeypressCommands::Reply(result, m_editorState), m_pageID);
289     return result;
290 }
291
292 String WebPageProxy::stringSelectionForPasteboard()
293 {
294     String value;
295     if (!isValid())
296         return value;
297     
298     const double messageTimeout = 20;
299     process()->sendSync(Messages::WebPage::GetStringSelectionForPasteboard(), Messages::WebPage::GetStringSelectionForPasteboard::Reply(value), m_pageID, messageTimeout);
300     return value;
301 }
302
303 PassRefPtr<WebCore::SharedBuffer> WebPageProxy::dataSelectionForPasteboard(const String& pasteboardType)
304 {
305     if (!isValid())
306         return 0;
307     SharedMemory::Handle handle;
308     uint64_t size = 0;
309     const double messageTimeout = 20;
310     process()->sendSync(Messages::WebPage::GetDataSelectionForPasteboard(pasteboardType),
311                                                 Messages::WebPage::GetDataSelectionForPasteboard::Reply(handle, size), m_pageID, messageTimeout);
312     if (handle.isNull())
313         return 0;
314     RefPtr<SharedMemory> sharedMemoryBuffer = SharedMemory::create(handle, SharedMemory::ReadOnly);
315     return SharedBuffer::create(static_cast<unsigned char *>(sharedMemoryBuffer->data()), size);
316 }
317
318 bool WebPageProxy::readSelectionFromPasteboard(const String& pasteboardName)
319 {
320     if (!isValid())
321         return false;
322
323     bool result = false;
324     const double messageTimeout = 20;
325     process()->sendSync(Messages::WebPage::ReadSelectionFromPasteboard(pasteboardName), Messages::WebPage::ReadSelectionFromPasteboard::Reply(result), m_pageID, messageTimeout);
326     return result;
327 }
328
329 #if ENABLE(DRAG_SUPPORT)
330 void WebPageProxy::setDragImage(const WebCore::IntPoint& clientPosition, const ShareableBitmap::Handle& dragImageHandle, bool isLinkDrag)
331 {
332     RefPtr<ShareableBitmap> dragImage = ShareableBitmap::create(dragImageHandle);
333     if (!dragImage)
334         return;
335     
336     m_pageClient->setDragImage(clientPosition, dragImage.release(), isLinkDrag);
337 }
338
339 void WebPageProxy::setPromisedData(const String& pasteboardName, const SharedMemory::Handle& imageHandle, uint64_t imageSize, const String& filename, const String& extension,
340                                    const String& title, const String& url, const String& visibleURL, const SharedMemory::Handle& archiveHandle, uint64_t archiveSize)
341 {
342     RefPtr<SharedMemory> sharedMemoryImage = SharedMemory::create(imageHandle, SharedMemory::ReadOnly);
343     RefPtr<SharedBuffer> imageBuffer = SharedBuffer::create(static_cast<unsigned char*>(sharedMemoryImage->data()), imageSize);
344     RefPtr<SharedBuffer> archiveBuffer;
345     
346     if (!archiveHandle.isNull()) {
347         RefPtr<SharedMemory> sharedMemoryArchive = SharedMemory::create(archiveHandle, SharedMemory::ReadOnly);;
348         archiveBuffer = SharedBuffer::create(static_cast<unsigned char*>(sharedMemoryArchive->data()), archiveSize);
349     }
350     m_pageClient->setPromisedData(pasteboardName, imageBuffer, filename, extension, title, url, visibleURL, archiveBuffer);
351 }
352 #endif
353
354 void WebPageProxy::performDictionaryLookupAtLocation(const WebCore::FloatPoint& point)
355 {
356     if (!isValid())
357         return;
358
359     process()->send(Messages::WebPage::PerformDictionaryLookupAtLocation(point), m_pageID); 
360 }
361
362 void WebPageProxy::interpretQueuedKeyEvent(const EditorState& state, bool& handled, Vector<WebCore::KeypressCommand>& commands)
363 {
364     m_editorState = state;
365     handled = m_pageClient->interpretKeyEvent(m_keyEventQueue.first(), commands);
366 }
367
368 // Complex text input support for plug-ins.
369 void WebPageProxy::sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
370 {
371     if (!isValid())
372         return;
373     
374     process()->send(Messages::WebPage::SendComplexTextInputToPlugin(pluginComplexTextInputIdentifier, textInput), m_pageID);
375 }
376
377 void WebPageProxy::uppercaseWord()
378 {
379     process()->send(Messages::WebPage::UppercaseWord(), m_pageID);
380 }
381
382 void WebPageProxy::lowercaseWord()
383 {
384     process()->send(Messages::WebPage::LowercaseWord(), m_pageID);
385 }
386
387 void WebPageProxy::capitalizeWord()
388 {
389     process()->send(Messages::WebPage::CapitalizeWord(), m_pageID);
390 }
391
392 void WebPageProxy::setSmartInsertDeleteEnabled(bool isSmartInsertDeleteEnabled)
393 {
394     if (m_isSmartInsertDeleteEnabled == isSmartInsertDeleteEnabled)
395         return;
396
397     TextChecker::setSmartInsertDeleteEnabled(isSmartInsertDeleteEnabled);
398     m_isSmartInsertDeleteEnabled = isSmartInsertDeleteEnabled;
399     process()->send(Messages::WebPage::SetSmartInsertDeleteEnabled(isSmartInsertDeleteEnabled), m_pageID);
400 }
401
402 void WebPageProxy::didPerformDictionaryLookup(const String& text, const DictionaryPopupInfo& dictionaryPopupInfo)
403 {
404     m_pageClient->didPerformDictionaryLookup(text, m_pageScaleFactor, dictionaryPopupInfo);
405 }
406     
407 void WebPageProxy::registerWebProcessAccessibilityToken(const CoreIPC::DataReference& data)
408 {
409     m_pageClient->accessibilityWebProcessTokenReceived(data);
410 }    
411     
412 void WebPageProxy::makeFirstResponder()
413 {
414     m_pageClient->makeFirstResponder();
415 }
416
417 ColorSpaceData WebPageProxy::colorSpace()
418 {
419     return m_pageClient->colorSpace();
420 }
421
422 void WebPageProxy::registerUIProcessAccessibilityTokens(const CoreIPC::DataReference& elementToken, const CoreIPC::DataReference& windowToken)
423 {
424     if (!isValid())
425         return;
426
427     process()->send(Messages::WebPage::RegisterUIProcessAccessibilityTokens(elementToken, windowToken), m_pageID);
428 }
429
430 void WebPageProxy::pluginFocusOrWindowFocusChanged(uint64_t pluginComplexTextInputIdentifier, bool pluginHasFocusAndWindowHasFocus)
431 {
432     m_pageClient->pluginFocusOrWindowFocusChanged(pluginComplexTextInputIdentifier, pluginHasFocusAndWindowHasFocus);
433 }
434
435 void WebPageProxy::setPluginComplexTextInputState(uint64_t pluginComplexTextInputIdentifier, uint64_t pluginComplexTextInputState)
436 {
437     MESSAGE_CHECK(isValidPluginComplexTextInputState(pluginComplexTextInputState));
438
439     m_pageClient->setPluginComplexTextInputState(pluginComplexTextInputIdentifier, static_cast<PluginComplexTextInputState>(pluginComplexTextInputState));
440 }
441
442 void WebPageProxy::executeSavedCommandBySelector(const String& selector, bool& handled)
443 {
444     MESSAGE_CHECK(isValidKeypressCommandName(selector));
445
446     handled = m_pageClient->executeSavedCommandBySelector(selector);
447 }
448
449 bool WebPageProxy::shouldDelayWindowOrderingForEvent(const WebKit::WebMouseEvent& event)
450 {
451     if (!process()->isValid())
452         return false;
453
454     bool result = false;
455     const double messageTimeout = 3;
456     process()->sendSync(Messages::WebPage::ShouldDelayWindowOrderingEvent(event), Messages::WebPage::ShouldDelayWindowOrderingEvent::Reply(result), m_pageID, messageTimeout);
457     return result;
458 }
459
460 bool WebPageProxy::acceptsFirstMouse(int eventNumber, const WebKit::WebMouseEvent& event)
461 {
462     if (!isValid())
463         return false;
464
465     bool result = false;
466     const double messageTimeout = 3;
467     process()->sendSync(Messages::WebPage::AcceptsFirstMouse(eventNumber, event), Messages::WebPage::AcceptsFirstMouse::Reply(result), m_pageID, messageTimeout);
468     return result;
469 }
470
471 WKView* WebPageProxy::wkView() const
472 {
473     return m_pageClient->wkView();
474 }
475
476 } // namespace WebKit