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