Reviewed by Darin.
[WebKit-https.git] / WebKit / Plugins / WebPluginController.m
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 <WebKit/WebDocumentLoader.h>
33 #import <WebKit/WebFrameBridge.h>
34 #import <WebKit/WebFrameInternal.h>
35 #import <WebKit/WebFrameView.h>
36 #import <WebKit/WebHTMLViewPrivate.h>
37 #import <WebKit/WebKitErrorsPrivate.h>
38 #import <WebKit/WebKitLogging.h>
39 #import <WebKit/WebNSURLExtras.h>
40 #import <WebKit/WebNSViewExtras.h>
41 #import <WebKit/WebPlugin.h>
42 #import <WebKit/WebPluginContainer.h>
43 #import <WebKit/WebPluginContainerCheck.h>
44 #import <WebKit/WebPluginPackage.h>
45 #import <WebKit/WebPluginPrivate.h>
46 #import <WebKit/WebPluginViewFactory.h>
47 #import <WebKit/WebViewInternal.h>
48 #import <WebKit/WebUIDelegate.h>
49 #import <WebKit/WebDataSourceInternal.h>
50 #import <WebKit/WebFrameLoader.h>
51
52 #import <WebCore/WebCoreFrameBridge.h>
53
54 #import <Foundation/NSURLRequest.h>
55
56 @interface NSView (PluginSecrets)
57 - (void)setContainingWindow:(NSWindow *)w;
58 @end
59
60 // For compatibility only.
61 @interface NSObject (OldPluginAPI)
62 + (NSView *)pluginViewWithArguments:(NSDictionary *)arguments;
63 @end
64
65 @interface NSView (OldPluginAPI)
66 - (void)pluginInitialize;
67 - (void)pluginStart;
68 - (void)pluginStop;
69 - (void)pluginDestroy;
70 @end
71
72 static NSMutableSet *pluginViews = nil;
73
74 @implementation WebPluginController
75
76 + (NSView *)plugInViewWithArguments:(NSDictionary *)arguments fromPluginPackage:(WebPluginPackage *)pluginPackage
77 {
78     [pluginPackage load];
79     Class viewFactory = [pluginPackage viewFactory];
80     
81     NSView *view = nil;
82     if ([viewFactory respondsToSelector:@selector(plugInViewWithArguments:)]) {
83         view = [viewFactory plugInViewWithArguments:arguments];
84     } else if ([viewFactory respondsToSelector:@selector(pluginViewWithArguments:)]) {
85         view = [viewFactory pluginViewWithArguments:arguments];
86     }
87     
88     if (view == nil) {
89         return nil;
90     }
91     
92     if (pluginViews == nil) {
93         pluginViews = [[NSMutableSet alloc] init];
94     }
95     [pluginViews addObject:view];
96     
97     return view;
98 }
99
100 + (BOOL)isPlugInView:(NSView *)view
101 {
102     return [pluginViews containsObject:view];
103 }
104
105 - (id)initWithDocumentView:(NSView *)view
106 {
107     [super init];
108     _documentView = view;
109     _views = [[NSMutableArray alloc] init];
110     _checksInProgress = (NSMutableSet *)CFMakeCollectable(CFSetCreateMutable(NULL, 0, NULL));
111     return self;
112 }
113
114 - (void)setDataSource:(WebDataSource *)dataSource
115 {
116     _dataSource = dataSource;    
117 }
118
119 - (void)dealloc
120 {
121     [_views release];
122     [_checksInProgress release];
123     [super dealloc];
124 }
125
126 - (void)startAllPlugins
127 {
128     if (_started) {
129         return;
130     }
131     
132     if ([_views count] > 0) {
133         LOG(Plugins, "starting WebKit plugins : %@", [_views description]);
134     }
135     
136     int i, count = [_views count];
137     for (i = 0; i < count; i++) {
138         id aView = [_views objectAtIndex:i];
139         if ([aView respondsToSelector:@selector(webPlugInStart)])
140             [aView webPlugInStart];
141         else if ([aView respondsToSelector:@selector(pluginStart)])
142             [aView pluginStart];
143     }
144     _started = YES;
145 }
146
147 - (void)stopAllPlugins
148 {
149     if (!_started) {
150         return;
151     }
152
153     if ([_views count] > 0) {
154         LOG(Plugins, "stopping WebKit plugins: %@", [_views description]);
155     }
156     
157     int i, count = [_views count];
158     for (i = 0; i < count; i++) {
159         id aView = [_views objectAtIndex:i];
160         if ([aView respondsToSelector:@selector(webPlugInStop)])
161             [aView webPlugInStop];
162         else if ([aView respondsToSelector:@selector(pluginStop)])
163             [aView pluginStop];
164     }
165     _started = NO;
166 }
167
168 - (void)addPlugin:(NSView *)view
169 {
170     if (!_documentView) {
171         LOG_ERROR("can't add a plug-in to a defunct WebPluginController");
172         return;
173     }
174     
175     if (![_views containsObject:view]) {
176         [_views addObject:view];
177         
178         LOG(Plugins, "initializing plug-in %@", view);
179         if ([view respondsToSelector:@selector(webPlugInInitialize)])
180             [view webPlugInInitialize];
181         else if ([view respondsToSelector:@selector(pluginInitialize)])
182             [view pluginInitialize];
183
184         if (_started) {
185             LOG(Plugins, "starting plug-in %@", view);
186             if ([view respondsToSelector:@selector(webPlugInStart)])
187                 [view webPlugInStart];
188             else if ([view respondsToSelector:@selector(pluginStart)])
189                 [view pluginStart];
190             
191             if ([view respondsToSelector:@selector(setContainingWindow:)])
192                 [view setContainingWindow:[_documentView window]];
193         }
194     }
195 }
196
197 - (void)destroyPlugin:(NSView *)view
198 {
199     if ([_views containsObject:view]) {
200         if (_started) {
201             if ([view respondsToSelector:@selector(webPlugInStop)])
202                 [view webPlugInStop];
203             else if ([view respondsToSelector:@selector(pluginStop)])
204                 [view pluginStop];
205         }
206         
207         if ([view respondsToSelector:@selector(webPlugInDestroy)])
208             [view webPlugInDestroy];
209         else if ([view respondsToSelector:@selector(pluginDestroy)])
210             [view pluginDestroy];
211         
212         [pluginViews removeObject:view];
213         [_views removeObject:view];
214     }
215 }
216
217 - (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier
218 {
219     [checkIdentifier cancel];
220     [_checksInProgress removeObject:checkIdentifier];
221 }
222
223 static void cancelOutstandingCheck(const void *item, void *context)
224 {
225     [(id)item cancel];
226 }
227
228 - (void)_cancelOutstandingChecks
229 {
230     if (_checksInProgress) {
231         CFSetApplyFunction((CFSetRef)_checksInProgress, cancelOutstandingCheck, NULL);
232         [_checksInProgress release];
233         _checksInProgress = nil;
234     }
235 }
236
237 - (void)destroyAllPlugins
238 {    
239     [self stopAllPlugins];
240
241     if ([_views count] > 0) {
242         LOG(Plugins, "destroying WebKit plugins: %@", [_views description]);
243     }
244
245     [self _cancelOutstandingChecks];
246     
247     int i, count = [_views count];
248     for (i = 0; i < count; i++) {
249         id aView = [_views objectAtIndex:i];
250         if ([aView respondsToSelector:@selector(webPlugInDestroy)]) {
251             [aView webPlugInDestroy];
252         } else if ([aView respondsToSelector:@selector(pluginDestroy)]) {
253             [aView pluginDestroy];
254         }
255         [pluginViews removeObject:aView];
256     }
257     [_views makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)];
258     [_views release];
259     _views = nil;
260
261     _documentView = nil;
262 }
263
264 - (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector
265 {
266     WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:request target:target resultObject:obj selector:selector controller:self];
267     [_checksInProgress addObject:check];
268     [check start];
269
270     return check;
271 }
272
273 - (void)webPlugInContainerLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target
274 {
275     if (!request) {
276         LOG_ERROR("nil URL passed");
277         return;
278     }
279     if (!_documentView) {
280         LOG_ERROR("could not load URL %@ because plug-in has already been destroyed", request);
281         return;
282     }
283     WebFrame *frame = [_dataSource webFrame];
284     if (!frame) {
285         LOG_ERROR("could not load URL %@ because plug-in has already been stopped", request);
286         return;
287     }
288     if (!target) {
289         target = @"_top";
290     }
291     NSString *JSString = [[request URL] _webkit_scriptIfJavaScriptURL];
292     if (JSString) {
293         if ([frame findFrameNamed:target] != frame) {
294             LOG_ERROR("JavaScript requests can only be made on the frame that contains the plug-in");
295             return;
296         }
297         [[frame _bridge] stringByEvaluatingJavaScriptFromString:JSString];
298     } else {
299         if (!request) {
300             LOG_ERROR("could not load URL %@", [request URL]);
301             return;
302         }
303         [frame _loadRequest:request inFrameNamed:target];
304     }
305 }
306
307 // For compatibility only.
308 - (void)showURL:(NSURL *)URL inFrame:(NSString *)target
309 {
310     [self webPlugInContainerLoadRequest:[NSURLRequest requestWithURL:URL] inFrame:target];
311 }
312
313 - (void)webPlugInContainerShowStatus:(NSString *)message
314 {
315     if (!message) {
316         message = @"";
317     }
318     if (!_documentView) {
319         LOG_ERROR("could not show status message (%@) because plug-in has already been destroyed", message);
320         return;
321     }
322     WebView *v = [_dataSource _webView];
323     [[v _UIDelegateForwarder] webView:v setStatusText:message];
324 }
325
326 // For compatibility only.
327 - (void)showStatus:(NSString *)message
328 {
329     [self webPlugInContainerShowStatus:message];
330 }
331
332 - (NSColor *)webPlugInContainerSelectionColor
333 {
334     return [[_dataSource _bridge] selectionColor];
335 }
336
337 // For compatibility only.
338 - (NSColor *)selectionColor
339 {
340     return [self webPlugInContainerSelectionColor];
341 }
342
343 - (WebFrame *)webFrame
344 {
345     return [_dataSource webFrame];
346 }
347
348 - (WebFrameBridge *)bridge
349 {
350     return [[self webFrame] _bridge];
351 }
352
353 - (WebView *)webView
354 {
355     return [[self webFrame] webView];
356 }
357
358 - (NSString *)URLPolicyCheckReferrer
359 {
360     NSURL *responseURL = [[[[self webFrame] dataSource] response] URL];
361     ASSERT(responseURL);
362     return [responseURL _web_originalDataAsString];
363 }
364
365 - (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
366 {    
367     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveResponse:)])
368         [pluginView webPlugInMainResourceDidReceiveResponse:response];
369     else {
370         // Cancel the load since this plug-in does its own loading.
371
372         // FIXME: See <rdar://problem/4258008>
373         NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad
374                                                         contentURL:[response URL]
375                                                      pluginPageURL:nil
376                                                         pluginName:nil // FIXME: Get this from somewhere
377                                                           MIMEType:[response MIMEType]];
378         [[[_dataSource _documentLoader] frameLoader] stopLoadingWithError:error];
379         [error release];
380     }        
381 }
382
383 - (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
384 {
385     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveData:)])
386         [pluginView webPlugInMainResourceDidReceiveData:data];
387 }
388
389 - (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
390 {
391     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFailWithError:)])
392         [pluginView webPlugInMainResourceDidFailWithError:error];
393 }
394
395 - (void)pluginViewFinishedLoading:(NSView *)pluginView
396 {
397     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFinishLoading)])
398         [pluginView webPlugInMainResourceDidFinishLoading];
399 }
400
401 @end