summaryrefslogtreecommitdiffstats
path: root/WebCore/page/mac/WebCoreFrameBridge.mm
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/page/mac/WebCoreFrameBridge.mm')
-rw-r--r--WebCore/page/mac/WebCoreFrameBridge.mm1242
1 files changed, 1242 insertions, 0 deletions
diff --git a/WebCore/page/mac/WebCoreFrameBridge.mm b/WebCore/page/mac/WebCoreFrameBridge.mm
new file mode 100644
index 0000000..3f20706
--- /dev/null
+++ b/WebCore/page/mac/WebCoreFrameBridge.mm
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2008 Apple 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 "ColorMac.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 "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 "SimpleFontData.h"
+#import "SmartReplace.h"
+#import "SubresourceLoader.h"
+#import "SystemTime.h"
+#import "Text.h"
+#import "TextEncoding.h"
+#import "TextIterator.h"
+#import "TextResourceDecoder.h"
+#import "TypingCommand.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 <OpenScripting/ASRegistry.h>
+#import <JavaScriptCore/array_instance.h>
+#import <JavaScriptCore/date_object.h>
+#import <JavaScriptCore/runtime_root.h>
+#import <wtf/RetainPtr.h>
+
+@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<RootObject> 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()->globalObject());
+}
+
+static pthread_t mainThread = 0;
+
+static void updateRenderingForBindings(ExecState* exec, JSObject* rootObject)
+{
+ if (pthread_self() != mainThread)
+ return;
+
+ if (!rootObject)
+ return;
+
+ Window* window = static_cast<Window*>(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<DateInstance*>(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<JSObject*> visitedElems;
+ if (!visitedElems.contains(object)) {
+ visitedElems.add(object);
+
+ ArrayInstance* array = static_cast<ArrayInstance*>(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(_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<Node*> *)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<Node*> nodeList;
+ NSString *markupString = createMarkup([node _node], IncludeNode, nodes ? &nodeList : 0);
+ 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<Node*> nodeList;
+ NSString *markupString = createMarkup([range _range], nodes ? &nodeList : 0, AnnotateForInterchange);
+ 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->reapplyStyles();
+}
+
+- (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)sendScrollEvent
+{
+ m_frame->sendScrollEvent();
+}
+
+- (void)drawRect:(NSRect)rect
+{
+ PlatformGraphicsContext* platformContext = static_cast<PlatformGraphicsContext*>([[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<RenderView *>(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 <WebCoreRenderTreeCopier>)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<RenderWidget*>(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 <WebCoreRenderTreeCopier>)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<HTMLInputElement*>(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 <form> elements that aren't in any namespace, so
+ // they come out as generic CML elements
+ if (node && node->hasTagName(formTag)) {
+ return static_cast<HTMLFormElement *>(node);
+ }
+ return nil;
+}
+
+- (DOMElement *)elementWithName:(NSString *)name inForm:(DOMElement *)form
+{
+ HTMLFormElement *formElement = formElementFromDOMElement(form);
+ if (formElement) {
+ Vector<HTMLGenericFormElement*>& 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<HTMLGenericFormElement*>& 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?
+ return doc->completeURL(parseURL(string));
+}
+
+- (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<IntRect> 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()->globalObject()->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()->globalObject()->globalExec(), result);
+}
+
+- (NSRect)caretRectAtNode:(DOMNode *)node offset:(int)offset affinity:(NSSelectionAffinity)affinity
+{
+ return [node _node]->renderer()->caretRect(offset, static_cast<EAffinity>(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->document()->baseURL();
+}
+
+- (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<const char*>([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<const char*>([data bytes]), [data length]);
+}
+
+- (BOOL)needsLayout
+{
+ return m_frame->view() ? m_frame->view()->needsLayout() : false;
+}
+
+- (NSString *)renderTreeAsExternalRepresentation
+{
+ return externalRepresentation(m_frame->renderer());
+}
+
+- (void)setShouldCreateRenderers:(BOOL)f
+{
+ _shouldCreateRenderers = f;
+}
+
+- (id)accessibilityTree
+{
+ AXObjectCache::enableAccessibility();
+ if (!m_frame || !m_frame->document())
+ return nil;
+ RenderView* root = static_cast<RenderView *>(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()) {
+ Color color = colorFromNSColor([backgroundColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
+ 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;
+
+ SelectionController selectionController;
+ selectionController.setSelection(m_frame->selectionController()->selection());
+ selectionController.modify(alteration, direction, granularity);
+ return [DOMRange _wrapRange:selectionController.toRange().get()];
+}
+
+- (TextGranularity)selectionGranularity
+{
+ 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<Range> 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<Range>)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<Range> 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()];
+}
+
+- (DOMRange *)markDOMRange
+{
+ return [DOMRange _wrapRange:m_frame->mark().toRange().get()];
+}
+
+- (NSRange)markedTextNSRange
+{
+ return [self convertToNSRange:m_frame->editor()->compositionRange().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 = [[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;
+
+ newStart = rangeCompliantEquivalent(newStart);
+ newEnd = rangeCompliantEquivalent(newEnd);
+
+ RefPtr<Range> 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<Node*> 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);
+}
+
+- (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 *)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;
+}
+
+- (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);
+}
+
+- (NSFont *)fontForSelection:(BOOL *)hasMultipleFonts
+{
+ bool multipleFonts = false;
+ NSFont *font = nil;
+ if (m_frame) {
+ const SimpleFontData* fd = m_frame->editor()->fontForSelection(multipleFonts);
+ if (fd)
+ font = fd->getNSFont();
+ }
+
+ if (hasMultipleFonts)
+ *hasMultipleFonts = multipleFonts;
+ return font;
+}
+
+- (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);
+ }
+}
+
+- (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<String, CachedResource*>& allResources = doc->docLoader()->allCachedResources();
+
+ NSMutableArray *d = [[NSMutableArray alloc] initWithCapacity:allResources.size()];
+ NSMutableArray *r = [[NSMutableArray alloc] initWithCapacity:allResources.size()];
+
+ HashMap<String, CachedResource*>::const_iterator end = allResources.end();
+ for (HashMap<String, CachedResource*>::const_iterator it = allResources.begin(); it != end; ++it) {
+ SharedBuffer* buffer = it->second->data();
+ NSData *data;
+ if (buffer)
+ data = buffer->createNSData();
+ else
+ data = [[NSData alloc] init];
+ [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