• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "WebPluginController.h"
31
32#import "DOMNodeInternal.h"
33#import "WebDataSourceInternal.h"
34#import "WebFrameInternal.h"
35#import "WebFrameView.h"
36#import "WebHTMLViewPrivate.h"
37#import "WebKitErrorsPrivate.h"
38#import "WebKitLogging.h"
39#import "WebNSURLExtras.h"
40#import "WebNSViewExtras.h"
41#import "WebPlugin.h"
42#import "WebPluginContainer.h"
43#import "WebPluginContainerCheck.h"
44#import "WebPluginPackage.h"
45#import "WebPluginPrivate.h"
46#import "WebPluginViewFactory.h"
47#import "WebUIDelegate.h"
48#import "WebViewInternal.h"
49#import <Foundation/NSURLRequest.h>
50#import <WebCore/DocumentLoader.h>
51#import <WebCore/Frame.h>
52#import <WebCore/FrameLoader.h>
53#import <WebCore/HTMLMediaElement.h>
54#import <WebCore/HTMLNames.h>
55#import <WebCore/MediaPlayerProxy.h>
56#import <WebCore/PlatformString.h>
57#import <WebCore/ResourceRequest.h>
58#import <WebCore/ScriptController.h>
59#import <WebCore/WebCoreURLResponse.h>
60#import <runtime/JSLock.h>
61
62using namespace WebCore;
63using namespace HTMLNames;
64
65@interface NSView (PluginSecrets)
66- (void)setContainingWindow:(NSWindow *)w;
67@end
68
69// For compatibility only.
70@interface NSObject (OldPluginAPI)
71+ (NSView *)pluginViewWithArguments:(NSDictionary *)arguments;
72@end
73
74@interface NSView (OldPluginAPI)
75- (void)pluginInitialize;
76- (void)pluginStart;
77- (void)pluginStop;
78- (void)pluginDestroy;
79@end
80
81static NSMutableSet *pluginViews = nil;
82
83@implementation WebPluginController
84
85+ (NSView *)plugInViewWithArguments:(NSDictionary *)arguments fromPluginPackage:(WebPluginPackage *)pluginPackage
86{
87    [pluginPackage load];
88    Class viewFactory = [pluginPackage viewFactory];
89
90    NSView *view = nil;
91
92    if ([viewFactory respondsToSelector:@selector(plugInViewWithArguments:)]) {
93        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
94        view = [viewFactory plugInViewWithArguments:arguments];
95    } else if ([viewFactory respondsToSelector:@selector(pluginViewWithArguments:)]) {
96        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
97        view = [viewFactory pluginViewWithArguments:arguments];
98    }
99
100    if (view == nil) {
101        return nil;
102    }
103
104    if (pluginViews == nil) {
105        pluginViews = [[NSMutableSet alloc] init];
106    }
107    [pluginViews addObject:view];
108
109    return view;
110}
111
112+ (BOOL)isPlugInView:(NSView *)view
113{
114    return [pluginViews containsObject:view];
115}
116
117- (id)initWithDocumentView:(NSView *)view
118{
119    [super init];
120    _documentView = view;
121    _views = [[NSMutableArray alloc] init];
122    _checksInProgress = (NSMutableSet *)CFMakeCollectable(CFSetCreateMutable(NULL, 0, NULL));
123    return self;
124}
125
126- (void)setDataSource:(WebDataSource *)dataSource
127{
128    _dataSource = dataSource;
129}
130
131- (void)dealloc
132{
133    [_views release];
134    [_checksInProgress release];
135    [super dealloc];
136}
137
138- (void)startAllPlugins
139{
140    if (_started)
141        return;
142
143    if ([_views count] > 0)
144        LOG(Plugins, "starting WebKit plugins : %@", [_views description]);
145
146    int i, count = [_views count];
147    for (i = 0; i < count; i++) {
148        id aView = [_views objectAtIndex:i];
149        if ([aView respondsToSelector:@selector(webPlugInStart)]) {
150            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
151            [aView webPlugInStart];
152        } else if ([aView respondsToSelector:@selector(pluginStart)]) {
153            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
154            [aView pluginStart];
155        }
156    }
157    _started = YES;
158}
159
160- (void)stopAllPlugins
161{
162    if (!_started)
163        return;
164
165    if ([_views count] > 0) {
166        LOG(Plugins, "stopping WebKit plugins: %@", [_views description]);
167    }
168
169    int i, count = [_views count];
170    for (i = 0; i < count; i++) {
171        id aView = [_views objectAtIndex:i];
172        if ([aView respondsToSelector:@selector(webPlugInStop)]) {
173            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
174            [aView webPlugInStop];
175        } else if ([aView respondsToSelector:@selector(pluginStop)]) {
176            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
177            [aView pluginStop];
178        }
179    }
180    _started = NO;
181}
182
183- (void)addPlugin:(NSView *)view
184{
185    if (!_documentView) {
186        LOG_ERROR("can't add a plug-in to a defunct WebPluginController");
187        return;
188    }
189
190    if (![_views containsObject:view]) {
191        [_views addObject:view];
192        [[_documentView _webView] addPluginInstanceView:view];
193
194        BOOL oldDefersCallbacks = [[self webView] defersCallbacks];
195        if (!oldDefersCallbacks)
196            [[self webView] setDefersCallbacks:YES];
197
198        LOG(Plugins, "initializing plug-in %@", view);
199        if ([view respondsToSelector:@selector(webPlugInInitialize)]) {
200            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
201            [view webPlugInInitialize];
202        } else if ([view respondsToSelector:@selector(pluginInitialize)]) {
203            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
204            [view pluginInitialize];
205        }
206
207        if (!oldDefersCallbacks)
208            [[self webView] setDefersCallbacks:NO];
209
210        if (_started) {
211            LOG(Plugins, "starting plug-in %@", view);
212            if ([view respondsToSelector:@selector(webPlugInStart)]) {
213                JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
214                [view webPlugInStart];
215            } else if ([view respondsToSelector:@selector(pluginStart)]) {
216                JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
217                [view pluginStart];
218            }
219
220            if ([view respondsToSelector:@selector(setContainingWindow:)]) {
221                JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
222                [view setContainingWindow:[_documentView window]];
223            }
224        }
225    }
226}
227
228- (void)destroyPlugin:(NSView *)view
229{
230    if ([_views containsObject:view]) {
231        if (_started) {
232            if ([view respondsToSelector:@selector(webPlugInStop)]) {
233                JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
234                [view webPlugInStop];
235            } else if ([view respondsToSelector:@selector(pluginStop)]) {
236                JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
237                [view pluginStop];
238            }
239        }
240
241        if ([view respondsToSelector:@selector(webPlugInDestroy)]) {
242            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
243            [view webPlugInDestroy];
244        } else if ([view respondsToSelector:@selector(pluginDestroy)]) {
245            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
246            [view pluginDestroy];
247        }
248
249#if ENABLE(NETSCAPE_PLUGIN_API)
250        if (Frame* frame = core([self webFrame]))
251            frame->script()->cleanupScriptObjectsForPlugin(self);
252#endif
253
254        [pluginViews removeObject:view];
255        [[_documentView _webView] removePluginInstanceView:view];
256        [_views removeObject:view];
257    }
258}
259
260- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier
261{
262    [checkIdentifier cancel];
263    [_checksInProgress removeObject:checkIdentifier];
264}
265
266static void cancelOutstandingCheck(const void *item, void *context)
267{
268    [(id)item cancel];
269}
270
271- (void)_cancelOutstandingChecks
272{
273    if (_checksInProgress) {
274        CFSetApplyFunction((CFSetRef)_checksInProgress, cancelOutstandingCheck, NULL);
275        [_checksInProgress release];
276        _checksInProgress = nil;
277    }
278}
279
280- (void)destroyAllPlugins
281{
282    [self stopAllPlugins];
283
284    if ([_views count] > 0) {
285        LOG(Plugins, "destroying WebKit plugins: %@", [_views description]);
286    }
287
288    [self _cancelOutstandingChecks];
289
290    int i, count = [_views count];
291    for (i = 0; i < count; i++) {
292        id aView = [_views objectAtIndex:i];
293        if ([aView respondsToSelector:@selector(webPlugInDestroy)]) {
294            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
295            [aView webPlugInDestroy];
296        } else if ([aView respondsToSelector:@selector(pluginDestroy)]) {
297            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
298            [aView pluginDestroy];
299        }
300
301#if ENABLE(NETSCAPE_PLUGIN_API)
302        if (Frame* frame = core([self webFrame]))
303            frame->script()->cleanupScriptObjectsForPlugin(self);
304#endif
305
306        [pluginViews removeObject:aView];
307        [[_documentView _webView] removePluginInstanceView:aView];
308    }
309    [_views makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)];
310    [_views release];
311    _views = nil;
312
313    _documentView = nil;
314}
315
316- (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector
317{
318    WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:request target:target resultObject:obj selector:selector controller:self contextInfo:nil];
319    [_checksInProgress addObject:check];
320    [check start];
321
322    return check;
323}
324
325- (void)webPlugInContainerLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target
326{
327    if (!request) {
328        LOG_ERROR("nil URL passed");
329        return;
330    }
331    if (!_documentView) {
332        LOG_ERROR("could not load URL %@ because plug-in has already been destroyed", request);
333        return;
334    }
335    WebFrame *frame = [_dataSource webFrame];
336    if (!frame) {
337        LOG_ERROR("could not load URL %@ because plug-in has already been stopped", request);
338        return;
339    }
340    if (!target) {
341        target = @"_top";
342    }
343    NSString *JSString = [[request URL] _webkit_scriptIfJavaScriptURL];
344    if (JSString) {
345        if ([frame findFrameNamed:target] != frame) {
346            LOG_ERROR("JavaScript requests can only be made on the frame that contains the plug-in");
347            return;
348        }
349        [frame _stringByEvaluatingJavaScriptFromString:JSString];
350    } else {
351        if (!request) {
352            LOG_ERROR("could not load URL %@", [request URL]);
353            return;
354        }
355        core(frame)->loader()->load(request, target, false);
356    }
357}
358
359// For compatibility only.
360- (void)showURL:(NSURL *)URL inFrame:(NSString *)target
361{
362    [self webPlugInContainerLoadRequest:[NSURLRequest requestWithURL:URL] inFrame:target];
363}
364
365- (void)webPlugInContainerShowStatus:(NSString *)message
366{
367    if (!message) {
368        message = @"";
369    }
370    if (!_documentView) {
371        LOG_ERROR("could not show status message (%@) because plug-in has already been destroyed", message);
372        return;
373    }
374    WebView *v = [_dataSource _webView];
375    [[v _UIDelegateForwarder] webView:v setStatusText:message];
376}
377
378// For compatibility only.
379- (void)showStatus:(NSString *)message
380{
381    [self webPlugInContainerShowStatus:message];
382}
383
384- (NSColor *)webPlugInContainerSelectionColor
385{
386    bool primary = true;
387    if (Frame* frame = core([self webFrame]))
388        primary = frame->selection()->isFocusedAndActive();
389    return primary ? [NSColor selectedTextBackgroundColor] : [NSColor secondarySelectedControlColor];
390}
391
392// For compatibility only.
393- (NSColor *)selectionColor
394{
395    return [self webPlugInContainerSelectionColor];
396}
397
398- (WebFrame *)webFrame
399{
400    return [_dataSource webFrame];
401}
402
403- (WebView *)webView
404{
405    return [[self webFrame] webView];
406}
407
408- (NSString *)URLPolicyCheckReferrer
409{
410    NSURL *responseURL = [[[[self webFrame] _dataSource] response] URL];
411    ASSERT(responseURL);
412    return [responseURL _web_originalDataAsString];
413}
414
415- (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
416{
417    if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveResponse:)])
418        [pluginView webPlugInMainResourceDidReceiveResponse:response];
419    else {
420        // Cancel the load since this plug-in does its own loading.
421        // FIXME: See <rdar://problem/4258008> for a problem with this.
422        NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad
423                                                        contentURL:[response URL]
424                                                     pluginPageURL:nil
425                                                        pluginName:nil // FIXME: Get this from somewhere
426                                                          MIMEType:[response MIMEType]];
427        [_dataSource _documentLoader]->cancelMainResourceLoad(error);
428        [error release];
429    }
430}
431
432- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
433{
434    if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveData:)])
435        [pluginView webPlugInMainResourceDidReceiveData:data];
436}
437
438- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
439{
440    if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFailWithError:)])
441        [pluginView webPlugInMainResourceDidFailWithError:error];
442}
443
444- (void)pluginViewFinishedLoading:(NSView *)pluginView
445{
446    if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFinishLoading)])
447        [pluginView webPlugInMainResourceDidFinishLoading];
448}
449
450#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
451static WebCore::HTMLMediaElement* mediaProxyClient(DOMElement* element)
452{
453    if (!element) {
454        LOG_ERROR("nil element passed");
455        return nil;
456    }
457
458    Element* node = core(element);
459    if (!node || (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))) {
460        LOG_ERROR("invalid media element passed");
461        return nil;
462    }
463
464    return static_cast<WebCore::HTMLMediaElement*>(node);
465}
466
467- (void)_webPluginContainerSetMediaPlayerProxy:(WebMediaPlayerProxy *)proxy forElement:(DOMElement *)element
468{
469    WebCore::HTMLMediaElement* client = mediaProxyClient(element);
470    if (client)
471        client->setMediaPlayerProxy(proxy);
472}
473
474- (void)_webPluginContainerPostMediaPlayerNotification:(int)notification forElement:(DOMElement *)element
475{
476    WebCore::HTMLMediaElement* client = mediaProxyClient(element);
477    if (client)
478        client->deliverNotification((MediaPlayerProxyNotificationType)notification);
479}
480#endif
481
482@end
483