WebCore:
[WebKit-https.git] / WebKit / mac / Plugins / WebPluginController.mm
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29
30 #import <WebKit/WebPluginController.h>
31
32 #import <Foundation/NSURLRequest.h>
33 #import <WebCore/Frame.h>
34 #import <WebCore/FrameLoader.h>
35 #import <WebCore/ResourceRequest.h>
36 #import <WebCore/PlatformString.h>
37 #import <WebCore/DocumentLoader.h>
38 #import <WebKit/WebDataSourceInternal.h>
39 #import <WebKit/WebFrameInternal.h>
40 #import <WebKit/WebFrameView.h>
41 #import <WebKit/WebHTMLViewPrivate.h>
42 #import <WebKit/WebKitErrorsPrivate.h>
43 #import <WebKit/WebKitLogging.h>
44 #import <WebKit/WebNSURLExtras.h>
45 #import <WebKit/WebNSViewExtras.h>
46 #import <WebKit/WebPlugin.h>
47 #import <WebKit/WebPluginContainer.h>
48 #import <WebKit/WebPluginContainerCheck.h>
49 #import <WebKit/WebPluginPackage.h>
50 #import <WebKit/WebPluginPrivate.h>
51 #import <WebKit/WebPluginViewFactory.h>
52 #import <WebKit/WebUIDelegate.h>
53 #import <WebKit/WebViewInternal.h>
54
55 using namespace WebCore;
56
57 @interface NSView (PluginSecrets)
58 - (void)setContainingWindow:(NSWindow *)w;
59 @end
60
61 // For compatibility only.
62 @interface NSObject (OldPluginAPI)
63 + (NSView *)pluginViewWithArguments:(NSDictionary *)arguments;
64 @end
65
66 @interface NSView (OldPluginAPI)
67 - (void)pluginInitialize;
68 - (void)pluginStart;
69 - (void)pluginStop;
70 - (void)pluginDestroy;
71 @end
72
73 static NSMutableSet *pluginViews = nil;
74
75 @implementation WebPluginController
76
77 + (NSView *)plugInViewWithArguments:(NSDictionary *)arguments fromPluginPackage:(WebPluginPackage *)pluginPackage
78 {
79     [pluginPackage load];
80     Class viewFactory = [pluginPackage viewFactory];
81     
82     NSView *view = nil;
83
84     if ([viewFactory respondsToSelector:@selector(plugInViewWithArguments:)]) {
85         KJS::JSLock::DropAllLocks dropAllLocks;
86         view = [viewFactory plugInViewWithArguments:arguments];
87     } else if ([viewFactory respondsToSelector:@selector(pluginViewWithArguments:)]) {
88         KJS::JSLock::DropAllLocks dropAllLocks;
89         view = [viewFactory pluginViewWithArguments:arguments];
90     }
91     
92     if (view == nil) {
93         return nil;
94     }
95     
96     if (pluginViews == nil) {
97         pluginViews = [[NSMutableSet alloc] init];
98     }
99     [pluginViews addObject:view];
100     
101     return view;
102 }
103
104 + (BOOL)isPlugInView:(NSView *)view
105 {
106     return [pluginViews containsObject:view];
107 }
108
109 - (id)initWithDocumentView:(NSView *)view
110 {
111     [super init];
112     _documentView = view;
113     _views = [[NSMutableArray alloc] init];
114     _checksInProgress = (NSMutableSet *)CFMakeCollectable(CFSetCreateMutable(NULL, 0, NULL));
115     return self;
116 }
117
118 - (void)setDataSource:(WebDataSource *)dataSource
119 {
120     _dataSource = dataSource;    
121 }
122
123 - (void)dealloc
124 {
125     [_views release];
126     [_checksInProgress release];
127     [super dealloc];
128 }
129
130 - (void)startAllPlugins
131 {
132     if (_started)
133         return;
134     
135     if ([_views count] > 0)
136         LOG(Plugins, "starting WebKit plugins : %@", [_views description]);
137     
138     int i, count = [_views count];
139     for (i = 0; i < count; i++) {
140         id aView = [_views objectAtIndex:i];
141         if ([aView respondsToSelector:@selector(webPlugInStart)]) {
142             KJS::JSLock::DropAllLocks dropAllLocks;
143             [aView webPlugInStart];
144         } else if ([aView respondsToSelector:@selector(pluginStart)]) {
145             KJS::JSLock::DropAllLocks dropAllLocks;
146             [aView pluginStart];
147         }
148     }
149     _started = YES;
150 }
151
152 - (void)stopAllPlugins
153 {
154     if (!_started)
155         return;
156
157     if ([_views count] > 0) {
158         LOG(Plugins, "stopping WebKit plugins: %@", [_views description]);
159     }
160     
161     int i, count = [_views count];
162     for (i = 0; i < count; i++) {
163         id aView = [_views objectAtIndex:i];
164         if ([aView respondsToSelector:@selector(webPlugInStop)]) {
165             KJS::JSLock::DropAllLocks dropAllLocks;
166             [aView webPlugInStop];
167         } else if ([aView respondsToSelector:@selector(pluginStop)]) {
168             KJS::JSLock::DropAllLocks dropAllLocks;
169             [aView pluginStop];
170         }
171     }
172     _started = NO;
173 }
174
175 - (void)addPlugin:(NSView *)view
176 {
177     if (!_documentView) {
178         LOG_ERROR("can't add a plug-in to a defunct WebPluginController");
179         return;
180     }
181     
182     if (![_views containsObject:view]) {
183         [_views addObject:view];
184         
185         LOG(Plugins, "initializing plug-in %@", view);
186         if ([view respondsToSelector:@selector(webPlugInInitialize)]) {
187             KJS::JSLock::DropAllLocks dropAllLocks;
188             [view webPlugInInitialize];
189         } else if ([view respondsToSelector:@selector(pluginInitialize)]) {
190             KJS::JSLock::DropAllLocks dropAllLocks;
191             [view pluginInitialize];
192         }
193
194         if (_started) {
195             LOG(Plugins, "starting plug-in %@", view);
196             if ([view respondsToSelector:@selector(webPlugInStart)]) {
197                 KJS::JSLock::DropAllLocks dropAllLocks;
198                 [view webPlugInStart];
199             } else if ([view respondsToSelector:@selector(pluginStart)]) {
200                 KJS::JSLock::DropAllLocks dropAllLocks;
201                 [view pluginStart];
202             }
203             
204             if ([view respondsToSelector:@selector(setContainingWindow:)]) {
205                 KJS::JSLock::DropAllLocks dropAllLocks;
206                 [view setContainingWindow:[_documentView window]];
207             }
208         }
209     }
210 }
211
212 - (void)destroyPlugin:(NSView *)view
213 {
214     if ([_views containsObject:view]) {
215         if (_started) {
216             if ([view respondsToSelector:@selector(webPlugInStop)]) {
217                 KJS::JSLock::DropAllLocks dropAllLocks;
218                 [view webPlugInStop];
219             } else if ([view respondsToSelector:@selector(pluginStop)]) {
220                 KJS::JSLock::DropAllLocks dropAllLocks;
221                 [view pluginStop];
222             }
223         }
224         
225         if ([view respondsToSelector:@selector(webPlugInDestroy)]) {
226             KJS::JSLock::DropAllLocks dropAllLocks;
227             [view webPlugInDestroy];
228         } else if ([view respondsToSelector:@selector(pluginDestroy)]) {
229             KJS::JSLock::DropAllLocks dropAllLocks;
230             [view pluginDestroy];
231         }
232         
233         if (Frame* frame = core([self webFrame]))
234             frame->cleanupScriptObjectsForPlugin(self);
235         
236         [pluginViews removeObject:view];
237         [_views removeObject:view];
238     }
239 }
240
241 - (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier
242 {
243     [checkIdentifier cancel];
244     [_checksInProgress removeObject:checkIdentifier];
245 }
246
247 static void cancelOutstandingCheck(const void *item, void *context)
248 {
249     [(id)item cancel];
250 }
251
252 - (void)_cancelOutstandingChecks
253 {
254     if (_checksInProgress) {
255         CFSetApplyFunction((CFSetRef)_checksInProgress, cancelOutstandingCheck, NULL);
256         [_checksInProgress release];
257         _checksInProgress = nil;
258     }
259 }
260
261 - (void)destroyAllPlugins
262 {    
263     [self stopAllPlugins];
264
265     if ([_views count] > 0) {
266         LOG(Plugins, "destroying WebKit plugins: %@", [_views description]);
267     }
268
269     [self _cancelOutstandingChecks];
270     
271     int i, count = [_views count];
272     for (i = 0; i < count; i++) {
273         id aView = [_views objectAtIndex:i];
274         if ([aView respondsToSelector:@selector(webPlugInDestroy)]) {
275             KJS::JSLock::DropAllLocks dropAllLocks;
276             [aView webPlugInDestroy];
277         } else if ([aView respondsToSelector:@selector(pluginDestroy)]) {
278             KJS::JSLock::DropAllLocks dropAllLocks;
279             [aView pluginDestroy];
280         }
281         
282         if (Frame* frame = core([self webFrame]))
283             frame->cleanupScriptObjectsForPlugin(self);
284         
285         [pluginViews removeObject:aView];
286     }
287     [_views makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)];
288     [_views release];
289     _views = nil;
290
291     _documentView = nil;
292 }
293
294 - (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector
295 {
296     WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:request target:target resultObject:obj selector:selector controller:self];
297     [_checksInProgress addObject:check];
298     [check start];
299
300     return check;
301 }
302
303 - (void)webPlugInContainerLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target
304 {
305     if (!request) {
306         LOG_ERROR("nil URL passed");
307         return;
308     }
309     if (!_documentView) {
310         LOG_ERROR("could not load URL %@ because plug-in has already been destroyed", request);
311         return;
312     }
313     WebFrame *frame = [_dataSource webFrame];
314     if (!frame) {
315         LOG_ERROR("could not load URL %@ because plug-in has already been stopped", request);
316         return;
317     }
318     if (!target) {
319         target = @"_top";
320     }
321     NSString *JSString = [[request URL] _webkit_scriptIfJavaScriptURL];
322     if (JSString) {
323         if ([frame findFrameNamed:target] != frame) {
324             LOG_ERROR("JavaScript requests can only be made on the frame that contains the plug-in");
325             return;
326         }
327         [frame _stringByEvaluatingJavaScriptFromString:JSString];
328     } else {
329         if (!request) {
330             LOG_ERROR("could not load URL %@", [request URL]);
331             return;
332         }
333         [frame _frameLoader]->load(request, target);
334     }
335 }
336
337 // For compatibility only.
338 - (void)showURL:(NSURL *)URL inFrame:(NSString *)target
339 {
340     [self webPlugInContainerLoadRequest:[NSURLRequest requestWithURL:URL] inFrame:target];
341 }
342
343 - (void)webPlugInContainerShowStatus:(NSString *)message
344 {
345     if (!message) {
346         message = @"";
347     }
348     if (!_documentView) {
349         LOG_ERROR("could not show status message (%@) because plug-in has already been destroyed", message);
350         return;
351     }
352     WebView *v = [_dataSource _webView];
353     [[v _UIDelegateForwarder] webView:v setStatusText:message];
354 }
355
356 // For compatibility only.
357 - (void)showStatus:(NSString *)message
358 {
359     [self webPlugInContainerShowStatus:message];
360 }
361
362 - (NSColor *)webPlugInContainerSelectionColor
363 {
364     bool primary = true;
365     if (Frame* frame = core([self webFrame]))
366         primary = frame->selectionController()->isFocusedAndActive();
367     return primary ? [NSColor selectedTextBackgroundColor] : [NSColor secondarySelectedControlColor];
368 }
369
370 // For compatibility only.
371 - (NSColor *)selectionColor
372 {
373     return [self webPlugInContainerSelectionColor];
374 }
375
376 - (WebFrame *)webFrame
377 {
378     return [_dataSource webFrame];
379 }
380
381 - (WebView *)webView
382 {
383     return [[self webFrame] webView];
384 }
385
386 - (NSString *)URLPolicyCheckReferrer
387 {
388     NSURL *responseURL = [[[[self webFrame] _dataSource] response] URL];
389     ASSERT(responseURL);
390     return [responseURL _web_originalDataAsString];
391 }
392
393 - (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
394 {    
395     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveResponse:)])
396         [pluginView webPlugInMainResourceDidReceiveResponse:response];
397     else {
398         // Cancel the load since this plug-in does its own loading.
399         // FIXME: See <rdar://problem/4258008> for a problem with this.
400         NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad
401                                                         contentURL:[response URL]
402                                                      pluginPageURL:nil
403                                                         pluginName:nil // FIXME: Get this from somewhere
404                                                           MIMEType:[response MIMEType]];
405         [_dataSource _documentLoader]->cancelMainResourceLoad(error);
406         [error release];
407     }        
408 }
409
410 - (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
411 {
412     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveData:)])
413         [pluginView webPlugInMainResourceDidReceiveData:data];
414 }
415
416 - (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
417 {
418     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFailWithError:)])
419         [pluginView webPlugInMainResourceDidFailWithError:error];
420 }
421
422 - (void)pluginViewFinishedLoading:(NSView *)pluginView
423 {
424     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFinishLoading)])
425         [pluginView webPlugInMainResourceDidFinishLoading];
426 }
427
428 @end