d3cce464731143d63bec0bdb23e23cf38cd4a378
[WebKit-https.git] / Source / WebKit / mac / Plugins / Hosted / WebHostedNetscapePluginView.mm
1 /*
2  * Copyright (C) 2008 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
27
28 #import "WebHostedNetscapePluginView.h"
29
30 #import "HostedNetscapePluginStream.h"
31 #import "NetscapePluginInstanceProxy.h"
32 #import "NetscapePluginHostManager.h"
33 #import "NetscapePluginHostProxy.h"
34 #import "WebTextInputWindowController.h"
35 #import "WebFrameInternal.h"
36 #import "WebView.h"
37 #import "WebViewInternal.h"
38 #import "WebUIDelegate.h"
39
40 #import <CoreFoundation/CoreFoundation.h>
41 #import <WebCore/BridgeJSC.h>
42 #import <WebCore/Frame.h>
43 #import <WebCore/FrameLoaderTypes.h>
44 #import <WebCore/FrameView.h>
45 #import <WebCore/HTMLPlugInElement.h>
46 #import <WebCore/RenderEmbeddedObject.h>
47 #import <WebCore/WebCoreObjCExtras.h>
48 #import <WebCore/runtime_root.h>
49 #import <runtime/InitializeThreading.h>
50 #import <wtf/Assertions.h>
51 #import <wtf/Threading.h>
52
53 using namespace WebCore;
54 using namespace WebKit;
55
56 extern "C" {
57 #include "WebKitPluginClientServer.h"
58 #include "WebKitPluginHost.h"
59 }
60
61 @implementation WebHostedNetscapePluginView
62
63 + (void)initialize
64 {
65     JSC::initializeThreading();
66     WTF::initializeMainThreadToProcessMainThread();
67 #ifndef BUILDING_ON_TIGER
68     WebCoreObjCFinalizeOnMainThread(self);
69 #endif
70     WKSendUserChangeNotifications();
71 }
72
73 - (id)initWithFrame:(NSRect)frame
74       pluginPackage:(WebNetscapePluginPackage *)pluginPackage
75                 URL:(NSURL *)URL
76             baseURL:(NSURL *)baseURL
77            MIMEType:(NSString *)MIME
78       attributeKeys:(NSArray *)keys
79     attributeValues:(NSArray *)values
80        loadManually:(BOOL)loadManually
81             element:(PassRefPtr<WebCore::HTMLPlugInElement>)element
82 {
83     self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element:element];
84     if (!self)
85         return nil;
86     
87     return self;
88 }    
89
90 - (void)handleMouseMoved:(NSEvent *)event
91 {
92     if (_isStarted && _proxy)
93         _proxy->mouseEvent(self, event, NPCocoaEventMouseMoved);
94 }
95
96 - (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values
97 {
98     ASSERT(!_attributeKeys);
99     ASSERT(!_attributeValues);
100     
101     _attributeKeys.adoptNS([keys copy]);
102     _attributeValues.adoptNS([values copy]);
103 }    
104
105 - (BOOL)createPlugin
106 {
107     ASSERT(!_proxy);
108
109     NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()];
110     BOOL accleratedCompositingEnabled = false;
111 #if USE(ACCELERATED_COMPOSITING)
112     accleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled];
113 #endif
114     
115     _proxy = NetscapePluginHostManager::shared().instantiatePlugin([_pluginPackage.get() path], [_pluginPackage.get() pluginHostArchitecture], [_pluginPackage.get() bundleIdentifier], self, _MIMEType.get(), _attributeKeys.get(), _attributeValues.get(), userAgent, _sourceURL.get(), 
116                                                                    _mode == NP_FULL, _isPrivateBrowsingEnabled, accleratedCompositingEnabled);
117     if (!_proxy) 
118         return NO;
119
120     if (_proxy->rendererType() == UseSoftwareRenderer)
121         _softwareRenderer = WKSoftwareCARendererCreate(_proxy->renderContextID());
122     else {
123         _pluginLayer = WKMakeRenderLayer(_proxy->renderContextID());
124
125         if (accleratedCompositingEnabled && _proxy->rendererType() == UseAcceleratedCompositing) {
126             // FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView.
127 #ifndef BUILDING_ON_LEOPARD
128             // Since this layer isn't going to be inserted into a view, we need to create another layer and flip its geometry
129             // in order to get the coordinate system right.
130             RetainPtr<CALayer> realPluginLayer(AdoptNS, _pluginLayer.releaseRef());
131             
132             _pluginLayer.adoptNS([[CALayer alloc] init]);
133             _pluginLayer.get().bounds = realPluginLayer.get().bounds;
134             _pluginLayer.get().geometryFlipped = YES;
135             
136             realPluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
137             [_pluginLayer.get() addSublayer:realPluginLayer.get()];
138 #endif
139             
140             // Eagerly enter compositing mode, since we know we'll need it. This avoids firing setNeedsStyleRecalc()
141             // for iframes that contain composited plugins at bad times. https://bugs.webkit.org/show_bug.cgi?id=39033
142             core([self webFrame])->view()->enterCompositingMode();
143             [self element]->setNeedsStyleRecalc(SyntheticStyleChange);
144         } else
145             self.wantsLayer = YES;
146     }
147     
148     // Update the window frame.
149     _proxy->windowFrameChanged([[self window] frame]);
150     
151     return YES;
152 }
153
154 // FIXME: This method is an ideal candidate to move up to the base class
155 - (CALayer *)pluginLayer
156 {
157     return _pluginLayer.get();
158 }
159
160 - (void)setLayer:(CALayer *)newLayer
161 {
162     // FIXME: This should use the same implementation as WebNetscapePluginView (and move to the base class).
163     [super setLayer:newLayer];
164     
165     if (_pluginLayer)
166         [newLayer addSublayer:_pluginLayer.get()];
167 }
168
169 - (void)privateBrowsingModeDidChange
170 {
171     if (_proxy)
172         _proxy->privateBrowsingModeDidChange(_isPrivateBrowsingEnabled);
173 }
174
175 - (void)loadStream
176 {
177 }
178
179 - (void)updateAndSetWindow
180 {
181     if (!_proxy)
182         return;
183     
184     // The base coordinates of a window and it's contentView happen to be the equal at a userSpaceScaleFactor
185     // of 1. For non-1.0 scale factors this assumption is false.
186     NSView *windowContentView = [[self window] contentView];
187     NSRect boundsInWindow = [self convertRect:[self bounds] toView:windowContentView];
188
189     NSRect visibleRectInWindow;
190     
191     // Core Animation plug-ins need to be updated (with a 0,0,0,0 clipRect) when
192     // moved to a background tab. We don't do this for Core Graphics plug-ins as
193     // older versions of Flash have historical WebKit-specific code that isn't
194     // compatible with this behavior.    
195     BOOL shouldClipOutPlugin = _pluginLayer && [self shouldClipOutPlugin];
196     if (!shouldClipOutPlugin)
197         visibleRectInWindow = [self actualVisibleRectInWindow];
198     else
199         visibleRectInWindow = NSZeroRect;
200     
201     // Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
202     float borderViewHeight = [[self currentWindow] frame].size.height;
203     boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
204         
205     if (!shouldClipOutPlugin)
206         visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
207
208     _previousSize = boundsInWindow.size;
209     
210     _proxy->resize(boundsInWindow, visibleRectInWindow);
211
212     CGRect bounds = NSRectToCGRect([self bounds]);
213     CGRect frame = NSRectToCGRect([self frame]);
214     
215     // We're not scaled, or in a subframe
216     CATransform3D scaleTransform = CATransform3DIdentity;
217     if (CGSizeEqualToSize(bounds.size, frame.size)) {
218         // We're in a subframe. Backing store is boundsInWindow.size.
219         if (boundsInWindow.size.width && boundsInWindow.size.height)
220             scaleTransform = CATransform3DMakeScale(frame.size.width / boundsInWindow.size.width, frame.size.height / boundsInWindow.size.height, 1);
221     } else {
222         // We're in the main frame with scaling. Need to mimic the frame/bounds scaling on Widgets.
223         if (frame.size.width && frame.size.height)
224             scaleTransform = CATransform3DMakeScale(bounds.size.width / frame.size.width, bounds.size.height / frame.size.height, 1);
225     }
226
227     _pluginLayer.get().sublayerTransform = scaleTransform;
228 }
229
230 - (void)windowFocusChanged:(BOOL)hasFocus
231 {
232     if (_proxy)
233         _proxy->windowFocusChanged(hasFocus);
234 }
235
236 - (BOOL)shouldStop
237 {
238     if (!_proxy)
239         return YES;
240     
241     return _proxy->shouldStop();
242 }
243
244 - (void)destroyPlugin
245 {
246     if (_proxy) {
247         if (_softwareRenderer) {
248             WKSoftwareCARendererDestroy(_softwareRenderer);
249             _softwareRenderer = 0;
250         }
251         
252         _proxy->destroy();
253         _proxy = 0;
254     }
255     
256     _pluginLayer = 0;
257 }
258
259 - (void)startTimers
260 {
261     if (_proxy)
262         _proxy->startTimers(_isCompletelyObscured);
263 }
264
265 - (void)stopTimers
266 {
267     if (_proxy)
268         _proxy->stopTimers();
269 }
270
271 - (void)focusChanged
272 {
273     if (_proxy)
274         _proxy->focusChanged(_hasFocus);
275 }
276
277 - (void)windowFrameDidChange:(NSNotification *)notification 
278 {
279     if (_proxy && [self window])
280         _proxy->windowFrameChanged([[self window] frame]);
281 }
282
283 - (void)addWindowObservers
284 {
285     [super addWindowObservers];
286     
287     ASSERT([self window]);
288     
289     NSWindow *window = [self window];
290     
291     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
292     [notificationCenter addObserver:self selector:@selector(windowFrameDidChange:) 
293                                name:NSWindowDidMoveNotification object:window];
294     [notificationCenter addObserver:self selector:@selector(windowFrameDidChange:)
295                                name:NSWindowDidResizeNotification object:window];    
296
297     if (_proxy)
298         _proxy->windowFrameChanged([window frame]);
299     [self updateAndSetWindow];
300 }
301
302 - (void)removeWindowObservers
303 {
304     [super removeWindowObservers];
305     
306     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
307     [notificationCenter removeObserver:self name:NSWindowDidMoveNotification object:nil];
308     [notificationCenter removeObserver:self name:NSWindowDidResizeNotification object:nil];
309 }
310
311 - (void)mouseDown:(NSEvent *)event
312 {
313     if (_isStarted && _proxy)
314         _proxy->mouseEvent(self, event, NPCocoaEventMouseDown);
315 }
316
317 - (void)mouseUp:(NSEvent *)event
318 {
319     if (_isStarted && _proxy)
320         _proxy->mouseEvent(self, event, NPCocoaEventMouseUp);
321 }
322
323 - (void)mouseDragged:(NSEvent *)event
324 {
325     if (_isStarted && _proxy)
326         _proxy->mouseEvent(self, event, NPCocoaEventMouseDragged);
327 }
328
329 - (void)handleMouseEntered:(NSEvent *)event
330 {
331     // Set cursor to arrow. Plugins often handle cursor internally, but those that don't will just get this default one.
332     [[NSCursor arrowCursor] set];
333
334     if (_isStarted && _proxy)
335         _proxy->mouseEvent(self, event, NPCocoaEventMouseEntered);
336 }
337
338 - (void)handleMouseExited:(NSEvent *)event
339 {
340     if (_isStarted && _proxy)
341         _proxy->mouseEvent(self, event, NPCocoaEventMouseExited);
342
343     // Set cursor back to arrow cursor.  Because NSCursor doesn't know about changes that the plugin made, we could get confused about what we think the
344     // current cursor is otherwise.  Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin.
345     // FIXME: This should be job of plugin host, see <rdar://problem/7654434>.
346     [[NSCursor arrowCursor] set];
347 }
348
349 - (void)scrollWheel:(NSEvent *)event
350 {
351     bool processedEvent = false;
352     
353     if (_isStarted && _proxy)
354         processedEvent = _proxy->wheelEvent(self, event);
355     
356     if (!processedEvent)
357         [super scrollWheel:event];
358 }
359
360 - (NSTextInputContext *)inputContext
361 {
362     return [[WebTextInputWindowController sharedTextInputWindowController] inputContext];
363 }
364
365 - (void)keyDown:(NSEvent *)event
366 {
367     if (!_isStarted || !_proxy)
368         return;
369     
370     NSString *string = nil;
371     if ([[WebTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event string:&string]) {
372         if (string)
373             _proxy->insertText(string);
374         return;
375     }
376     
377     _proxy->keyEvent(self, event, NPCocoaEventKeyDown);
378 }
379
380 - (void)keyUp:(NSEvent *)event
381 {
382     if (_isStarted && _proxy)
383         _proxy->keyEvent(self, event, NPCocoaEventKeyUp);
384 }
385
386 - (void)flagsChanged:(NSEvent *)event
387 {
388     if (_isStarted && _proxy)
389         _proxy->flagsChanged(event);
390 }
391
392 - (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
393 {
394     if (_isStarted && _proxy)
395         _proxy->syntheticKeyDownWithCommandModifier(keyCode, character);
396 }
397
398 - (void)pluginHostDied
399 {
400     if (_element->renderer() && _element->renderer()->isEmbeddedObject()) {
401         // FIXME: The renderer could also be a RenderApplet, we should handle that.
402         RenderEmbeddedObject* renderer = toRenderEmbeddedObject(_element->renderer());
403         renderer->setShowsCrashedPluginIndicator();
404     }
405
406     _pluginLayer = nil;
407     _proxy = 0;
408     
409     // No need for us to be layer backed anymore
410     self.wantsLayer = NO;
411     
412     [self invalidatePluginContentRect:[self bounds]];
413 }
414
415 - (void)visibleRectDidChange
416 {
417     [super visibleRectDidChange];
418     WKSyncSurfaceToView(self);
419 }
420
421 - (void)drawRect:(NSRect)rect
422 {
423     if (_cachedSnapshot) {
424         NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] };
425         [_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositeSourceOver fraction:1];
426         return;
427     }
428
429     if (_proxy) {
430         if (_softwareRenderer) {
431             if ([NSGraphicsContext currentContextDrawingToScreen]) {
432                 WKSoftwareCARendererRender(_softwareRenderer, (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort], NSRectToCGRect(rect));
433                 _proxy->didDraw();
434             } else
435                 _proxy->print(reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height);
436         } else if (_snapshotting && [self supportsSnapshotting]) {
437             _proxy->snapshot(reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height);
438         }
439
440         return;
441     }
442 }
443
444 - (PassRefPtr<JSC::Bindings::Instance>)createPluginBindingsInstance:(PassRefPtr<JSC::Bindings::RootObject>)rootObject
445 {
446     if (!_proxy)
447         return 0;
448     
449     return _proxy->createBindingsInstance(rootObject);
450 }
451
452 - (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
453 {
454     ASSERT(_loadManually);
455     if (!_proxy)
456         return;
457     
458     ASSERT(!_proxy->manualStream());
459
460     _proxy->setManualStream(HostedNetscapePluginStream::create(_proxy.get(), core([self webFrame])->loader()));
461     _proxy->manualStream()->startStreamWithResponse(response);
462 }
463
464 - (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
465 {
466     ASSERT(_loadManually);
467     if (!_proxy)
468         return;
469     
470     if (HostedNetscapePluginStream* manualStream = _proxy->manualStream())
471         manualStream->didReceiveData(0, static_cast<const char*>([data bytes]), [data length]);
472 }
473
474 - (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
475 {
476     ASSERT(_loadManually);
477     if (!_proxy)
478         return;
479     
480     if (HostedNetscapePluginStream* manualStream = _proxy->manualStream())
481         manualStream->didFail(0, error);
482 }
483
484 - (void)pluginViewFinishedLoading:(NSView *)pluginView
485 {
486     ASSERT(_loadManually);
487     if (!_proxy)
488         return;
489     
490     if (HostedNetscapePluginStream* manualStream = _proxy->manualStream())
491         manualStream->didFinishLoading(0);
492 }
493
494 - (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)webPluginContainerCheck
495 {
496     ASSERT([webPluginContainerCheck isKindOfClass:[WebPluginContainerCheck class]]);
497     
498     id contextInfo = [webPluginContainerCheck contextInfo];
499     ASSERT([contextInfo isKindOfClass:[NSNumber class]]);
500
501     if (!_proxy)
502         return;
503
504     uint32_t checkID = [(NSNumber *)contextInfo unsignedIntValue];
505     _proxy->cancelCheckIfAllowedToLoadURL(checkID);
506 }
507
508 - (void)_containerCheckResult:(PolicyAction)policy contextInfo:(id)contextInfo
509 {
510     ASSERT([contextInfo isKindOfClass:[NSNumber class]]);
511     if (!_proxy)
512         return;
513
514     uint32_t checkID = [(NSNumber *)contextInfo unsignedIntValue];
515     _proxy->checkIfAllowedToLoadURLResult(checkID, (policy == PolicyUse));
516 }
517
518 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
519 {
520     if (_isStarted && _proxy)
521         _proxy->webFrameDidFinishLoadWithReason(webFrame, reason);
522 }
523
524 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
525 {
526     NPReason reason = NPRES_DONE;
527     if (error)
528         reason = HostedNetscapePluginStream::reasonForError(error);
529     [self webFrame:webFrame didFinishLoadWithReason:reason];
530 }
531
532 @end
533
534 #endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)