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 "PageClientImpl.h"
28
29#import "DataReference.h"
30#import "DictionaryPopupInfo.h"
31#import "FindIndicator.h"
32#import "NativeWebKeyboardEvent.h"
33#import "WKAPICast.h"
34#import "WKStringCF.h"
35#import "WKViewInternal.h"
36#import "WebContextMenuProxyMac.h"
37#import "WebEditCommandProxy.h"
38#import "WebPopupMenuProxyMac.h"
39#import <WebCore/Cursor.h>
40#import <WebCore/FloatRect.h>
41#import <WebCore/FoundationExtras.h>
42#import <WebCore/GraphicsContext.h>
43#import <WebCore/KeyboardEvent.h>
44#import <WebCore/NotImplemented.h>
45#import <wtf/PassOwnPtr.h>
46#import <wtf/text/CString.h>
47#import <wtf/text/WTFString.h>
48#import <WebKitSystemInterface.h>
49
50@interface NSApplication (WebNSApplicationDetails)
51- (NSCursor *)_cursorRectCursor;
52@end
53
54using namespace WebCore;
55using namespace WebKit;
56
57@interface WKEditCommandObjC : NSObject
58{
59    RefPtr<WebEditCommandProxy> m_command;
60}
61- (id)initWithWebEditCommandProxy:(PassRefPtr<WebEditCommandProxy>)command;
62- (WebEditCommandProxy*)command;
63@end
64
65@interface WKEditorUndoTargetObjC : NSObject
66- (void)undoEditing:(id)sender;
67- (void)redoEditing:(id)sender;
68@end
69
70@implementation WKEditCommandObjC
71
72- (id)initWithWebEditCommandProxy:(PassRefPtr<WebEditCommandProxy>)command
73{
74    self = [super init];
75    if (!self)
76        return nil;
77
78    m_command = command;
79    return self;
80}
81
82- (WebEditCommandProxy*)command
83{
84    return m_command.get();
85}
86
87@end
88
89@implementation WKEditorUndoTargetObjC
90
91- (void)undoEditing:(id)sender
92{
93    ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]);
94    [sender command]->unapply();
95}
96
97- (void)redoEditing:(id)sender
98{
99    ASSERT([sender isKindOfClass:[WKEditCommandObjC class]]);
100    [sender command]->reapply();
101}
102
103@end
104
105namespace WebKit {
106
107NSString* nsStringFromWebCoreString(const String& string)
108{
109    return string.impl() ? HardAutorelease(WKStringCopyCFString(0, toAPI(string.impl()))) : @"";
110}
111
112PassOwnPtr<PageClientImpl> PageClientImpl::create(WKView* wkView)
113{
114    return adoptPtr(new PageClientImpl(wkView));
115}
116
117PageClientImpl::PageClientImpl(WKView* wkView)
118    : m_wkView(wkView)
119    , m_undoTarget(AdoptNS, [[WKEditorUndoTargetObjC alloc] init])
120{
121}
122
123PageClientImpl::~PageClientImpl()
124{
125}
126
127PassOwnPtr<DrawingAreaProxy> PageClientImpl::createDrawingAreaProxy()
128{
129    return [m_wkView _createDrawingAreaProxy];
130}
131
132void PageClientImpl::setViewNeedsDisplay(const WebCore::IntRect& rect)
133{
134    [m_wkView setNeedsDisplayInRect:rect];
135}
136
137void PageClientImpl::displayView()
138{
139    [m_wkView displayIfNeeded];
140}
141
142void PageClientImpl::scrollView(const IntRect& scrollRect, const IntSize& scrollOffset)
143{
144    NSRect clippedScrollRect = NSIntersectionRect(scrollRect, NSOffsetRect(scrollRect, -scrollOffset.width(), -scrollOffset.height()));
145
146    [m_wkView translateRectsNeedingDisplayInRect:clippedScrollRect by:scrollOffset];
147    [m_wkView scrollRect:clippedScrollRect by:scrollOffset];
148}
149
150IntSize PageClientImpl::viewSize()
151{
152    return IntSize([m_wkView bounds].size);
153}
154
155bool PageClientImpl::isViewWindowActive()
156{
157    return [[m_wkView window] isKeyWindow] || [NSApp keyWindow] == [m_wkView window];
158}
159
160bool PageClientImpl::isViewFocused()
161{
162    return [m_wkView _isFocused];
163}
164
165bool PageClientImpl::isViewVisible()
166{
167    if (![m_wkView window])
168        return false;
169
170    if (![[m_wkView window] isVisible])
171        return false;
172
173    if ([m_wkView isHiddenOrHasHiddenAncestor])
174        return false;
175
176    return true;
177}
178
179bool PageClientImpl::isViewInWindow()
180{
181    return [m_wkView window];
182}
183
184void PageClientImpl::processDidCrash()
185{
186    [m_wkView _processDidCrash];
187}
188
189void PageClientImpl::pageClosed()
190{
191    [m_wkView _pageClosed];
192}
193
194void PageClientImpl::didRelaunchProcess()
195{
196    [m_wkView _didRelaunchProcess];
197}
198
199void PageClientImpl::toolTipChanged(const String& oldToolTip, const String& newToolTip)
200{
201    [m_wkView _toolTipChangedFrom:nsStringFromWebCoreString(oldToolTip) to:nsStringFromWebCoreString(newToolTip)];
202}
203
204void PageClientImpl::setCursor(const WebCore::Cursor& cursor)
205{
206    if (![NSApp _cursorRectCursor])
207        [m_wkView _setCursor:cursor.platformCursor()];
208}
209
210void PageClientImpl::setViewportArguments(const WebCore::ViewportArguments&)
211{
212}
213
214void PageClientImpl::registerEditCommand(PassRefPtr<WebEditCommandProxy> prpCommand, WebPageProxy::UndoOrRedo undoOrRedo)
215{
216    RefPtr<WebEditCommandProxy> command = prpCommand;
217
218    RetainPtr<WKEditCommandObjC> commandObjC(AdoptNS, [[WKEditCommandObjC alloc] initWithWebEditCommandProxy:command]);
219    String actionName = WebEditCommandProxy::nameForEditAction(command->editAction());
220
221    NSUndoManager *undoManager = [m_wkView undoManager];
222    [undoManager registerUndoWithTarget:m_undoTarget.get() selector:((undoOrRedo == WebPageProxy::Undo) ? @selector(undoEditing:) : @selector(redoEditing:)) object:commandObjC.get()];
223    if (!actionName.isEmpty())
224        [undoManager setActionName:(NSString *)actionName];
225}
226
227void PageClientImpl::clearAllEditCommands()
228{
229    [[m_wkView undoManager] removeAllActionsWithTarget:m_undoTarget.get()];
230}
231
232bool PageClientImpl::canUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo)
233{
234    return (undoOrRedo == WebPageProxy::Undo) ? [[m_wkView undoManager] canUndo] : [[m_wkView undoManager] canRedo];
235}
236
237void PageClientImpl::executeUndoRedo(WebPageProxy::UndoOrRedo undoOrRedo)
238{
239    return (undoOrRedo == WebPageProxy::Undo) ? [[m_wkView undoManager] undo] : [[m_wkView undoManager] redo];
240}
241
242bool PageClientImpl::interpretKeyEvent(const NativeWebKeyboardEvent& event, Vector<WebCore::KeypressCommand>& commands)
243{
244    return [m_wkView _interpretKeyEvent:event.nativeEvent() savingCommandsTo:commands];
245}
246
247void PageClientImpl::setDragImage(const IntPoint& clientPosition, PassRefPtr<ShareableBitmap> dragImage, bool isLinkDrag)
248{
249    RetainPtr<CGImageRef> dragCGImage = dragImage->makeCGImage();
250    RetainPtr<NSImage> dragNSImage(AdoptNS, [[NSImage alloc] initWithCGImage:dragCGImage.get() size:dragImage->size()]);
251
252    [m_wkView _setDragImage:dragNSImage.get() at:clientPosition linkDrag:isLinkDrag];
253}
254
255void PageClientImpl::updateSecureInputState()
256{
257    [m_wkView _updateSecureInputState];
258}
259
260FloatRect PageClientImpl::convertToDeviceSpace(const FloatRect& rect)
261{
262    return [m_wkView _convertToDeviceSpace:rect];
263}
264
265FloatRect PageClientImpl::convertToUserSpace(const FloatRect& rect)
266{
267    return [m_wkView _convertToUserSpace:rect];
268}
269
270IntRect PageClientImpl::windowToScreen(const IntRect& rect)
271{
272    NSRect tempRect = rect;
273    tempRect = [m_wkView convertRect:tempRect toView:nil];
274    tempRect.origin = [[m_wkView window] convertBaseToScreen:tempRect.origin];
275    return enclosingIntRect(tempRect);
276}
277
278void PageClientImpl::doneWithKeyEvent(const NativeWebKeyboardEvent& event, bool wasEventHandled)
279{
280    NSEvent* nativeEvent = event.nativeEvent();
281    if ([nativeEvent type] != NSKeyDown)
282        return;
283    if (wasEventHandled)
284        [NSCursor setHiddenUntilMouseMoves:YES];
285    else
286        [m_wkView _resendKeyDownEvent:nativeEvent];
287}
288
289PassRefPtr<WebPopupMenuProxy> PageClientImpl::createPopupMenuProxy(WebPageProxy* page)
290{
291    return WebPopupMenuProxyMac::create(m_wkView, page);
292}
293
294PassRefPtr<WebContextMenuProxy> PageClientImpl::createContextMenuProxy(WebPageProxy* page)
295{
296    return WebContextMenuProxyMac::create(m_wkView, page);
297}
298
299void PageClientImpl::setFindIndicator(PassRefPtr<FindIndicator> findIndicator, bool fadeOut)
300{
301    [m_wkView _setFindIndicator:findIndicator fadeOut:fadeOut];
302}
303
304void PageClientImpl::accessibilityWebProcessTokenReceived(const CoreIPC::DataReference& data)
305{
306    NSData* remoteToken = [NSData dataWithBytes:data.data() length:data.size()];
307    [m_wkView _setAccessibilityWebProcessToken:remoteToken];
308}
309
310#if USE(ACCELERATED_COMPOSITING)
311void PageClientImpl::enterAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext)
312{
313    [m_wkView _enterAcceleratedCompositingMode:layerTreeContext];
314}
315
316void PageClientImpl::exitAcceleratedCompositingMode()
317{
318    [m_wkView _exitAcceleratedCompositingMode];
319}
320#endif // USE(ACCELERATED_COMPOSITING)
321
322void PageClientImpl::setComplexTextInputEnabled(uint64_t pluginComplexTextInputIdentifier, bool complexTextInputEnabled)
323{
324    [m_wkView _setComplexTextInputEnabled:complexTextInputEnabled pluginComplexTextInputIdentifier:pluginComplexTextInputIdentifier];
325}
326
327CGContextRef PageClientImpl::containingWindowGraphicsContext()
328{
329    NSWindow *window = [m_wkView window];
330
331    // Don't try to get the graphics context if the NSWindow doesn't have a window device.
332    if ([window windowNumber] <= 0)
333        return 0;
334
335    return static_cast<CGContextRef>([[window graphicsContext] graphicsPort]);
336}
337
338void PageClientImpl::didChangeScrollbarsForMainFrame() const
339{
340    [m_wkView _didChangeScrollbarsForMainFrame];
341}
342
343void PageClientImpl::didCommitLoadForMainFrame(bool useCustomRepresentation)
344{
345    [m_wkView _setPageHasCustomRepresentation:useCustomRepresentation];
346}
347
348void PageClientImpl::didFinishLoadingDataForCustomRepresentation(const String& suggestedFilename, const CoreIPC::DataReference& dataReference)
349{
350    [m_wkView _didFinishLoadingDataForCustomRepresentationWithSuggestedFilename:suggestedFilename dataReference:dataReference];
351}
352
353double PageClientImpl::customRepresentationZoomFactor()
354{
355    return [m_wkView _customRepresentationZoomFactor];
356}
357
358void PageClientImpl::setCustomRepresentationZoomFactor(double zoomFactor)
359{
360    [m_wkView _setCustomRepresentationZoomFactor:zoomFactor];
361}
362
363void PageClientImpl::findStringInCustomRepresentation(const String& string, FindOptions options, unsigned maxMatchCount)
364{
365    [m_wkView _findStringInCustomRepresentation:string withFindOptions:options maxMatchCount:maxMatchCount];
366}
367
368void PageClientImpl::countStringMatchesInCustomRepresentation(const String& string, FindOptions options, unsigned maxMatchCount)
369{
370    [m_wkView _countStringMatchesInCustomRepresentation:string withFindOptions:options maxMatchCount:maxMatchCount];
371}
372
373void PageClientImpl::flashBackingStoreUpdates(const Vector<IntRect>&)
374{
375    notImplemented();
376}
377
378void PageClientImpl::didPerformDictionaryLookup(const String& text, double scaleFactor, const DictionaryPopupInfo& dictionaryPopupInfo)
379{
380    NSFontDescriptor *fontDescriptor = [NSFontDescriptor fontDescriptorWithFontAttributes:(NSDictionary *)dictionaryPopupInfo.fontInfo.fontAttributeDictionary.get()];
381    NSFont *font = [NSFont fontWithDescriptor:fontDescriptor size:((scaleFactor != 1) ? [fontDescriptor pointSize] * scaleFactor : 0)];
382
383    RetainPtr<NSMutableAttributedString> attributedString(AdoptNS, [[NSMutableAttributedString alloc] initWithString:nsStringFromWebCoreString(text)]);
384    [attributedString.get() addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [attributedString.get() length])];
385
386    NSPoint textBaselineOrigin = dictionaryPopupInfo.origin;
387    textBaselineOrigin.y += [font ascender];
388
389#if !defined(BUILDING_ON_SNOW_LEOPARD)
390    // Convert to screen coordinates.
391    textBaselineOrigin = [m_wkView convertPoint:textBaselineOrigin toView:nil];
392    textBaselineOrigin = [m_wkView.window convertRectToScreen:NSMakeRect(textBaselineOrigin.x, textBaselineOrigin.y, 0, 0)].origin;
393
394    WKShowWordDefinitionWindow(attributedString.get(), textBaselineOrigin, (NSDictionary *)dictionaryPopupInfo.options.get());
395#else
396    // If the dictionary lookup is being triggered by a hot key, force the overlay style.
397    NSDictionary *options = (dictionaryPopupInfo.type == DictionaryPopupInfo::HotKey) ? [NSDictionary dictionaryWithObject:NSDefinitionPresentationTypeOverlay forKey:NSDefinitionPresentationTypeKey] : 0;
398    [m_wkView showDefinitionForAttributedString:attributedString.get() range:NSMakeRange(0, [attributedString.get() length]) options:options baselineOriginProvider:^(NSRange adjustedRange) { return (NSPoint)textBaselineOrigin; }];
399#endif
400}
401
402void PageClientImpl::dismissDictionaryLookupPanel()
403{
404#if !defined(BUILDING_ON_SNOW_LEOPARD)
405    WKHideWordDefinitionWindow();
406#endif
407}
408
409void PageClientImpl::showCorrectionPanel(CorrectionPanelInfo::PanelType type, const FloatRect& boundingBoxOfReplacedString, const String& replacedString, const String& replacementString, const Vector<String>& alternativeReplacementStrings)
410{
411#if !defined(BUILDING_ON_SNOW_LEOPARD)
412    if (!isViewVisible() || !isViewInWindow())
413        return;
414    m_correctionPanel.show(m_wkView, type, boundingBoxOfReplacedString, replacedString, replacementString, alternativeReplacementStrings);
415#endif
416}
417
418void PageClientImpl::dismissCorrectionPanel(ReasonForDismissingCorrectionPanel reason)
419{
420#if !defined(BUILDING_ON_SNOW_LEOPARD)
421    m_correctionPanel.dismiss(reason);
422#endif
423}
424
425String PageClientImpl::dismissCorrectionPanelSoon(WebCore::ReasonForDismissingCorrectionPanel reason)
426{
427#if !defined(BUILDING_ON_SNOW_LEOPARD)
428    return m_correctionPanel.dismissSoon(reason);
429#else
430    return String();
431#endif
432}
433
434void PageClientImpl::recordAutocorrectionResponse(EditorClient::AutocorrectionResponseType responseType, const String& replacedString, const String& replacementString)
435{
436#if !defined(BUILDING_ON_SNOW_LEOPARD)
437    NSCorrectionResponse response = responseType == EditorClient::AutocorrectionReverted ? NSCorrectionResponseReverted : NSCorrectionResponseEdited;
438    CorrectionPanel::recordAutocorrectionResponse(m_wkView, response, replacedString, replacementString);
439#endif
440}
441
442float PageClientImpl::userSpaceScaleFactor() const
443{
444    NSWindow *window = [m_wkView window];
445#if !defined(BUILDING_ON_SNOW_LEOPARD)
446    if (window)
447        return [window backingScaleFactor];
448    return [[NSScreen mainScreen] backingScaleFactor];
449#else
450    if (window)
451        return [window userSpaceScaleFactor];
452    return [[NSScreen mainScreen] userSpaceScaleFactor];
453#endif
454}
455
456bool PageClientImpl::executeSavedCommandBySelector(const String& selectorString)
457{
458    return [m_wkView _executeSavedCommandBySelector:NSSelectorFromString(selectorString)];
459}
460
461} // namespace WebKit
462