/* * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. * Copyright (C) 2005, 2006 Alexey Proskuryakov (ap@nypop.com) * Copyright (C) 2006 David Smith (catfish.man@gmail.com) * * 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. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR * 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 "config.h" #import "WebCoreFrameBridge.h" #import "AXObjectCache.h" #import "CSSHelper.h" #import "Cache.h" #import "ClipboardMac.h" #import "DOMImplementation.h" #import "DOMInternal.h" #import "DOMWindow.h" #import "DeleteSelectionCommand.h" #import "DocLoader.h" #import "DocumentFragment.h" #import "DocumentLoader.h" #import "DocumentType.h" #import "Editor.h" #import "EditorClient.h" #import "EventHandler.h" #import "FloatRect.h" #import "FontData.h" #import "FormDataStreamMac.h" #import "Frame.h" #import "FrameLoader.h" #import "FrameLoaderClient.h" #import "FrameTree.h" #import "FrameView.h" #import "GraphicsContext.h" #import "HTMLDocument.h" #import "HTMLFormElement.h" #import "HTMLInputElement.h" #import "HTMLNames.h" #import "HitTestResult.h" #import "Image.h" #import "LoaderNSURLExtras.h" #import "MoveSelectionCommand.h" #import "Page.h" #import "PlatformMouseEvent.h" #import "PlatformScreen.h" #import "PlugInInfoStore.h" #import "RenderImage.h" #import "RenderPart.h" #import "RenderTreeAsText.h" #import "RenderView.h" #import "RenderWidget.h" #import "ReplaceSelectionCommand.h" #import "ResourceRequest.h" #import "SelectionController.h" #import "SmartReplace.h" #import "SubresourceLoader.h" #import "SystemTime.h" #import "TextEncoding.h" #import "TextIterator.h" #import "TextResourceDecoder.h" #import "TypingCommand.h" #import "WebCoreSystemInterface.h" #import "WebCoreViewFactory.h" #import "XMLTokenizer.h" #import "htmlediting.h" #import "kjs_proxy.h" #import "kjs_window.h" #import "markup.h" #import "visible_units.h" #import #import #import #import @class NSView; using namespace std; using namespace WebCore; using namespace HTMLNames; using KJS::ArrayInstance; using KJS::BooleanType; using KJS::DateInstance; using KJS::ExecState; using KJS::GetterSetterType; using KJS::JSImmediate; using KJS::JSLock; using KJS::JSObject; using KJS::JSValue; using KJS::NullType; using KJS::NumberType; using KJS::ObjectType; using KJS::SavedBuiltins; using KJS::SavedProperties; using KJS::StringType; using KJS::UndefinedType; using KJS::UnspecifiedType; using KJS::Window; using KJS::Bindings::RootObject; static PassRefPtr createRootObject(void* nativeHandle) { NSView *view = (NSView *)nativeHandle; WebCoreFrameBridge *bridge = [[WebCoreViewFactory sharedFactory] bridgeForView:view]; if (!bridge) return 0; Frame* frame = [bridge _frame]; return frame->createRootObject(nativeHandle, frame->scriptProxy()->interpreter()); } static pthread_t mainThread = 0; static void updateRenderingForBindings(ExecState* exec, JSObject* rootObject) { if (pthread_self() != mainThread) return; if (!rootObject) return; Window* window = static_cast(rootObject); if (!window) return; if (Frame* frame = window->impl()->frame()) if (Document* doc = frame->document()) doc->updateRendering(); } static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue* jsValue) { NSAppleEventDescriptor* aeDesc = 0; switch (jsValue->type()) { case BooleanType: aeDesc = [NSAppleEventDescriptor descriptorWithBoolean:jsValue->getBoolean()]; break; case StringType: aeDesc = [NSAppleEventDescriptor descriptorWithString:String(jsValue->getString())]; break; case NumberType: { double value = jsValue->getNumber(); int intValue = (int)value; if (value == intValue) aeDesc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeSInt32 bytes:&intValue length:sizeof(intValue)]; else aeDesc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE64BitFloatingPoint bytes:&value length:sizeof(value)]; break; } case ObjectType: { JSObject* object = jsValue->getObject(); if (object->inherits(&DateInstance::info)) { DateInstance* date = static_cast(object); double ms = 0; int tzOffset = 0; if (date->getTime(ms, tzOffset)) { CFAbsoluteTime utcSeconds = ms / 1000 - kCFAbsoluteTimeIntervalSince1970; LongDateTime ldt; if (noErr == UCConvertCFAbsoluteTimeToLongDateTime(utcSeconds, &ldt)) aeDesc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeLongDateTime bytes:&ldt length:sizeof(ldt)]; } } else if (object->inherits(&ArrayInstance::info)) { static HashSet visitedElems; if (!visitedElems.contains(object)) { visitedElems.add(object); ArrayInstance* array = static_cast(object); aeDesc = [NSAppleEventDescriptor listDescriptor]; unsigned numItems = array->getLength(); for (unsigned i = 0; i < numItems; ++i) [aeDesc insertDescriptor:aeDescFromJSValue(exec, array->getItem(i)) atIndex:0]; visitedElems.remove(object); } } if (!aeDesc) { JSValue* primitive = object->toPrimitive(exec); if (exec->hadException()) { exec->clearException(); return [NSAppleEventDescriptor nullDescriptor]; } return aeDescFromJSValue(exec, primitive); } break; } case UndefinedType: aeDesc = [NSAppleEventDescriptor descriptorWithTypeCode:cMissingValue]; break; default: LOG_ERROR("Unknown JavaScript type: %d", jsValue->type()); // no break; case UnspecifiedType: case NullType: case GetterSetterType: aeDesc = [NSAppleEventDescriptor nullDescriptor]; break; } return aeDesc; } @implementation WebCoreFrameBridge static inline WebCoreFrameBridge *bridge(Frame *frame) { if (!frame) return nil; return frame->bridge(); } - (NSString *)domain { Document *doc = m_frame->document(); if (doc) return doc->domain(); return nil; } + (WebCoreFrameBridge *)bridgeForDOMDocument:(DOMDocument *)document { return bridge([document _document]->frame()); } - (id)init { static bool initializedKJS; if (!initializedKJS) { initializedKJS = true; mainThread = pthread_self(); RootObject::setCreateRootObject(createRootObject); KJS::Bindings::Instance::setDidExecuteFunction(updateRenderingForBindings); } if (!(self = [super init])) return nil; _shouldCreateRenderers = YES; return self; } - (void)dealloc { ASSERT(_closed); [super dealloc]; } - (void)finalize { ASSERT(_closed); [super finalize]; } - (void)close { [self clearFrame]; _closed = YES; } - (void)addData:(NSData *)data { Document *doc = m_frame->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 (doc) { doc->setShouldCreateRenderers([self shouldCreateRenderers]); m_frame->loader()->addData((const char *)[data bytes], [data length]); } } - (BOOL)scrollOverflowInDirection:(WebScrollDirection)direction granularity:(WebScrollGranularity)granularity { if (!m_frame) return NO; return m_frame->eventHandler()->scrollOverflow((ScrollDirection)direction, (ScrollGranularity)granularity); } - (void)clearFrame { m_frame = 0; } - (void)createFrameViewWithNSView:(NSView *)view marginWidth:(int)mw marginHeight:(int)mh { // If we own the view, delete the old one - otherwise the render m_frame will take care of deleting the view. if (m_frame) m_frame->setView(0); FrameView* frameView = new FrameView(m_frame); m_frame->setView(frameView); frameView->deref(); frameView->setView(view); if (mw >= 0) frameView->setMarginWidth(mw); if (mh >= 0) frameView->setMarginHeight(mh); } - (NSString *)_stringWithDocumentTypeStringAndMarkupString:(NSString *)markupString { return m_frame->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:[DOMNode _wrapNode:(*nodesVector)[i]]]; return nodes; } - (NSString *)markupStringFromNode:(DOMNode *)node nodes:(NSArray **)nodes { // FIXME: This is never "for interchange". Is that right? See the next method. Vector nodeList; NSString *markupString = createMarkup([node _node], IncludeNode, nodes ? &nodeList : 0).getNSString(); if (nodes) *nodes = [self nodesFromList:&nodeList]; return [self _stringWithDocumentTypeStringAndMarkupString:markupString]; } - (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([range _range], nodes ? &nodeList : 0, AnnotateForInterchange).getNSString(); if (nodes) *nodes = [self nodesFromList:&nodeList]; return [self _stringWithDocumentTypeStringAndMarkupString:markupString]; } - (NSString *)selectedString { String text = m_frame->selectedText(); text.replace('\\', m_frame->backslashAsCurrencySymbol()); return text; } - (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([range _range], length); if (!buf) return [NSString string]; UChar backslashAsCurrencySymbol = m_frame->backslashAsCurrencySymbol(); if (backslashAsCurrencySymbol != '\\') for (unsigned n = 0; n < length; n++) if (buf[n] == '\\') buf[n] = backslashAsCurrencySymbol; // Transfer buffer ownership to NSString return [[[NSString alloc] initWithCharactersNoCopy:buf length:length freeWhenDone:YES] autorelease]; } - (void)reapplyStylesForDeviceType:(WebCoreDeviceType)deviceType { if (m_frame->view()) m_frame->view()->setMediaType(deviceType == WebCoreDeviceScreen ? "screen" : "print"); Document *doc = m_frame->document(); if (doc) doc->setPrinting(deviceType == WebCoreDevicePrinter); m_frame->reparseConfiguration(); } - (void)forceLayoutAdjustingViewSize:(BOOL)flag { m_frame->forceLayout(!flag); if (flag) m_frame->view()->adjustViewSize(); } - (void)forceLayoutWithMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)flag { m_frame->forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, flag); } - (void)sendResizeEvent { m_frame->sendResizeEvent(); } - (void)sendScrollEvent { m_frame->sendScrollEvent(); } - (void)drawRect:(NSRect)rect { PlatformGraphicsContext* platformContext = static_cast([[NSGraphicsContext currentContext] graphicsPort]); ASSERT([[NSGraphicsContext currentContext] isFlipped]); GraphicsContext context(platformContext); m_frame->paint(&context, enclosingIntRect(rect)); } // 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 (!m_frame || !m_frame->document() || !m_frame->view()) return pages; RenderView* root = static_cast(m_frame->document()->renderer()); if (!root) return pages; FrameView* view = m_frame->view(); if (!view) return pages; NSView* documentView = view->getDocumentView(); if (!documentView) return pages; float currPageHeight = printHeight; float docHeight = root->layer()->height(); float docWidth = root->layer()->width(); float printWidth = docWidth/printWidthScaleFactor; // We need to give the part the opportunity to adjust the page height at each step. for (float i = 0; i < docHeight; i += currPageHeight) { float proposedBottom = min(docHeight, i + printHeight); m_frame->adjustPageHeight(&proposedBottom, i, proposedBottom, i); currPageHeight = max(1.0f, proposedBottom - i); for (float j = 0; j < docWidth; j += printWidth) { NSValue* val = [NSValue valueWithRect: NSMakeRect(j, i, printWidth, currPageHeight)]; [pages addObject: val]; } } return pages; } // This is to support the case where a webview is embedded in the view that's being printed - (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit { m_frame->adjustPageHeight(newBottom, oldTop, oldBottom, bottomLimit); } - (NSObject *)copyRenderNode:(RenderObject *)node copier:(id )copier { NSMutableArray *children = [[NSMutableArray alloc] init]; for (RenderObject *child = node->firstChild(); child; child = child->nextSibling()) { [children addObject:[self copyRenderNode:child copier:copier]]; } NSString *name = [[NSString alloc] initWithUTF8String:node->renderName()]; RenderWidget* renderWidget = node->isWidget() ? static_cast(node) : 0; Widget* widget = renderWidget ? renderWidget->widget() : 0; NSView *view = widget ? widget->getView() : nil; int nx, ny; node->absolutePosition(nx, ny); NSObject *copiedNode = [copier nodeWithName:name position:NSMakePoint(nx,ny) rect:NSMakeRect(node->xPos(), node->yPos(), node->width(), node->height()) view:view children:children]; [name release]; [children release]; return copiedNode; } - (NSObject *)copyRenderTree:(id )copier { RenderObject *renderer = m_frame->renderer(); if (!renderer) { return nil; } return [self copyRenderNode:renderer copier:copier]; } - (void)installInFrame:(NSView *)view { // If this isn't the main frame, it must have a render m_frame set, or it // won't ever get installed in the view hierarchy. ASSERT(m_frame == m_frame->page()->mainFrame() || m_frame->ownerElement()); m_frame->view()->setView(view); // FIXME: frame tries to do this too, is it needed? if (m_frame->ownerRenderer()) { m_frame->ownerRenderer()->setWidget(m_frame->view()); // Now the render part owns the view, so we don't any more. } m_frame->view()->initScrollbars(); } static HTMLInputElement* inputElementFromDOMElement(DOMElement* element) { Node* node = [element _node]; if (node->hasTagName(inputTag)) return static_cast(node); return nil; } static HTMLFormElement *formElementFromDOMElement(DOMElement *element) { Node *node = [element _node]; // This should not be necessary, but an XSL file on // maps.google.com crashes otherwise because it is an xslt file // that contains
elements that aren't in any namespace, so // they come out as generic CML elements if (node && node->hasTagName(formTag)) { return static_cast(node); } return nil; } - (DOMElement *)elementWithName:(NSString *)name inForm:(DOMElement *)form { HTMLFormElement *formElement = formElementFromDOMElement(form); if (formElement) { Vector& elements = formElement->formElements; AtomicString targetName = name; for (unsigned int i = 0; i < elements.size(); i++) { HTMLGenericFormElement *elt = elements[i]; // Skip option elements, other duds if (elt->name() == targetName) return [DOMElement _wrapElement:elt]; } } return nil; } - (BOOL)elementDoesAutoComplete:(DOMElement *)element { HTMLInputElement *inputElement = inputElementFromDOMElement(element); return inputElement != nil && inputElement->inputType() == HTMLInputElement::TEXT && inputElement->autoComplete(); } - (BOOL)elementIsPassword:(DOMElement *)element { HTMLInputElement *inputElement = inputElementFromDOMElement(element); return inputElement != nil && inputElement->inputType() == HTMLInputElement::PASSWORD; } - (DOMElement *)formForElement:(DOMElement *)element; { HTMLInputElement *inputElement = inputElementFromDOMElement(element); if (inputElement) { HTMLFormElement *formElement = inputElement->form(); if (formElement) { return [DOMElement _wrapElement:formElement]; } } return nil; } - (DOMElement *)currentForm { return [DOMElement _wrapElement:m_frame->currentForm()]; } - (NSArray *)controlsInForm:(DOMElement *)form { NSMutableArray *results = nil; HTMLFormElement *formElement = formElementFromDOMElement(form); if (formElement) { Vector& elements = formElement->formElements; for (unsigned int i = 0; i < elements.size(); i++) { if (elements.at(i)->isEnumeratable()) { // Skip option elements, other duds DOMElement *de = [DOMElement _wrapElement:elements.at(i)]; if (!results) { results = [NSMutableArray arrayWithObject:de]; } else { [results addObject:de]; } } } } return results; } - (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element { return m_frame->searchForLabelsBeforeElement(labels, [element _element]); } - (NSString *)matchLabels:(NSArray *)labels againstElement:(DOMElement *)element { return m_frame->matchLabelsAgainstElement(labels, [element _element]); } - (NSURL *)URLWithAttributeString:(NSString *)string { Document *doc = m_frame->document(); if (!doc) return nil; // FIXME: is parseURL appropriate here? DeprecatedString rel = parseURL(string).deprecatedString(); return KURL(doc->completeURL(rel)).getNSURL(); } - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection { return m_frame->findString(string, forward, caseFlag, wrapFlag, startInSelection); } - (unsigned)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(unsigned)limit { return m_frame->markAllMatchesForText(string, caseFlag, limit); } - (BOOL)markedTextMatchesAreHighlighted { return m_frame->markedTextMatchesAreHighlighted(); } - (void)setMarkedTextMatchesAreHighlighted:(BOOL)doHighlight { m_frame->setMarkedTextMatchesAreHighlighted(doHighlight); } - (void)unmarkAllTextMatches { Document *doc = m_frame->document(); if (!doc) { return; } doc->removeMarkers(DocumentMarker::TextMatch); } - (NSArray *)rectsForTextMatches { Document *doc = m_frame->document(); if (!doc) return [NSArray array]; NSMutableArray *result = [NSMutableArray array]; Vector rects = doc->renderedRectsForMarkers(DocumentMarker::TextMatch); unsigned count = rects.size(); for (unsigned index = 0; index < count; ++index) [result addObject:[NSValue valueWithRect:rects[index]]]; return result; } - (void)setTextSizeMultiplier:(float)multiplier { int newZoomFactor = (int)rint(multiplier * 100); if (m_frame->zoomFactor() == newZoomFactor) { return; } m_frame->setZoomFactor(newZoomFactor); } - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)string { return [self stringByEvaluatingJavaScriptFromString:string forceUserGesture:true]; } - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)string forceUserGesture:(BOOL)forceUserGesture { ASSERT(m_frame->document()); JSValue* result = m_frame->loader()->executeScript(string, forceUserGesture); if (!m_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; return String(result->toString(m_frame->scriptProxy()->interpreter()->globalExec())); } - (NSAppleEventDescriptor *)aeDescByEvaluatingJavaScriptFromString:(NSString *)string { ASSERT(m_frame->document()); ASSERT(m_frame == m_frame->page()->mainFrame()); JSValue* result = m_frame->loader()->executeScript(string, true); if (!result) // FIXME: pass errors return 0; JSLock lock; return aeDescFromJSValue(m_frame->scriptProxy()->interpreter()->globalExec(), result); } - (NSRect)caretRectAtNode:(DOMNode *)node offset:(int)offset affinity:(NSSelectionAffinity)affinity { return [node _node]->renderer()->caretRect(offset, static_cast(affinity)); } - (NSRect)firstRectForDOMRange:(DOMRange *)range { return m_frame->firstRectForRange([range _range]); } - (void)scrollDOMRangeToVisible:(DOMRange *)range { NSRect rangeRect = [self firstRectForDOMRange:range]; Node *startNode = [[range startContainer] _node]; if (startNode && startNode->renderer()) { RenderLayer *layer = startNode->renderer()->enclosingLayer(); if (layer) layer->scrollRectToVisible(enclosingIntRect(rangeRect), RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignToEdgeIfNeeded); } } - (NSURL *)baseURL { return m_frame->loader()->completeURL(m_frame->document()->baseURL()).getNSURL(); } - (NSString *)stringWithData:(NSData *)data { Document* doc = m_frame->document(); if (!doc) return nil; TextResourceDecoder* decoder = doc->decoder(); if (!decoder) return nil; return decoder->encoding().decode(reinterpret_cast([data bytes]), [data length]); } + (NSString *)stringWithData:(NSData *)data textEncodingName:(NSString *)textEncodingName { WebCore::TextEncoding encoding(textEncodingName); if (!encoding.isValid()) encoding = WindowsLatin1Encoding(); return encoding.decode(reinterpret_cast([data bytes]), [data length]); } - (BOOL)needsLayout { return m_frame->view() ? m_frame->view()->needsLayout() : false; } - (void)setNeedsLayout { if (m_frame->view()) m_frame->view()->setNeedsLayout(); } - (NSString *)renderTreeAsExternalRepresentation { return externalRepresentation(m_frame->renderer()).getNSString(); } - (void)setShouldCreateRenderers:(BOOL)f { _shouldCreateRenderers = f; } - (BOOL)shouldCreateRenderers { return _shouldCreateRenderers; } - (NSColor *)selectionColor { return m_frame->isActive() ? [NSColor selectedTextBackgroundColor] : [NSColor secondarySelectedControlColor]; } - (id)accessibilityTree { AXObjectCache::enableAccessibility(); if (!m_frame || !m_frame->document()) return nil; RenderView* root = static_cast(m_frame->document()->renderer()); if (!root) return nil; return m_frame->document()->axObjectCache()->get(root); } - (void)setBaseBackgroundColor:(NSColor *)backgroundColor { if (m_frame && m_frame->view()) { NSColor *deviceColor = [backgroundColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]; Color color = Color(makeRGBA((int)(255 * [deviceColor redComponent]), (int)(255 * [deviceColor blueComponent]), (int)(255 * [deviceColor greenComponent]), (int)(255 * [deviceColor alphaComponent]))); m_frame->view()->setBaseBackgroundColor(color); } } - (void)setDrawsBackground:(BOOL)drawsBackground { if (m_frame && m_frame->view()) m_frame->view()->setTransparent(!drawsBackground); } - (DOMRange *)rangeByAlteringCurrentSelection:(SelectionController::EAlteration)alteration direction:(SelectionController::EDirection)direction granularity:(TextGranularity)granularity { if (m_frame->selectionController()->isNone()) return nil; // NOTE: The enums *must* match the very similar ones declared in SelectionController.h SelectionController selectionController; selectionController.setSelection(m_frame->selectionController()->selection()); selectionController.modify(alteration, direction, granularity); return [DOMRange _wrapRange:selectionController.toRange().get()]; } - (void)alterCurrentSelection:(SelectionController::EAlteration)alteration verticalDistance:(float)verticalDistance { if (m_frame->selectionController()->isNone()) return; SelectionController* selectionController = m_frame->selectionController(); selectionController->modify(alteration, static_cast(verticalDistance), true); } - (TextGranularity)selectionGranularity { // NOTE: The enums *must* match the very similar ones declared in SelectionController.h return m_frame->selectionGranularity(); } - (NSRange)convertToNSRange:(Range *)range { int exception = 0; if (!range || range->isDetached()) return NSMakeRange(NSNotFound, 0); Element* selectionRoot = m_frame->selectionController()->rootEditableElement(); Element* scope = selectionRoot ? selectionRoot : m_frame->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(exception) != scope && !range->startContainer(exception)->isDescendantOf(scope)) return NSMakeRange(NSNotFound, 0); if(range->endContainer(exception) != scope && !range->endContainer(exception)->isDescendantOf(scope)) return NSMakeRange(NSNotFound, 0); RefPtr testRange = new Range(scope->document(), scope, 0, range->startContainer(exception), range->startOffset(exception)); ASSERT(testRange->startContainer(exception) == scope); int startPosition = TextIterator::rangeLength(testRange.get()); testRange->setEnd(range->endContainer(exception), range->endOffset(exception), exception); ASSERT(testRange->startContainer(exception) == 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 = m_frame->selectionController()->rootEditableElement(); Element* scope = selectionRoot ? selectionRoot : m_frame->document()->documentElement(); return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length); } - (DOMRange *)convertNSRangeToDOMRange:(NSRange)nsrange { return [DOMRange _wrapRange:[self convertToDOMRange:nsrange].get()]; } - (NSRange)convertDOMRangeToNSRange:(DOMRange *)range { return [self convertToNSRange:[range _range]]; } - (void)selectNSRange:(NSRange)range { RefPtr domRange = [self convertToDOMRange:range]; if (domRange) m_frame->selectionController()->setSelection(Selection(domRange.get(), SEL_DEFAULT_AFFINITY)); } - (NSRange)selectedNSRange { return [self convertToNSRange:m_frame->selectionController()->toRange().get()]; } - (void)setMarkDOMRange:(DOMRange *)range { Range* r = [range _range]; m_frame->setMark(Selection(startPosition(r), endPosition(r), SEL_DEFAULT_AFFINITY)); } - (DOMRange *)markDOMRange { return [DOMRange _wrapRange:m_frame->mark().toRange().get()]; } - (void)setMarkedTextDOMRange:(DOMRange *)range customAttributes:(NSArray *)attributes ranges:(NSArray *)ranges { m_frame->setMarkedTextRange([range _range], attributes, ranges); } - (DOMRange *)markedTextDOMRange { return [DOMRange _wrapRange:m_frame->markedTextRange()]; } - (NSRange)markedTextNSRange { return [self convertToNSRange:m_frame->markedTextRange()]; } // 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 = [[proposedRange startContainer] _node]; Node *endContainer = [[proposedRange endContainer] _node]; if (startContainer == nil || endContainer == nil) return nil; ASSERT(startContainer->document() == endContainer->document()); m_frame->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; RefPtr range = m_frame->document()->createRange(); int exception = 0; range->setStart(newStart.node(), newStart.offset(), exception); range->setEnd(newStart.node(), newStart.offset(), exception); return [DOMRange _wrapRange: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 = [[rangeToReplace startContainer] _node]; Node *endContainer = [[rangeToReplace endContainer] _node]; 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 (!m_frame || !m_frame->document()) return 0; return [DOMDocumentFragment _wrapDocumentFragment:createFragmentFromMarkup(m_frame->document(), markupString, baseURLString).get()]; } - (DOMDocumentFragment *)documentFragmentWithText:(NSString *)text inContext:(DOMRange *)context { return [DOMDocumentFragment _wrapDocumentFragment:createFragmentFromText([context _range], text).get()]; } - (DOMDocumentFragment *)documentFragmentWithNodesAsParagraphs:(NSArray *)nodes { if (!m_frame || !m_frame->document()) return 0; NSEnumerator *nodeEnum = [nodes objectEnumerator]; Vector nodesVector; DOMNode *node; while ((node = [nodeEnum nextObject])) nodesVector.append([node _node]); return [DOMDocumentFragment _wrapDocumentFragment:createFragmentFromNodes(m_frame->document(), nodesVector).get()]; } - (void)replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle { if (m_frame->selectionController()->isNone() || !fragment) return; applyCommand(new ReplaceSelectionCommand(m_frame->document(), [fragment _documentFragment], selectReplacement, smartReplace, matchStyle)); m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); } - (void)replaceSelectionWithNode:(DOMNode *)node selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle { DOMDocumentFragment *fragment = [DOMDocumentFragment _wrapDocumentFragment:m_frame->document()->createDocumentFragment().get()]; [fragment appendChild:node]; [self replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:matchStyle]; } - (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]; } - (void)replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace { [self replaceSelectionWithFragment:[self documentFragmentWithText:text inContext:[DOMRange _wrapRange:m_frame->selectionController()->toRange().get()]] selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:YES]; } - (void)insertParagraphSeparatorInQuotedContent { if (m_frame->selectionController()->isNone()) return; TypingCommand::insertParagraphSeparatorInQuotedContent(m_frame->document()); m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); } - (void)setSelectionToDragCaret { m_frame->selectionController()->setSelection(m_frame->dragCaretController()->selection()); } - (void)moveSelectionToDragCaret:(DOMDocumentFragment *)selectionFragment smartMove:(BOOL)smartMove { applyCommand(new MoveSelectionCommand([selectionFragment _documentFragment], m_frame->dragCaretController()->base(), smartMove)); } - (VisiblePosition)_visiblePositionForPoint:(NSPoint)point { IntPoint outerPoint(point); HitTestResult result = m_frame->eventHandler()->hitTestResultAtPoint(outerPoint, true); Node* node = result.innerNode(); if (!node) return VisiblePosition(); RenderObject* renderer = node->renderer(); if (!renderer) return VisiblePosition(); VisiblePosition visiblePos = renderer->positionForCoordinates(result.localPoint().x(), result.localPoint().y()); if (visiblePos.isNull()) visiblePos = VisiblePosition(Position(node, 0)); return visiblePos; } - (DOMRange *)dragCaretDOMRange { return [DOMRange _wrapRange:m_frame->dragCaretController()->toRange().get()]; } - (BOOL)isDragCaretRichlyEditable { return m_frame->dragCaretController()->isContentRichlyEditable(); } - (DOMRange *)characterRangeAtPoint:(NSPoint)point { VisiblePosition position = [self _visiblePositionForPoint:point]; if (position.isNull()) return nil; VisiblePosition previous = position.previous(); if (previous.isNotNull()) { DOMRange *previousCharacterRange = [DOMRange _wrapRange:makeRange(previous, position).get()]; NSRect rect = [self firstRectForDOMRange:previousCharacterRange]; if (NSPointInRect(point, rect)) return previousCharacterRange; } VisiblePosition next = position.next(); if (next.isNotNull()) { DOMRange *nextCharacterRange = [DOMRange _wrapRange:makeRange(position, next).get()]; NSRect rect = [self firstRectForDOMRange:nextCharacterRange]; if (NSPointInRect(point, rect)) return nextCharacterRange; } return nil; } - (void)deleteKeyPressedWithSmartDelete:(BOOL)smartDelete granularity:(TextGranularity)granularity { if (!m_frame || !m_frame->document()) return; TypingCommand::deleteKeyPressed(m_frame->document(), smartDelete, granularity); m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); } - (void)forwardDeleteKeyPressedWithSmartDelete:(BOOL)smartDelete granularity:(TextGranularity)granularity { if (!m_frame || !m_frame->document()) return; TypingCommand::forwardDeleteKeyPressed(m_frame->document(), smartDelete, granularity); m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); } - (DOMCSSStyleDeclaration *)typingStyle { if (!m_frame || !m_frame->typingStyle()) return nil; return [DOMCSSStyleDeclaration _wrapCSSStyleDeclaration:m_frame->typingStyle()->copy().get()]; } - (void)setTypingStyle:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction { if (!m_frame) return; m_frame->computeAndSetTypingStyle([style _CSSStyleDeclaration], undoAction); } - (NSCellStateValue)selectionHasStyle:(DOMCSSStyleDeclaration *)style { if (!m_frame) return NSOffState; switch (m_frame->selectionHasStyle([style _CSSStyleDeclaration])) { case Frame::falseTriState: return NSOffState; case Frame::trueTriState: return NSOnState; case Frame::mixedTriState: return NSMixedState; } return NSOffState; } - (NSFont *)fontForSelection:(BOOL *)hasMultipleFonts { bool multipleFonts = false; NSFont *font = nil; if (m_frame) { const FontData* fd = m_frame->editor()->fontForSelection(multipleFonts); if (fd) font = fd->getNSFont(); } if (hasMultipleFonts) *hasMultipleFonts = multipleFonts; return font; } - (NSWritingDirection)baseWritingDirectionForSelectionStart { return m_frame ? m_frame->baseWritingDirectionForSelectionStart() : (NSWritingDirection)NSWritingDirectionLeftToRight; } - (void)dragSourceMovedTo:(NSPoint)windowLoc { if (m_frame) { // FIXME: Fake modifier keys here. PlatformMouseEvent event(IntPoint(windowLoc), globalPoint(windowLoc, [self window]), LeftButton, MouseEventMoved, 0, false, false, false, false, currentTime()); m_frame->eventHandler()->dragSourceMovedTo(event); } } - (void)dragSourceEndedAt:(NSPoint)windowLoc operation:(NSDragOperation)operation { if (m_frame) { // FIXME: Fake modifier keys here. PlatformMouseEvent event(IntPoint(windowLoc), globalPoint(windowLoc, [self window]), LeftButton, MouseEventMoved, 0, false, false, false, false, currentTime()); m_frame->eventHandler()->dragSourceEndedAt(event, (DragOperation)operation); } } - (DOMRange *)rangeOfCharactersAroundCaret { if (!m_frame) return nil; Selection selection(m_frame->selectionController()->selection()); if (!selection.isCaret()) return nil; VisiblePosition caret(selection.visibleStart()); VisiblePosition next = caret.next(); VisiblePosition previous = caret.previous(); if (previous.isNull() || next.isNull() || caret == next || caret == previous) return nil; return [DOMRange _wrapRange:makeRange(previous, next).get()]; } - (BOOL)getData:(NSData **)data andResponse:(NSURLResponse **)response forURL:(NSString *)URL { Document* doc = m_frame->document(); if (!doc) return NO; CachedResource* resource = doc->docLoader()->cachedResource(URL); if (!resource) return NO; SharedBuffer* buffer = resource->data(); if (buffer) *data = [buffer->createNSData() autorelease]; else *data = nil; *response = resource->response().nsURLResponse(); return YES; } - (void)getAllResourceDatas:(NSArray **)datas andResponses:(NSArray **)responses { Document* doc = m_frame->document(); if (!doc) { NSArray* emptyArray = [NSArray array]; *datas = emptyArray; *responses = emptyArray; return; } const HashMap& allResources = doc->docLoader()->allCachedResources(); NSMutableArray *d = [[NSMutableArray alloc] initWithCapacity:allResources.size()]; NSMutableArray *r = [[NSMutableArray alloc] initWithCapacity:allResources.size()]; HashMap::const_iterator end = allResources.end(); for (HashMap::const_iterator it = allResources.begin(); it != end; ++it) { SharedBuffer* buffer = it->second->data(); NSData *data; if (buffer) data = buffer->createNSData(); else data = nil; // It's clearly a bug to pass a nil value for data here, and doing so is part of the problem in // . However, fixing this in the obvious ways makes the symptom in 5268311 // worse, so don't just fix this without investigating that bug further. [d addObject:data]; [data release]; [r addObject:it->second->response().nsURLResponse()]; } *datas = [d autorelease]; *responses = [r autorelease]; } - (BOOL)canProvideDocumentSource { String mimeType = m_frame->loader()->responseMIMEType(); if (WebCore::DOMImplementation::isTextMIMEType(mimeType) || Image::supportsType(mimeType) || PlugInInfoStore::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; if (m_frame) encoding = m_frame->loader()->documentLoader()->overrideEncoding(); bool userChosen = !encoding.isNull(); if (encoding.isNull()) encoding = textEncodingName; m_frame->loader()->setEncoding(encoding, userChosen); [self addData:data]; } // ------------------- - (Frame*)_frame { return m_frame; } @end