Reviewed by Darin.
[WebKit-https.git] / WebKit / WebCoreSupport.subproj / WebBridge.m
1 /*
2  * Copyright (C) 2005 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 #import <WebKit/WebBridge.h>
30
31 #import <WebKit/WebAssertions.h>
32 #import <WebKit/WebBackForwardList.h>
33 #import <WebKit/WebBaseNetscapePluginView.h>
34 #import <WebKit/WebBasePluginPackage.h>
35 #import <WebKit/WebBaseResourceHandleDelegate.h>
36 #import "WebControllerSets.h"
37 #import <WebKit/WebDataSourcePrivate.h>
38 #import <WebKit/WebDefaultUIDelegate.h>
39 #import <WebKit/WebEditingDelegate.h>
40 #import <WebKit/WebFileButton.h>
41 #import <WebKit/WebFormDataStream.h>
42 #import <WebKit/WebFormDelegate.h>
43 #import <WebKit/WebFrameInternal.h>
44 #import <WebKit/WebFrameLoadDelegate.h>
45 #import <WebKit/WebFrameViewInternal.h>
46 #import <WebKit/WebHistoryItemPrivate.h>
47 #import <WebKit/WebHTMLRepresentationPrivate.h>
48 #import <WebKit/WebHTMLViewInternal.h>
49 #import <WebKit/WebImageView.h>
50 #import <WebKit/WebJavaPlugIn.h>
51 #import <WebKit/WebJavaScriptTextInputPanel.h>
52 #import <WebKit/WebKitErrorsPrivate.h>
53 #import <WebKit/WebKitLogging.h>
54 #import <WebKit/WebKitNSStringExtras.h>
55 #import <WebKit/WebKitStatisticsPrivate.h>
56 #import <WebKit/WebKitSystemBits.h>
57 #import <WebKit/WebNetscapePluginEmbeddedView.h>
58 #import <WebKit/WebNetscapePluginPackage.h>
59 #import <WebKit/WebNSObjectExtras.h>
60 #import <WebKit/WebNSURLExtras.h>
61 #import <WebKit/WebNSURLRequestExtras.h>
62 #import <WebKit/WebNullPluginView.h>
63 #import <WebKit/WebPlugin.h>
64 #import <WebKit/WebPluginController.h>
65 #import <WebKit/WebPluginDatabase.h>
66 #import <WebKit/WebPluginDocumentView.h>
67 #import <WebKit/WebPluginPackage.h>
68 #import <WebKit/WebPluginViewFactoryPrivate.h>
69 #import <WebKit/WebNetscapePluginDocumentView.h>
70 #import <WebKit/WebPreferencesPrivate.h>
71 #import <WebKit/WebResourcePrivate.h>
72 #import <WebKit/WebSubresourceClient.h>
73 #import <WebKit/WebViewInternal.h>
74 #import <WebKit/WebViewPrivate.h>
75 #import <WebKit/WebUIDelegatePrivate.h>
76 #import <WebKitSystemInterface.h>
77
78 #import <Foundation/NSURLRequest.h>
79 #import <Foundation/NSURLConnection.h>
80 #import <Foundation/NSURLResponse.h>
81
82 #import <WebKit/WebLocalizableStrings.h>
83
84 #import <JavaVM/jni.h>
85
86 #define KeyboardUIModeDidChangeNotification @"com.apple.KeyboardUIModeDidChange"
87 #define AppleKeyboardUIMode CFSTR("AppleKeyboardUIMode")
88 #define UniversalAccessDomain CFSTR("com.apple.universalaccess")
89
90 // For compatibility only with old SPI. 
91 @interface NSObject (OldWebPlugin)
92 - (void)setIsSelected:(BOOL)f;
93 @end
94
95 @interface NSApplication (DeclarationStolenFromAppKit)
96 - (void)_cycleWindowsReversed:(BOOL)reversed;
97 @end
98
99 @interface NSView (AppKitSecretsWebBridgeKnowsAbout)
100 - (NSView *)_findLastViewInKeyViewLoop;
101 @end
102
103 @interface NSURLResponse (FoundationSecretsWebBridgeKnowsAbout)
104 - (NSTimeInterval)_calculatedExpiration;
105 @end
106
107 @interface NSView (JavaPluginSecrets)
108 - (jobject)pollForAppletInWindow: (NSWindow *)window;
109 @end
110
111 NSString *WebPluginBaseURLKey =     @"WebPluginBaseURL";
112 NSString *WebPluginAttributesKey =  @"WebPluginAttributes";
113 NSString *WebPluginContainerKey =   @"WebPluginContainer";
114
115 @implementation WebBridge
116
117 - (id)initWithWebFrame:(WebFrame *)webFrame
118 {
119     self = [super init];
120
121     ++WebBridgeCount;
122     
123     WebView *webView = [webFrame webView];
124     
125     // Non-retained because data source owns representation owns bridge.
126     // But WebFrame will call close on us before it goes away, which
127     // guarantees we will not have a stale reference.
128     _frame = webFrame;
129
130     [self setName:[webFrame name]];
131     [self initializeSettings:[webView _settings]];
132     [self setTextSizeMultiplier:[webView textSizeMultiplier]];
133
134     return self;
135 }
136
137 - (void)fini
138 {
139     ASSERT(_frame == nil);
140
141     if (_keyboardUIModeAccessed) {
142         [[NSDistributedNotificationCenter defaultCenter] 
143             removeObserver:self name:KeyboardUIModeDidChangeNotification object:nil];
144         [[NSNotificationCenter defaultCenter] 
145             removeObserver:self name:WebPreferencesChangedNotification object:nil];
146     }
147
148     --WebBridgeCount;
149 }
150
151 - (void)dealloc
152 {
153     [lastDashboardRegions release];
154     
155     [self fini];
156     [super dealloc];
157 }
158
159 - (void)finalize
160 {
161     [self fini];
162     [super finalize];
163 }
164
165 - (WebFrame *)webFrame
166 {
167     return _frame;
168 }
169
170 - (NSArray *)childFrames
171 {
172     ASSERT(_frame != nil);
173     NSArray *frames = [_frame childFrames];
174     NSEnumerator *e = [frames objectEnumerator];
175     NSMutableArray *frameBridges = [NSMutableArray arrayWithCapacity:[frames count]];
176     WebFrame *childFrame;
177     while ((childFrame = [e nextObject])) {
178         id frameBridge = [childFrame _bridge];
179         if (frameBridge)
180             [frameBridges addObject:frameBridge];
181     }
182     return frameBridges;
183 }
184
185 - (WebCoreBridge *)mainFrame
186 {
187     ASSERT(_frame != nil);
188     return [[[_frame webView] mainFrame] _bridge];
189 }
190
191 - (WebCoreBridge *)findFrameNamed:(NSString *)name;
192 {
193     ASSERT(_frame != nil);
194     return [[_frame findFrameNamed:name] _bridge];
195 }
196
197 - (NSView *)documentView
198 {
199     ASSERT(_frame != nil);
200     return [[_frame frameView] documentView];
201 }
202
203 - (WebCoreBridge *)createWindowWithURL:(NSURL *)URL frameName:(NSString *)name
204 {
205     ASSERT(_frame != nil);
206
207     NSMutableURLRequest *request = nil;
208
209     if (URL != nil && ![URL _web_isEmpty]) {
210         request = [NSMutableURLRequest requestWithURL:URL];
211         [request _web_setHTTPReferrer:[self referrer]];
212     }
213
214     WebView *currentWebView = [_frame webView];
215     id wd = [currentWebView UIDelegate];
216     WebView *newWebView = nil;
217     
218     if ([wd respondsToSelector:@selector(webView:createWebViewWithRequest:)])
219         newWebView = [wd webView:currentWebView createWebViewWithRequest:request];
220     else
221         newWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:currentWebView createWebViewWithRequest:request];
222     [newWebView _setTopLevelFrameName:name];
223     return [[newWebView mainFrame] _bridge];
224 }
225
226 - (void)showWindow
227 {
228     WebView *wv = [_frame webView];
229     [[wv _UIDelegateForwarder] webViewShow: wv];
230 }
231
232 - (BOOL)areToolbarsVisible
233 {
234     ASSERT(_frame != nil);
235     WebView *wv = [_frame webView];
236     id wd = [wv UIDelegate];
237     if ([wd respondsToSelector: @selector(webViewAreToolbarsVisible:)])
238         return [wd webViewAreToolbarsVisible: wv];
239     return [[WebDefaultUIDelegate sharedUIDelegate] webViewAreToolbarsVisible:wv];
240 }
241
242 - (void)setToolbarsVisible:(BOOL)visible
243 {
244     ASSERT(_frame != nil);
245     WebView *wv = [_frame webView];
246     [[wv _UIDelegateForwarder] webView:wv setToolbarsVisible:visible];
247 }
248
249 - (BOOL)areScrollbarsVisible
250 {
251     ASSERT(_frame != nil);
252     return [[_frame frameView] allowsScrolling];
253 }
254
255 - (void)setScrollbarsVisible:(BOOL)visible
256 {
257     ASSERT(_frame != nil);
258     [[_frame frameView] setAllowsScrolling:visible];
259 }
260
261 - (BOOL)isStatusBarVisible
262 {
263     ASSERT(_frame != nil);
264     WebView *wv = [_frame webView];
265     id wd = [wv UIDelegate];
266     if ([wd respondsToSelector: @selector(webViewIsStatusBarVisible:)])
267         return [wd webViewIsStatusBarVisible:wv];
268     return [[WebDefaultUIDelegate sharedUIDelegate] webViewIsStatusBarVisible:wv];
269 }
270
271 - (void)setStatusBarVisible:(BOOL)visible
272 {
273     ASSERT(_frame != nil);
274     WebView *wv = [_frame webView];
275     [[wv _UIDelegateForwarder] webView:wv setStatusBarVisible:visible];
276 }
277
278 - (void)setWindowFrame:(NSRect)frameRect
279 {
280     ASSERT(_frame != nil);
281     WebView *webView = [_frame webView];
282     [[webView _UIDelegateForwarder] webView:webView setFrame:frameRect];
283 }
284
285 - (NSRect)windowFrame
286 {
287     ASSERT(_frame != nil);
288     WebView *webView = [_frame webView];
289     return [[webView _UIDelegateForwarder] webViewFrame:webView];
290 }
291
292 - (void)setWindowContentRect:(NSRect)contentRect
293 {
294     ASSERT(_frame != nil);
295     WebView *webView = [_frame webView];
296     [[webView _UIDelegateForwarder] webView:webView setFrame:contentRect];
297 }
298
299 - (NSRect)windowContentRect
300 {
301     ASSERT(_frame != nil);
302     WebView *webView = [_frame webView];
303     return [[webView _UIDelegateForwarder] webViewContentRect:webView];
304 }
305
306 - (void)setWindowIsResizable:(BOOL)resizable
307 {
308     ASSERT(_frame != nil);
309     WebView *webView = [_frame webView];
310     [[webView _UIDelegateForwarder] webView:webView setResizable:resizable];
311 }
312
313 - (BOOL)windowIsResizable
314 {
315     ASSERT(_frame != nil);
316     WebView *webView = [_frame webView];
317     return [[webView _UIDelegateForwarder] webViewIsResizable:webView];
318 }
319
320 - (NSResponder *)firstResponder
321 {
322     ASSERT(_frame != nil);
323     WebView *webView = [_frame webView];
324     return [[webView _UIDelegateForwarder] webViewFirstResponder:webView];
325 }
326
327 - (void)makeFirstResponder:(NSResponder *)view
328 {
329     ASSERT(_frame != nil);
330     WebView *webView = [_frame webView];
331     [webView _pushPerformingProgrammaticFocus];
332     [[webView _UIDelegateForwarder] webView:webView makeFirstResponder:view];
333     [webView _popPerformingProgrammaticFocus];
334 }
335
336 - (BOOL)wasFirstResponderAtMouseDownTime:(NSResponder *)responder;
337 {
338     ASSERT(_frame != nil);
339     NSView *documentView = [[_frame frameView] documentView];
340     if (![documentView isKindOfClass:[WebHTMLView class]]) {
341         return NO;
342     }
343     WebHTMLView *webHTMLView = (WebHTMLView *)documentView;
344     return [webHTMLView _wasFirstResponderAtMouseDownTime:responder];
345 }
346
347 - (void)closeWindowSoon
348 {
349     [[_frame webView] performSelector:@selector(_closeWindow) withObject:nil afterDelay:0.0];
350 }
351
352 - (NSWindow *)window
353 {
354     ASSERT(_frame != nil);
355     return [[_frame frameView] window];
356 }
357
358 - (void)runJavaScriptAlertPanelWithMessage:(NSString *)message
359 {
360     WebView *wv = [_frame webView];
361     [[wv _UIDelegateForwarder] webView:wv runJavaScriptAlertPanelWithMessage:message];
362 }
363
364 - (BOOL)runJavaScriptConfirmPanelWithMessage:(NSString *)message
365 {
366     WebView *wv = [_frame webView];
367     id wd = [wv UIDelegate];
368     if ([wd respondsToSelector: @selector(webView:runJavaScriptConfirmPanelWithMessage:)])
369         return [wd webView:wv runJavaScriptConfirmPanelWithMessage:message];
370     return [[WebDefaultUIDelegate sharedUIDelegate] webView:wv runJavaScriptConfirmPanelWithMessage:message];
371 }
372
373 - (BOOL)runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText returningText:(NSString **)result
374 {
375     WebView *wv = [_frame webView];
376     id wd = [wv UIDelegate];
377     if ([wd respondsToSelector: @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:)])
378         *result = [wd webView:wv runJavaScriptTextInputPanelWithPrompt:prompt defaultText:defaultText];
379     else
380         *result = [[WebDefaultUIDelegate sharedUIDelegate] webView:wv runJavaScriptTextInputPanelWithPrompt:prompt defaultText:defaultText];
381     return *result != nil;
382 }
383
384 - (void)addMessageToConsole:(NSDictionary *)message
385 {
386     WebView *wv = [_frame webView];
387     id wd = [wv UIDelegate];
388     if ([wd respondsToSelector: @selector(webView:addMessageToConsole:)])
389         [wd webView:wv addMessageToConsole:message];
390 }
391
392 - (NSView <WebCoreFileButton> *)fileButtonWithDelegate:(id <WebCoreFileButtonDelegate>)delegate
393 {
394     return [[WebFileButton alloc] initWithBridge:self delegate:delegate];
395 }
396
397 - (void)runOpenPanelForFileButtonWithResultListener:(id<WebOpenPanelResultListener>)resultListener
398 {
399     WebView *wv = [_frame webView];
400     [[wv _UIDelegateForwarder] webView:wv runOpenPanelForFileButtonWithResultListener:resultListener];
401 }
402
403
404 - (WebDataSource *)dataSource
405 {
406     ASSERT(_frame != nil);
407     WebDataSource *dataSource = [_frame dataSource];
408
409     ASSERT(dataSource != nil);
410     ASSERT([dataSource _isCommitted]);
411
412     return dataSource;
413 }
414
415 - (void)setTitle:(NSString *)title
416 {
417     [[self dataSource] _setTitle:[title _webkit_stringByCollapsingNonPrintingCharacters]];
418 }
419
420 - (void)setStatusText:(NSString *)status
421 {
422     ASSERT(_frame != nil);
423     WebView *wv = [_frame webView];
424     [[wv _UIDelegateForwarder] webView:wv setStatusText:status];
425 }
426
427 - (void)receivedData:(NSData *)data textEncodingName:(NSString *)textEncodingName
428 {
429     // Set the encoding. This only needs to be done once, but it's harmless to do it again later.
430     NSString *encoding = [[self dataSource] _overrideEncoding];
431     BOOL userChosen = encoding != nil;
432     if (encoding == nil) {
433         encoding = textEncodingName;
434     }
435     [self setEncoding:encoding userChosen:userChosen];
436
437     [self addData:data];
438 }
439
440 - (id <WebCoreResourceHandle>)startLoadingResource:(id <WebCoreResourceLoader>)resourceLoader withURL:(NSURL *)URL customHeaders:(NSDictionary *)customHeaders
441 {
442     // If we are no longer attached to a WebView, this must be an attempted load from an
443     // onUnload handler, so let's just block it.
444     if ([[self dataSource] _webView] == nil) {
445         return nil;
446     }
447
448     // Since this is a subresource, we can load any URL (we ignore the return value).
449     // But we still want to know whether we should hide the referrer or not, so we call the canLoadURL method.
450     BOOL hideReferrer;
451     [self canLoadURL:URL fromReferrer:[self referrer] hideReferrer:&hideReferrer];
452
453     return [WebSubresourceClient startLoadingResource:resourceLoader
454                                               withURL:URL
455                                         customHeaders:customHeaders
456                                              referrer:(hideReferrer ? nil : [self referrer])
457                                         forDataSource:[self dataSource]];
458 }
459
460 - (id <WebCoreResourceHandle>)startLoadingResource:(id <WebCoreResourceLoader>)resourceLoader withURL:(NSURL *)URL customHeaders:(NSDictionary *)customHeaders postData:(NSArray *)postData
461 {
462     // If we are no longer attached to a WebView, this must be an attempted load from an
463     // onUnload handler, so let's just block it.
464     if ([[self dataSource] _webView] == nil) {
465         return nil;
466     }
467
468     // Since this is a subresource, we can load any URL (we ignore the return value).
469     // But we still want to know whether we should hide the referrer or not, so we call the canLoadURL method.
470     BOOL hideReferrer;
471     [self canLoadURL:URL fromReferrer:[self referrer] hideReferrer:&hideReferrer];
472
473     return [WebSubresourceClient startLoadingResource:resourceLoader
474                                               withURL:URL
475                                         customHeaders:customHeaders
476                                              postData:postData
477                                              referrer:(hideReferrer ? nil : [self referrer])
478                                         forDataSource:[self dataSource]];
479 }
480
481 - (void)objectLoadedFromCacheWithURL:(NSURL *)URL response:(NSURLResponse *)response data:(NSData *)data
482 {
483     // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load.
484     NSError *error;
485     id identifier;
486     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL];
487     [_frame _requestFromDelegateForRequest:request identifier:&identifier error:&error];    
488     [_frame _saveResourceAndSendRemainingDelegateMessagesWithRequest:request identifier:identifier response:response data:data error:error];
489     [request release];
490 }
491
492 - (NSData *)syncLoadResourceWithURL:(NSURL *)URL customHeaders:(NSDictionary *)requestHeaders postData:(NSArray *)postData finalURL:(NSURL **)finalURL responseHeaders:(NSDictionary **)responseHeaderDict statusCode:(int *)statusCode
493 {
494     // Since this is a subresource, we can load any URL (we ignore the return value).
495     // But we still want to know whether we should hide the referrer or not, so we call the canLoadURL method.
496     BOOL hideReferrer;
497     [self canLoadURL:URL fromReferrer:[self referrer] hideReferrer:&hideReferrer];
498
499     NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
500
501     if (postData) {
502         [request setHTTPMethod:@"POST"];
503         webSetHTTPBody(request, postData);
504     }
505
506     NSEnumerator *e = [requestHeaders keyEnumerator];
507     NSString *key;
508     while ((key = (NSString *)[e nextObject]) != nil) {
509         [request addValue:[requestHeaders objectForKey:key] forHTTPHeaderField:key];
510     }
511     
512     // Never use cached data for these requests (xmlhttprequests).
513     [request setCachePolicy:[[[self dataSource] request] cachePolicy]];
514     if (!hideReferrer)
515         [request _web_setHTTPReferrer:[self referrer]];
516     
517     WebView *webView = [_frame webView];
518     [request setMainDocumentURL:[[[[webView mainFrame] dataSource] request] URL]];
519     [request _web_setHTTPUserAgent:[webView userAgentForURL:[request URL]]];
520     
521     NSError *error = nil;
522     id identifier = nil;    
523     NSURLRequest *newRequest = [_frame _requestFromDelegateForRequest:request identifier:&identifier error:&error];
524     
525     NSURLResponse *response = nil;
526     NSData *result = nil;
527     if (error == nil) {
528         ASSERT(newRequest != nil);
529         result = [NSURLConnection sendSynchronousRequest:newRequest returningResponse:&response error:&error];
530     }
531     
532     if (error == nil) {
533         *finalURL = [response URL];
534         if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
535             NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; 
536             *responseHeaderDict = [httpResponse allHeaderFields];
537             *statusCode = [httpResponse statusCode];
538         } else {
539             *responseHeaderDict = [NSDictionary dictionary];
540             *statusCode = 200;
541         }
542     } else {
543         *finalURL = URL;
544         *responseHeaderDict = [NSDictionary dictionary];
545         if ([error domain] == NSURLErrorDomain) {
546             *statusCode = [error code];
547         } else {
548             *statusCode = 404;
549         }
550     }
551     
552     [_frame _saveResourceAndSendRemainingDelegateMessagesWithRequest:newRequest identifier:identifier response:response data:result error:error];
553     [request release];
554     
555     return result;
556 }
557
558 - (BOOL)isReloading
559 {
560     return [[[self dataSource] request] cachePolicy] == NSURLRequestReloadIgnoringCacheData;
561 }
562
563 // We would like a better value for a maximum time_t,
564 // but there is no way to do that in C with any certainty.
565 // INT_MAX should work well enough for our purposes.
566 #define MAX_TIME_T ((time_t)INT_MAX)    
567
568 - (time_t)expiresTimeForResponse:(NSURLResponse *)response
569 {
570     // This check can be removed when the new Foundation method
571     // has been around long enough for everyone to have it.
572     if ([response respondsToSelector:@selector(_calculatedExpiration)]) {
573         NSTimeInterval expiration = [response _calculatedExpiration];
574         expiration += kCFAbsoluteTimeIntervalSince1970;
575         return expiration > MAX_TIME_T ? MAX_TIME_T : expiration;
576     }
577
578     // Fall back to the older calculation
579     time_t now = time(NULL);
580     NSTimeInterval lifetime = WKGetNSURLResponseFreshnessLifetime(response);
581     if (lifetime < 0)
582         lifetime = 0;
583     
584     if (now + lifetime > MAX_TIME_T)
585         return MAX_TIME_T;
586     
587     return now + lifetime;
588 }
589
590 - (void)reportClientRedirectToURL:(NSURL *)URL delay:(NSTimeInterval)seconds fireDate:(NSDate *)date lockHistory:(BOOL)lockHistory isJavaScriptFormAction:(BOOL)isJavaScriptFormAction
591 {
592     [_frame _clientRedirectedTo:URL delay:seconds fireDate:date lockHistory:lockHistory isJavaScriptFormAction:(BOOL)isJavaScriptFormAction];
593 }
594
595 - (void)reportClientRedirectCancelled:(BOOL)cancelWithLoadInProgress
596 {
597     [_frame _clientRedirectCancelled:cancelWithLoadInProgress];
598 }
599
600 - (void)close
601 {
602     _frame = nil;
603 }
604
605 - (void)focusWindow
606 {
607     [[[_frame webView] _UIDelegateForwarder] webViewFocus:[_frame webView]];
608 }
609
610 - (void)unfocusWindow
611 {
612     if ([[self window] isKeyWindow] || [[[self window] attachedSheet] isKeyWindow]) {
613         [NSApp _cycleWindowsReversed:FALSE];
614     }
615 }
616
617 - (void)setIconURL:(NSURL *)URL
618 {
619     [[self dataSource] _setIconURL:URL];
620 }
621
622 - (void)setIconURL:(NSURL *)URL withType:(NSString *)type
623 {
624     [[self dataSource] _setIconURL:URL withType:type];
625 }
626
627 - (BOOL)canTargetLoadInFrame:(WebFrame *)targetFrame
628 {
629     // This method prevents this exploit:
630     // <rdar://problem/3715785> multiple frame injection vulnerability reported by Secunia, affects almost all browsers
631     
632     // don't mess with navigation purely within the same frame
633     if ([[self webFrame] webView] == [targetFrame webView]) {
634         return YES;
635     }
636
637     // Normally, domain should be called on the DOMDocument since it is a DOM method, but this fix is needed for
638     // Jaguar as well where the DOM API doesn't exist.
639     NSString *thisDomain = [self domain];
640     if ([thisDomain length] == 0) {
641         // Allow if the request is made from a local file.
642         return YES;
643     }
644     
645     WebFrame *parentFrame = [targetFrame parentFrame];
646     if (parentFrame == nil) {
647         // Allow if target is an entire window.
648         return YES;
649     }
650     
651     NSString *parentDomain = [[parentFrame _bridge] domain];
652     if (parentDomain != nil && [thisDomain _webkit_isCaseInsensitiveEqualToString:parentDomain]) {
653         // Allow if the domain of the parent of the targeted frame equals this domain.
654         return YES;
655     }
656
657     return NO;
658 }
659
660 - (void)loadURL:(NSURL *)URL referrer:(NSString *)referrer reload:(BOOL)reload userGesture:(BOOL)forUser target:(NSString *)target triggeringEvent:(NSEvent *)event form:(DOMElement *)form formValues:(NSDictionary *)values
661 {
662     BOOL hideReferrer;
663     if (![self canLoadURL:URL fromReferrer:referrer hideReferrer:&hideReferrer])
664         return;
665
666     if ([target length] == 0) {
667         target = nil;
668     }
669
670     WebFrame *targetFrame = [_frame findFrameNamed:target];
671     if (![self canTargetLoadInFrame:targetFrame]) {
672         return;
673     }
674     
675     WebFrameLoadType loadType;
676     
677     if (reload)
678         loadType = WebFrameLoadTypeReload;
679     else if (!forUser)
680         loadType = WebFrameLoadTypeInternal;
681     else
682         loadType = WebFrameLoadTypeStandard;
683     [_frame _loadURL:URL referrer:(hideReferrer ? nil : referrer) loadType:loadType target:target triggeringEvent:event form:form formValues:values];
684
685     if (targetFrame != nil && _frame != targetFrame) {
686         [[targetFrame _bridge] focusWindow];
687     }
688 }
689
690 - (void)postWithURL:(NSURL *)URL referrer:(NSString *)referrer target:(NSString *)target data:(NSArray *)postData contentType:(NSString *)contentType triggeringEvent:(NSEvent *)event form:(DOMElement *)form formValues:(NSDictionary *)values
691 {
692     BOOL hideReferrer;
693     if (![self canLoadURL:URL fromReferrer:referrer hideReferrer:&hideReferrer])
694         return;
695
696     if ([target length] == 0) {
697         target = nil;
698     }
699
700     WebFrame *targetFrame = [_frame findFrameNamed:target];
701     if (![self canTargetLoadInFrame:targetFrame]) {
702         return;
703     }
704
705     [_frame _postWithURL:URL referrer:(hideReferrer ? nil : referrer) target:target data:postData contentType:contentType triggeringEvent:event form:form formValues:values];
706
707     if (targetFrame != nil && _frame != targetFrame) {
708         [[targetFrame _bridge] focusWindow];
709     }
710 }
711
712 - (NSString *)generateFrameName
713 {
714     return [_frame _generateFrameName];
715 }
716
717 - (WebCoreBridge *)createChildFrameNamed:(NSString *)frameName 
718                                  withURL:(NSURL *)URL
719                                 referrer:(NSString *)referrer
720                               renderPart:(KHTMLRenderPart *)childRenderPart
721                          allowsScrolling:(BOOL)allowsScrolling 
722                              marginWidth:(int)width
723                             marginHeight:(int)height
724 {
725     BOOL hideReferrer;
726     if (![self canLoadURL:URL fromReferrer:referrer hideReferrer:&hideReferrer])
727         return nil;
728
729     ASSERT(_frame != nil);
730     WebFrame *newFrame = [[_frame webView] _createFrameNamed:frameName inParent:_frame allowsScrolling:allowsScrolling];
731     if (newFrame == nil) {
732         return nil;
733     }
734     
735     [[newFrame _bridge] setRenderPart:childRenderPart];
736
737     [[newFrame frameView] _setMarginWidth:width];
738     [[newFrame frameView] _setMarginHeight:height];
739
740     [_frame _loadURL:URL referrer:(hideReferrer ? nil : referrer) intoChild:newFrame];
741
742     return [newFrame _bridge];
743 }
744
745 - (void)saveDocumentState: (NSArray *)documentState
746 {
747     WebHistoryItem *item = [_frame _itemForSavingDocState];
748     LOG(Loading, "%@: saving form state from to 0x%x", [_frame name], item);
749     if (item) {
750         [item setDocumentState: documentState];
751         // You might think we could save the scroll state here too, but unfortunately this
752         // often gets called after WebFrame::_transitionToCommitted has restored the scroll
753         // position of the next document.
754     }
755 }
756
757 - (NSArray *)documentState
758 {
759     LOG(Loading, "%@: restoring form state from item 0x%x", [_frame name], [_frame _itemForRestoringDocState]);
760     return [[_frame _itemForRestoringDocState] documentState];
761 }
762
763 - (BOOL)saveDocumentToPageCache: documentInfo
764 {
765     WebHistoryItem *item = [_frame _itemForSavingDocState];
766     if (![item hasPageCache]) {
767         return false;
768     }
769     [[item pageCache] setObject: documentInfo forKey: WebCorePageCacheStateKey];
770     return true;
771 }
772
773 - (NSString *)userAgentForURL:(NSURL *)URL
774 {
775     return [[_frame webView] userAgentForURL:URL];
776 }
777
778 - (BOOL)inNextKeyViewOutsideWebFrameViews
779 {
780     return _inNextKeyViewOutsideWebFrameViews;
781 }
782
783 - (NSView *)_nextKeyViewOutsideWebFrameViewsWithValidityCheck:(BOOL)mustBeValid
784 {
785     if (_inNextKeyViewOutsideWebFrameViews) {
786         // We should never get here, but unrepro bug 3997185 says we sometimes do.
787         // So we'll fail on debug builds to try to catch the problem, but on
788         // deployment builds we'll return nil to avoid recursing forever.
789         ASSERT_NOT_REACHED();
790         return nil;
791     }
792     
793     _inNextKeyViewOutsideWebFrameViews = YES;
794     WebView *webView = [_frame webView];
795     // Do not ask webView for its next key view, but rather, ask it for 
796     // the next key view of the last view in its key view loop.
797     // Doing so gives us the correct answer as calculated by AppKit, 
798     // and makes HTML views behave like other views.
799     NSView *lastViewInLoop = [webView _findLastViewInKeyViewLoop];
800     NSView *nextKeyView = mustBeValid ? [lastViewInLoop nextValidKeyView] : [lastViewInLoop nextKeyView];
801     _inNextKeyViewOutsideWebFrameViews = NO;
802     return nextKeyView;
803 }
804
805 - (NSView *)nextKeyViewOutsideWebFrameViews
806 {
807     return [self _nextKeyViewOutsideWebFrameViewsWithValidityCheck:NO];
808 }
809
810 - (NSView *)nextValidKeyViewOutsideWebFrameViews
811 {
812     return [self _nextKeyViewOutsideWebFrameViewsWithValidityCheck:YES];
813 }
814
815 - (NSView *)previousKeyViewOutsideWebFrameViews
816 {
817     WebView *webView = [_frame webView];
818     NSView *previousKeyView = [webView previousKeyView];
819     return previousKeyView;
820 }
821
822 - (BOOL)defersLoading
823 {
824     return [[_frame webView] defersCallbacks];
825 }
826
827 - (void)setDefersLoading:(BOOL)defers
828 {
829     [[_frame webView] setDefersCallbacks:defers];
830 }
831
832 - (void)setNeedsReapplyStyles
833 {
834     NSView <WebDocumentView> *view = [[_frame frameView] documentView];
835     if ([view isKindOfClass:[WebHTMLView class]]) {
836         [(WebHTMLView *)view setNeedsToApplyStyles:YES];
837         [view setNeedsLayout:YES];
838         [view setNeedsDisplay:YES];
839     }
840 }
841
842 - (void)tokenizerProcessedData
843 {
844     [_frame _checkLoadComplete];
845 }
846
847 // OK to be an NSString rather than an NSURL.
848 // This URL is only used for coloring visited links.
849 - (NSString *)requestedURLString
850 {
851     return [[[[self dataSource] request] URL] _web_originalDataAsString];
852 }
853
854 - (NSString *)incomingReferrer
855 {
856     return [[[self dataSource] request] _web_HTTPReferrer];
857 }
858
859 - (NSView *)pluginViewWithPackage:(WebPluginPackage *)pluginPackage
860                    attributeNames:(NSArray *)attributeNames
861                   attributeValues:(NSArray *)attributeValues
862                           baseURL:(NSURL *)baseURL
863 {
864     WebHTMLView *docView = (WebHTMLView *)[[_frame frameView] documentView];
865     ASSERT([docView isKindOfClass:[WebHTMLView class]]);
866         
867     WebPluginController *pluginController = [docView _pluginController];
868     
869     // Store attributes in a dictionary so they can be passed to WebPlugins.
870     NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
871     unsigned count = [attributeNames count];
872     unsigned i;
873     for (i = 0; i < count; i++) {
874         [attributes setObject:[attributeValues objectAtIndex:i] forKey:[attributeNames objectAtIndex:i]];
875     }    
876     
877     [pluginPackage load];
878     Class viewFactory = [pluginPackage viewFactory];
879     
880     NSView *view = nil;
881     NSDictionary *arguments = nil;
882     
883     if ([viewFactory respondsToSelector:@selector(plugInViewWithArguments:)]) {
884         arguments = [NSDictionary dictionaryWithObjectsAndKeys:
885             baseURL, WebPlugInBaseURLKey,
886             attributes, WebPlugInAttributesKey,
887             pluginController, WebPlugInContainerKey,
888             [NSNumber numberWithInt:WebPlugInModeEmbed], WebPlugInModeKey,
889             nil];
890         LOG(Plugins, "arguments:\n%@", arguments);
891     } else if ([viewFactory respondsToSelector:@selector(pluginViewWithArguments:)]) {
892         arguments = [NSDictionary dictionaryWithObjectsAndKeys:
893             baseURL, WebPluginBaseURLKey,
894             attributes, WebPluginAttributesKey,
895             pluginController, WebPluginContainerKey,
896             nil];
897         LOG(Plugins, "arguments:\n%@", arguments);
898     }
899     
900     view = [WebPluginController plugInViewWithArguments:arguments fromPluginPackage:pluginPackage];
901     
902     [attributes release];
903     return view;
904 }
905
906 - (NSString *)valueForKey:(NSString *)key keys:(NSArray *)keys values:(NSArray *)values
907 {
908     unsigned count = [keys count];
909     unsigned i;
910     for (i = 0; i < count; i++) {
911         if ([[keys objectAtIndex:i] _webkit_isCaseInsensitiveEqualToString:key]) {
912             return [values objectAtIndex:i];
913         }
914     }
915     return nil;
916 }
917
918 - (NSView *)viewForPluginWithURL:(NSURL *)URL
919                   attributeNames:(NSArray *)attributeNames
920                  attributeValues:(NSArray *)attributeValues
921                         MIMEType:(NSString *)MIMEType
922 {
923     BOOL hideReferrer;
924     if (![self canLoadURL:URL fromReferrer:[self referrer] hideReferrer:&hideReferrer])
925         return nil;
926
927     ASSERT([attributeNames count] == [attributeValues count]);
928
929     WebBasePluginPackage *pluginPackage = nil;
930     NSView *view = nil;
931     int errorCode = 0;
932     
933     if ([MIMEType length] != 0) {
934         pluginPackage = [[WebPluginDatabase installedPlugins] pluginForMIMEType:MIMEType];
935     } else {
936         MIMEType = nil;
937     }
938     
939     NSString *extension = [[URL path] pathExtension];
940     if (!pluginPackage && [extension length] != 0) {
941         pluginPackage = [[WebPluginDatabase installedPlugins] pluginForExtension:extension];
942         if (pluginPackage) {
943             NSString *newMIMEType = [pluginPackage MIMETypeForExtension:extension];
944             if ([newMIMEType length] != 0) {
945                 MIMEType = newMIMEType;
946             }
947         }
948     }
949
950     NSURL *baseURL = [self baseURL];
951     if (pluginPackage) {
952         if ([pluginPackage isKindOfClass:[WebPluginPackage class]]) {
953             view = [self pluginViewWithPackage:(WebPluginPackage *)pluginPackage
954                                 attributeNames:attributeNames
955                                attributeValues:attributeValues
956                                        baseURL:baseURL];
957         } else if ([pluginPackage isKindOfClass:[WebNetscapePluginPackage class]]) {
958             view = [[[WebNetscapePluginEmbeddedView alloc] initWithFrame:NSZeroRect
959                                                                   plugin:(WebNetscapePluginPackage *)pluginPackage
960                                                                      URL:URL
961                                                                  baseURL:baseURL
962                                                                 MIMEType:MIMEType
963                                                            attributeKeys:attributeNames
964                                                          attributeValues:attributeValues] autorelease];
965         } else {
966             ASSERT_NOT_REACHED();
967         }
968     } else {
969         errorCode = WebKitErrorCannotFindPlugIn;
970     }
971
972     if (!errorCode && !view) {
973         errorCode = WebKitErrorCannotLoadPlugIn;
974     }
975
976     if (errorCode) {
977         NSString *pluginPage = [self valueForKey:@"pluginspage" keys:attributeNames values:attributeValues];
978         NSURL *pluginPageURL = pluginPage != nil ? [self URLWithAttributeString:pluginPage] : nil;
979         NSError *error = [[NSError alloc] _initWithPluginErrorCode:errorCode
980                                                         contentURL:URL
981                                                      pluginPageURL:pluginPageURL
982                                                         pluginName:[pluginPackage name]
983                                                           MIMEType:MIMEType];
984         view = [[[WebNullPluginView alloc] initWithFrame:NSZeroRect error:error] autorelease];
985         [error release];
986     }
987     
988     ASSERT(view);
989     return view;
990 }
991
992 - (NSView *)viewForJavaAppletWithFrame:(NSRect)theFrame
993                         attributeNames:(NSArray *)attributeNames
994                        attributeValues:(NSArray *)attributeValues
995                                baseURL:(NSURL *)baseURL;
996 {
997     NSString *MIMEType = @"application/x-java-applet";
998     WebBasePluginPackage *pluginPackage;
999     NSView *view = nil;
1000     
1001     pluginPackage = [[WebPluginDatabase installedPlugins] pluginForMIMEType:MIMEType];
1002
1003     if (pluginPackage) {
1004         if ([pluginPackage isKindOfClass:[WebPluginPackage class]]) {
1005             // For some reason, the Java plug-in requires that we pass the dimension of the plug-in as attributes.
1006             NSMutableArray *names = [attributeNames mutableCopy];
1007             NSMutableArray *values = [attributeValues mutableCopy];
1008             if ([self valueForKey:@"width" keys:attributeNames values:attributeValues] == nil) {
1009                 [names addObject:@"width"];
1010                 [values addObject:[NSString stringWithFormat:@"%d", (int)theFrame.size.width]];
1011             }
1012             if ([self valueForKey:@"height" keys:attributeNames values:attributeValues] == nil) {
1013                 [names addObject:@"height"];
1014                 [values addObject:[NSString stringWithFormat:@"%d", (int)theFrame.size.height]];
1015             }
1016             view = [self pluginViewWithPackage:(WebPluginPackage *)pluginPackage
1017                                 attributeNames:names
1018                                attributeValues:values
1019                                        baseURL:baseURL];
1020             [names release];
1021             [values release];
1022         } else if ([pluginPackage isKindOfClass:[WebNetscapePluginPackage class]]) {
1023             view = [[[WebNetscapePluginEmbeddedView alloc] initWithFrame:theFrame
1024                                                                   plugin:(WebNetscapePluginPackage *)pluginPackage
1025                                                                      URL:nil
1026                                                                  baseURL:baseURL
1027                                                                 MIMEType:MIMEType
1028                                                            attributeKeys:attributeNames
1029                                                          attributeValues:attributeValues] autorelease];
1030         } else {
1031             ASSERT_NOT_REACHED();
1032         }
1033     }
1034
1035     if (!view) {
1036         NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorJavaUnavailable
1037                                                         contentURL:nil
1038                                                      pluginPageURL:nil
1039                                                         pluginName:[pluginPackage name]
1040                                                           MIMEType:MIMEType];
1041         view = [[[WebNullPluginView alloc] initWithFrame:theFrame error:error] autorelease];
1042         [error release];
1043     }
1044
1045     ASSERT(view);
1046
1047     return view;
1048 }
1049
1050 #ifndef NDEBUG
1051 static BOOL loggedObjectCacheSize = NO;
1052 #endif
1053
1054 - (WebPreferences *)_preferences
1055 {
1056     WebPreferences *prefs = [[_frame webView] preferences];
1057     if (prefs == nil) {
1058         prefs = [WebPreferences standardPreferences];
1059     }
1060     return prefs;
1061 }
1062
1063 -(int)getObjectCacheSize
1064 {
1065     vm_size_t memSize = WebSystemMainMemory();
1066     int cacheSize = [[self _preferences] _objectCacheSize];
1067     int multiplier = 1;
1068     if (memSize >= 1024 * 1024 * 1024)
1069         multiplier = 4;
1070     else if (memSize >= 512 * 1024 * 1024)
1071         multiplier = 2;
1072
1073 #ifndef NDEBUG
1074     if (!loggedObjectCacheSize){
1075         LOG (CacheSizes, "Object cache size set to %d bytes.", cacheSize * multiplier);
1076         loggedObjectCacheSize = YES;
1077     }
1078 #endif
1079
1080     return cacheSize * multiplier;
1081 }
1082
1083 - (ObjectElementType)determineObjectFromMIMEType:(NSString*)MIMEType URL:(NSURL*)URL
1084 {
1085     if ([MIMEType length] == 0) {
1086         // Try to guess the MIME type based off the extension.
1087         NSString *extension = [[URL path] pathExtension];
1088         if ([extension length] > 0) {
1089             MIMEType = WKGetMIMETypeForExtension(extension);
1090             if ([MIMEType length] == 0 && [[WebPluginDatabase installedPlugins] pluginForExtension:extension])
1091                 // If no MIME type is specified, use a plug-in if we have one that can handle the extension.
1092                 return ObjectElementPlugin;
1093         }
1094     }
1095
1096     if ([MIMEType length] == 0)
1097         return ObjectElementFrame; // Go ahead and hope that we can display the content.
1098                 
1099     Class viewClass = [WebFrameView _viewClassForMIMEType:MIMEType];
1100     if (!viewClass)
1101         // Nothing is registered at all.
1102         return ObjectElementNone;
1103     
1104     if ([viewClass isSubclassOfClass:[WebImageView class]])
1105         return ObjectElementImage;
1106     
1107     // If we're a supported type other than a plugin, we want to make a frame.
1108     // Ultimately we should just use frames for all mime types (plugins and HTML/XML/text documents),
1109     // but for now we're burdened with making a distinction between the two.
1110     if ([viewClass isSubclassOfClass:[WebNetscapePluginDocumentView class]] || [viewClass isSubclassOfClass:[WebPluginDocumentView class]])
1111         return ObjectElementPlugin;
1112     return ObjectElementFrame;
1113 }
1114
1115 - (void)loadEmptyDocumentSynchronously
1116 {
1117     NSURL *url = [[NSURL alloc] initWithString:@""];
1118     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
1119     [_frame loadRequest:request];
1120     [request release];
1121     [url release];
1122 }
1123
1124 - (NSString *)MIMETypeForPath:(NSString *)path
1125 {
1126     ASSERT(path);
1127     NSString *extension = [path pathExtension];
1128     NSString *type = WKGetMIMETypeForExtension(extension);
1129     return [type length] == 0 ? @"application/octet-stream" : type;
1130 }
1131
1132 - (void)allowDHTMLDrag:(BOOL *)flagDHTML UADrag:(BOOL *)flagUA
1133 {
1134     WebHTMLView *docView = (WebHTMLView *)[[_frame frameView] documentView];
1135     ASSERT([docView isKindOfClass:[WebHTMLView class]]);
1136     unsigned int mask = [docView _delegateDragSourceActionMask];
1137     *flagDHTML = (mask & WebDragSourceActionDHTML) != 0;
1138     *flagUA = ((mask & WebDragSourceActionImage) || (mask & WebDragSourceActionLink) || (mask & WebDragSourceActionSelection));
1139 }
1140
1141 - (BOOL)startDraggingImage:(NSImage *)dragImage at:(NSPoint)dragLoc operation:(NSDragOperation)op event:(NSEvent *)event sourceIsDHTML:(BOOL)flag DHTMLWroteData:(BOOL)dhtmlWroteData
1142 {
1143     WebHTMLView *docView = (WebHTMLView *)[[_frame frameView] documentView];
1144     ASSERT([docView isKindOfClass:[WebHTMLView class]]);
1145     return [docView _startDraggingImage:dragImage at:dragLoc operation:op event:event sourceIsDHTML:flag DHTMLWroteData:dhtmlWroteData];
1146 }
1147
1148 - (void)handleAutoscrollForMouseDragged:(NSEvent *)event;
1149 {
1150     WebHTMLView *docView = (WebHTMLView *)[[_frame frameView] documentView];
1151
1152     ASSERT([docView isKindOfClass:[WebHTMLView class]]);
1153
1154     [docView _handleAutoscrollForMouseDragged:event];
1155 }
1156
1157 - (BOOL)mayStartDragAtEventLocation:(NSPoint)location
1158 {
1159     WebHTMLView *docView = (WebHTMLView *)[[_frame frameView] documentView];
1160
1161     ASSERT([docView isKindOfClass:[WebHTMLView class]]);
1162
1163     return [docView _mayStartDragAtEventLocation:location];
1164 }
1165
1166 - (BOOL)selectWordBeforeMenuEvent
1167 {
1168     return [[_frame webView] _selectWordBeforeMenuEvent];
1169 }
1170
1171 - (int)historyLength
1172 {
1173     return [[[_frame webView] backForwardList] backListCount] + 1;
1174 }
1175
1176 - (BOOL)canGoBackOrForward:(int)distance
1177 {
1178     if (distance == 0)
1179         return TRUE;
1180
1181     if (distance > 0 && distance <= [[[_frame webView] backForwardList] forwardListCount])
1182         return TRUE;
1183
1184     if (distance < 0 && -distance <= [[[_frame webView] backForwardList] backListCount])
1185         return TRUE;
1186     
1187     return FALSE;
1188 }
1189
1190 - (void)goBackOrForward:(int)distance
1191 {
1192     if (distance == 0) {
1193         return;
1194     }
1195     WebView *webView = [_frame webView];
1196     WebBackForwardList *list = [webView backForwardList];
1197     WebHistoryItem *item = [list itemAtIndex:distance];
1198     if (!item) {
1199         if (distance > 0) {
1200             int forwardListCount = [list forwardListCount];
1201             if (forwardListCount > 0) {
1202                 item = [list itemAtIndex:forwardListCount];
1203             }
1204         } else {
1205             int backListCount = [list forwardListCount];
1206             if (backListCount > 0) {
1207                 item = [list itemAtIndex:-backListCount];
1208             }
1209         }
1210     }
1211     if (item) {
1212         [webView goToBackForwardItem:item];
1213     }
1214 }
1215
1216 static id <WebFormDelegate> formDelegate(WebBridge *self)
1217 {
1218     ASSERT(self->_frame != nil);
1219     return [[self->_frame webView] _formDelegate];
1220 }
1221
1222 #define FormDelegateLog(ctrl)  LOG(FormDelegate, "control=%@", ctrl)
1223
1224 - (void)controlTextDidBeginEditing:(NSNotification *)obj
1225 {
1226     FormDelegateLog([obj object]);
1227     [formDelegate(self) controlTextDidBeginEditing:obj inFrame:_frame];
1228 }
1229
1230 - (void)controlTextDidEndEditing:(NSNotification *)obj
1231 {
1232     FormDelegateLog([obj object]);
1233     [formDelegate(self) controlTextDidEndEditing:obj inFrame:_frame];
1234 }
1235
1236 - (void)controlTextDidChange:(NSNotification *)obj
1237 {
1238     FormDelegateLog([obj object]);
1239     [formDelegate(self) controlTextDidChange:obj inFrame:_frame];
1240 }
1241
1242 - (void)textDidChange:(NSNotification *)obj
1243 {
1244     FormDelegateLog([obj object]);
1245     [formDelegate(self) textDidChange:obj inFrame:_frame];
1246 }
1247
1248 - (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor
1249 {
1250     FormDelegateLog(control);
1251     return [formDelegate(self) control:control textShouldBeginEditing:fieldEditor inFrame:_frame];
1252 }
1253
1254 - (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
1255 {
1256     FormDelegateLog(control);
1257     return [formDelegate(self) control:control textShouldEndEditing:fieldEditor inFrame:_frame];
1258 }
1259
1260 - (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)string errorDescription:(NSString *)error
1261 {
1262     FormDelegateLog(control);
1263     return [formDelegate(self) control:control didFailToFormatString:string errorDescription:error inFrame:_frame];
1264 }
1265
1266 - (void)control:(NSControl *)control didFailToValidatePartialString:(NSString *)string errorDescription:(NSString *)error
1267 {
1268     FormDelegateLog(control);
1269     [formDelegate(self) control:control didFailToValidatePartialString:string errorDescription:error inFrame:_frame];
1270 }
1271
1272 - (BOOL)control:(NSControl *)control isValidObject:(id)obj
1273 {
1274     FormDelegateLog(control);
1275     return [formDelegate(self) control:control isValidObject:obj inFrame:_frame];
1276 }
1277
1278 - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector
1279 {
1280     FormDelegateLog(control);
1281     return [formDelegate(self) control:control textView:textView doCommandBySelector:commandSelector inFrame:_frame];
1282 }
1283
1284 - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView shouldHandleEvent:(NSEvent *)event
1285 {
1286     FormDelegateLog(control);
1287     return [formDelegate(self) control:control textView:textView shouldHandleEvent:event inFrame:_frame];
1288 }
1289
1290 - (void)frameDetached
1291 {
1292     // Put _frame into a local variable because _detachFromParent
1293     // will disconnect the bridge from the frame and make _frame nil.
1294     WebFrame *frame = _frame;
1295
1296     [frame stopLoading];
1297     [frame _detachFromParent];
1298     [[frame parentFrame] _removeChild:frame];
1299 }
1300
1301 - (void)setHasBorder:(BOOL)hasBorder
1302 {
1303     [[_frame frameView] _setHasBorder:hasBorder];
1304 }
1305
1306 - (void)_retrieveKeyboardUIModeFromPreferences:(NSNotification *)notification
1307 {
1308     CFPreferencesAppSynchronize(UniversalAccessDomain);
1309
1310     Boolean keyExistsAndHasValidFormat;
1311     int mode = CFPreferencesGetAppIntegerValue(AppleKeyboardUIMode, UniversalAccessDomain, &keyExistsAndHasValidFormat);
1312     
1313     // The keyboard access mode is reported by two bits:
1314     // Bit 0 is set if feature is on
1315     // Bit 1 is set if full keyboard access works for any control, not just text boxes and lists
1316     // We require both bits to be on.
1317     // I do not know that we would ever get one bit on and the other off since
1318     // checking the checkbox in system preferences which is marked as "Turn on full keyboard access"
1319     // turns on both bits.
1320     _keyboardUIMode = (mode & 0x2) ? WebCoreKeyboardAccessFull : WebCoreKeyboardAccessDefault;
1321     
1322     // check for tabbing to links
1323     if ([[self _preferences] tabsToLinks]) {
1324         _keyboardUIMode |= WebCoreKeyboardAccessTabsToLinks;
1325     }
1326 }
1327
1328 - (WebCoreKeyboardUIMode)keyboardUIMode
1329 {
1330     if (!_keyboardUIModeAccessed) {
1331         _keyboardUIModeAccessed = YES;
1332         [self _retrieveKeyboardUIModeFromPreferences:nil];
1333         
1334         [[NSDistributedNotificationCenter defaultCenter] 
1335             addObserver:self selector:@selector(_retrieveKeyboardUIModeFromPreferences:) 
1336             name:KeyboardUIModeDidChangeNotification object:nil];
1337
1338         [[NSNotificationCenter defaultCenter] 
1339             addObserver:self selector:@selector(_retrieveKeyboardUIModeFromPreferences:) 
1340                    name:WebPreferencesChangedNotification object:nil];
1341     }
1342     return _keyboardUIMode;
1343 }
1344
1345 - (void)didSetName:(NSString *)name
1346 {
1347     [_frame _setName:name];
1348 }
1349
1350 - (NSFileWrapper *)fileWrapperForURL:(NSURL *)URL
1351 {
1352     return [[_frame dataSource] _fileWrapperForURL:URL];
1353 }
1354
1355 - (void)print
1356 {
1357     id wd = [[_frame webView] UIDelegate];
1358     
1359     if ([wd respondsToSelector:@selector(webView:printFrameView:)]) {
1360         [wd webView:[_frame webView] printFrameView:[_frame frameView]];
1361     } else if ([wd respondsToSelector:@selector(webViewPrint:)]) {
1362         // Backward-compatible, but webViewPrint: was never public, so probably not needed.
1363         [wd webViewPrint:[_frame webView]];
1364     } else {
1365         [[WebDefaultUIDelegate sharedUIDelegate] webView:[_frame webView] printFrameView:[_frame frameView]];
1366     }
1367 }
1368
1369 - (jobject)getAppletInView:(NSView *)view
1370 {
1371     jobject applet = 0;
1372
1373     if ([view respondsToSelector: @selector(webPlugInGetApplet)])
1374         applet = [view webPlugInGetApplet];
1375     else
1376         applet = [self pollForAppletInView:view];
1377         
1378     return applet;
1379 }
1380
1381 // NOTE: pollForAppletInView: will block until the block is ready to use, or
1382 // until a timeout is exceeded.  It will return nil if the timeour is
1383 // exceeded.
1384 // Deprecated, use getAppletInView:.
1385 - (jobject)pollForAppletInView: (NSView *)view
1386 {
1387     jobject applet = 0;
1388     
1389     if ([view respondsToSelector: @selector(pollForAppletInWindow:)]) {
1390         // The Java VM needs the containing window of the view to
1391         // initialize.  The view may not yet be in the window's view 
1392         // hierarchy, so we have to pass the window when requesting
1393         // the applet.
1394         applet = [view pollForAppletInWindow:[[_frame webView] window]];
1395     }
1396     
1397     return applet;
1398 }
1399
1400 - (void)respondToChangedContents
1401 {
1402     NSView <WebDocumentView> *view = [[_frame frameView] documentView];
1403     if ([view isKindOfClass:[WebHTMLView class]]) {
1404         [(WebHTMLView *)view _updateFontPanel];
1405     }
1406     [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidChangeNotification object:[_frame webView]];
1407 }
1408
1409 - (void)respondToChangedSelection
1410 {
1411     NSView <WebDocumentView> *view = [[_frame frameView] documentView];
1412     if ([view isKindOfClass:[WebHTMLView class]]) {
1413         [(WebHTMLView *)view _selectionChanged];
1414     }
1415     [[NSNotificationCenter defaultCenter] postNotificationName:WebViewDidChangeSelectionNotification object:[_frame webView]];
1416 }
1417
1418 - (NSUndoManager *)undoManager
1419 {
1420     return [[_frame webView] undoManager];
1421 }
1422
1423 - (void)issueCutCommand
1424 {
1425     [[_frame webView] cut:nil];
1426 }
1427
1428 - (void)issueCopyCommand
1429 {
1430     [[_frame webView] copy:nil];
1431 }
1432
1433 - (void)issuePasteCommand
1434 {
1435     [[_frame webView] paste:nil];
1436 }
1437
1438 - (void)issuePasteAndMatchStyleCommand
1439 {
1440     [[_frame webView] pasteAsPlainText:nil];
1441 }
1442
1443 - (void)issueTransposeCommand
1444 {
1445     NSView <WebDocumentView> *view = [[_frame frameView] documentView];
1446     if ([view isKindOfClass:[WebHTMLView class]]) {
1447         [(WebHTMLView *)view transpose:nil];
1448     }
1449 }
1450
1451 - (BOOL)canPaste
1452 {
1453     return [[_frame webView] _canPaste];
1454 }
1455
1456 - (void)setIsSelected:(BOOL)isSelected forView:(NSView *)view
1457 {
1458     if ([view respondsToSelector:@selector(webPlugInSetIsSelected:)]) {
1459         [view webPlugInSetIsSelected:isSelected];
1460     }
1461     else if ([view respondsToSelector:@selector(setIsSelected:)]) {
1462         [view setIsSelected:isSelected];
1463     }
1464 }
1465
1466 - (NSString *)overrideMediaType
1467 {
1468     return [[_frame webView] mediaStyle];
1469 }
1470
1471 - (BOOL)isEditable
1472 {
1473     return [[_frame webView] isEditable];
1474 }
1475
1476 - (BOOL)shouldBeginEditing:(DOMRange *)range
1477 {
1478     return [[_frame webView] _shouldBeginEditingInDOMRange:range];
1479 }
1480
1481 - (BOOL)shouldEndEditing:(DOMRange *)range
1482 {
1483     return [[_frame webView] _shouldEndEditingInDOMRange:range];
1484 }
1485
1486 - (void)windowObjectCleared
1487 {
1488     WebView *wv = [_frame webView];
1489     [[wv _frameLoadDelegateForwarder] webView:wv windowScriptObjectAvailable:[self windowScriptObject]];
1490 }
1491
1492 - (int)spellCheckerDocumentTag
1493 {
1494     return [[_frame webView] spellCheckerDocumentTag];
1495 }
1496
1497 - (BOOL)isContinuousSpellCheckingEnabled
1498 {
1499     return [[_frame webView] isContinuousSpellCheckingEnabled];
1500 }
1501
1502 - (void)didFirstLayout
1503 {
1504     WebView *wv = [_frame webView];
1505     [[wv _frameLoadDelegateForwarder] webView:wv didFirstLayoutInFrame:_frame];
1506 }
1507
1508 - (BOOL)_compareDashboardRegions:(NSDictionary *)regions
1509 {
1510     return [lastDashboardRegions isEqualToDictionary:regions];
1511 }
1512
1513 - (void)dashboardRegionsChanged:(NSMutableDictionary *)regions
1514 {
1515     WebView *wv = [_frame webView];
1516     id wd = [wv UIDelegate];
1517     
1518     [wv _addScrollerDashboardRegions:regions];
1519     
1520     if (![self _compareDashboardRegions:regions]) {
1521         if ([wd respondsToSelector: @selector(webView:dashboardRegionsChanged:)]) {
1522             [wd webView:wv dashboardRegionsChanged:regions];
1523             [lastDashboardRegions release];
1524             lastDashboardRegions = [regions retain];
1525         }
1526     }
1527 }
1528
1529 - (NSString *)nameForUndoAction:(WebUndoAction)undoAction
1530 {
1531     switch (undoAction) {
1532         case WebUndoActionUnspecified: return nil;
1533         case WebUndoActionSetColor: return UI_STRING_KEY("Set Color", "Set Color (Undo action name)", "Undo action name");
1534         case WebUndoActionSetBackgroundColor: return UI_STRING_KEY("Set Background Color", "Set Background Color (Undo action name)", "Undo action name");
1535         case WebUndoActionTurnOffKerning: return UI_STRING_KEY("Turn Off Kerning", "Turn Off Kerning (Undo action name)", "Undo action name");
1536         case WebUndoActionTightenKerning: return UI_STRING_KEY("Tighten Kerning", "Tighten Kerning (Undo action name)", "Undo action name");
1537         case WebUndoActionLoosenKerning: return UI_STRING_KEY("Loosen Kerning", "Loosen Kerning (Undo action name)", "Undo action name");
1538         case WebUndoActionUseStandardKerning: return UI_STRING_KEY("Use Standard Kerning", "Use Standard Kerning (Undo action name)", "Undo action name");
1539         case WebUndoActionTurnOffLigatures: return UI_STRING_KEY("Turn Off Ligatures", "Turn Off Ligatures (Undo action name)", "Undo action name");
1540         case WebUndoActionUseStandardLigatures: return UI_STRING_KEY("Use Standard Ligatures", "Use Standard Ligatures (Undo action name)", "Undo action name");
1541         case WebUndoActionUseAllLigatures: return UI_STRING_KEY("Use All Ligatures", "Use All Ligatures (Undo action name)", "Undo action name");
1542         case WebUndoActionRaiseBaseline: return UI_STRING_KEY("Raise Baseline", "Raise Baseline (Undo action name)", "Undo action name");
1543         case WebUndoActionLowerBaseline: return UI_STRING_KEY("Lower Baseline", "Lower Baseline (Undo action name)", "Undo action name");
1544         case WebUndoActionSetTraditionalCharacterShape: return UI_STRING_KEY("Set Traditional Character Shape", "Set Traditional Character Shape (Undo action name)", "Undo action name");
1545         case WebUndoActionSetFont: return UI_STRING_KEY("Set Font", "Set Font (Undo action name)", "Undo action name");
1546         case WebUndoActionChangeAttributes: return UI_STRING_KEY("Change Attributes", "Change Attributes (Undo action name)", "Undo action name");
1547         case WebUndoActionAlignLeft: return UI_STRING_KEY("Align Left", "Align Left (Undo action name)", "Undo action name");
1548         case WebUndoActionAlignRight: return UI_STRING_KEY("Align Right", "Align Right (Undo action name)", "Undo action name");
1549         case WebUndoActionCenter: return UI_STRING_KEY("Center", "Center (Undo action name)", "Undo action name");
1550         case WebUndoActionJustify: return UI_STRING_KEY("Justify", "Justify (Undo action name)", "Undo action name");
1551         case WebUndoActionSetWritingDirection: return UI_STRING_KEY("Set Writing Direction", "Set Writing Direction (Undo action name)", "Undo action name");
1552         case WebUndoActionSubscript: return UI_STRING_KEY("Subscript", "Subscript (Undo action name)", "Undo action name");
1553         case WebUndoActionSuperscript: return UI_STRING_KEY("Superscript", "Superscript (Undo action name)", "Undo action name");
1554         case WebUndoActionUnderline: return UI_STRING_KEY("Underline", "Underline (Undo action name)", "Undo action name");
1555         case WebUndoActionOutline: return UI_STRING_KEY("Outline", "Outline (Undo action name)", "Undo action name");
1556         case WebUndoActionUnscript: return UI_STRING_KEY("Unscript", "Unscript (Undo action name)", "Undo action name");
1557         case WebUndoActionDrag: return UI_STRING_KEY("Drag", "Drag (Undo action name)", "Undo action name");
1558         case WebUndoActionCut: return UI_STRING_KEY("Cut", "Cut (Undo action name)", "Undo action name");
1559         case WebUndoActionPaste: return UI_STRING_KEY("Paste", "Paste (Undo action name)", "Undo action name");
1560         case WebUndoActionPasteFont: return UI_STRING_KEY("Paste Font", "Paste Font (Undo action name)", "Undo action name");
1561         case WebUndoActionPasteRuler: return UI_STRING_KEY("Paste Ruler", "Paste Ruler (Undo action name)", "Undo action name");
1562         case WebUndoActionTyping: return UI_STRING_KEY("Typing", "Typing (Undo action name)", "Undo action name");
1563     }
1564     return nil;
1565 }
1566
1567 // FIXME: The following 2 functions are copied from AppKit. It would be best share code.
1568
1569 // MF:!!! For now we will use static character sets for the computation, but we should eventually probably make these keys in the language dictionaries.
1570 // MF:!!! The following characters (listed with their nextstep encoding values) were in the preSmartTable in the old text objet, but aren't yet in the new text object: NS_FIGSPACE (0x80), exclamdown (0xa1), sterling (0xa3), yen (0xa5), florin (0xa6) section (0xa7), currency (0xa8), quotesingle (0xa9), quotedblleft (0xaa), guillemotleft (0xab), guilsinglleft (0xac), endash (0xb1), quotesinglbase (0xb8), quotedblbase (0xb9), questiondown (0xbf), emdash (0xd0), plusminus (0xd1).
1571 // MF:!!! The following characters (listed with their nextstep encoding values) were in the postSmartTable in the old text objet, but aren't yet in the new text object: NS_FIGSPACE (0x80), cent (0xa2), guilsinglright (0xad), registered (0xb0), dagger (0xa2), daggerdbl (0xa3), endash (0xb1), quotedblright (0xba), guillemotright (0xbb), perthousand (0xbd), onesuperior (0xc0), twosuperior (0xc9), threesuperior (0xcc), emdash (0xd0), ordfeminine (0xe3), ordmasculine (0xeb).
1572 // MF:!!! Another difference in both of these sets from the old text object is we include all the whitespace in whitespaceAndNewlineCharacterSet.
1573 #define _preSmartString @"([\"\'#$/-`{"
1574 #define _postSmartString @")].,;:?\'!\"%*-/}"
1575
1576 static NSCharacterSet *_getPreSmartSet(void)
1577 {
1578     static NSMutableCharacterSet *_preSmartSet = nil;
1579     if (!_preSmartSet) {
1580         _preSmartSet = [[NSMutableCharacterSet characterSetWithCharactersInString:_preSmartString] retain];
1581         [_preSmartSet formUnionWithCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
1582         // Adding CJK ranges
1583         [_preSmartSet addCharactersInRange:NSMakeRange(0x1100, 256)]; // Hangul Jamo (0x1100 - 0x11FF)
1584         [_preSmartSet addCharactersInRange:NSMakeRange(0x2E80, 352)]; // CJK & Kangxi Radicals (0x2E80 - 0x2FDF)
1585         [_preSmartSet addCharactersInRange:NSMakeRange(0x2FF0, 464)]; // Ideograph Descriptions, CJK Symbols, Hiragana, Katakana, Bopomofo, Hangul Compatibility Jamo, Kanbun, & Bopomofo Ext (0x2FF0 - 0x31BF)
1586         [_preSmartSet addCharactersInRange:NSMakeRange(0x3200, 29392)]; // Enclosed CJK, CJK Ideographs (Uni Han & Ext A), & Yi (0x3200 - 0xA4CF)
1587         [_preSmartSet addCharactersInRange:NSMakeRange(0xAC00, 11183)]; // Hangul Syllables (0xAC00 - 0xD7AF)
1588         [_preSmartSet addCharactersInRange:NSMakeRange(0xF900, 352)]; // CJK Compatibility Ideographs (0xF900 - 0xFA5F)
1589         [_preSmartSet addCharactersInRange:NSMakeRange(0xFE30, 32)]; // CJK Compatibility From (0xFE30 - 0xFE4F)
1590         [_preSmartSet addCharactersInRange:NSMakeRange(0xFF00, 240)]; // Half/Full Width Form (0xFF00 - 0xFFEF)
1591         [_preSmartSet addCharactersInRange:NSMakeRange(0x20000, 0xA6D7)]; // CJK Ideograph Exntension B
1592         [_preSmartSet addCharactersInRange:NSMakeRange(0x2F800, 0x021E)]; // CJK Compatibility Ideographs (0x2F800 - 0x2FA1D)
1593     }
1594     return _preSmartSet;
1595 }
1596
1597 static NSCharacterSet *_getPostSmartSet(void)
1598 {
1599     static NSMutableCharacterSet *_postSmartSet = nil;
1600     if (!_postSmartSet) {
1601         _postSmartSet = [[NSMutableCharacterSet characterSetWithCharactersInString:_postSmartString] retain];
1602         [_postSmartSet formUnionWithCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
1603         [_postSmartSet addCharactersInRange:NSMakeRange(0x1100, 256)]; // Hangul Jamo (0x1100 - 0x11FF)
1604         [_postSmartSet addCharactersInRange:NSMakeRange(0x2E80, 352)]; // CJK & Kangxi Radicals (0x2E80 - 0x2FDF)
1605         [_postSmartSet addCharactersInRange:NSMakeRange(0x2FF0, 464)]; // Ideograph Descriptions, CJK Symbols, Hiragana, Katakana, Bopomofo, Hangul Compatibility Jamo, Kanbun, & Bopomofo Ext (0x2FF0 - 0x31BF)
1606         [_postSmartSet addCharactersInRange:NSMakeRange(0x3200, 29392)]; // Enclosed CJK, CJK Ideographs (Uni Han & Ext A), & Yi (0x3200 - 0xA4CF)
1607         [_postSmartSet addCharactersInRange:NSMakeRange(0xAC00, 11183)]; // Hangul Syllables (0xAC00 - 0xD7AF)
1608         [_postSmartSet addCharactersInRange:NSMakeRange(0xF900, 352)]; // CJK Compatibility Ideographs (0xF900 - 0xFA5F)
1609         [_postSmartSet addCharactersInRange:NSMakeRange(0xFE30, 32)]; // CJK Compatibility From (0xFE30 - 0xFE4F)
1610         [_postSmartSet addCharactersInRange:NSMakeRange(0xFF00, 240)]; // Half/Full Width Form (0xFF00 - 0xFFEF)
1611         [_postSmartSet addCharactersInRange:NSMakeRange(0x20000, 0xA6D7)]; // CJK Ideograph Exntension B
1612         [_postSmartSet addCharactersInRange:NSMakeRange(0x2F800, 0x021E)]; // CJK Compatibility Ideographs (0x2F800 - 0x2FA1D)        
1613         [_postSmartSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
1614     }
1615     return _postSmartSet;
1616 }
1617
1618 - (BOOL)isCharacterSmartReplaceExempt:(unichar)c isPreviousCharacter:(BOOL)isPreviousCharacter
1619 {
1620     return [isPreviousCharacter ? _getPreSmartSet() : _getPostSmartSet() characterIsMember:c];
1621 }
1622
1623 - (WebCoreBridge *)createModalDialogWithURL:(NSURL *)URL
1624 {
1625     ASSERT(_frame != nil);
1626
1627     NSMutableURLRequest *request = nil;
1628
1629     if (URL != nil && ![URL _web_isEmpty]) {
1630         request = [NSMutableURLRequest requestWithURL:URL];
1631         [request _web_setHTTPReferrer:[self referrer]];
1632     }
1633
1634     WebView *currentWebView = [_frame webView];
1635     id UIDelegate = [currentWebView UIDelegate];
1636
1637     WebView *newWebView = nil;
1638     if ([UIDelegate respondsToSelector:@selector(webView:createWebViewModalDialogWithRequest:)])
1639         newWebView = [UIDelegate webView:currentWebView createWebViewModalDialogWithRequest:request];
1640     else if ([UIDelegate respondsToSelector:@selector(webView:createWebViewWithRequest:)])
1641         newWebView = [UIDelegate webView:currentWebView createWebViewWithRequest:request];
1642     else
1643         newWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:currentWebView createWebViewWithRequest:request];
1644
1645     return [[newWebView mainFrame] _bridge];
1646 }
1647
1648 - (BOOL)canRunModal
1649 {
1650     WebView *webView = [_frame webView];
1651     id UIDelegate = [webView UIDelegate];
1652     return [UIDelegate respondsToSelector:@selector(webViewRunModal:)];
1653 }
1654
1655 - (BOOL)canRunModalNow
1656 {
1657     return [self canRunModal] && ![WebBaseResourceHandleDelegate inConnectionCallback];
1658 }
1659
1660 - (void)runModal
1661 {
1662     if (![self canRunModal])
1663         return;
1664
1665     WebView *webView = [_frame webView];
1666     if ([webView defersCallbacks]) {
1667         ERROR("tried to run modal in a view when it was deferring callbacks -- should never happen");
1668         return;
1669     }
1670
1671     // Defer callbacks in all the other views in this group, so we don't try to run JavaScript
1672     // in a way that could interact with this view.
1673     NSMutableArray *deferredWebViews = [NSMutableArray array];
1674     NSString *setName = [webView groupName];
1675     if (setName) {
1676         NSEnumerator *enumerator = [WebViewSets webViewsInSetNamed:setName];
1677         WebView *otherWebView;
1678         while ((otherWebView = [enumerator nextObject]) != nil) {
1679             if (otherWebView != webView && ![otherWebView defersCallbacks]) {
1680                 [otherWebView setDefersCallbacks:YES];
1681                 [deferredWebViews addObject:otherWebView];
1682             }
1683         }
1684     }
1685
1686     // Go run the modal event loop.
1687     [[webView UIDelegate] webViewRunModal:webView];
1688
1689     // Restore the callbacks for any views that we deferred them for.
1690     unsigned count = [deferredWebViews count];
1691     unsigned i;
1692     for (i = 0; i < count; ++i) {
1693         WebView *otherWebView = [deferredWebViews objectAtIndex:i];
1694         [otherWebView setDefersCallbacks:NO];
1695     }
1696 }
1697
1698 @end