diff -r 000000000000 -r 4f2f89ce4247 WebKit/mac/WebView/WebFrame.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebKit/mac/WebView/WebFrame.mm Fri Sep 17 09:02:29 2010 +0300 @@ -0,0 +1,1585 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "WebFrameInternal.h" + +#import "DOMCSSStyleDeclarationInternal.h" +#import "DOMDocumentFragmentInternal.h" +#import "DOMDocumentInternal.h" +#import "DOMElementInternal.h" +#import "DOMHTMLElementInternal.h" +#import "DOMNodeInternal.h" +#import "DOMRangeInternal.h" +#import "WebArchiveInternal.h" +#import "WebChromeClient.h" +#import "WebDataSourceInternal.h" +#import "WebDocumentLoaderMac.h" +#import "WebDynamicScrollBarsView.h" +#import "WebFrameLoaderClient.h" +#import "WebFrameViewInternal.h" +#import "WebHTMLView.h" +#import "WebHTMLViewInternal.h" +#import "WebIconFetcherInternal.h" +#import "WebKitStatisticsPrivate.h" +#import "WebKitVersionChecks.h" +#import "WebNSObjectExtras.h" +#import "WebNSURLExtras.h" +#import "WebScriptDebugger.h" +#import "WebScriptWorldInternal.h" +#import "WebViewInternal.h" +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +using namespace std; +using namespace WebCore; +using namespace HTMLNames; + +using JSC::JSGlobalObject; +using JSC::JSLock; +using JSC::JSValue; +using JSC::SilenceAssertionsOnly; + +/* +Here is the current behavior matrix for four types of navigations: + +Standard Nav: + + Restore form state: YES + Restore scroll and focus state: YES + Cache policy: NSURLRequestUseProtocolCachePolicy + Add to back/forward list: YES + +Back/Forward: + + Restore form state: YES + Restore scroll and focus state: YES + Cache policy: NSURLRequestReturnCacheDataElseLoad + Add to back/forward list: NO + +Reload (meaning only the reload button): + + Restore form state: NO + Restore scroll and focus state: YES + Cache policy: NSURLRequestReloadIgnoringCacheData + Add to back/forward list: NO + +Repeat load of the same URL (by any other means of navigation other than the reload button, including hitting return in the location field): + + Restore form state: NO + Restore scroll and focus state: NO, reset to initial conditions + Cache policy: NSURLRequestReloadIgnoringCacheData + Add to back/forward list: NO +*/ + +NSString *WebPageCacheEntryDateKey = @"WebPageCacheEntryDateKey"; +NSString *WebPageCacheDataSourceKey = @"WebPageCacheDataSourceKey"; +NSString *WebPageCacheDocumentViewKey = @"WebPageCacheDocumentViewKey"; + +NSString *WebFrameMainDocumentError = @"WebFrameMainDocumentErrorKey"; +NSString *WebFrameHasPlugins = @"WebFrameHasPluginsKey"; +NSString *WebFrameHasUnloadListener = @"WebFrameHasUnloadListenerKey"; +NSString *WebFrameUsesDatabases = @"WebFrameUsesDatabasesKey"; +NSString *WebFrameUsesGeolocation = @"WebFrameUsesGeolocationKey"; +NSString *WebFrameUsesApplicationCache = @"WebFrameUsesApplicationCacheKey"; +NSString *WebFrameCanSuspendActiveDOMObjects = @"WebFrameCanSuspendActiveDOMObjectsKey"; + +// FIXME: Remove when this key becomes publicly defined +NSString *NSAccessibilityEnhancedUserInterfaceAttribute = @"AXEnhancedUserInterface"; + +@implementation WebFramePrivate + +- (void)dealloc +{ + [webFrameView release]; + + delete scriptDebugger; + + [super dealloc]; +} + +- (void)finalize +{ + delete scriptDebugger; + + [super finalize]; +} + +- (void)setWebFrameView:(WebFrameView *)v +{ + [v retain]; + [webFrameView release]; + webFrameView = v; +} + +@end + +EditableLinkBehavior core(WebKitEditableLinkBehavior editableLinkBehavior) +{ + switch (editableLinkBehavior) { + case WebKitEditableLinkDefaultBehavior: + return EditableLinkDefaultBehavior; + case WebKitEditableLinkAlwaysLive: + return EditableLinkAlwaysLive; + case WebKitEditableLinkOnlyLiveWithShiftKey: + return EditableLinkOnlyLiveWithShiftKey; + case WebKitEditableLinkLiveWhenNotFocused: + return EditableLinkLiveWhenNotFocused; + case WebKitEditableLinkNeverLive: + return EditableLinkNeverLive; + } + ASSERT_NOT_REACHED(); + return EditableLinkDefaultBehavior; +} + +WebCore::EditingBehaviorType core(WebKitEditingBehavior behavior) +{ + switch (behavior) { + case WebKitEditingMacBehavior: + return WebCore::EditingMacBehavior; + case WebKitEditingWinBehavior: + return WebCore::EditingWindowsBehavior; + } + ASSERT_NOT_REACHED(); + return WebCore::EditingMacBehavior; +} + +TextDirectionSubmenuInclusionBehavior core(WebTextDirectionSubmenuInclusionBehavior behavior) +{ + switch (behavior) { + case WebTextDirectionSubmenuNeverIncluded: + return TextDirectionSubmenuNeverIncluded; + case WebTextDirectionSubmenuAutomaticallyIncluded: + return TextDirectionSubmenuAutomaticallyIncluded; + case WebTextDirectionSubmenuAlwaysIncluded: + return TextDirectionSubmenuAlwaysIncluded; + } + ASSERT_NOT_REACHED(); + return TextDirectionSubmenuNeverIncluded; +} + +@implementation WebFrame (WebInternal) + +Frame* core(WebFrame *frame) +{ + return frame ? frame->_private->coreFrame : 0; +} + +WebFrame *kit(Frame* frame) +{ + return frame ? static_cast(frame->loader()->client())->webFrame() : nil; +} + +Page* core(WebView *webView) +{ + return [webView page]; +} + +WebView *kit(Page* page) +{ + return page ? static_cast(page->chrome()->client())->webView() : nil; +} + +WebView *getWebView(WebFrame *webFrame) +{ + Frame* coreFrame = core(webFrame); + if (!coreFrame) + return nil; + return kit(coreFrame->page()); +} + ++ (PassRefPtr)_createFrameWithPage:(Page*)page frameName:(const String&)name frameView:(WebFrameView *)frameView ownerElement:(HTMLFrameOwnerElement*)ownerElement +{ + WebView *webView = kit(page); + + WebFrame *frame = [[self alloc] _initWithWebFrameView:frameView webView:webView]; + RefPtr coreFrame = Frame::create(page, ownerElement, new WebFrameLoaderClient(frame)); + [frame release]; + frame->_private->coreFrame = coreFrame.get(); + + coreFrame->tree()->setName(name); + if (ownerElement) { + ASSERT(ownerElement->document()->frame()); + ownerElement->document()->frame()->tree()->appendChild(coreFrame.get()); + } + + coreFrame->init(); + + [webView _setZoomMultiplier:[webView _realZoomMultiplier] isTextOnly:[webView _realZoomMultiplierIsTextOnly]]; + + return coreFrame.release(); +} + ++ (void)_createMainFrameWithPage:(Page*)page frameName:(const String&)name frameView:(WebFrameView *)frameView +{ + [self _createFrameWithPage:page frameName:name frameView:frameView ownerElement:0]; +} + ++ (PassRefPtr)_createSubframeWithOwnerElement:(HTMLFrameOwnerElement*)ownerElement frameName:(const String&)name frameView:(WebFrameView *)frameView +{ + return [self _createFrameWithPage:ownerElement->document()->frame()->page() frameName:name frameView:frameView ownerElement:ownerElement]; +} + +- (BOOL)_isIncludedInWebKitStatistics +{ + return _private && _private->includedInWebKitStatistics; +} + +- (void)_attachScriptDebugger +{ + ScriptController* scriptController = _private->coreFrame->script(); + + // Calling ScriptController::globalObject() would create a window shell, and dispatch corresponding callbacks, which may be premature + // if the script debugger is attached before a document is created. These calls use the debuggerWorld(), we will need to pass a world + // to be able to debug isolated worlds. + if (!scriptController->existingWindowShell(debuggerWorld())) + return; + + JSGlobalObject* globalObject = scriptController->globalObject(debuggerWorld()); + if (!globalObject) + return; + + if (_private->scriptDebugger) { + ASSERT(_private->scriptDebugger == globalObject->debugger()); + return; + } + + _private->scriptDebugger = new WebScriptDebugger(globalObject); +} + +- (void)_detachScriptDebugger +{ + if (!_private->scriptDebugger) + return; + + delete _private->scriptDebugger; + _private->scriptDebugger = 0; +} + +- (id)_initWithWebFrameView:(WebFrameView *)fv webView:(WebView *)v +{ + self = [super init]; + if (!self) + return nil; + + _private = [[WebFramePrivate alloc] init]; + + // Set includedInWebKitStatistics before calling WebFrameView _setWebFrame, since + // it calls WebFrame _isIncludedInWebKitStatistics. + if ((_private->includedInWebKitStatistics = [[v class] shouldIncludeInWebKitStatistics])) + ++WebFrameCount; + + if (fv) { + [_private setWebFrameView:fv]; + [fv _setWebFrame:self]; + } + + _private->shouldCreateRenderers = YES; + + return self; +} + +- (void)_clearCoreFrame +{ + _private->coreFrame = 0; +} + +- (void)_updateBackgroundAndUpdatesWhileOffscreen +{ + WebView *webView = getWebView(self); + BOOL drawsBackground = [webView drawsBackground]; + NSColor *backgroundColor = [webView backgroundColor]; + + Frame* coreFrame = _private->coreFrame; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + if ([webView _usesDocumentViews]) { + // Don't call setDrawsBackground:YES here because it may be NO because of a load + // in progress; WebFrameLoaderClient keeps it set to NO during the load process. + WebFrame *webFrame = kit(frame); + if (!drawsBackground) + [[[webFrame frameView] _scrollView] setDrawsBackground:NO]; + [[[webFrame frameView] _scrollView] setBackgroundColor:backgroundColor]; + id documentView = [[webFrame frameView] documentView]; + if ([documentView respondsToSelector:@selector(setDrawsBackground:)]) + [documentView setDrawsBackground:drawsBackground]; + if ([documentView respondsToSelector:@selector(setBackgroundColor:)]) + [documentView setBackgroundColor:backgroundColor]; + } + + if (FrameView* view = frame->view()) { + view->setTransparent(!drawsBackground); + view->setBaseBackgroundColor(colorFromNSColor([backgroundColor colorUsingColorSpaceName:NSDeviceRGBColorSpace])); + view->setShouldUpdateWhileOffscreen([webView shouldUpdateWhileOffscreen]); + } + } +} + +- (void)_setInternalLoadDelegate:(id)internalLoadDelegate +{ + _private->internalLoadDelegate = internalLoadDelegate; +} + +- (id)_internalLoadDelegate +{ + return _private->internalLoadDelegate; +} + +#ifndef BUILDING_ON_TIGER +- (void)_unmarkAllBadGrammar +{ + Frame* coreFrame = _private->coreFrame; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + if (Document* document = frame->document()) + document->removeMarkers(DocumentMarker::Grammar); + } +} +#endif + +- (void)_unmarkAllMisspellings +{ + Frame* coreFrame = _private->coreFrame; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + if (Document* document = frame->document()) + document->removeMarkers(DocumentMarker::Spelling); + } +} + +- (BOOL)_hasSelection +{ + if ([getWebView(self) _usesDocumentViews]) { + id documentView = [_private->webFrameView documentView]; + + // optimization for common case to avoid creating potentially large selection string + if ([documentView isKindOfClass:[WebHTMLView class]]) + if (Frame* coreFrame = _private->coreFrame) + return coreFrame->selection()->isRange(); + + if ([documentView conformsToProtocol:@protocol(WebDocumentText)]) + return [[documentView selectedString] length] > 0; + + return NO; + } + + Frame* coreFrame = _private->coreFrame; + return coreFrame && coreFrame->selection()->isRange(); +} + +- (void)_clearSelection +{ + ASSERT([getWebView(self) _usesDocumentViews]); + id documentView = [_private->webFrameView documentView]; + if ([documentView conformsToProtocol:@protocol(WebDocumentText)]) + [documentView deselectAll]; +} + +#if !ASSERT_DISABLED +- (BOOL)_atMostOneFrameHasSelection +{ + // FIXME: 4186050 is one known case that makes this debug check fail. + BOOL found = NO; + Frame* coreFrame = _private->coreFrame; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) + if ([kit(frame) _hasSelection]) { + if (found) + return NO; + found = YES; + } + return YES; +} +#endif + +- (WebFrame *)_findFrameWithSelection +{ + Frame* coreFrame = _private->coreFrame; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + WebFrame *webFrame = kit(frame); + if ([webFrame _hasSelection]) + return webFrame; + } + return nil; +} + +- (void)_clearSelectionInOtherFrames +{ + // We rely on WebDocumentSelection protocol implementors to call this method when they become first + // responder. It would be nicer to just notice first responder changes here instead, but there's no + // notification sent when the first responder changes in general (Radar 2573089). + WebFrame *frameWithSelection = [[getWebView(self) mainFrame] _findFrameWithSelection]; + if (frameWithSelection != self) + [frameWithSelection _clearSelection]; + + // While we're in the general area of selection and frames, check that there is only one now. + ASSERT([[getWebView(self) mainFrame] _atMostOneFrameHasSelection]); +} + +static inline WebDataSource *dataSource(DocumentLoader* loader) +{ + return loader ? static_cast(loader)->dataSource() : nil; +} + +- (WebDataSource *)_dataSource +{ + return dataSource(_private->coreFrame->loader()->documentLoader()); +} + +- (void)_addData:(NSData *)data +{ + Document* document = _private->coreFrame->document(); + + // Document may be nil if the part is about to redirect + // as a result of JS executing during load, i.e. one frame + // changing another's location before the frame's document + // has been created. + if (!document) + return; + + document->setShouldCreateRenderers(_private->shouldCreateRenderers); + _private->coreFrame->loader()->addData((const char *)[data bytes], [data length]); +} + +- (NSString *)_stringWithDocumentTypeStringAndMarkupString:(NSString *)markupString +{ + return _private->coreFrame->documentTypeString() + markupString; +} + +- (NSArray *)_nodesFromList:(Vector *)nodesVector +{ + size_t size = nodesVector->size(); + NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:size]; + for (size_t i = 0; i < size; ++i) + [nodes addObject:kit((*nodesVector)[i])]; + return nodes; +} + +- (NSString *)_markupStringFromRange:(DOMRange *)range nodes:(NSArray **)nodes +{ + // FIXME: This is always "for interchange". Is that right? See the previous method. + Vector nodeList; + NSString *markupString = createMarkup(core(range), nodes ? &nodeList : 0, AnnotateForInterchange); + if (nodes) + *nodes = [self _nodesFromList:&nodeList]; + + return [self _stringWithDocumentTypeStringAndMarkupString:markupString]; +} + +- (NSString *)_selectedString +{ + return _private->coreFrame->displayStringModifiedByEncoding(_private->coreFrame->selectedText()); +} + +- (NSString *)_stringForRange:(DOMRange *)range +{ + // This will give a system malloc'd buffer that can be turned directly into an NSString + unsigned length; + UChar* buf = plainTextToMallocAllocatedBuffer(core(range), length, true); + + if (!buf) + return [NSString string]; + + // Transfer buffer ownership to NSString + return [[[NSString alloc] initWithCharactersNoCopy:buf length:length freeWhenDone:YES] autorelease]; +} + +- (void)_drawRect:(NSRect)rect contentsOnly:(BOOL)contentsOnly +{ + ASSERT([[NSGraphicsContext currentContext] isFlipped]); + + CGContextRef ctx = static_cast([[NSGraphicsContext currentContext] graphicsPort]); + GraphicsContext context(ctx); + + FrameView* view = _private->coreFrame->view(); + + bool shouldFlatten = false; + if (Frame* parentFrame = _private->coreFrame->tree()->parent()) { + // For subframes, we need to inherit the paint behavior from our parent + FrameView* parentView = parentFrame ? parentFrame->view() : 0; + if (parentView) + shouldFlatten = parentView->paintBehavior() & PaintBehaviorFlattenCompositingLayers; + } else + shouldFlatten = WKCGContextIsBitmapContext(ctx) && [getWebView(self) _includesFlattenedCompositingLayersWhenDrawingToBitmap]; + + PaintBehavior oldBehavior = PaintBehaviorNormal; + if (shouldFlatten) { + oldBehavior = view->paintBehavior(); + view->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers); + } + + if (contentsOnly) + _private->coreFrame->view()->paintContents(&context, enclosingIntRect(rect)); + else + _private->coreFrame->view()->paint(&context, enclosingIntRect(rect)); + + if (shouldFlatten) + view->setPaintBehavior(oldBehavior); +} + +// Used by pagination code called from AppKit when a standalone web page is printed. +- (NSArray*)_computePageRectsWithPrintWidthScaleFactor:(float)printWidthScaleFactor printHeight:(float)printHeight +{ + NSMutableArray* pages = [NSMutableArray arrayWithCapacity:5]; + if (printWidthScaleFactor <= 0) { + LOG_ERROR("printWidthScaleFactor has bad value %.2f", printWidthScaleFactor); + return pages; + } + + if (printHeight <= 0) { + LOG_ERROR("printHeight has bad value %.2f", printHeight); + return pages; + } + + if (!_private->coreFrame || !_private->coreFrame->document() || !_private->coreFrame->view()) return pages; + RenderView* root = toRenderView(_private->coreFrame->document()->renderer()); + if (!root) return pages; + + FrameView* view = _private->coreFrame->view(); + if (!view) + return pages; + + NSView* documentView = view->documentView(); + if (!documentView) + return pages; + + float docWidth = root->layer()->width(); + float printWidth = docWidth / printWidthScaleFactor; + + PrintContext printContext(_private->coreFrame); + printContext.computePageRectsWithPageSize(FloatSize(printWidth, printHeight), true); + + const Vector& pageRects = printContext.pageRects(); + const size_t pageCount = pageRects.size(); + for (size_t pageNumber = 0; pageNumber < pageCount; ++pageNumber) + [pages addObject: [NSValue valueWithRect: NSRect(pageRects[pageNumber])]]; + return pages; +} + +- (BOOL)_getVisibleRect:(NSRect*)rect +{ + ASSERT_ARG(rect, rect); + if (RenderPart* ownerRenderer = _private->coreFrame->ownerRenderer()) { + if (ownerRenderer->needsLayout()) + return NO; + *rect = ownerRenderer->absoluteClippedOverflowRect(); + return YES; + } + + return NO; +} + +- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string +{ + return [self _stringByEvaluatingJavaScriptFromString:string forceUserGesture:true]; +} + +- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string forceUserGesture:(BOOL)forceUserGesture +{ + ASSERT(_private->coreFrame->document()); + + JSValue result = _private->coreFrame->script()->executeScript(string, forceUserGesture).jsValue(); + + if (!_private->coreFrame) // In case the script removed our frame from the page. + return @""; + + // This bizarre set of rules matches behavior from WebKit for Safari 2.0. + // If you don't like it, use -[WebScriptObject evaluateWebScript:] or + // JSEvaluateScript instead, since they have less surprising semantics. + if (!result || !result.isBoolean() && !result.isString() && !result.isNumber()) + return @""; + + JSLock lock(SilenceAssertionsOnly); + return ustringToString(result.toString(_private->coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec())); +} + +- (NSRect)_caretRectAtNode:(DOMNode *)node offset:(int)offset affinity:(NSSelectionAffinity)affinity +{ + VisiblePosition visiblePosition(core(node), offset, static_cast(affinity)); + return visiblePosition.absoluteCaretBounds(); +} + +- (NSRect)_firstRectForDOMRange:(DOMRange *)range +{ + return _private->coreFrame->firstRectForRange(core(range)); +} + +- (void)_scrollDOMRangeToVisible:(DOMRange *)range +{ + NSRect rangeRect = [self _firstRectForDOMRange:range]; + Node *startNode = core([range startContainer]); + + if (startNode && startNode->renderer()) { + RenderLayer *layer = startNode->renderer()->enclosingLayer(); + if (layer) + layer->scrollRectToVisible(enclosingIntRect(rangeRect), false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); + } +} + +- (BOOL)_needsLayout +{ + return _private->coreFrame->view() ? _private->coreFrame->view()->needsLayout() : false; +} + +- (id)_accessibilityTree +{ +#if HAVE(ACCESSIBILITY) + if (!AXObjectCache::accessibilityEnabled()) { + AXObjectCache::enableAccessibility(); + if ([[NSApp accessibilityAttributeValue:NSAccessibilityEnhancedUserInterfaceAttribute] boolValue]) + AXObjectCache::enableEnhancedUserInterfaceAccessibility(); + } + + if (!_private->coreFrame || !_private->coreFrame->document()) + return nil; + RenderView* root = toRenderView(_private->coreFrame->document()->renderer()); + if (!root) + return nil; + return _private->coreFrame->document()->axObjectCache()->getOrCreate(root)->wrapper(); +#else + return nil; +#endif +} + +- (DOMRange *)_rangeByAlteringCurrentSelection:(SelectionController::EAlteration)alteration direction:(SelectionController::EDirection)direction granularity:(TextGranularity)granularity +{ + if (_private->coreFrame->selection()->isNone()) + return nil; + + SelectionController selection; + selection.setSelection(_private->coreFrame->selection()->selection()); + selection.modify(alteration, direction, granularity); + return kit(selection.toNormalizedRange().get()); +} + +- (TextGranularity)_selectionGranularity +{ + return _private->coreFrame->selectionGranularity(); +} + +- (NSRange)_convertToNSRange:(Range *)range +{ + if (!range || !range->startContainer()) + return NSMakeRange(NSNotFound, 0); + + Element* selectionRoot = _private->coreFrame->selection()->rootEditableElement(); + Element* scope = selectionRoot ? selectionRoot : _private->coreFrame->document()->documentElement(); + + // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view + // that is not inside the current editable region. These checks ensure we don't produce + // potentially invalid data when responding to such requests. + if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope)) + return NSMakeRange(NSNotFound, 0); + if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope)) + return NSMakeRange(NSNotFound, 0); + + RefPtr testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset()); + ASSERT(testRange->startContainer() == scope); + int startPosition = TextIterator::rangeLength(testRange.get()); + + ExceptionCode ec; + testRange->setEnd(range->endContainer(), range->endOffset(), ec); + ASSERT(testRange->startContainer() == scope); + int endPosition = TextIterator::rangeLength(testRange.get()); + + return NSMakeRange(startPosition, endPosition - startPosition); +} + +- (PassRefPtr)_convertToDOMRange:(NSRange)nsrange +{ + if (nsrange.location > INT_MAX) + return 0; + if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX) + nsrange.length = INT_MAX - nsrange.location; + + // our critical assumption is that we are only called by input methods that + // concentrate on a given area containing the selection + // We have to do this because of text fields and textareas. The DOM for those is not + // directly in the document DOM, so serialization is problematic. Our solution is + // to use the root editable element of the selection start as the positional base. + // That fits with AppKit's idea of an input context. + Element* selectionRoot = _private->coreFrame->selection()->rootEditableElement(); + Element* scope = selectionRoot ? selectionRoot : _private->coreFrame->document()->documentElement(); + return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length); +} + +- (DOMRange *)convertNSRangeToDOMRange:(NSRange)nsrange +{ + // This method exists to maintain compatibility with Leopard's Dictionary.app. + return [self _convertNSRangeToDOMRange:nsrange]; +} + +- (DOMRange *)_convertNSRangeToDOMRange:(NSRange)nsrange +{ + return kit([self _convertToDOMRange:nsrange].get()); +} + +- (NSRange)convertDOMRangeToNSRange:(DOMRange *)range +{ + // This method exists to maintain compatibility with Leopard's Dictionary.app. + return [self _convertDOMRangeToNSRange:range]; +} + +- (NSRange)_convertDOMRangeToNSRange:(DOMRange *)range +{ + return [self _convertToNSRange:core(range)]; +} + +- (DOMRange *)_markDOMRange +{ + return kit(_private->coreFrame->mark().toNormalizedRange().get()); +} + +// Given proposedRange, returns an extended range that includes adjacent whitespace that should +// be deleted along with the proposed range in order to preserve proper spacing and punctuation of +// the text surrounding the deletion. +- (DOMRange *)_smartDeleteRangeForProposedRange:(DOMRange *)proposedRange +{ + Node* startContainer = core([proposedRange startContainer]); + Node* endContainer = core([proposedRange endContainer]); + if (startContainer == nil || endContainer == nil) + return nil; + + ASSERT(startContainer->document() == endContainer->document()); + + _private->coreFrame->document()->updateLayoutIgnorePendingStylesheets(); + + Position start(startContainer, [proposedRange startOffset]); + Position end(endContainer, [proposedRange endOffset]); + Position newStart = start.upstream().leadingWhitespacePosition(DOWNSTREAM, true); + if (newStart.isNull()) + newStart = start; + Position newEnd = end.downstream().trailingWhitespacePosition(DOWNSTREAM, true); + if (newEnd.isNull()) + newEnd = end; + + newStart = rangeCompliantEquivalent(newStart); + newEnd = rangeCompliantEquivalent(newEnd); + + RefPtr range = _private->coreFrame->document()->createRange(); + int exception = 0; + range->setStart(newStart.node(), newStart.deprecatedEditingOffset(), exception); + range->setEnd(newStart.node(), newStart.deprecatedEditingOffset(), exception); + return kit(range.get()); +} + +// Determines whether whitespace needs to be added around aString to preserve proper spacing and +// punctuation when it’s inserted into the receiver’s text over charRange. Returns by reference +// in beforeString and afterString any whitespace that should be added, unless either or both are +// nil. Both are returned as nil if aString is nil or if smart insertion and deletion are disabled. +- (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString +{ + // give back nil pointers in case of early returns + if (beforeString) + *beforeString = nil; + if (afterString) + *afterString = nil; + + // inspect destination + Node *startContainer = core([rangeToReplace startContainer]); + Node *endContainer = core([rangeToReplace endContainer]); + + Position startPos(startContainer, [rangeToReplace startOffset]); + Position endPos(endContainer, [rangeToReplace endOffset]); + + VisiblePosition startVisiblePos = VisiblePosition(startPos, VP_DEFAULT_AFFINITY); + VisiblePosition endVisiblePos = VisiblePosition(endPos, VP_DEFAULT_AFFINITY); + + // this check also ensures startContainer, startPos, endContainer, and endPos are non-null + if (startVisiblePos.isNull() || endVisiblePos.isNull()) + return; + + bool addLeadingSpace = startPos.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull() && !isStartOfParagraph(startVisiblePos); + if (addLeadingSpace) + if (UChar previousChar = startVisiblePos.previous().characterAfter()) + addLeadingSpace = !isCharacterSmartReplaceExempt(previousChar, true); + + bool addTrailingSpace = endPos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull() && !isEndOfParagraph(endVisiblePos); + if (addTrailingSpace) + if (UChar thisChar = endVisiblePos.characterAfter()) + addTrailingSpace = !isCharacterSmartReplaceExempt(thisChar, false); + + // inspect source + bool hasWhitespaceAtStart = false; + bool hasWhitespaceAtEnd = false; + unsigned pasteLength = [pasteString length]; + if (pasteLength > 0) { + NSCharacterSet *whiteSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + + if ([whiteSet characterIsMember:[pasteString characterAtIndex:0]]) { + hasWhitespaceAtStart = YES; + } + if ([whiteSet characterIsMember:[pasteString characterAtIndex:(pasteLength - 1)]]) { + hasWhitespaceAtEnd = YES; + } + } + + // issue the verdict + if (beforeString && addLeadingSpace && !hasWhitespaceAtStart) + *beforeString = @" "; + if (afterString && addTrailingSpace && !hasWhitespaceAtEnd) + *afterString = @" "; +} + +- (DOMDocumentFragment *)_documentFragmentWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString +{ + if (!_private->coreFrame || !_private->coreFrame->document()) + return nil; + + return kit(createFragmentFromMarkup(_private->coreFrame->document(), markupString, baseURLString, FragmentScriptingNotAllowed).get()); +} + +- (DOMDocumentFragment *)_documentFragmentWithNodesAsParagraphs:(NSArray *)nodes +{ + if (!_private->coreFrame || !_private->coreFrame->document()) + return nil; + + NSEnumerator *nodeEnum = [nodes objectEnumerator]; + Vector nodesVector; + DOMNode *node; + while ((node = [nodeEnum nextObject])) + nodesVector.append(core(node)); + + return kit(createFragmentFromNodes(_private->coreFrame->document(), nodesVector).get()); +} + +- (void)_replaceSelectionWithNode:(DOMNode *)node selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle +{ + DOMDocumentFragment *fragment = kit(_private->coreFrame->document()->createDocumentFragment().get()); + [fragment appendChild:node]; + [self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:matchStyle]; +} + +- (void)_insertParagraphSeparatorInQuotedContent +{ + if (_private->coreFrame->selection()->isNone()) + return; + + TypingCommand::insertParagraphSeparatorInQuotedContent(_private->coreFrame->document()); + _private->coreFrame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); +} + +- (VisiblePosition)_visiblePositionForPoint:(NSPoint)point +{ + // FIXME: Someone with access to Apple's sources could remove this needless wrapper call. + return _private->coreFrame->visiblePositionForPoint(IntPoint(point)); +} + +- (DOMRange *)_characterRangeAtPoint:(NSPoint)point +{ + VisiblePosition position = [self _visiblePositionForPoint:point]; + if (position.isNull()) + return nil; + + VisiblePosition previous = position.previous(); + if (previous.isNotNull()) { + DOMRange *previousCharacterRange = kit(makeRange(previous, position).get()); + NSRect rect = [self _firstRectForDOMRange:previousCharacterRange]; + if (NSPointInRect(point, rect)) + return previousCharacterRange; + } + + VisiblePosition next = position.next(); + if (next.isNotNull()) { + DOMRange *nextCharacterRange = kit(makeRange(position, next).get()); + NSRect rect = [self _firstRectForDOMRange:nextCharacterRange]; + if (NSPointInRect(point, rect)) + return nextCharacterRange; + } + + return nil; +} + +- (DOMCSSStyleDeclaration *)_typingStyle +{ + if (!_private->coreFrame || !_private->coreFrame->typingStyle()) + return nil; + return kit(_private->coreFrame->typingStyle()->copy().get()); +} + +- (void)_setTypingStyle:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction +{ + if (!_private->coreFrame) + return; + _private->coreFrame->computeAndSetTypingStyle(core(style), undoAction); +} + +- (void)_dragSourceEndedAt:(NSPoint)windowLoc operation:(NSDragOperation)operation +{ + if (!_private->coreFrame) + return; + FrameView* view = _private->coreFrame->view(); + if (!view) + return; + ASSERT([getWebView(self) _usesDocumentViews]); + // FIXME: These are fake modifier keys here, but they should be real ones instead. + PlatformMouseEvent event(IntPoint(windowLoc), globalPoint(windowLoc, [view->platformWidget() window]), + LeftButton, MouseEventMoved, 0, false, false, false, false, currentTime()); + _private->coreFrame->eventHandler()->dragSourceEndedAt(event, (DragOperation)operation); +} + +- (BOOL)_canProvideDocumentSource +{ + Frame* frame = _private->coreFrame; + String mimeType = frame->loader()->writer()->mimeType(); + PluginData* pluginData = frame->page() ? frame->page()->pluginData() : 0; + + if (WebCore::DOMImplementation::isTextMIMEType(mimeType) || + Image::supportsType(mimeType) || + (pluginData && pluginData->supportsMimeType(mimeType))) + return NO; + + return YES; +} + +- (BOOL)_canSaveAsWebArchive +{ + // Currently, all documents that we can view source for + // (HTML and XML documents) can also be saved as web archives + return [self _canProvideDocumentSource]; +} + +- (void)_receivedData:(NSData *)data textEncodingName:(NSString *)textEncodingName +{ + // Set the encoding. This only needs to be done once, but it's harmless to do it again later. + String encoding = _private->coreFrame->loader()->documentLoader()->overrideEncoding(); + bool userChosen = !encoding.isNull(); + if (encoding.isNull()) + encoding = textEncodingName; + _private->coreFrame->loader()->writer()->setEncoding(encoding, userChosen); + [self _addData:data]; +} + +@end + +@implementation WebFrame (WebPrivate) + +// FIXME: This exists only as a convenience for Safari, consider moving there. +- (BOOL)_isDescendantOfFrame:(WebFrame *)ancestor +{ + Frame* coreFrame = _private->coreFrame; + return coreFrame && coreFrame->tree()->isDescendantOf(core(ancestor)); +} + +- (void)_setShouldCreateRenderers:(BOOL)shouldCreateRenderers +{ + _private->shouldCreateRenderers = shouldCreateRenderers; +} + +- (NSColor *)_bodyBackgroundColor +{ + Document* document = _private->coreFrame->document(); + if (!document) + return nil; + HTMLElement* body = document->body(); + if (!body) + return nil; + RenderObject* bodyRenderer = body->renderer(); + if (!bodyRenderer) + return nil; + Color color = bodyRenderer->style()->visitedDependentColor(CSSPropertyBackgroundColor); + if (!color.isValid()) + return nil; + return nsColor(color); +} + +- (BOOL)_isFrameSet +{ + Document* document = _private->coreFrame->document(); + return document && document->isFrameSet(); +} + +- (BOOL)_firstLayoutDone +{ + return _private->coreFrame->loader()->stateMachine()->firstLayoutDone(); +} + +- (WebFrameLoadType)_loadType +{ + return (WebFrameLoadType)_private->coreFrame->loader()->loadType(); +} + +- (NSRange)_selectedNSRange +{ + return [self _convertToNSRange:_private->coreFrame->selection()->toNormalizedRange().get()]; +} + +- (void)_selectNSRange:(NSRange)range +{ + RefPtr domRange = [self _convertToDOMRange:range]; + if (domRange) + _private->coreFrame->selection()->setSelection(VisibleSelection(domRange.get(), SEL_DEFAULT_AFFINITY)); +} + +- (BOOL)_isDisplayingStandaloneImage +{ + Document* document = _private->coreFrame->document(); + return document && document->isImageDocument(); +} + +- (unsigned)_pendingFrameUnloadEventCount +{ + return _private->coreFrame->domWindow()->pendingUnloadEventListeners(); +} + +- (WebIconFetcher *)fetchApplicationIcon:(id)target + selector:(SEL)selector +{ + return [WebIconFetcher _fetchApplicationIconForFrame:self + target:target + selector:selector]; +} + +- (void)_setIsDisconnected:(bool)isDisconnected +{ + _private->coreFrame->setIsDisconnected(isDisconnected); +} + +- (void)_setExcludeFromTextSearch:(bool)exclude +{ + _private->coreFrame->setExcludeFromTextSearch(exclude); +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +- (void)_recursive_resumeNullEventsForAllNetscapePlugins +{ + Frame* coreFrame = core(self); + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + NSView *documentView = [[kit(frame) frameView] documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) + [(WebHTMLView *)documentView _resumeNullEventsForAllNetscapePlugins]; + } +} + +- (void)_recursive_pauseNullEventsForAllNetscapePlugins +{ + Frame* coreFrame = core(self); + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + NSView *documentView = [[kit(frame) frameView] documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) + [(WebHTMLView *)documentView _pauseNullEventsForAllNetscapePlugins]; + } +} +#endif + +- (BOOL)_pauseAnimation:(NSString*)name onNode:(DOMNode *)node atTime:(NSTimeInterval)time +{ + Frame* frame = core(self); + if (!frame) + return false; + + AnimationController* controller = frame->animation(); + if (!controller) + return false; + + Node* coreNode = core(node); + if (!coreNode || !coreNode->renderer()) + return false; + + return controller->pauseAnimationAtTime(coreNode->renderer(), name, time); +} + +- (BOOL)_pauseTransitionOfProperty:(NSString*)name onNode:(DOMNode*)node atTime:(NSTimeInterval)time +{ + Frame* frame = core(self); + if (!frame) + return false; + + AnimationController* controller = frame->animation(); + if (!controller) + return false; + + Node* coreNode = core(node); + if (!coreNode || !coreNode->renderer()) + return false; + + return controller->pauseTransitionAtTime(coreNode->renderer(), name, time); +} + +// Pause a given SVG animation on the target node at a specific time. +// This method is only intended to be used for testing the SVG animation system. +- (BOOL)_pauseSVGAnimation:(NSString*)elementId onSMILNode:(DOMNode *)node atTime:(NSTimeInterval)time +{ + Frame* frame = core(self); + if (!frame) + return false; + + Document* document = frame->document(); + if (!document || !document->svgExtensions()) + return false; + + Node* coreNode = core(node); + if (!coreNode || !SVGSMILElement::isSMILElement(coreNode)) + return false; + +#if ENABLE(SVG) + return document->accessSVGExtensions()->sampleAnimationAtTime(elementId, static_cast(coreNode), time); +#else + return false; +#endif +} + +- (unsigned) _numberOfActiveAnimations +{ + Frame* frame = core(self); + if (!frame) + return false; + + AnimationController* controller = frame->animation(); + if (!controller) + return false; + + return controller->numberOfActiveAnimations(); +} + +- (void)_replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle +{ + if (_private->coreFrame->selection()->isNone() || !fragment) + return; + + applyCommand(ReplaceSelectionCommand::create(_private->coreFrame->document(), core(fragment), selectReplacement, smartReplace, matchStyle)); + _private->coreFrame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); +} + +- (void)_replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace +{ + DOMDocumentFragment* fragment = kit(createFragmentFromText(_private->coreFrame->selection()->toNormalizedRange().get(), text).get()); + [self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:YES]; +} + +- (void)_replaceSelectionWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace +{ + DOMDocumentFragment *fragment = [self _documentFragmentWithMarkupString:markupString baseURLString:baseURLString]; + [self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:NO]; +} + +- (NSMutableDictionary *)_cacheabilityDictionary +{ + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + + FrameLoader* frameLoader = _private->coreFrame->loader(); + DocumentLoader* documentLoader = frameLoader->documentLoader(); + if (documentLoader && !documentLoader->mainDocumentError().isNull()) + [result setObject:(NSError *)documentLoader->mainDocumentError() forKey:WebFrameMainDocumentError]; + + if (frameLoader->subframeLoader()->containsPlugins()) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameHasPlugins]; + + if (DOMWindow* domWindow = _private->coreFrame->domWindow()) { + if (domWindow->hasEventListeners(eventNames().unloadEvent)) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameHasUnloadListener]; + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (domWindow->optionalApplicationCache()) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameUsesApplicationCache]; +#endif + } + + if (Document* document = _private->coreFrame->document()) { +#if ENABLE(DATABASE) + if (document->hasOpenDatabases()) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameUsesDatabases]; +#endif + + if (document->usingGeolocation()) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameUsesGeolocation]; + + if (!document->canSuspendActiveDOMObjects()) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameCanSuspendActiveDOMObjects]; + } + + return result; +} + +- (BOOL)_allowsFollowingLink:(NSURL *)URL +{ + if (!_private->coreFrame) + return YES; + return SecurityOrigin::canLoad(URL, String(), _private->coreFrame->document()); +} + +- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string withGlobalObject:(JSObjectRef)globalObjectRef inScriptWorld:(WebScriptWorld *)world +{ + // Start off with some guess at a frame and a global object, we'll try to do better...! + JSDOMWindow* anyWorldGlobalObject = _private->coreFrame->script()->globalObject(mainThreadNormalWorld()); + + // The global object is probably a shell object? - if so, we know how to use this! + JSC::JSObject* globalObjectObj = toJS(globalObjectRef); + if (!strcmp(globalObjectObj->classInfo()->className, "JSDOMWindowShell")) + anyWorldGlobalObject = static_cast(globalObjectObj)->window(); + + // Get the frame frome the global object we've settled on. + Frame* frame = anyWorldGlobalObject->impl()->frame(); + ASSERT(frame->document()); + JSValue result = frame->script()->executeScriptInWorld(core(world), string, true).jsValue(); + + if (!frame) // In case the script removed our frame from the page. + return @""; + + // This bizarre set of rules matches behavior from WebKit for Safari 2.0. + // If you don't like it, use -[WebScriptObject evaluateWebScript:] or + // JSEvaluateScript instead, since they have less surprising semantics. + if (!result || !result.isBoolean() && !result.isString() && !result.isNumber()) + return @""; + + JSLock lock(SilenceAssertionsOnly); + return ustringToString(result.toString(anyWorldGlobalObject->globalExec())); +} + +- (JSGlobalContextRef)_globalContextForScriptWorld:(WebScriptWorld *)world +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return 0; + DOMWrapperWorld* coreWorld = core(world); + if (!coreWorld) + return 0; + return toGlobalRef(coreFrame->script()->globalObject(coreWorld)->globalExec()); +} + +- (void)setAllowsScrollersToOverlapContent:(BOOL)flag +{ + ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]); + [(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAllowsScrollersToOverlapContent:flag]; +} + +- (void)setAlwaysHideHorizontalScroller:(BOOL)flag +{ + ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]); + [(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAlwaysHideHorizontalScroller:flag]; +} +- (void)setAlwaysHideVerticalScroller:(BOOL)flag +{ + ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]); + [(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAlwaysHideVerticalScroller:flag]; +} + +- (void)setAccessibleName:(NSString *)name +{ +#if HAVE(ACCESSIBILITY) + if (!AXObjectCache::accessibilityEnabled()) + return; + + RenderView* root = toRenderView(_private->coreFrame->document()->renderer()); + if (!root) + return; + + AccessibilityObject* rootObject = _private->coreFrame->document()->axObjectCache()->getOrCreate(root); + String strName(name); + rootObject->setAccessibleName(strName); +#endif +} + +- (NSString*)_layerTreeAsText +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return @""; + + return coreFrame->layerTreeAsText(); +} + +@end + +@implementation WebFrame + +- (id)init +{ + return nil; +} + +// Should be deprecated. +- (id)initWithName:(NSString *)name webFrameView:(WebFrameView *)view webView:(WebView *)webView +{ + return nil; +} + +- (void)dealloc +{ + if (_private && _private->includedInWebKitStatistics) + --WebFrameCount; + + [_private release]; + + [super dealloc]; +} + +- (void)finalize +{ + if (_private && _private->includedInWebKitStatistics) + --WebFrameCount; + + [super finalize]; +} + +- (NSString *)name +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return nil; + return coreFrame->tree()->name(); +} + +- (WebFrameView *)frameView +{ + ASSERT(!getWebView(self) || [getWebView(self) _usesDocumentViews]); + return _private->webFrameView; +} + +- (WebView *)webView +{ + return getWebView(self); +} + +static bool needsMicrosoftMessengerDOMDocumentWorkaround() +{ + static bool needsWorkaround = applicationIsMicrosoftMessenger() && [[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] compare:@"7.1" options:NSNumericSearch] == NSOrderedAscending; + return needsWorkaround; +} + +- (DOMDocument *)DOMDocument +{ + if (needsMicrosoftMessengerDOMDocumentWorkaround() && !pthread_main_np()) + return nil; + + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return nil; + + // FIXME: When loading a custom view/representation + // into a web frame, the old document can still be around. This makes sure that + // we'll return nil in those cases. + if (![[self _dataSource] _isDocumentHTML]) + return nil; + + Document* document = coreFrame->document(); + + // According to the documentation, we should return nil if the frame doesn't have a document. + // While full-frame images and plugins do have an underlying HTML document, we return nil here to be + // backwards compatible. + if (document && (document->isPluginDocument() || document->isImageDocument())) + return nil; + + return kit(coreFrame->document()); +} + +- (DOMHTMLElement *)frameElement +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return nil; + return kit(coreFrame->ownerElement()); +} + +- (WebDataSource *)provisionalDataSource +{ + Frame* coreFrame = _private->coreFrame; + return coreFrame ? dataSource(coreFrame->loader()->provisionalDocumentLoader()) : nil; +} + +- (WebDataSource *)dataSource +{ + Frame* coreFrame = _private->coreFrame; + return coreFrame && coreFrame->loader()->frameHasLoaded() ? [self _dataSource] : nil; +} + +- (void)loadRequest:(NSURLRequest *)request +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return; + coreFrame->loader()->load(request, false); +} + +static NSURL *createUniqueWebDataURL() +{ + CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault); + NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef); + CFRelease(UUIDRef); + NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"applewebdata://%@", UUIDString]]; + CFRelease(UUIDString); + return URL; +} + +- (void)_loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL unreachableURL:(NSURL *)unreachableURL +{ + if (!pthread_main_np()) + return [[self _webkit_invokeOnMainThread] _loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL unreachableURL:unreachableURL]; + + KURL responseURL; + if (!baseURL) { + baseURL = blankURL(); + responseURL = createUniqueWebDataURL(); + } + + ResourceRequest request([baseURL absoluteURL]); + + // hack because Mail checks for this property to detect data / archive loads + [NSURLProtocol setProperty:@"" forKey:@"WebDataRequest" inRequest:(NSMutableURLRequest *)request.nsURLRequest()]; + + SubstituteData substituteData(WebCore::SharedBuffer::wrapNSData(data), MIMEType, encodingName, [unreachableURL absoluteURL], responseURL); + + _private->coreFrame->loader()->load(request, substituteData, false); +} + + +- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL +{ + WebCoreThreadViolationCheckRoundTwo(); + + if (!MIMEType) + MIMEType = @"text/html"; + [self _loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL unreachableURL:nil]; +} + +- (void)_loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL unreachableURL:(NSURL *)unreachableURL +{ + NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; + [self _loadData:data MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:baseURL unreachableURL:unreachableURL]; +} + +- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL +{ + WebCoreThreadViolationCheckRoundTwo(); + + [self _loadHTMLString:string baseURL:baseURL unreachableURL:nil]; +} + +- (void)loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL +{ + WebCoreThreadViolationCheckRoundTwo(); + + [self _loadHTMLString:string baseURL:baseURL unreachableURL:unreachableURL]; +} + +- (void)loadArchive:(WebArchive *)archive +{ + if (LegacyWebArchive* coreArchive = [archive _coreLegacyWebArchive]) + _private->coreFrame->loader()->loadArchive(coreArchive); +} + +- (void)stopLoading +{ + if (!_private->coreFrame) + return; + _private->coreFrame->loader()->stopForUserCancel(); +} + +- (void)reload +{ + if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_RELOAD_FROM_ORIGIN) && applicationIsSafari()) + _private->coreFrame->loader()->reload(GetCurrentKeyModifiers() & shiftKey); + else + _private->coreFrame->loader()->reload(false); +} + +- (void)reloadFromOrigin +{ + _private->coreFrame->loader()->reload(true); +} + +- (WebFrame *)findFrameNamed:(NSString *)name +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return nil; + return kit(coreFrame->tree()->find(name)); +} + +- (WebFrame *)parentFrame +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return nil; + return [[kit(coreFrame->tree()->parent()) retain] autorelease]; +} + +- (NSArray *)childFrames +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return [NSArray array]; + NSMutableArray *children = [NSMutableArray arrayWithCapacity:coreFrame->tree()->childCount()]; + for (Frame* child = coreFrame->tree()->firstChild(); child; child = child->tree()->nextSibling()) + [children addObject:kit(child)]; + return children; +} + +- (WebScriptObject *)windowObject +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return 0; + return coreFrame->script()->windowScriptObject(); +} + +- (JSGlobalContextRef)globalContext +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return 0; + return toGlobalRef(coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec()); +} + +@end