• 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)stopOnePlugin:(NSView *)view
139{
140    if ([view respondsToSelector:@selector(webPlugInStop)]) {
141        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
142        [view webPlugInStop];
143    } else if ([view respondsToSelector:@selector(pluginStop)]) {
144        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
145        [view pluginStop];
146    }
147}
148
149- (void)destroyOnePlugin:(NSView *)view
150{
151    if ([view respondsToSelector:@selector(webPlugInDestroy)]) {
152        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
153        [view webPlugInDestroy];
154    } else if ([view respondsToSelector:@selector(pluginDestroy)]) {
155        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
156        [view pluginDestroy];
157    }
158}
159
160- (void)startAllPlugins
161{
162    if (_started)
163        return;
164
165    if ([_views count] > 0)
166        LOG(Plugins, "starting WebKit plugins : %@", [_views description]);
167
168    int i, count = [_views count];
169    for (i = 0; i < count; i++) {
170        id aView = [_views objectAtIndex:i];
171        if ([aView respondsToSelector:@selector(webPlugInStart)]) {
172            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
173            [aView webPlugInStart];
174        } else if ([aView respondsToSelector:@selector(pluginStart)]) {
175            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
176            [aView pluginStart];
177        }
178    }
179    _started = YES;
180}
181
182- (void)stopAllPlugins
183{
184    if (!_started)
185        return;
186
187    if ([_views count] > 0) {
188        LOG(Plugins, "stopping WebKit plugins: %@", [_views description]);
189    }
190
191    int i, count = [_views count];
192    for (i = 0; i < count; i++)
193        [self stopOnePlugin:[_views objectAtIndex:i]];
194
195    _started = NO;
196}
197
198- (void)addPlugin:(NSView *)view
199{
200    if (!_documentView) {
201        LOG_ERROR("can't add a plug-in to a defunct WebPluginController");
202        return;
203    }
204
205    if (![_views containsObject:view]) {
206        [_views addObject:view];
207        [[_documentView _webView] addPluginInstanceView:view];
208
209        BOOL oldDefersCallbacks = [[self webView] defersCallbacks];
210        if (!oldDefersCallbacks)
211            [[self webView] setDefersCallbacks:YES];
212
213        LOG(Plugins, "initializing plug-in %@", view);
214        if ([view respondsToSelector:@selector(webPlugInInitialize)]) {
215            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
216            [view webPlugInInitialize];
217        } else if ([view respondsToSelector:@selector(pluginInitialize)]) {
218            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
219            [view pluginInitialize];
220        }
221
222        if (!oldDefersCallbacks)
223            [[self webView] setDefersCallbacks:NO];
224
225        if (_started) {
226            LOG(Plugins, "starting plug-in %@", view);
227            if ([view respondsToSelector:@selector(webPlugInStart)]) {
228                JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
229                [view webPlugInStart];
230            } else if ([view respondsToSelector:@selector(pluginStart)]) {
231                JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
232                [view pluginStart];
233            }
234
235            if ([view respondsToSelector:@selector(setContainingWindow:)]) {
236                JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
237                [view setContainingWindow:[_documentView window]];
238            }
239        }
240    }
241}
242
243- (void)destroyPlugin:(NSView *)view
244{
245    if ([_views containsObject:view]) {
246        if (_started)
247            [self stopOnePlugin:view];
248        [self destroyOnePlugin:view];
249
250#if ENABLE(NETSCAPE_PLUGIN_API)
251        if (Frame* frame = core([self webFrame]))
252            frame->script()->cleanupScriptObjectsForPlugin(self);
253#endif
254
255        [pluginViews removeObject:view];
256        [[_documentView _webView] removePluginInstanceView:view];
257        [_views removeObject:view];
258    }
259}
260
261- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier
262{
263    [checkIdentifier cancel];
264    [_checksInProgress removeObject:checkIdentifier];
265}
266
267static void cancelOutstandingCheck(const void *item, void *context)
268{
269    [(id)item cancel];
270}
271
272- (void)_cancelOutstandingChecks
273{
274    if (_checksInProgress) {
275        CFSetApplyFunction((CFSetRef)_checksInProgress, cancelOutstandingCheck, NULL);
276        [_checksInProgress release];
277        _checksInProgress = nil;
278    }
279}
280
281- (void)destroyAllPlugins
282{
283    [self stopAllPlugins];
284
285    if ([_views count] > 0) {
286        LOG(Plugins, "destroying WebKit plugins: %@", [_views description]);
287    }
288
289    [self _cancelOutstandingChecks];
290
291    int i, count = [_views count];
292    for (i = 0; i < count; i++) {
293        id aView = [_views objectAtIndex:i];
294        [self destroyOnePlugin:aView];
295
296#if ENABLE(NETSCAPE_PLUGIN_API)
297        if (Frame* frame = core([self webFrame]))
298            frame->script()->cleanupScriptObjectsForPlugin(self);
299#endif
300
301        [pluginViews removeObject:aView];
302        [[_documentView _webView] removePluginInstanceView:aView];
303    }
304    [_views makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)];
305    [_views release];
306    _views = nil;
307
308    _documentView = nil;
309}
310
311- (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector
312{
313    WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:request target:target resultObject:obj selector:selector controller:self contextInfo:nil];
314    [_checksInProgress addObject:check];
315    [check start];
316
317    return check;
318}
319
320- (void)webPlugInContainerLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target
321{
322    if (!request) {
323        LOG_ERROR("nil URL passed");
324        return;
325    }
326    if (!_documentView) {
327        LOG_ERROR("could not load URL %@ because plug-in has already been destroyed", request);
328        return;
329    }
330    WebFrame *frame = [_dataSource webFrame];
331    if (!frame) {
332        LOG_ERROR("could not load URL %@ because plug-in has already been stopped", request);
333        return;
334    }
335    if (!target) {
336        target = @"_top";
337    }
338    NSString *JSString = [[request URL] _webkit_scriptIfJavaScriptURL];
339    if (JSString) {
340        if ([frame findFrameNamed:target] != frame) {
341            LOG_ERROR("JavaScript requests can only be made on the frame that contains the plug-in");
342            return;
343        }
344        [frame _stringByEvaluatingJavaScriptFromString:JSString];
345    } else {
346        if (!request) {
347            LOG_ERROR("could not load URL %@", [request URL]);
348            return;
349        }
350        core(frame)->loader()->load(request, target, false);
351    }
352}
353
354// For compatibility only.
355- (void)showURL:(NSURL *)URL inFrame:(NSString *)target
356{
357    [self webPlugInContainerLoadRequest:[NSURLRequest requestWithURL:URL] inFrame:target];
358}
359
360- (void)webPlugInContainerShowStatus:(NSString *)message
361{
362    if (!message) {
363        message = @"";
364    }
365    if (!_documentView) {
366        LOG_ERROR("could not show status message (%@) because plug-in has already been destroyed", message);
367        return;
368    }
369    WebView *v = [_dataSource _webView];
370    [[v _UIDelegateForwarder] webView:v setStatusText:message];
371}
372
373// For compatibility only.
374- (void)showStatus:(NSString *)message
375{
376    [self webPlugInContainerShowStatus:message];
377}
378
379- (NSColor *)webPlugInContainerSelectionColor
380{
381    bool primary = true;
382    if (Frame* frame = core([self webFrame]))
383        primary = frame->selection()->isFocusedAndActive();
384    return primary ? [NSColor selectedTextBackgroundColor] : [NSColor secondarySelectedControlColor];
385}
386
387// For compatibility only.
388- (NSColor *)selectionColor
389{
390    return [self webPlugInContainerSelectionColor];
391}
392
393- (WebFrame *)webFrame
394{
395    return [_dataSource webFrame];
396}
397
398- (WebView *)webView
399{
400    return [[self webFrame] webView];
401}
402
403- (NSString *)URLPolicyCheckReferrer
404{
405    NSURL *responseURL = [[[[self webFrame] _dataSource] response] URL];
406    ASSERT(responseURL);
407    return [responseURL _web_originalDataAsString];
408}
409
410- (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
411{
412    if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveResponse:)])
413        [pluginView webPlugInMainResourceDidReceiveResponse:response];
414    else {
415        // Cancel the load since this plug-in does its own loading.
416        // FIXME: See <rdar://problem/4258008> for a problem with this.
417        NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad
418                                                        contentURL:[response URL]
419                                                     pluginPageURL:nil
420                                                        pluginName:nil // FIXME: Get this from somewhere
421                                                          MIMEType:[response MIMEType]];
422        [_dataSource _documentLoader]->cancelMainResourceLoad(error);
423        [error release];
424    }
425}
426
427- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
428{
429    if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveData:)])
430        [pluginView webPlugInMainResourceDidReceiveData:data];
431}
432
433- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
434{
435    if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFailWithError:)])
436        [pluginView webPlugInMainResourceDidFailWithError:error];
437}
438
439- (void)pluginViewFinishedLoading:(NSView *)pluginView
440{
441    if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFinishLoading)])
442        [pluginView webPlugInMainResourceDidFinishLoading];
443}
444
445#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
446static WebCore::HTMLMediaElement* mediaProxyClient(DOMElement* element)
447{
448    if (!element) {
449        LOG_ERROR("nil element passed");
450        return nil;
451    }
452
453    Element* node = core(element);
454    if (!node || (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))) {
455        LOG_ERROR("invalid media element passed");
456        return nil;
457    }
458
459    return static_cast<WebCore::HTMLMediaElement*>(node);
460}
461
462- (void)_webPluginContainerSetMediaPlayerProxy:(WebMediaPlayerProxy *)proxy forElement:(DOMElement *)element
463{
464    WebCore::HTMLMediaElement* client = mediaProxyClient(element);
465    if (client)
466        client->setMediaPlayerProxy(proxy);
467}
468
469- (void)_webPluginContainerPostMediaPlayerNotification:(int)notification forElement:(DOMElement *)element
470{
471    WebCore::HTMLMediaElement* client = mediaProxyClient(element);
472    if (client)
473        client->deliverNotification((MediaPlayerProxyNotificationType)notification);
474}
475#endif
476
477@end
478