https://bugs.webkit.org/show_bug.cgi?id=46422, make printing and pagination work
[WebKit-https.git] / Source / WebKit / mac / WebView / WebFrame.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 Apple 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 "WebFrameInternal.h"
30
31 #import "DOMCSSStyleDeclarationInternal.h"
32 #import "DOMDocumentFragmentInternal.h"
33 #import "DOMDocumentInternal.h"
34 #import "DOMElementInternal.h"
35 #import "DOMHTMLElementInternal.h"
36 #import "DOMNodeInternal.h"
37 #import "DOMRangeInternal.h"
38 #import "WebArchiveInternal.h"
39 #import "WebChromeClient.h"
40 #import "WebDataSourceInternal.h"
41 #import "WebDocumentLoaderMac.h"
42 #import "WebDynamicScrollBarsView.h"
43 #import "WebFrameLoaderClient.h"
44 #import "WebFrameViewInternal.h"
45 #import "WebHTMLView.h"
46 #import "WebHTMLViewInternal.h"
47 #import "WebKitStatisticsPrivate.h"
48 #import "WebKitVersionChecks.h"
49 #import "WebNSObjectExtras.h"
50 #import "WebNSURLExtras.h"
51 #import "WebScriptDebugger.h"
52 #import "WebScriptWorldInternal.h"
53 #import "WebViewInternal.h"
54 #import <JavaScriptCore/APICast.h>
55 #import <WebCore/AXObjectCache.h>
56 #import <WebCore/AccessibilityObject.h>
57 #import <WebCore/AnimationController.h>
58 #import <WebCore/CSSMutableStyleDeclaration.h>
59 #import <WebCore/CachedResourceLoader.h>
60 #import <WebCore/Chrome.h>
61 #import <WebCore/ColorMac.h>
62 #import <WebCore/DOMImplementation.h>
63 #import <WebCore/DocumentFragment.h>
64 #import <WebCore/DocumentMarkerController.h>
65 #import <WebCore/EventHandler.h>
66 #import <WebCore/EventNames.h>
67 #import <WebCore/Frame.h>
68 #import <WebCore/FrameLoader.h>
69 #import <WebCore/FrameLoaderStateMachine.h>
70 #import <WebCore/FrameTree.h>
71 #import <WebCore/GraphicsContext.h>
72 #import <WebCore/HTMLFrameOwnerElement.h>
73 #import <WebCore/HistoryItem.h>
74 #import <WebCore/HitTestResult.h>
75 #import <WebCore/LegacyWebArchive.h>
76 #import <WebCore/Page.h>
77 #import <WebCore/PluginData.h>
78 #import <WebCore/PrintContext.h>
79 #import <WebCore/RenderLayer.h>
80 #import <WebCore/RenderPart.h>
81 #import <WebCore/RenderView.h>
82 #import <WebCore/ReplaceSelectionCommand.h>
83 #import <WebCore/RuntimeApplicationChecks.h>
84 #import <WebCore/ScriptValue.h>
85 #import <WebCore/SmartReplace.h>
86 #import <WebCore/SVGDocumentExtensions.h>
87 #import <WebCore/SVGSMILElement.h>
88 #import <WebCore/TextIterator.h>
89 #import <WebCore/ThreadCheck.h>
90 #import <WebCore/TypingCommand.h>
91 #import <WebCore/htmlediting.h>
92 #import <WebCore/markup.h>
93 #import <WebCore/visible_units.h>
94 #import <WebKitSystemInterface.h>
95 #import <runtime/JSLock.h>
96 #import <runtime/JSObject.h>
97 #import <runtime/JSValue.h>
98 #import <wtf/CurrentTime.h>
99
100 using namespace std;
101 using namespace WebCore;
102 using namespace HTMLNames;
103
104 using JSC::JSGlobalObject;
105 using JSC::JSLock;
106 using JSC::JSValue;
107 using JSC::SilenceAssertionsOnly;
108
109 /*
110 Here is the current behavior matrix for four types of navigations:
111
112 Standard Nav:
113
114  Restore form state:   YES
115  Restore scroll and focus state:  YES
116  Cache policy: NSURLRequestUseProtocolCachePolicy
117  Add to back/forward list: YES
118  
119 Back/Forward:
120
121  Restore form state:   YES
122  Restore scroll and focus state:  YES
123  Cache policy: NSURLRequestReturnCacheDataElseLoad
124  Add to back/forward list: NO
125
126 Reload (meaning only the reload button):
127
128  Restore form state:   NO
129  Restore scroll and focus state:  YES
130  Cache policy: NSURLRequestReloadIgnoringCacheData
131  Add to back/forward list: NO
132
133 Repeat load of the same URL (by any other means of navigation other than the reload button, including hitting return in the location field):
134
135  Restore form state:   NO
136  Restore scroll and focus state:  NO, reset to initial conditions
137  Cache policy: NSURLRequestReloadIgnoringCacheData
138  Add to back/forward list: NO
139 */
140
141 NSString *WebPageCacheEntryDateKey = @"WebPageCacheEntryDateKey";
142 NSString *WebPageCacheDataSourceKey = @"WebPageCacheDataSourceKey";
143 NSString *WebPageCacheDocumentViewKey = @"WebPageCacheDocumentViewKey";
144
145 NSString *WebFrameMainDocumentError = @"WebFrameMainDocumentErrorKey";
146 NSString *WebFrameHasPlugins = @"WebFrameHasPluginsKey";
147 NSString *WebFrameHasUnloadListener = @"WebFrameHasUnloadListenerKey";
148 NSString *WebFrameUsesDatabases = @"WebFrameUsesDatabasesKey";
149 NSString *WebFrameUsesGeolocation = @"WebFrameUsesGeolocationKey";
150 NSString *WebFrameUsesApplicationCache = @"WebFrameUsesApplicationCacheKey";
151 NSString *WebFrameCanSuspendActiveDOMObjects = @"WebFrameCanSuspendActiveDOMObjectsKey";
152
153 // FIXME: Remove when this key becomes publicly defined
154 NSString *NSAccessibilityEnhancedUserInterfaceAttribute = @"AXEnhancedUserInterface";
155
156 @implementation WebFramePrivate
157
158 - (void)dealloc
159 {
160     [webFrameView release];
161
162     delete scriptDebugger;
163
164     [super dealloc];
165 }
166
167 - (void)finalize
168 {
169     delete scriptDebugger;
170
171     [super finalize];
172 }
173
174 - (void)setWebFrameView:(WebFrameView *)v 
175
176     [v retain];
177     [webFrameView release];
178     webFrameView = v;
179 }
180
181 @end
182
183 EditableLinkBehavior core(WebKitEditableLinkBehavior editableLinkBehavior)
184 {
185     switch (editableLinkBehavior) {
186         case WebKitEditableLinkDefaultBehavior:
187             return EditableLinkDefaultBehavior;
188         case WebKitEditableLinkAlwaysLive:
189             return EditableLinkAlwaysLive;
190         case WebKitEditableLinkOnlyLiveWithShiftKey:
191             return EditableLinkOnlyLiveWithShiftKey;
192         case WebKitEditableLinkLiveWhenNotFocused:
193             return EditableLinkLiveWhenNotFocused;
194         case WebKitEditableLinkNeverLive:
195             return EditableLinkNeverLive;
196     }
197     ASSERT_NOT_REACHED();
198     return EditableLinkDefaultBehavior;
199 }
200
201 WebCore::EditingBehaviorType core(WebKitEditingBehavior behavior)
202 {
203     switch (behavior) {
204         case WebKitEditingMacBehavior:
205             return WebCore::EditingMacBehavior;
206         case WebKitEditingWinBehavior:
207             return WebCore::EditingWindowsBehavior;
208         case WebKitEditingUnixBehavior:
209             return WebCore::EditingUnixBehavior;
210     }
211     ASSERT_NOT_REACHED();
212     return WebCore::EditingMacBehavior;
213 }
214
215 TextDirectionSubmenuInclusionBehavior core(WebTextDirectionSubmenuInclusionBehavior behavior)
216 {
217     switch (behavior) {
218         case WebTextDirectionSubmenuNeverIncluded:
219             return TextDirectionSubmenuNeverIncluded;
220         case WebTextDirectionSubmenuAutomaticallyIncluded:
221             return TextDirectionSubmenuAutomaticallyIncluded;
222         case WebTextDirectionSubmenuAlwaysIncluded:
223             return TextDirectionSubmenuAlwaysIncluded;
224     }
225     ASSERT_NOT_REACHED();
226     return TextDirectionSubmenuNeverIncluded;
227 }
228
229 @implementation WebFrame (WebInternal)
230
231 Frame* core(WebFrame *frame)
232 {
233     return frame ? frame->_private->coreFrame : 0;
234 }
235
236 WebFrame *kit(Frame* frame)
237 {
238     return frame ? static_cast<WebFrameLoaderClient*>(frame->loader()->client())->webFrame() : nil;
239 }
240
241 Page* core(WebView *webView)
242 {
243     return [webView page];
244 }
245
246 WebView *kit(Page* page)
247 {
248     return page ? static_cast<WebChromeClient*>(page->chrome()->client())->webView() : nil;
249 }
250
251 WebView *getWebView(WebFrame *webFrame)
252 {
253     Frame* coreFrame = core(webFrame);
254     if (!coreFrame)
255         return nil;
256     return kit(coreFrame->page());
257 }
258
259 + (PassRefPtr<Frame>)_createFrameWithPage:(Page*)page frameName:(const String&)name frameView:(WebFrameView *)frameView ownerElement:(HTMLFrameOwnerElement*)ownerElement
260 {
261     WebView *webView = kit(page);
262
263     WebFrame *frame = [[self alloc] _initWithWebFrameView:frameView webView:webView];
264     RefPtr<Frame> coreFrame = Frame::create(page, ownerElement, new WebFrameLoaderClient(frame));
265     [frame release];
266     frame->_private->coreFrame = coreFrame.get();
267
268     coreFrame->tree()->setName(name);
269     if (ownerElement) {
270         ASSERT(ownerElement->document()->frame());
271         ownerElement->document()->frame()->tree()->appendChild(coreFrame.get());
272     }
273
274     coreFrame->init();
275
276     [webView _setZoomMultiplier:[webView _realZoomMultiplier] isTextOnly:[webView _realZoomMultiplierIsTextOnly]];
277
278     return coreFrame.release();
279 }
280
281 + (void)_createMainFrameWithPage:(Page*)page frameName:(const String&)name frameView:(WebFrameView *)frameView
282 {
283     [self _createFrameWithPage:page frameName:name frameView:frameView ownerElement:0];
284 }
285
286 + (PassRefPtr<WebCore::Frame>)_createSubframeWithOwnerElement:(HTMLFrameOwnerElement*)ownerElement frameName:(const String&)name frameView:(WebFrameView *)frameView
287 {
288     return [self _createFrameWithPage:ownerElement->document()->frame()->page() frameName:name frameView:frameView ownerElement:ownerElement];
289 }
290
291 - (BOOL)_isIncludedInWebKitStatistics
292 {
293     return _private && _private->includedInWebKitStatistics;
294 }
295
296 - (void)_attachScriptDebugger
297 {
298     ScriptController* scriptController = _private->coreFrame->script();
299
300     // Calling ScriptController::globalObject() would create a window shell, and dispatch corresponding callbacks, which may be premature
301     // if the script debugger is attached before a document is created.  These calls use the debuggerWorld(), we will need to pass a world
302     // to be able to debug isolated worlds.
303     if (!scriptController->existingWindowShell(debuggerWorld()))
304         return;
305
306     JSGlobalObject* globalObject = scriptController->globalObject(debuggerWorld());
307     if (!globalObject)
308         return;
309
310     if (_private->scriptDebugger) {
311         ASSERT(_private->scriptDebugger == globalObject->debugger());
312         return;
313     }
314
315     _private->scriptDebugger = new WebScriptDebugger(globalObject);
316 }
317
318 - (void)_detachScriptDebugger
319 {
320     if (!_private->scriptDebugger)
321         return;
322
323     delete _private->scriptDebugger;
324     _private->scriptDebugger = 0;
325 }
326
327 - (id)_initWithWebFrameView:(WebFrameView *)fv webView:(WebView *)v
328 {
329     self = [super init];
330     if (!self)
331         return nil;
332
333     _private = [[WebFramePrivate alloc] init];
334
335     // Set includedInWebKitStatistics before calling WebFrameView _setWebFrame, since
336     // it calls WebFrame _isIncludedInWebKitStatistics.
337     if ((_private->includedInWebKitStatistics = [[v class] shouldIncludeInWebKitStatistics]))
338         ++WebFrameCount;
339
340     if (fv) {
341         [_private setWebFrameView:fv];
342         [fv _setWebFrame:self];
343     }
344
345     _private->shouldCreateRenderers = YES;
346
347     return self;
348 }
349
350 - (void)_clearCoreFrame
351 {
352     _private->coreFrame = 0;
353 }
354
355 - (void)_updateBackgroundAndUpdatesWhileOffscreen
356 {
357     WebView *webView = getWebView(self);
358     BOOL drawsBackground = [webView drawsBackground];
359     NSColor *backgroundColor = [webView backgroundColor];
360
361     Frame* coreFrame = _private->coreFrame;
362     for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) {
363         if ([webView _usesDocumentViews]) {
364             // Don't call setDrawsBackground:YES here because it may be NO because of a load
365             // in progress; WebFrameLoaderClient keeps it set to NO during the load process.
366             WebFrame *webFrame = kit(frame);
367             if (!drawsBackground)
368                 [[[webFrame frameView] _scrollView] setDrawsBackground:NO];
369             [[[webFrame frameView] _scrollView] setBackgroundColor:backgroundColor];
370             id documentView = [[webFrame frameView] documentView];
371             if ([documentView respondsToSelector:@selector(setDrawsBackground:)])
372                 [documentView setDrawsBackground:drawsBackground];
373             if ([documentView respondsToSelector:@selector(setBackgroundColor:)])
374                 [documentView setBackgroundColor:backgroundColor];
375         }
376
377         if (FrameView* view = frame->view()) {
378             view->setTransparent(!drawsBackground);
379             view->setBaseBackgroundColor(colorFromNSColor([backgroundColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]));
380             view->setShouldUpdateWhileOffscreen([webView shouldUpdateWhileOffscreen]);
381         }
382     }
383 }
384
385 - (void)_setInternalLoadDelegate:(id)internalLoadDelegate
386 {
387     _private->internalLoadDelegate = internalLoadDelegate;
388 }
389
390 - (id)_internalLoadDelegate
391 {
392     return _private->internalLoadDelegate;
393 }
394
395 #ifndef BUILDING_ON_TIGER
396 - (void)_unmarkAllBadGrammar
397 {
398     Frame* coreFrame = _private->coreFrame;
399     for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) {
400         if (Document* document = frame->document())
401             document->markers()->removeMarkers(DocumentMarker::Grammar);
402     }
403 }
404 #endif
405
406 - (void)_unmarkAllMisspellings
407 {
408     Frame* coreFrame = _private->coreFrame;
409     for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) {
410         if (Document* document = frame->document())
411             document->markers()->removeMarkers(DocumentMarker::Spelling);
412     }
413 }
414
415 - (BOOL)_hasSelection
416 {
417     if ([getWebView(self) _usesDocumentViews]) {
418         id documentView = [_private->webFrameView documentView];    
419
420         // optimization for common case to avoid creating potentially large selection string
421         if ([documentView isKindOfClass:[WebHTMLView class]])
422             if (Frame* coreFrame = _private->coreFrame)
423                 return coreFrame->selection()->isRange();
424
425         if ([documentView conformsToProtocol:@protocol(WebDocumentText)])
426             return [[documentView selectedString] length] > 0;
427         
428         return NO;
429     }
430
431     Frame* coreFrame = _private->coreFrame;
432     return coreFrame && coreFrame->selection()->isRange();
433 }
434
435 - (void)_clearSelection
436 {
437     ASSERT([getWebView(self) _usesDocumentViews]);
438     id documentView = [_private->webFrameView documentView];    
439     if ([documentView conformsToProtocol:@protocol(WebDocumentText)])
440         [documentView deselectAll];
441 }
442
443 #if !ASSERT_DISABLED
444 - (BOOL)_atMostOneFrameHasSelection
445 {
446     // FIXME: 4186050 is one known case that makes this debug check fail.
447     BOOL found = NO;
448     Frame* coreFrame = _private->coreFrame;
449     for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame))
450         if ([kit(frame) _hasSelection]) {
451             if (found)
452                 return NO;
453             found = YES;
454         }
455     return YES;
456 }
457 #endif
458
459 - (WebFrame *)_findFrameWithSelection
460 {
461     Frame* coreFrame = _private->coreFrame;
462     for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) {
463         WebFrame *webFrame = kit(frame);
464         if ([webFrame _hasSelection])
465             return webFrame;
466     }
467     return nil;
468 }
469
470 - (void)_clearSelectionInOtherFrames
471 {
472     // We rely on WebDocumentSelection protocol implementors to call this method when they become first 
473     // responder. It would be nicer to just notice first responder changes here instead, but there's no 
474     // notification sent when the first responder changes in general (Radar 2573089).
475     WebFrame *frameWithSelection = [[getWebView(self) mainFrame] _findFrameWithSelection];
476     if (frameWithSelection != self)
477         [frameWithSelection _clearSelection];
478
479     // While we're in the general area of selection and frames, check that there is only one now.
480     ASSERT([[getWebView(self) mainFrame] _atMostOneFrameHasSelection]);
481 }
482
483 static inline WebDataSource *dataSource(DocumentLoader* loader)
484 {
485     return loader ? static_cast<WebDocumentLoaderMac*>(loader)->dataSource() : nil;
486 }
487
488 - (WebDataSource *)_dataSource
489 {
490     return dataSource(_private->coreFrame->loader()->documentLoader());
491 }
492
493 - (NSString *)_stringWithDocumentTypeStringAndMarkupString:(NSString *)markupString
494 {
495     return _private->coreFrame->documentTypeString() + markupString;
496 }
497
498 - (NSArray *)_nodesFromList:(Vector<Node*> *)nodesVector
499 {
500     size_t size = nodesVector->size();
501     NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:size];
502     for (size_t i = 0; i < size; ++i)
503         [nodes addObject:kit((*nodesVector)[i])];
504     return nodes;
505 }
506
507 - (NSString *)_markupStringFromRange:(DOMRange *)range nodes:(NSArray **)nodes
508 {
509     // FIXME: This is always "for interchange". Is that right? See the previous method.
510     Vector<Node*> nodeList;
511     NSString *markupString = createMarkup(core(range), nodes ? &nodeList : 0, AnnotateForInterchange);
512     if (nodes)
513         *nodes = [self _nodesFromList:&nodeList];
514
515     return [self _stringWithDocumentTypeStringAndMarkupString:markupString];
516 }
517
518 - (NSString *)_selectedString
519 {
520     return _private->coreFrame->displayStringModifiedByEncoding(_private->coreFrame->editor()->selectedText());
521 }
522
523 - (NSString *)_stringForRange:(DOMRange *)range
524 {
525     // This will give a system malloc'd buffer that can be turned directly into an NSString
526     unsigned length;
527     UChar* buf = plainTextToMallocAllocatedBuffer(core(range), length, true);
528     
529     if (!buf)
530         return [NSString string];
531
532     // Transfer buffer ownership to NSString
533     return [[[NSString alloc] initWithCharactersNoCopy:buf length:length freeWhenDone:YES] autorelease];
534 }
535
536 - (BOOL)_shouldFlattenCompositingLayers:(CGContextRef)context
537 {
538     // -currentContextDrawingToScreen returns YES for bitmap contexts.
539     BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen];
540     if (isPrinting)
541         return YES;
542
543     if (!WKCGContextIsBitmapContext(context))
544         return NO;
545
546     // If we're drawing into a bitmap, we might be snapshotting, or drawing into a layer-backed view.
547     if ([getWebView(self) _usesDocumentViews]) {
548         id documentView = [_private->webFrameView documentView];
549         if ([documentView isKindOfClass:[WebHTMLView class]] && [(WebHTMLView *)documentView _web_isDrawingIntoLayer])
550             return NO;
551     }
552
553     return [getWebView(self) _includesFlattenedCompositingLayersWhenDrawingToBitmap];
554 }
555
556 - (void)_drawRect:(NSRect)rect contentsOnly:(BOOL)contentsOnly
557 {
558     ASSERT([[NSGraphicsContext currentContext] isFlipped]);
559
560     CGContextRef ctx = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
561     GraphicsContext context(ctx);
562
563     FrameView* view = _private->coreFrame->view();
564     
565     bool shouldFlatten = false;
566     if (Frame* parentFrame = _private->coreFrame->tree()->parent()) {
567         // For subframes, we need to inherit the paint behavior from our parent
568         FrameView* parentView = parentFrame ? parentFrame->view() : 0;
569         if (parentView)
570             shouldFlatten = parentView->paintBehavior() & PaintBehaviorFlattenCompositingLayers;
571     } else
572         shouldFlatten = [self _shouldFlattenCompositingLayers:ctx];
573
574     PaintBehavior oldBehavior = PaintBehaviorNormal;
575     if (shouldFlatten) {
576         oldBehavior = view->paintBehavior();
577         view->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers);
578     }
579     
580     if (contentsOnly)
581         _private->coreFrame->view()->paintContents(&context, enclosingIntRect(rect));
582     else
583         _private->coreFrame->view()->paint(&context, enclosingIntRect(rect));
584
585     if (shouldFlatten)
586         view->setPaintBehavior(oldBehavior);
587 }
588
589 // Used by pagination code called from AppKit when a standalone web page is printed.
590 - (NSArray*)_computePageRectsWithPrintScaleFactor:(float)printScaleFactor pageSize:(NSSize)pageSize
591 {
592     NSMutableArray* pages = [NSMutableArray arrayWithCapacity:5];
593     if (printScaleFactor <= 0) {
594         LOG_ERROR("printScaleFactor has bad value %.2f", printScaleFactor);
595         return pages;
596     }
597
598     if (!_private->coreFrame || !_private->coreFrame->document() || !_private->coreFrame->view()) return pages;
599     RenderView* root = toRenderView(_private->coreFrame->document()->renderer());
600     if (!root) return pages;
601     
602     FrameView* view = _private->coreFrame->view();
603     if (!view)
604         return pages;
605
606     NSView* documentView = view->documentView();
607     if (!documentView)
608         return pages;
609
610     float docWidth = root->docWidth();
611     float docHeight = root->docHeight();
612
613     float printWidth = root->style()->isHorizontalWritingMode() ? docWidth / printScaleFactor : pageSize.width;
614     float printHeight = root->style()->isHorizontalWritingMode() ? pageSize.height : docHeight / printScaleFactor;
615
616     PrintContext printContext(_private->coreFrame);
617     printContext.computePageRectsWithPageSize(FloatSize(printWidth, printHeight), true);
618
619     const Vector<IntRect>& pageRects = printContext.pageRects();
620     const size_t pageCount = pageRects.size();
621     for (size_t pageNumber = 0; pageNumber < pageCount; ++pageNumber)
622         [pages addObject: [NSValue valueWithRect: NSRect(pageRects[pageNumber])]];
623     return pages;
624 }
625
626 - (BOOL)_getVisibleRect:(NSRect*)rect
627 {
628     ASSERT_ARG(rect, rect);
629     if (RenderPart* ownerRenderer = _private->coreFrame->ownerRenderer()) {
630         if (ownerRenderer->needsLayout())
631             return NO;
632         *rect = ownerRenderer->absoluteClippedOverflowRect();
633         return YES;
634     }
635
636     return NO;
637 }
638
639 - (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string
640 {
641     return [self _stringByEvaluatingJavaScriptFromString:string forceUserGesture:true];
642 }
643
644 - (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string forceUserGesture:(BOOL)forceUserGesture
645 {
646     ASSERT(_private->coreFrame->document());
647     
648     JSValue result = _private->coreFrame->script()->executeScript(string, forceUserGesture).jsValue();
649
650     if (!_private->coreFrame) // In case the script removed our frame from the page.
651         return @"";
652
653     // This bizarre set of rules matches behavior from WebKit for Safari 2.0.
654     // If you don't like it, use -[WebScriptObject evaluateWebScript:] or 
655     // JSEvaluateScript instead, since they have less surprising semantics.
656     if (!result || (!result.isBoolean() && !result.isString() && !result.isNumber()))
657         return @"";
658
659     JSLock lock(SilenceAssertionsOnly);
660     return ustringToString(result.toString(_private->coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec()));
661 }
662
663 - (NSRect)_caretRectAtNode:(DOMNode *)node offset:(int)offset affinity:(NSSelectionAffinity)affinity
664 {
665     VisiblePosition visiblePosition(core(node), offset, static_cast<EAffinity>(affinity));
666     return visiblePosition.absoluteCaretBounds();
667 }
668
669 - (NSRect)_firstRectForDOMRange:(DOMRange *)range
670 {
671    return _private->coreFrame->editor()->firstRectForRange(core(range));
672 }
673
674 - (void)_scrollDOMRangeToVisible:(DOMRange *)range
675 {
676     NSRect rangeRect = [self _firstRectForDOMRange:range];    
677     Node *startNode = core([range startContainer]);
678         
679     if (startNode && startNode->renderer()) {
680         RenderLayer *layer = startNode->renderer()->enclosingLayer();
681         if (layer)
682             layer->scrollRectToVisible(enclosingIntRect(rangeRect), false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded);
683     }
684 }
685
686 - (BOOL)_needsLayout
687 {
688     return _private->coreFrame->view() ? _private->coreFrame->view()->needsLayout() : false;
689 }
690
691 - (DOMRange *)_rangeByAlteringCurrentSelection:(SelectionController::EAlteration)alteration direction:(SelectionDirection)direction granularity:(TextGranularity)granularity
692 {
693     if (_private->coreFrame->selection()->isNone())
694         return nil;
695
696     SelectionController selection;
697     selection.setSelection(_private->coreFrame->selection()->selection());
698     selection.modify(alteration, direction, granularity);
699     return kit(selection.toNormalizedRange().get());
700 }
701
702 - (TextGranularity)_selectionGranularity
703 {
704     return _private->coreFrame->selection()->granularity();
705 }
706
707 - (NSRange)_convertToNSRange:(Range *)range
708 {
709     if (!range || !range->startContainer())
710         return NSMakeRange(NSNotFound, 0);
711
712     Element* selectionRoot = _private->coreFrame->selection()->rootEditableElement();
713     Element* scope = selectionRoot ? selectionRoot : _private->coreFrame->document()->documentElement();
714     
715     // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view
716     // that is not inside the current editable region.  These checks ensure we don't produce
717     // potentially invalid data when responding to such requests.
718     if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope))
719         return NSMakeRange(NSNotFound, 0);
720     if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope))
721         return NSMakeRange(NSNotFound, 0);
722     
723     RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset());
724     ASSERT(testRange->startContainer() == scope);
725     int startPosition = TextIterator::rangeLength(testRange.get());
726
727     ExceptionCode ec;
728     testRange->setEnd(range->endContainer(), range->endOffset(), ec);
729     ASSERT(testRange->startContainer() == scope);
730     int endPosition = TextIterator::rangeLength(testRange.get());
731
732     return NSMakeRange(startPosition, endPosition - startPosition);
733 }
734
735 - (PassRefPtr<Range>)_convertToDOMRange:(NSRange)nsrange
736 {
737     if (nsrange.location > INT_MAX)
738         return 0;
739     if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
740         nsrange.length = INT_MAX - nsrange.location;
741
742     // our critical assumption is that we are only called by input methods that
743     // concentrate on a given area containing the selection
744     // We have to do this because of text fields and textareas. The DOM for those is not
745     // directly in the document DOM, so serialization is problematic. Our solution is
746     // to use the root editable element of the selection start as the positional base.
747     // That fits with AppKit's idea of an input context.
748     Element* selectionRoot = _private->coreFrame->selection()->rootEditableElement();
749     Element* scope = selectionRoot ? selectionRoot : _private->coreFrame->document()->documentElement();
750     return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
751 }
752
753 - (DOMRange *)convertNSRangeToDOMRange:(NSRange)nsrange
754 {
755     // This method exists to maintain compatibility with Leopard's Dictionary.app. <rdar://problem/6002160>
756     return [self _convertNSRangeToDOMRange:nsrange];
757 }
758
759 - (DOMRange *)_convertNSRangeToDOMRange:(NSRange)nsrange
760 {
761     return kit([self _convertToDOMRange:nsrange].get());
762 }
763
764 - (NSRange)convertDOMRangeToNSRange:(DOMRange *)range
765 {
766     // This method exists to maintain compatibility with Leopard's Dictionary.app. <rdar://problem/6002160>
767     return [self _convertDOMRangeToNSRange:range];
768 }
769
770 - (NSRange)_convertDOMRangeToNSRange:(DOMRange *)range
771 {
772     return [self _convertToNSRange:core(range)];
773 }
774
775 - (DOMRange *)_markDOMRange
776 {
777     return kit(_private->coreFrame->editor()->mark().toNormalizedRange().get());
778 }
779
780 // Given proposedRange, returns an extended range that includes adjacent whitespace that should
781 // be deleted along with the proposed range in order to preserve proper spacing and punctuation of
782 // the text surrounding the deletion.
783 - (DOMRange *)_smartDeleteRangeForProposedRange:(DOMRange *)proposedRange
784 {
785     Node* startContainer = core([proposedRange startContainer]);
786     Node* endContainer = core([proposedRange endContainer]);
787     if (startContainer == nil || endContainer == nil)
788         return nil;
789
790     ASSERT(startContainer->document() == endContainer->document());
791     
792     _private->coreFrame->document()->updateLayoutIgnorePendingStylesheets();
793
794     Position start(startContainer, [proposedRange startOffset]);
795     Position end(endContainer, [proposedRange endOffset]);
796     Position newStart = start.upstream().leadingWhitespacePosition(DOWNSTREAM, true);
797     if (newStart.isNull())
798         newStart = start;
799     Position newEnd = end.downstream().trailingWhitespacePosition(DOWNSTREAM, true);
800     if (newEnd.isNull())
801         newEnd = end;
802
803     newStart = newStart.parentAnchoredEquivalent();
804     newEnd = newEnd.parentAnchoredEquivalent();
805
806     RefPtr<Range> range = _private->coreFrame->document()->createRange();
807     int exception = 0;
808     range->setStart(newStart.node(), newStart.deprecatedEditingOffset(), exception);
809     range->setEnd(newStart.node(), newStart.deprecatedEditingOffset(), exception);
810     return kit(range.get());
811 }
812
813 - (DOMDocumentFragment *)_documentFragmentWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString 
814 {
815     if (!_private->coreFrame || !_private->coreFrame->document())
816         return nil;
817
818     return kit(createFragmentFromMarkup(_private->coreFrame->document(), markupString, baseURLString, FragmentScriptingNotAllowed).get());
819 }
820
821 - (DOMDocumentFragment *)_documentFragmentWithNodesAsParagraphs:(NSArray *)nodes
822 {
823     if (!_private->coreFrame || !_private->coreFrame->document())
824         return nil;
825     
826     NSEnumerator *nodeEnum = [nodes objectEnumerator];
827     Vector<Node*> nodesVector;
828     DOMNode *node;
829     while ((node = [nodeEnum nextObject]))
830         nodesVector.append(core(node));
831     
832     return kit(createFragmentFromNodes(_private->coreFrame->document(), nodesVector).get());
833 }
834
835 - (void)_replaceSelectionWithNode:(DOMNode *)node selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle
836 {
837     DOMDocumentFragment *fragment = kit(_private->coreFrame->document()->createDocumentFragment().get());
838     [fragment appendChild:node];
839     [self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:matchStyle];
840 }
841
842 - (void)_insertParagraphSeparatorInQuotedContent
843 {
844     if (_private->coreFrame->selection()->isNone())
845         return;
846     
847     TypingCommand::insertParagraphSeparatorInQuotedContent(_private->coreFrame->document());
848     _private->coreFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
849 }
850
851 - (VisiblePosition)_visiblePositionForPoint:(NSPoint)point
852 {
853     // FIXME: Someone with access to Apple's sources could remove this needless wrapper call.
854     return _private->coreFrame->visiblePositionForPoint(IntPoint(point));
855 }
856
857 - (DOMRange *)_characterRangeAtPoint:(NSPoint)point
858 {
859     VisiblePosition position = [self _visiblePositionForPoint:point];
860     if (position.isNull())
861         return nil;
862     
863     VisiblePosition previous = position.previous();
864     if (previous.isNotNull()) {
865         DOMRange *previousCharacterRange = kit(makeRange(previous, position).get());
866         NSRect rect = [self _firstRectForDOMRange:previousCharacterRange];
867         if (NSPointInRect(point, rect))
868             return previousCharacterRange;
869     }
870
871     VisiblePosition next = position.next();
872     if (next.isNotNull()) {
873         DOMRange *nextCharacterRange = kit(makeRange(position, next).get());
874         NSRect rect = [self _firstRectForDOMRange:nextCharacterRange];
875         if (NSPointInRect(point, rect))
876             return nextCharacterRange;
877     }
878     
879     return nil;
880 }
881
882 - (DOMCSSStyleDeclaration *)_typingStyle
883 {
884     if (!_private->coreFrame)
885         return nil;
886     RefPtr<CSSMutableStyleDeclaration> typingStyle = _private->coreFrame->selection()->copyTypingStyle();
887     if (!typingStyle)
888         return nil;
889     return kit(typingStyle.get());
890 }
891
892 - (void)_setTypingStyle:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction
893 {
894     if (!_private->coreFrame)
895         return;
896     _private->coreFrame->editor()->computeAndSetTypingStyle(core(style), undoAction);
897 }
898
899 - (void)_dragSourceEndedAt:(NSPoint)windowLoc operation:(NSDragOperation)operation
900 {
901     if (!_private->coreFrame)
902         return;
903     FrameView* view = _private->coreFrame->view();
904     if (!view)
905         return;
906     ASSERT([getWebView(self) _usesDocumentViews]);
907     // FIXME: These are fake modifier keys here, but they should be real ones instead.
908     PlatformMouseEvent event(IntPoint(windowLoc), globalPoint(windowLoc, [view->platformWidget() window]),
909         LeftButton, MouseEventMoved, 0, false, false, false, false, currentTime());
910     _private->coreFrame->eventHandler()->dragSourceEndedAt(event, (DragOperation)operation);
911 }
912
913 - (BOOL)_canProvideDocumentSource
914 {
915     Frame* frame = _private->coreFrame;
916     String mimeType = frame->loader()->writer()->mimeType();
917     PluginData* pluginData = frame->page() ? frame->page()->pluginData() : 0;
918
919     if (WebCore::DOMImplementation::isTextMIMEType(mimeType) ||
920         Image::supportsType(mimeType) ||
921         (pluginData && pluginData->supportsMimeType(mimeType)))
922         return NO;
923
924     return YES;
925 }
926
927 - (BOOL)_canSaveAsWebArchive
928 {
929     // Currently, all documents that we can view source for
930     // (HTML and XML documents) can also be saved as web archives
931     return [self _canProvideDocumentSource];
932 }
933
934 - (void)_commitData:(NSData *)data
935 {
936     // FIXME: This really should be a setting.
937     Document* document = _private->coreFrame->document();
938     document->setShouldCreateRenderers(_private->shouldCreateRenderers);
939
940     _private->coreFrame->loader()->documentLoader()->commitData((const char *)[data bytes], [data length]);
941 }
942
943 @end
944
945 @implementation WebFrame (WebPrivate)
946
947 // FIXME: This exists only as a convenience for Safari, consider moving there.
948 - (BOOL)_isDescendantOfFrame:(WebFrame *)ancestor
949 {
950     Frame* coreFrame = _private->coreFrame;
951     return coreFrame && coreFrame->tree()->isDescendantOf(core(ancestor));
952 }
953
954 - (void)_setShouldCreateRenderers:(BOOL)shouldCreateRenderers
955 {
956     _private->shouldCreateRenderers = shouldCreateRenderers;
957 }
958
959 - (NSColor *)_bodyBackgroundColor
960 {
961     Document* document = _private->coreFrame->document();
962     if (!document)
963         return nil;
964     HTMLElement* body = document->body();
965     if (!body)
966         return nil;
967     RenderObject* bodyRenderer = body->renderer();
968     if (!bodyRenderer)
969         return nil;
970     Color color = bodyRenderer->style()->visitedDependentColor(CSSPropertyBackgroundColor);
971     if (!color.isValid())
972         return nil;
973     return nsColor(color);
974 }
975
976 - (BOOL)_isFrameSet
977 {
978     Document* document = _private->coreFrame->document();
979     return document && document->isFrameSet();
980 }
981
982 - (BOOL)_firstLayoutDone
983 {
984     return _private->coreFrame->loader()->stateMachine()->firstLayoutDone();
985 }
986
987 - (WebFrameLoadType)_loadType
988 {
989     return (WebFrameLoadType)_private->coreFrame->loader()->loadType();
990 }
991
992 - (NSRange)_selectedNSRange
993 {
994     return [self _convertToNSRange:_private->coreFrame->selection()->toNormalizedRange().get()];
995 }
996
997 - (void)_selectNSRange:(NSRange)range
998 {
999     RefPtr<Range> domRange = [self _convertToDOMRange:range];
1000     if (domRange)
1001         _private->coreFrame->selection()->setSelection(VisibleSelection(domRange.get(), SEL_DEFAULT_AFFINITY));
1002 }
1003
1004 - (BOOL)_isDisplayingStandaloneImage
1005 {
1006     Document* document = _private->coreFrame->document();
1007     return document && document->isImageDocument();
1008 }
1009
1010 - (unsigned)_pendingFrameUnloadEventCount
1011 {
1012     return _private->coreFrame->domWindow()->pendingUnloadEventListeners();
1013 }
1014
1015 - (void)_setIsDisconnected:(bool)isDisconnected
1016 {
1017     _private->coreFrame->setIsDisconnected(isDisconnected);
1018 }
1019
1020 - (void)_setExcludeFromTextSearch:(bool)exclude
1021 {
1022     _private->coreFrame->setExcludeFromTextSearch(exclude);
1023 }
1024
1025 #if ENABLE(NETSCAPE_PLUGIN_API)
1026 - (void)_recursive_resumeNullEventsForAllNetscapePlugins
1027 {
1028     Frame* coreFrame = core(self);
1029     for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) {
1030         NSView <WebDocumentView> *documentView = [[kit(frame) frameView] documentView];
1031         if ([documentView isKindOfClass:[WebHTMLView class]])
1032             [(WebHTMLView *)documentView _resumeNullEventsForAllNetscapePlugins];
1033     }
1034 }
1035
1036 - (void)_recursive_pauseNullEventsForAllNetscapePlugins
1037 {
1038     Frame* coreFrame = core(self);
1039     for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) {
1040         NSView <WebDocumentView> *documentView = [[kit(frame) frameView] documentView];
1041         if ([documentView isKindOfClass:[WebHTMLView class]])
1042             [(WebHTMLView *)documentView _pauseNullEventsForAllNetscapePlugins];
1043     }
1044 }
1045 #endif
1046
1047 - (BOOL)_pauseAnimation:(NSString*)name onNode:(DOMNode *)node atTime:(NSTimeInterval)time
1048 {
1049     Frame* frame = core(self);
1050     if (!frame)
1051         return false;
1052
1053     AnimationController* controller = frame->animation();
1054     if (!controller)
1055         return false;
1056
1057     Node* coreNode = core(node);
1058     if (!coreNode || !coreNode->renderer())
1059         return false;
1060
1061     return controller->pauseAnimationAtTime(coreNode->renderer(), name, time);
1062 }
1063
1064 - (BOOL)_pauseTransitionOfProperty:(NSString*)name onNode:(DOMNode*)node atTime:(NSTimeInterval)time
1065 {
1066     Frame* frame = core(self);
1067     if (!frame)
1068         return false;
1069
1070     AnimationController* controller = frame->animation();
1071     if (!controller)
1072         return false;
1073
1074     Node* coreNode = core(node);
1075     if (!coreNode || !coreNode->renderer())
1076         return false;
1077
1078     return controller->pauseTransitionAtTime(coreNode->renderer(), name, time);
1079 }
1080
1081 // Pause a given SVG animation on the target node at a specific time.
1082 // This method is only intended to be used for testing the SVG animation system.
1083 - (BOOL)_pauseSVGAnimation:(NSString*)elementId onSMILNode:(DOMNode *)node atTime:(NSTimeInterval)time
1084 {
1085     Frame* frame = core(self);
1086     if (!frame)
1087         return false;
1088  
1089     Document* document = frame->document();
1090     if (!document || !document->svgExtensions())
1091         return false;
1092
1093     Node* coreNode = core(node);
1094     if (!coreNode || !SVGSMILElement::isSMILElement(coreNode))
1095         return false;
1096
1097 #if ENABLE(SVG)
1098     return document->accessSVGExtensions()->sampleAnimationAtTime(elementId, static_cast<SVGSMILElement*>(coreNode), time);
1099 #else
1100     return false;
1101 #endif
1102 }
1103
1104 - (unsigned) _numberOfActiveAnimations
1105 {
1106     Frame* frame = core(self);
1107     if (!frame)
1108         return false;
1109
1110     AnimationController* controller = frame->animation();
1111     if (!controller)
1112         return false;
1113
1114     return controller->numberOfActiveAnimations();
1115 }
1116
1117 - (void) _suspendAnimations
1118 {
1119     Frame* frame = core(self);
1120     if (!frame)
1121         return;
1122         
1123     frame->animation()->suspendAnimations();
1124 }
1125
1126 - (void) _resumeAnimations
1127 {
1128     Frame* frame = core(self);
1129     if (!frame)
1130         return;
1131
1132     frame->animation()->resumeAnimations();
1133 }
1134
1135 - (void)_replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle
1136 {
1137     if (_private->coreFrame->selection()->isNone() || !fragment)
1138         return;
1139     
1140     applyCommand(ReplaceSelectionCommand::create(_private->coreFrame->document(), core(fragment), selectReplacement, smartReplace, matchStyle));
1141     _private->coreFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
1142 }
1143
1144 - (void)_replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace
1145 {   
1146     DOMDocumentFragment* fragment = kit(createFragmentFromText(_private->coreFrame->selection()->toNormalizedRange().get(), text).get());
1147     [self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:YES];
1148 }
1149
1150 - (void)_replaceSelectionWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace
1151 {
1152     DOMDocumentFragment *fragment = [self _documentFragmentWithMarkupString:markupString baseURLString:baseURLString];
1153     [self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:NO];
1154 }
1155
1156 // Determines whether whitespace needs to be added around aString to preserve proper spacing and
1157 // punctuation when it's inserted into the receiver's text over charRange. Returns by reference
1158 // in beforeString and afterString any whitespace that should be added, unless either or both are
1159 // nil. Both are returned as nil if aString is nil or if smart insertion and deletion are disabled.
1160 - (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString
1161 {
1162     // give back nil pointers in case of early returns
1163     if (beforeString)
1164         *beforeString = nil;
1165     if (afterString)
1166         *afterString = nil;
1167         
1168     // inspect destination
1169     Node *startContainer = core([rangeToReplace startContainer]);
1170     Node *endContainer = core([rangeToReplace endContainer]);
1171
1172     Position startPos(startContainer, [rangeToReplace startOffset]);
1173     Position endPos(endContainer, [rangeToReplace endOffset]);
1174
1175     VisiblePosition startVisiblePos = VisiblePosition(startPos, VP_DEFAULT_AFFINITY);
1176     VisiblePosition endVisiblePos = VisiblePosition(endPos, VP_DEFAULT_AFFINITY);
1177     
1178     // this check also ensures startContainer, startPos, endContainer, and endPos are non-null
1179     if (startVisiblePos.isNull() || endVisiblePos.isNull())
1180         return;
1181
1182     bool addLeadingSpace = startPos.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull() && !isStartOfParagraph(startVisiblePos);
1183     if (addLeadingSpace)
1184         if (UChar previousChar = startVisiblePos.previous().characterAfter())
1185             addLeadingSpace = !isCharacterSmartReplaceExempt(previousChar, true);
1186     
1187     bool addTrailingSpace = endPos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull() && !isEndOfParagraph(endVisiblePos);
1188     if (addTrailingSpace)
1189         if (UChar thisChar = endVisiblePos.characterAfter())
1190             addTrailingSpace = !isCharacterSmartReplaceExempt(thisChar, false);
1191     
1192     // inspect source
1193     bool hasWhitespaceAtStart = false;
1194     bool hasWhitespaceAtEnd = false;
1195     unsigned pasteLength = [pasteString length];
1196     if (pasteLength > 0) {
1197         NSCharacterSet *whiteSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
1198         
1199         if ([whiteSet characterIsMember:[pasteString characterAtIndex:0]]) {
1200             hasWhitespaceAtStart = YES;
1201         }
1202         if ([whiteSet characterIsMember:[pasteString characterAtIndex:(pasteLength - 1)]]) {
1203             hasWhitespaceAtEnd = YES;
1204         }
1205     }
1206     
1207     // issue the verdict
1208     if (beforeString && addLeadingSpace && !hasWhitespaceAtStart)
1209         *beforeString = @" ";
1210     if (afterString && addTrailingSpace && !hasWhitespaceAtEnd)
1211         *afterString = @" ";
1212 }
1213
1214 - (NSMutableDictionary *)_cacheabilityDictionary
1215 {
1216     NSMutableDictionary *result = [NSMutableDictionary dictionary];
1217     
1218     FrameLoader* frameLoader = _private->coreFrame->loader();
1219     DocumentLoader* documentLoader = frameLoader->documentLoader();
1220     if (documentLoader && !documentLoader->mainDocumentError().isNull())
1221         [result setObject:(NSError *)documentLoader->mainDocumentError() forKey:WebFrameMainDocumentError];
1222         
1223     if (frameLoader->subframeLoader()->containsPlugins())
1224         [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameHasPlugins];
1225     
1226     if (DOMWindow* domWindow = _private->coreFrame->domWindow()) {
1227         if (domWindow->hasEventListeners(eventNames().unloadEvent))
1228             [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameHasUnloadListener];
1229             
1230 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
1231         if (domWindow->optionalApplicationCache())
1232             [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameUsesApplicationCache];
1233 #endif
1234     }
1235     
1236     if (Document* document = _private->coreFrame->document()) {
1237 #if ENABLE(DATABASE)
1238         if (document->hasOpenDatabases())
1239             [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameUsesDatabases];
1240 #endif
1241             
1242         if (document->usingGeolocation())
1243             [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameUsesGeolocation];
1244             
1245         if (!document->canSuspendActiveDOMObjects())
1246             [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameCanSuspendActiveDOMObjects];
1247     }
1248     
1249     return result;
1250 }
1251
1252 - (BOOL)_allowsFollowingLink:(NSURL *)URL
1253 {
1254     if (!_private->coreFrame)
1255         return YES;
1256     return _private->coreFrame->document()->securityOrigin()->canDisplay(URL);
1257 }
1258
1259 - (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string withGlobalObject:(JSObjectRef)globalObjectRef inScriptWorld:(WebScriptWorld *)world
1260 {
1261     // Start off with some guess at a frame and a global object, we'll try to do better...!
1262     JSDOMWindow* anyWorldGlobalObject = _private->coreFrame->script()->globalObject(mainThreadNormalWorld());
1263
1264     // The global object is probably a shell object? - if so, we know how to use this!
1265     JSC::JSObject* globalObjectObj = toJS(globalObjectRef);
1266     if (!strcmp(globalObjectObj->classInfo()->className, "JSDOMWindowShell"))
1267         anyWorldGlobalObject = static_cast<JSDOMWindowShell*>(globalObjectObj)->window();
1268
1269     // Get the frame frome the global object we've settled on.
1270     Frame* frame = anyWorldGlobalObject->impl()->frame();
1271     ASSERT(frame->document());
1272     JSValue result = frame->script()->executeScriptInWorld(core(world), string, true).jsValue();
1273
1274     if (!frame) // In case the script removed our frame from the page.
1275         return @"";
1276
1277     // This bizarre set of rules matches behavior from WebKit for Safari 2.0.
1278     // If you don't like it, use -[WebScriptObject evaluateWebScript:] or 
1279     // JSEvaluateScript instead, since they have less surprising semantics.
1280     if (!result || (!result.isBoolean() && !result.isString() && !result.isNumber()))
1281         return @"";
1282
1283     JSLock lock(SilenceAssertionsOnly);
1284     return ustringToString(result.toString(anyWorldGlobalObject->globalExec()));
1285 }
1286
1287 - (JSGlobalContextRef)_globalContextForScriptWorld:(WebScriptWorld *)world
1288 {
1289     Frame* coreFrame = _private->coreFrame;
1290     if (!coreFrame)
1291         return 0;
1292     DOMWrapperWorld* coreWorld = core(world);
1293     if (!coreWorld)
1294         return 0;
1295     return toGlobalRef(coreFrame->script()->globalObject(coreWorld)->globalExec());
1296 }
1297
1298 - (void)setAllowsScrollersToOverlapContent:(BOOL)flag
1299 {
1300     ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]);
1301     [(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAllowsScrollersToOverlapContent:flag];
1302 }
1303
1304 - (void)setAlwaysHideHorizontalScroller:(BOOL)flag
1305 {
1306     ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]);
1307     [(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAlwaysHideHorizontalScroller:flag];
1308 }
1309 - (void)setAlwaysHideVerticalScroller:(BOOL)flag
1310 {
1311     ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]);
1312     [(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAlwaysHideVerticalScroller:flag];
1313 }
1314
1315 - (void)setAccessibleName:(NSString *)name
1316 {
1317 #if HAVE(ACCESSIBILITY)
1318     if (!AXObjectCache::accessibilityEnabled())
1319         return;
1320     
1321     if (!_private->coreFrame || !_private->coreFrame->document())
1322         return;
1323     
1324     AccessibilityObject* rootObject = _private->coreFrame->document()->axObjectCache()->rootObject();
1325     if (rootObject) {
1326         String strName(name);
1327         rootObject->setAccessibleName(strName);
1328     }
1329 #endif
1330 }
1331
1332 - (NSString*)_layerTreeAsText
1333 {
1334     Frame* coreFrame = _private->coreFrame;
1335     if (!coreFrame)
1336         return @"";
1337
1338     return coreFrame->layerTreeAsText();
1339 }
1340
1341 - (BOOL)hasSpellingMarker:(int)from length:(int)length
1342 {
1343     Frame* coreFrame = _private->coreFrame;
1344     if (!coreFrame)
1345         return NO;
1346     return coreFrame->editor()->selectionStartHasSpellingMarkerFor(from, length);
1347 }
1348
1349 - (id)accessibilityRoot
1350 {
1351 #if HAVE(ACCESSIBILITY)
1352     if (!AXObjectCache::accessibilityEnabled()) {
1353         AXObjectCache::enableAccessibility();
1354         if ([[NSApp accessibilityAttributeValue:NSAccessibilityEnhancedUserInterfaceAttribute] boolValue])
1355             AXObjectCache::enableEnhancedUserInterfaceAccessibility();
1356     }
1357     
1358     if (!_private->coreFrame || !_private->coreFrame->document())
1359         return nil;
1360     
1361     AccessibilityObject* rootObject = _private->coreFrame->document()->axObjectCache()->rootObject();
1362     if (rootObject)
1363         return rootObject->wrapper();
1364     return nil;
1365 #else
1366     return nil;
1367 #endif
1368 }
1369
1370 @end
1371
1372 @implementation WebFrame
1373
1374 - (id)init
1375 {
1376     return nil;
1377 }
1378
1379 // Should be deprecated.
1380 - (id)initWithName:(NSString *)name webFrameView:(WebFrameView *)view webView:(WebView *)webView
1381 {
1382     return nil;
1383 }
1384
1385 - (void)dealloc
1386 {
1387     if (_private && _private->includedInWebKitStatistics)
1388         --WebFrameCount;
1389
1390     [_private release];
1391
1392     [super dealloc];
1393 }
1394
1395 - (void)finalize
1396 {
1397     if (_private && _private->includedInWebKitStatistics)
1398         --WebFrameCount;
1399
1400     [super finalize];
1401 }
1402
1403 - (NSString *)name
1404 {
1405     Frame* coreFrame = _private->coreFrame;
1406     if (!coreFrame)
1407         return nil;
1408     return coreFrame->tree()->uniqueName();
1409 }
1410
1411 - (WebFrameView *)frameView
1412 {
1413     ASSERT(!getWebView(self) || [getWebView(self) _usesDocumentViews]);
1414     return _private->webFrameView;
1415 }
1416
1417 - (WebView *)webView
1418 {
1419     return getWebView(self);
1420 }
1421
1422 static bool needsMicrosoftMessengerDOMDocumentWorkaround()
1423 {
1424     static bool needsWorkaround = applicationIsMicrosoftMessenger() && [[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] compare:@"7.1" options:NSNumericSearch] == NSOrderedAscending;
1425     return needsWorkaround;
1426 }
1427
1428 - (DOMDocument *)DOMDocument
1429 {
1430     if (needsMicrosoftMessengerDOMDocumentWorkaround() && !pthread_main_np())
1431         return nil;
1432
1433     Frame* coreFrame = _private->coreFrame;
1434     if (!coreFrame)
1435         return nil;
1436     
1437     // FIXME: <rdar://problem/5145841> When loading a custom view/representation 
1438     // into a web frame, the old document can still be around. This makes sure that
1439     // we'll return nil in those cases.
1440     if (![[self _dataSource] _isDocumentHTML]) 
1441         return nil; 
1442
1443     Document* document = coreFrame->document();
1444     
1445     // According to the documentation, we should return nil if the frame doesn't have a document.
1446     // While full-frame images and plugins do have an underlying HTML document, we return nil here to be
1447     // backwards compatible.
1448     if (document && (document->isPluginDocument() || document->isImageDocument()))
1449         return nil;
1450     
1451     return kit(coreFrame->document());
1452 }
1453
1454 - (DOMHTMLElement *)frameElement
1455 {
1456     Frame* coreFrame = _private->coreFrame;
1457     if (!coreFrame)
1458         return nil;
1459     return kit(coreFrame->ownerElement());
1460 }
1461
1462 - (WebDataSource *)provisionalDataSource
1463 {
1464     Frame* coreFrame = _private->coreFrame;
1465     return coreFrame ? dataSource(coreFrame->loader()->provisionalDocumentLoader()) : nil;
1466 }
1467
1468 - (WebDataSource *)dataSource
1469 {
1470     Frame* coreFrame = _private->coreFrame;
1471     return coreFrame && coreFrame->loader()->frameHasLoaded() ? [self _dataSource] : nil;
1472 }
1473
1474 - (void)loadRequest:(NSURLRequest *)request
1475 {
1476     Frame* coreFrame = _private->coreFrame;
1477     if (!coreFrame)
1478         return;
1479     coreFrame->loader()->load(request, false);
1480 }
1481
1482 static NSURL *createUniqueWebDataURL()
1483 {
1484     CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
1485     NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
1486     CFRelease(UUIDRef);
1487     NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"applewebdata://%@", UUIDString]];
1488     CFRelease(UUIDString);
1489     return URL;
1490 }
1491
1492 - (void)_loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL unreachableURL:(NSURL *)unreachableURL
1493 {
1494     if (!pthread_main_np())
1495         return [[self _webkit_invokeOnMainThread] _loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL unreachableURL:unreachableURL];
1496     
1497     KURL responseURL;
1498     if (!baseURL) {
1499         baseURL = blankURL();
1500         responseURL = createUniqueWebDataURL();
1501     }
1502     
1503     ResourceRequest request([baseURL absoluteURL]);
1504
1505     // hack because Mail checks for this property to detect data / archive loads
1506     [NSURLProtocol setProperty:@"" forKey:@"WebDataRequest" inRequest:(NSMutableURLRequest *)request.nsURLRequest()];
1507
1508     SubstituteData substituteData(WebCore::SharedBuffer::wrapNSData(data), MIMEType, encodingName, [unreachableURL absoluteURL], responseURL);
1509
1510     _private->coreFrame->loader()->load(request, substituteData, false);
1511 }
1512
1513
1514 - (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
1515 {
1516     WebCoreThreadViolationCheckRoundTwo();
1517     
1518     if (!MIMEType)
1519         MIMEType = @"text/html";
1520     [self _loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL unreachableURL:nil];
1521 }
1522
1523 - (void)_loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL unreachableURL:(NSURL *)unreachableURL
1524 {
1525     NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
1526     [self _loadData:data MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:baseURL unreachableURL:unreachableURL];
1527 }
1528
1529 - (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
1530 {
1531     WebCoreThreadViolationCheckRoundTwo();
1532
1533     [self _loadHTMLString:string baseURL:baseURL unreachableURL:nil];
1534 }
1535
1536 - (void)loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
1537 {
1538     WebCoreThreadViolationCheckRoundTwo();
1539
1540     [self _loadHTMLString:string baseURL:baseURL unreachableURL:unreachableURL];
1541 }
1542
1543 - (void)loadArchive:(WebArchive *)archive
1544 {
1545     if (LegacyWebArchive* coreArchive = [archive _coreLegacyWebArchive])
1546         _private->coreFrame->loader()->loadArchive(coreArchive);
1547 }
1548
1549 - (void)stopLoading
1550 {
1551     if (!_private->coreFrame)
1552         return;
1553     _private->coreFrame->loader()->stopForUserCancel();
1554 }
1555
1556 - (void)reload
1557 {
1558     if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_RELOAD_FROM_ORIGIN) && applicationIsSafari())
1559         _private->coreFrame->loader()->reload(GetCurrentKeyModifiers() & shiftKey);
1560     else
1561         _private->coreFrame->loader()->reload(false);
1562 }
1563
1564 - (void)reloadFromOrigin
1565 {
1566     _private->coreFrame->loader()->reload(true);
1567 }
1568
1569 - (WebFrame *)findFrameNamed:(NSString *)name
1570 {
1571     Frame* coreFrame = _private->coreFrame;
1572     if (!coreFrame)
1573         return nil;
1574     return kit(coreFrame->tree()->find(name));
1575 }
1576
1577 - (WebFrame *)parentFrame
1578 {
1579     Frame* coreFrame = _private->coreFrame;
1580     if (!coreFrame)
1581         return nil;
1582     return [[kit(coreFrame->tree()->parent()) retain] autorelease];
1583 }
1584
1585 - (NSArray *)childFrames
1586 {
1587     Frame* coreFrame = _private->coreFrame;
1588     if (!coreFrame)
1589         return [NSArray array];
1590     NSMutableArray *children = [NSMutableArray arrayWithCapacity:coreFrame->tree()->childCount()];
1591     for (Frame* child = coreFrame->tree()->firstChild(); child; child = child->tree()->nextSibling())
1592         [children addObject:kit(child)];
1593     return children;
1594 }
1595
1596 - (WebScriptObject *)windowObject
1597 {
1598     Frame* coreFrame = _private->coreFrame;
1599     if (!coreFrame)
1600         return 0;
1601     return coreFrame->script()->windowScriptObject();
1602 }
1603
1604 - (JSGlobalContextRef)globalContext
1605 {
1606     Frame* coreFrame = _private->coreFrame;
1607     if (!coreFrame)
1608         return 0;
1609     return toGlobalRef(coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec());
1610 }
1611
1612 @end