summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--webkit/port/bridge/mac/FrameMac.mm726
-rw-r--r--webkit/port/platform/mac/ClipboardMac.mm372
-rw-r--r--webkit/port/platform/mac/DragDataMac.mm135
-rw-r--r--webkit/port/platform/mac/PasteboardMac.mm385
4 files changed, 1618 insertions, 0 deletions
diff --git a/webkit/port/bridge/mac/FrameMac.mm b/webkit/port/bridge/mac/FrameMac.mm
new file mode 100644
index 0000000..8604f8d
--- /dev/null
+++ b/webkit/port/bridge/mac/FrameMac.mm
@@ -0,0 +1,726 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ * Copyright (C) 2007 Trolltech ASA
+ *
+ * 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 "Frame.h"
+
+#import "AXObjectCache.h"
+#import "BeforeUnloadEvent.h"
+#import "BlockExceptions.h"
+#import "CSSHelper.h"
+#import "Cache.h"
+#import "Chrome.h"
+#import "ClipboardEvent.h"
+#import "ClipboardMac.h"
+#import "ColorMac.h"
+#import "Cursor.h"
+#if USE(JAVASCRIPTCORE_BINDINGS)
+#import "DOMInternal.h"
+#endif
+#import "DocumentLoader.h"
+#import "EditCommand.h"
+#import "EditorClient.h"
+#import "Event.h"
+#import "EventNames.h"
+#import "FloatRect.h"
+#import "FoundationExtras.h"
+#import "FrameLoadRequest.h"
+#import "FrameLoader.h"
+#import "FrameLoaderClient.h"
+#import "FrameLoaderTypes.h"
+#import "FramePrivate.h"
+#import "FrameView.h"
+#import "GraphicsContext.h"
+#import "HTMLDocument.h"
+#import "HTMLFormElement.h"
+#import "HTMLGenericFormElement.h"
+#import "HTMLInputElement.h"
+#import "HTMLNames.h"
+#import "HTMLTableCellElement.h"
+#import "HitTestRequest.h"
+#import "HitTestResult.h"
+#import "KeyboardEvent.h"
+#import "Logging.h"
+#import "MouseEventWithHitTestResults.h"
+#import "Page.h"
+#import "PlatformKeyboardEvent.h"
+#import "PlatformScrollBar.h"
+#import "PlatformWheelEvent.h"
+#import "Plugin.h"
+#import "RegularExpression.h"
+#import "RenderImage.h"
+#import "RenderListItem.h"
+#import "RenderPart.h"
+#import "RenderTableCell.h"
+#import "RenderTheme.h"
+#import "RenderView.h"
+#import "ResourceHandle.h"
+#import "Settings.h"
+#import "SimpleFontData.h"
+#import "SystemTime.h"
+#import "TextResourceDecoder.h"
+#import "UserStyleSheetLoader.h"
+#import "WebCoreFrameBridge.h"
+#import "WebCoreSystemInterface.h"
+#import "WebCoreViewFactory.h"
+#import "WebDashboardRegion.h"
+#import "visible_units.h"
+#import <Carbon/Carbon.h>
+#if USE(JAVASCRIPTCORE_BINDINGS)
+#import "kjs_proxy.h"
+#import "kjs_window.h"
+#import "WebScriptObjectPrivate.h"
+#import <JavaScriptCore/NP_jsobject.h>
+#import <JavaScriptCore/npruntime_impl.h>
+#else
+#include <../../../../../webkit/glue/webplugin_impl.h>
+#include <../../../../../port/bindings/v8/v8_npobject.h>
+#endif
+
+#undef _webcore_TIMING
+
+@interface NSObject (WebPlugIn)
+- (id)objectForWebScript;
+- (NPObject *)createPluginScriptableObject;
+@end
+
+@interface NSView (WebCoreHTMLDocumentView)
+- (void)drawSingleRect:(NSRect)rect;
+@end
+
+using namespace std;
+
+#if USE(JAVASCRIPTCORE_BINDINGS)
+using namespace KJS::Bindings;
+using KJS::JSLock;
+#endif
+
+namespace WebCore {
+
+using namespace EventNames;
+using namespace HTMLNames;
+
+void Frame::setBridge(WebCoreFrameBridge* bridge)
+{
+ if (d->m_bridge == bridge)
+ return;
+
+ if (!bridge) {
+ [d->m_bridge clearFrame];
+ HardRelease(d->m_bridge);
+ d->m_bridge = nil;
+ return;
+ }
+ HardRetain(bridge);
+ HardRelease(d->m_bridge);
+ d->m_bridge = bridge;
+}
+
+WebCoreFrameBridge* Frame::bridge() const
+{
+ return d->m_bridge;
+}
+
+// Either get cached regexp or build one that matches any of the labels.
+// The regexp we build is of the form: (STR1|STR2|STRN)
+RegularExpression* regExpForLabels(NSArray* labels)
+{
+ // All the ObjC calls in this method are simple array and string
+ // calls which we can assume do not raise exceptions
+
+
+ // Parallel arrays that we use to cache regExps. In practice the number of expressions
+ // that the app will use is equal to the number of locales is used in searching.
+ static const unsigned int regExpCacheSize = 4;
+ static NSMutableArray* regExpLabels = nil;
+ static Vector<RegularExpression*> regExps;
+ static RegularExpression wordRegExp = RegularExpression("\\w");
+
+ RegularExpression* result;
+ if (!regExpLabels)
+ regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
+ CFIndex cacheHit = [regExpLabels indexOfObject:labels];
+ if (cacheHit != NSNotFound)
+ result = regExps.at(cacheHit);
+ else {
+ DeprecatedString pattern("(");
+ unsigned int numLabels = [labels count];
+ unsigned int i;
+ for (i = 0; i < numLabels; i++) {
+ DeprecatedString label = DeprecatedString::fromNSString((NSString*)[labels objectAtIndex:i]);
+
+ bool startsWithWordChar = false;
+ bool endsWithWordChar = false;
+ if (label.length() != 0) {
+ startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
+ endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
+ }
+
+ if (i != 0)
+ pattern.append("|");
+ // Search for word boundaries only if label starts/ends with "word characters".
+ // If we always searched for word boundaries, this wouldn't work for languages
+ // such as Japanese.
+ if (startsWithWordChar) {
+ pattern.append("\\b");
+ }
+ pattern.append(label);
+ if (endsWithWordChar) {
+ pattern.append("\\b");
+ }
+ }
+ pattern.append(")");
+ result = new RegularExpression(pattern, false);
+ }
+
+ // add regexp to the cache, making sure it is at the front for LRU ordering
+ if (cacheHit != 0) {
+ if (cacheHit != NSNotFound) {
+ // remove from old spot
+ [regExpLabels removeObjectAtIndex:cacheHit];
+ regExps.remove(cacheHit);
+ }
+ // add to start
+ [regExpLabels insertObject:labels atIndex:0];
+ regExps.insert(0, result);
+ // trim if too big
+ if ([regExpLabels count] > regExpCacheSize) {
+ [regExpLabels removeObjectAtIndex:regExpCacheSize];
+ RegularExpression* last = regExps.last();
+ regExps.removeLast();
+ delete last;
+ }
+ }
+ return result;
+}
+
+NSString* Frame::searchForNSLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
+{
+ RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
+
+ if (cellRenderer && cellRenderer->isTableCell()) {
+ RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
+
+ if (cellAboveRenderer) {
+ HTMLTableCellElement* aboveCell =
+ static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
+
+ if (aboveCell) {
+ // search within the above cell we found for a match
+ for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
+ if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
+ // For each text chunk, run the regexp
+ DeprecatedString nodeString = n->nodeValue().deprecatedString();
+ int pos = regExp->searchRev(nodeString);
+ if (pos >= 0)
+ return nodeString.mid(pos, regExp->matchedLength()).getNSString();
+ }
+ }
+ }
+ }
+ }
+ // Any reason in practice to search all cells in that are above cell?
+ return nil;
+}
+
+NSString* Frame::searchForLabelsBeforeElement(NSArray* labels, Element* element)
+{
+ RegularExpression* regExp = regExpForLabels(labels);
+ // We stop searching after we've seen this many chars
+ const unsigned int charsSearchedThreshold = 500;
+ // This is the absolute max we search. We allow a little more slop than
+ // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
+ const unsigned int maxCharsSearched = 600;
+ // If the starting element is within a table, the cell that contains it
+ HTMLTableCellElement* startingTableCell = 0;
+ bool searchedCellAbove = false;
+
+ // walk backwards in the node tree, until another element, or form, or end of tree
+ int unsigned lengthSearched = 0;
+ Node* n;
+ for (n = element->traversePreviousNode();
+ n && lengthSearched < charsSearchedThreshold;
+ n = n->traversePreviousNode())
+ {
+ if (n->hasTagName(formTag)
+ || (n->isHTMLElement()
+ && static_cast<HTMLElement*>(n)->isGenericFormElement()))
+ {
+ // We hit another form element or the start of the form - bail out
+ break;
+ } else if (n->hasTagName(tdTag) && !startingTableCell) {
+ startingTableCell = static_cast<HTMLTableCellElement*>(n);
+ } else if (n->hasTagName(trTag) && startingTableCell) {
+ NSString* result = searchForLabelsAboveCell(regExp, startingTableCell);
+ if (result && [result length] > 0)
+ return result;
+ searchedCellAbove = true;
+ } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
+ // For each text chunk, run the regexp
+ DeprecatedString nodeString = n->nodeValue().deprecatedString();
+ // add 100 for slop, to make it more likely that we'll search whole nodes
+ if (lengthSearched + nodeString.length() > maxCharsSearched)
+ nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
+ int pos = regExp->searchRev(nodeString);
+ if (pos >= 0)
+ return nodeString.mid(pos, regExp->matchedLength()).getNSString();
+
+ lengthSearched += nodeString.length();
+ }
+ }
+
+ // If we started in a cell, but bailed because we found the start of the form or the
+ // previous element, we still might need to search the row above us for a label.
+ if (startingTableCell && !searchedCellAbove) {
+ NSString* result = searchForLabelsAboveCell(regExp, startingTableCell);
+ if (result && [result length] > 0)
+ return result;
+ }
+
+ return nil;
+}
+
+NSString* Frame::matchLabelsAgainstElement(NSArray* labels, Element* element)
+{
+ DeprecatedString name = element->getAttribute(nameAttr).deprecatedString();
+ // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
+ name.replace(RegularExpression("\\d"), " ");
+ name.replace('_', ' ');
+
+ RegularExpression* regExp = regExpForLabels(labels);
+ // Use the largest match we can find in the whole name string
+ int pos;
+ int length;
+ int bestPos = -1;
+ int bestLength = -1;
+ int start = 0;
+ do {
+ pos = regExp->search(name, start);
+ if (pos != -1) {
+ length = regExp->matchedLength();
+ if (length >= bestLength) {
+ bestPos = pos;
+ bestLength = length;
+ }
+ start = pos+1;
+ }
+ } while (pos != -1);
+
+ if (bestPos != -1)
+ return name.mid(bestPos, bestLength).getNSString();
+ return nil;
+}
+
+NSImage* Frame::imageFromRect(NSRect rect) const
+{
+#if !PLATFORM(SKIA)
+ NSView* view = d->m_view->getDocumentView();
+ if (!view)
+ return nil;
+ if (![view respondsToSelector:@selector(drawSingleRect:)])
+ return nil;
+
+ NSImage* resultImage;
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ NSRect bounds = [view bounds];
+
+ // Round image rect size in window coordinate space to avoid pixel cracks at HiDPI (4622794)
+ rect = [view convertRect:rect toView:nil];
+ rect.size.height = roundf(rect.size.height);
+ rect.size.width = roundf(rect.size.width);
+ rect = [view convertRect:rect fromView:nil];
+
+ resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
+
+ if (rect.size.width != 0 && rect.size.height != 0) {
+ [resultImage setFlipped:YES];
+ [resultImage lockFocus];
+ CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ CGContextSaveGState(context);
+ CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
+
+ // Note: Must not call drawRect: here, because drawRect: assumes that it's called from AppKit's
+ // display machinery. It calls getRectsBeingDrawn:count:, which can only be called inside
+ // when a real AppKit display is underway.
+ [view drawSingleRect:rect];
+
+ CGContextRestoreGState(context);
+ [resultImage unlockFocus];
+ [resultImage setFlipped:NO];
+ }
+
+ return resultImage;
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+#endif
+ return nil;
+}
+
+NSImage* Frame::selectionImage(bool forceBlackText) const
+{
+ d->m_paintRestriction = forceBlackText ? PaintRestrictionSelectionOnlyBlackText : PaintRestrictionSelectionOnly;
+ d->m_doc->updateLayout();
+ NSImage* result = imageFromRect(selectionRect());
+ d->m_paintRestriction = PaintRestrictionNone;
+ return result;
+}
+
+NSImage* Frame::snapshotDragImage(Node* node, NSRect* imageRect, NSRect* elementRect) const
+{
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
+ return nil;
+
+ renderer->updateDragState(true); // mark dragged nodes (so they pick up the right CSS)
+ d->m_doc->updateLayout(); // forces style recalc - needed since changing the drag state might
+ // imply new styles, plus JS could have changed other things
+ IntRect topLevelRect;
+ NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
+
+ d->m_elementToDraw = node; // invoke special sub-tree drawing mode
+ NSImage* result = imageFromRect(paintingRect);
+ renderer->updateDragState(false);
+ d->m_doc->updateLayout();
+ d->m_elementToDraw = 0;
+
+ if (elementRect)
+ *elementRect = topLevelRect;
+ if (imageRect)
+ *imageRect = paintingRect;
+ return result;
+}
+
+NSDictionary* Frame::fontAttributesForSelectionStart() const
+{
+ Node* nodeToRemove;
+ RenderStyle* style = styleForSelectionStart(nodeToRemove);
+ if (!style)
+ return nil;
+
+ NSMutableDictionary* result = [NSMutableDictionary dictionary];
+
+ if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
+ [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
+
+ if (style->font().primaryFont()->getNSFont())
+ [result setObject:style->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
+
+ if (style->color().isValid() && style->color() != Color::black)
+ [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
+
+ ShadowData* shadow = style->textShadow();
+ if (shadow) {
+ NSShadow* s = [[NSShadow alloc] init];
+ [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
+ [s setShadowBlurRadius:shadow->blur];
+ [s setShadowColor:nsColor(shadow->color)];
+ [result setObject:s forKey:NSShadowAttributeName];
+ }
+
+ int decoration = style->textDecorationsInEffect();
+ if (decoration & LINE_THROUGH)
+ [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
+
+ int superscriptInt = 0;
+ switch (style->verticalAlign()) {
+ case BASELINE:
+ case BOTTOM:
+ case BASELINE_MIDDLE:
+ case LENGTH:
+ case MIDDLE:
+ case TEXT_BOTTOM:
+ case TEXT_TOP:
+ case TOP:
+ break;
+ case SUB:
+ superscriptInt = -1;
+ break;
+ case SUPER:
+ superscriptInt = 1;
+ break;
+ }
+ if (superscriptInt)
+ [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
+
+ if (decoration & UNDERLINE)
+ [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
+
+ if (nodeToRemove) {
+ ExceptionCode ec = 0;
+ nodeToRemove->remove(ec);
+ ASSERT(ec == 0);
+ }
+
+ return result;
+}
+
+NSWritingDirection Frame::baseWritingDirectionForSelectionStart() const
+{
+ NSWritingDirection result = NSWritingDirectionLeftToRight;
+
+ Position pos = selectionController()->selection().visibleStart().deepEquivalent();
+ Node* node = pos.node();
+ if (!node || !node->renderer() || !node->renderer()->containingBlock())
+ return result;
+ RenderStyle* style = node->renderer()->containingBlock()->style();
+ if (!style)
+ return result;
+
+ switch (style->direction()) {
+ case LTR:
+ result = NSWritingDirectionLeftToRight;
+ break;
+ case RTL:
+ result = NSWritingDirectionRightToLeft;
+ break;
+ }
+
+ return result;
+}
+
+void Frame::issuePasteCommand()
+{
+ [d->m_bridge issuePasteCommand];
+}
+
+const short enableRomanKeyboardsOnly = -23;
+void Frame::setUseSecureKeyboardEntry(bool enable)
+{
+ if (enable == IsSecureEventInputEnabled())
+ return;
+ if (enable) {
+ EnableSecureEventInput();
+#ifdef BUILDING_ON_TIGER
+ KeyScript(enableRomanKeyboardsOnly);
+#else
+ CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
+ TSMSetDocumentProperty(TSMGetActiveDocument(), kTSMDocumentEnabledInputSourcesPropertyTag, sizeof(CFArrayRef), &inputSources);
+ CFRelease(inputSources);
+#endif
+ } else {
+ DisableSecureEventInput();
+#ifdef BUILDING_ON_TIGER
+ KeyScript(smKeyEnableKybds);
+#else
+ TSMRemoveDocumentProperty(TSMGetActiveDocument(), kTSMDocumentEnabledInputSourcesPropertyTag);
+#endif
+ }
+}
+
+NSMutableDictionary* Frame::dashboardRegionsDictionary()
+{
+ Document* doc = document();
+ if (!doc)
+ return nil;
+
+ const Vector<DashboardRegionValue>& regions = doc->dashboardRegions();
+ size_t n = regions.size();
+
+ // Convert the Vector<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
+ NSMutableDictionary* webRegions = [NSMutableDictionary dictionaryWithCapacity:n];
+ for (size_t i = 0; i < n; i++) {
+ const DashboardRegionValue& region = regions[i];
+
+ if (region.type == StyleDashboardRegion::None)
+ continue;
+
+ NSString *label = region.label;
+ WebDashboardRegionType type = WebDashboardRegionTypeNone;
+ if (region.type == StyleDashboardRegion::Circle)
+ type = WebDashboardRegionTypeCircle;
+ else if (region.type == StyleDashboardRegion::Rectangle)
+ type = WebDashboardRegionTypeRectangle;
+ NSMutableArray *regionValues = [webRegions objectForKey:label];
+ if (!regionValues) {
+ regionValues = [[NSMutableArray alloc] initWithCapacity:1];
+ [webRegions setObject:regionValues forKey:label];
+ [regionValues release];
+ }
+
+ WebDashboardRegion *webRegion = [[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type];
+ [regionValues addObject:webRegion];
+ [webRegion release];
+ }
+
+ return webRegions;
+}
+
+void Frame::dashboardRegionsChanged()
+{
+ NSMutableDictionary *webRegions = dashboardRegionsDictionary();
+ [d->m_bridge dashboardRegionsChanged:webRegions];
+}
+
+void Frame::willPopupMenu(NSMenu * menu)
+{
+ [d->m_bridge willPopupMenu:menu];
+}
+
+FloatRect Frame::customHighlightLineRect(const AtomicString& type, const FloatRect& lineRect, Node* node)
+{
+ return [d->m_bridge customHighlightRect:type forLine:lineRect representedNode:node];
+}
+
+void Frame::paintCustomHighlight(const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, bool text, bool line, Node* node)
+{
+ [d->m_bridge paintCustomHighlight:type forBox:boxRect onLine:lineRect behindText:text entireLine:line representedNode:node];
+}
+
+DragImageRef Frame::dragImageForSelection()
+{
+ if (!selectionController()->isRange())
+ return nil;
+ return selectionImage();
+}
+
+#if USE(JAVASCRIPTCORE_BINDINGS)
+KJS::Bindings::Instance* Frame::createScriptInstanceForWidget(WebCore::Widget* widget)
+{
+ NSView* aView = widget->getView();
+ if (!aView)
+ return 0;
+
+ void* nativeHandle = aView;
+ CreateRootObjectFunction createRootObject = RootObject::createRootObject();
+ RefPtr<RootObject> rootObject = createRootObject(nativeHandle);
+
+ if ([aView respondsToSelector:@selector(objectForWebScript)]) {
+ id objectForWebScript = [aView objectForWebScript];
+ if (objectForWebScript)
+ return Instance::createBindingForLanguageInstance(Instance::ObjectiveCLanguage, objectForWebScript, rootObject.release());
+ return 0;
+ } else if ([aView respondsToSelector:@selector(createPluginScriptableObject)]) {
+#if USE(NPOBJECT)
+ NPObject* npObject = [aView createPluginScriptableObject];
+ if (npObject) {
+ Instance* instance = Instance::createBindingForLanguageInstance(Instance::CLanguage, npObject, rootObject.release());
+
+ // -createPluginScriptableObject returns a retained NPObject. The caller is expected to release it.
+ _NPN_ReleaseObject(npObject);
+ return instance;
+ }
+#endif
+ return 0;
+ }
+
+ jobject applet;
+
+ // Get a pointer to the actual Java applet instance.
+ if ([d->m_bridge respondsToSelector:@selector(getAppletInView:)])
+ applet = [d->m_bridge getAppletInView:aView];
+ else
+ applet = [d->m_bridge pollForAppletInView:aView];
+
+ if (applet) {
+ // Wrap the Java instance in a language neutral binding and hand
+ // off ownership to the APPLET element.
+ Instance* instance = Instance::createBindingForLanguageInstance(Instance::JavaLanguage, applet, rootObject.release());
+ return instance;
+ }
+
+ return 0;
+}
+
+WebScriptObject* Frame::windowScriptObject()
+{
+ if (!scriptProxy()->isEnabled())
+ return 0;
+
+ if (!d->m_windowScriptObject) {
+ KJS::JSLock lock;
+ KJS::JSObject* win = KJS::Window::retrieveWindow(this);
+ KJS::Bindings::RootObject *root = bindingRootObject();
+ d->m_windowScriptObject = [WebScriptObject scriptObjectForJSObject:toRef(win) originRootObject:root rootObject:root];
+ }
+
+ return d->m_windowScriptObject.get();
+}
+
+void Frame::clearPlatformScriptObjects()
+{
+ if (d->m_windowScriptObject) {
+ KJS::Bindings::RootObject* root = bindingRootObject();
+ [d->m_windowScriptObject.get() _setOriginRootObject:root andRootObject:root];
+ }
+}
+
+#else
+
+#warning Untested V8 code!
+
+JSInstance Frame::createScriptInstanceForWidget(Widget* widget)
+{
+ // As in FrameWin.cpp, without the kjs branch. This should be shared!
+
+ ASSERT(widget != 0);
+
+ if (widget->isFrameView())
+ return JSInstanceHolder::EmptyInstance();
+
+ // Note: We have to trust that the widget passed to us here
+ // is a WebPluginImpl. There isn't a way to dynamically verify
+ // it, since the derived class (Widget) has no identifier.
+ WebPluginContainer* container = static_cast<WebPluginContainer*>(widget);
+ if (!container)
+ return JSInstanceHolder::EmptyInstance();
+
+ NPObject *npObject = container->GetPluginScriptableObject();
+ if (!npObject)
+ return JSInstanceHolder::EmptyInstance();
+
+ JSInstance instance = CreateV8ObjectForNPObject(npObject, nil);
+ // GetPluginScriptableObject returns a retained NPObject.
+ // The caller is expected to release it.
+ NPN_ReleaseObject(npObject);
+ return instance;
+}
+
+void Frame::clearPlatformScriptObjects()
+{
+ // no-op as in FrameWin.cpp
+}
+
+#endif
+
+void Frame::setUserStyleSheetLocation(const KURL& url)
+{
+ delete d->m_userStyleSheetLoader;
+ d->m_userStyleSheetLoader = 0;
+ if (d->m_doc && d->m_doc->docLoader())
+ d->m_userStyleSheetLoader = new UserStyleSheetLoader(d->m_doc, url.string());
+}
+
+void Frame::setUserStyleSheet(const String& styleSheet)
+{
+ delete d->m_userStyleSheetLoader;
+ d->m_userStyleSheetLoader = 0;
+ if (d->m_doc)
+ d->m_doc->setUserStyleSheet(styleSheet);
+}
+
+} // namespace WebCore
diff --git a/webkit/port/platform/mac/ClipboardMac.mm b/webkit/port/platform/mac/ClipboardMac.mm
new file mode 100644
index 0000000..eb434d2
--- /dev/null
+++ b/webkit/port/platform/mac/ClipboardMac.mm
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, 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.
+ *
+ * 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 "ClipboardMac.h"
+
+#import "CachedImage.h"
+#import "Document.h"
+#import "DragClient.h"
+#import "DragController.h"
+#import "Editor.h"
+#import "EventHandler.h"
+#import "FloatRect.h"
+#import "FoundationExtras.h"
+#import "Frame.h"
+#import "HTMLImageElement.h"
+#import "Image.h"
+#import "Page.h"
+#import "Pasteboard.h"
+#import "Range.h"
+#import "RenderImage.h"
+#import "WebCoreFrameBridge.h"
+#import "WebCoreSystemInterface.h"
+
+@class WebArchive;
+
+namespace WebCore {
+
+ClipboardMac::ClipboardMac(bool forDragging, NSPasteboard *pasteboard, ClipboardAccessPolicy policy, Frame *frame)
+ : Clipboard(policy, forDragging)
+ , m_pasteboard(pasteboard)
+ , m_frame(frame)
+{
+ m_changeCount = [m_pasteboard.get() changeCount];
+}
+
+ClipboardMac::~ClipboardMac()
+{
+}
+
+bool ClipboardMac::hasData()
+{
+ return m_pasteboard && [m_pasteboard.get() types] && [[m_pasteboard.get() types] count] > 0;
+}
+
+static NSString *cocoaTypeFromMIMEType(const String& type)
+{
+ String qType = type.stripWhiteSpace();
+
+ // two special cases for IE compatibility
+ if (qType == "Text")
+ return NSStringPboardType;
+ if (qType == "URL")
+ return NSURLPboardType;
+
+ // Ignore any trailing charset - JS strings are Unicode, which encapsulates the charset issue
+ if (qType == "text/plain" || qType.startsWith("text/plain;"))
+ return NSStringPboardType;
+ if (qType == "text/uri-list")
+ // special case because UTI doesn't work with Cocoa's URL type
+ return NSURLPboardType; // note special case in getData to read NSFilenamesType
+
+ // Try UTI now
+ NSString *mimeType = qType;
+ CFStringRef UTIType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)mimeType, NULL);
+ if (UTIType) {
+ CFStringRef pbType = UTTypeCopyPreferredTagWithClass(UTIType, kUTTagClassNSPboardType);
+ CFRelease(UTIType);
+ if (pbType)
+ return HardAutorelease(pbType);
+ }
+
+ // No mapping, just pass the whole string though
+ return qType;
+}
+
+static String MIMETypeFromCocoaType(NSString *type)
+{
+ // UTI may not do these right, so make sure we get the right, predictable result
+ if ([type isEqualToString:NSStringPboardType])
+ return "text/plain";
+ if ([type isEqualToString:NSURLPboardType] || [type isEqualToString:NSFilenamesPboardType])
+ return "text/uri-list";
+
+ // Now try the general UTI mechanism
+ CFStringRef UTIType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, (CFStringRef)type, NULL);
+ if (UTIType) {
+ CFStringRef mimeType = UTTypeCopyPreferredTagWithClass(UTIType, kUTTagClassMIMEType);
+ CFRelease(UTIType);
+ if (mimeType) {
+ String result = mimeType;
+ CFRelease(mimeType);
+ return result;
+ }
+ }
+
+ // No mapping, just pass the whole string though
+ return type;
+}
+
+void ClipboardMac::clearData(const String& type)
+{
+ if (policy() != ClipboardWritable)
+ return;
+
+ // note NSPasteboard enforces changeCount itself on writing - can't write if not the owner
+
+ NSString *cocoaType = cocoaTypeFromMIMEType(type);
+ if (cocoaType) {
+ [m_pasteboard.get() setString:@"" forType:cocoaType];
+ }
+}
+
+void ClipboardMac::clearAllData()
+{
+ if (policy() != ClipboardWritable)
+ return;
+
+ // note NSPasteboard enforces changeCount itself on writing - can't write if not the owner
+
+ [m_pasteboard.get() declareTypes:[NSArray array] owner:nil];
+}
+
+String ClipboardMac::getData(const String& type, bool& success) const
+{
+ success = false;
+ if (policy() != ClipboardReadable)
+ return String();
+
+ NSString *cocoaType = cocoaTypeFromMIMEType(type);
+ NSString *cocoaValue = nil;
+ NSArray *availableTypes = [m_pasteboard.get() types];
+
+ // Fetch the data in different ways for the different Cocoa types
+
+ if ([cocoaType isEqualToString:NSURLPboardType]) {
+ // When both URL and filenames are present, filenames is superior since it can contain a list.
+ // must check this or we get a printf from CF when there's no data of this type
+ if ([availableTypes containsObject:NSFilenamesPboardType]) {
+ NSArray *fileList = [m_pasteboard.get() propertyListForType:NSFilenamesPboardType];
+ if (fileList && [fileList isKindOfClass:[NSArray class]]) {
+ unsigned count = [fileList count];
+ if (count > 0) {
+ if (type != "text/uri-list")
+ count = 1;
+ NSMutableString *urls = [NSMutableString string];
+ unsigned i;
+ for (i = 0; i < count; i++) {
+ if (i > 0) {
+ [urls appendString:@"\n"];
+ }
+ NSString *string = [fileList objectAtIndex:i];
+ if (![string isKindOfClass:[NSString class]])
+ break;
+ NSURL *url = [NSURL fileURLWithPath:string];
+ [urls appendString:[url absoluteString]];
+ }
+ if (i == count)
+ cocoaValue = urls;
+ }
+ }
+ }
+ if (!cocoaValue) {
+ // must check this or we get a printf from CF when there's no data of this type
+ if ([availableTypes containsObject:NSURLPboardType]) {
+ NSURL *url = [NSURL URLFromPasteboard:m_pasteboard.get()];
+ if (url) {
+ cocoaValue = [url absoluteString];
+ }
+ }
+ }
+ } else if (cocoaType) {
+ cocoaValue = [m_pasteboard.get() stringForType:cocoaType];
+ }
+
+ // Enforce changeCount ourselves for security. We check after reading instead of before to be
+ // sure it doesn't change between our testing the change count and accessing the data.
+ if (cocoaValue && m_changeCount == [m_pasteboard.get() changeCount]) {
+ success = true;
+ return cocoaValue;
+ }
+
+ return String();
+}
+
+bool ClipboardMac::setData(const String &type, const String &data)
+{
+ if (policy() != ClipboardWritable)
+ return false;
+ // note NSPasteboard enforces changeCount itself on writing - can't write if not the owner
+
+ NSString *cocoaType = cocoaTypeFromMIMEType(type);
+ NSString *cocoaData = data;
+
+ if ([cocoaType isEqualToString:NSURLPboardType]) {
+ [m_pasteboard.get() addTypes:[NSArray arrayWithObject:NSURLPboardType] owner:nil];
+ NSURL *url = [[NSURL alloc] initWithString:cocoaData];
+ [url writeToPasteboard:m_pasteboard.get()];
+
+ if ([url isFileURL]) {
+ [m_pasteboard.get() addTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil];
+ NSArray *fileList = [NSArray arrayWithObject:[url path]];
+ [m_pasteboard.get() setPropertyList:fileList forType:NSFilenamesPboardType];
+ }
+
+ [url release];
+ return true;
+ }
+
+ if (cocoaType) {
+ // everything else we know of goes on the pboard as a string
+ [m_pasteboard.get() addTypes:[NSArray arrayWithObject:cocoaType] owner:nil];
+ return [m_pasteboard.get() setString:cocoaData forType:cocoaType];
+ }
+
+ return false;
+}
+
+HashSet<String> ClipboardMac::types() const
+{
+ if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
+ return HashSet<String>();
+
+ NSArray *types = [m_pasteboard.get() types];
+
+ // Enforce changeCount ourselves for security. We check after reading instead of before to be
+ // sure it doesn't change between our testing the change count and accessing the data.
+ if (m_changeCount != [m_pasteboard.get() changeCount])
+ return HashSet<String>();
+
+ HashSet<String> result;
+ if (types) {
+ unsigned count = [types count];
+ unsigned i;
+ for (i = 0; i < count; i++) {
+ NSString *pbType = [types objectAtIndex:i];
+ if ([pbType isEqualToString:@"NeXT plain ascii pasteboard type"])
+ continue; // skip this ancient type that gets auto-supplied by some system conversion
+
+ String str = MIMETypeFromCocoaType(pbType);
+ if (!result.contains(str))
+ result.add(str);
+ }
+ }
+ return result;
+}
+
+// The rest of these getters don't really have any impact on security, so for now make no checks
+
+void ClipboardMac::setDragImage(CachedImage* img, const IntPoint &loc)
+{
+ setDragImage(img, 0, loc);
+}
+
+void ClipboardMac::setDragImageElement(Node *node, const IntPoint &loc)
+{
+ setDragImage(0, node, loc);
+}
+
+void ClipboardMac::setDragImage(CachedImage* image, Node *node, const IntPoint &loc)
+{
+ if (policy() == ClipboardImageWritable || policy() == ClipboardWritable) {
+ if (m_dragImage)
+ m_dragImage->deref(this);
+ m_dragImage = image;
+ if (m_dragImage)
+ m_dragImage->ref(this);
+
+ m_dragLoc = loc;
+ m_dragImageElement = node;
+
+ if (dragStarted() && m_changeCount == [m_pasteboard.get() changeCount]) {
+ NSPoint cocoaLoc;
+ NSImage* cocoaImage = dragNSImage(cocoaLoc);
+ if (cocoaImage) {
+ // Dashboard wants to be able to set the drag image during dragging, but Cocoa does not allow this.
+ // Instead we must drop down to the CoreGraphics API.
+ wkSetDragImage(cocoaImage, cocoaLoc);
+
+ // Hack: We must post an event to wake up the NSDragManager, which is sitting in a nextEvent call
+ // up the stack from us because the CoreFoundation drag manager does not use the run loop by itself.
+ // This is the most innocuous event to use, per Kristen Forster.
+ NSEvent* ev = [NSEvent mouseEventWithType:NSMouseMoved location:NSZeroPoint
+ modifierFlags:0 timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:0 pressure:0];
+ [NSApp postEvent:ev atStart:YES];
+ }
+ }
+ // Else either 1) we haven't started dragging yet, so we rely on the part to install this drag image
+ // as part of getting the drag kicked off, or 2) Someone kept a ref to the clipboard and is trying to
+ // set the image way too late.
+ }
+}
+
+void ClipboardMac::writeRange(Range* range, Frame* frame)
+{
+ ASSERT(range);
+ ASSERT(frame);
+ Pasteboard::writeSelection(m_pasteboard.get(), range, frame->editor()->smartInsertDeleteEnabled() && frame->selectionGranularity() == WordGranularity, frame);
+}
+
+void ClipboardMac::writeURL(const KURL& url, const String& title, Frame* frame)
+{
+ ASSERT(frame);
+ ASSERT(m_pasteboard);
+ Pasteboard::writeURL(m_pasteboard.get(), nil, url, title, frame);
+}
+
+void ClipboardMac::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame)
+{
+#if 0
+ // TODO(pinkerton): find a way to do without objc bindings, similar
+ // to how win32 does it
+ ASSERT(frame);
+ if (Page* page = frame->page())
+ page->dragController()->client()->declareAndWriteDragImage(m_pasteboard.get(), [DOMElement _wrapElement:element], url.getNSURL(), title, frame);
+#endif
+}
+
+DragImageRef ClipboardMac::createDragImage(IntPoint& loc) const
+{
+ NSPoint nsloc = {loc.x(), loc.y()};
+ DragImageRef result = dragNSImage(nsloc);
+ loc = (IntPoint)nsloc;
+ return result;
+}
+
+NSImage *ClipboardMac::dragNSImage(NSPoint& loc) const
+{
+ NSImage *result = nil;
+ if (m_dragImageElement) {
+ if (m_frame) {
+ NSRect imageRect;
+ NSRect elementRect;
+ result = m_frame->snapshotDragImage(m_dragImageElement.get(), &imageRect, &elementRect);
+ // Client specifies point relative to element, not the whole image, which may include child
+ // layers spread out all over the place.
+ loc.x = elementRect.origin.x - imageRect.origin.x + m_dragLoc.x();
+ loc.y = elementRect.origin.y - imageRect.origin.y + m_dragLoc.y();
+ loc.y = imageRect.size.height - loc.y;
+ }
+ } else if (m_dragImage) {
+ result = m_dragImage->image()->getNSImage();
+
+ loc = m_dragLoc;
+ loc.y = [result size].height - loc.y;
+ }
+ return result;
+}
+
+}
diff --git a/webkit/port/platform/mac/DragDataMac.mm b/webkit/port/platform/mac/DragDataMac.mm
new file mode 100644
index 0000000..9f37cf9
--- /dev/null
+++ b/webkit/port/platform/mac/DragDataMac.mm
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2007 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.
+ *
+ * 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 "DragData.h"
+
+#import "ClipboardMac.h"
+#import "ClipboardAccessPolicy.h"
+#import "Document.h"
+#import "DocumentFragment.h"
+#import "MIMETypeRegistry.h"
+#import "Pasteboard.h"
+#import "PasteboardHelper.h"
+
+namespace WebCore {
+
+DragData::DragData(DragDataRef data, const IntPoint& clientPosition, const IntPoint& globalPosition,
+ DragOperation sourceOperationMask, PasteboardHelper* pasteboardHelper)
+ : m_clientPosition(clientPosition)
+ , m_globalPosition(globalPosition)
+ , m_platformDragData(data)
+ , m_draggingSourceOperationMask(sourceOperationMask)
+ , m_pasteboardHelper(pasteboardHelper)
+{
+ ASSERT(pasteboardHelper);
+}
+
+bool DragData::canSmartReplace() const
+{
+ //Need to call this so that the various Pasteboard type strings are intialised
+ Pasteboard::generalPasteboard();
+ return [[[m_platformDragData draggingPasteboard] types] containsObject:WebSmartPastePboardType];
+}
+
+bool DragData::containsColor() const
+{
+ return [[[m_platformDragData draggingPasteboard] types] containsObject:NSColorPboardType];
+}
+
+bool DragData::containsFiles() const
+{
+ return [[[m_platformDragData draggingPasteboard] types] containsObject:NSFilenamesPboardType];
+}
+
+void DragData::asFilenames(Vector<String>& result) const
+{
+ NSArray *filenames = [[m_platformDragData draggingPasteboard] propertyListForType:NSFilenamesPboardType];
+ NSEnumerator *fileEnumerator = [filenames objectEnumerator];
+
+ while (NSString *filename = [fileEnumerator nextObject])
+ result.append(filename);
+}
+
+bool DragData::containsPlainText() const
+{
+ NSPasteboard *pasteboard = [m_platformDragData draggingPasteboard];
+ NSArray *types = [pasteboard types];
+
+ return [types containsObject:NSStringPboardType]
+ || [types containsObject:NSRTFDPboardType]
+ || [types containsObject:NSRTFPboardType]
+ || [types containsObject:NSFilenamesPboardType]
+ || [NSURL URLFromPasteboard:pasteboard];
+}
+
+String DragData::asPlainText() const
+{
+ return m_pasteboardHelper->plainTextFromPasteboard([m_platformDragData draggingPasteboard]);
+}
+
+Color DragData::asColor() const
+{
+ NSColor *color = [NSColor colorFromPasteboard:[m_platformDragData draggingPasteboard]];
+ return makeRGBA((int)([color redComponent] * 255.0 + 0.5), (int)([color greenComponent] * 255.0 + 0.5),
+ (int)([color blueComponent] * 255.0 + 0.5), (int)([color alphaComponent] * 255.0 + 0.5));
+}
+
+Clipboard* DragData::createClipboard(ClipboardAccessPolicy policy) const
+{
+ return new ClipboardMac(true, [m_platformDragData draggingPasteboard], policy);
+}
+
+bool DragData::containsCompatibleContent() const
+{
+
+ NSPasteboard *pasteboard = [m_platformDragData draggingPasteboard];
+ NSMutableSet *types = [NSMutableSet setWithArray:[pasteboard types]];
+ [types intersectSet:[NSSet setWithArray:m_pasteboardHelper->insertablePasteboardTypes()]];
+ return [types count] != 0;
+}
+
+bool DragData::containsURL() const
+{
+ return !asURL().isEmpty();
+}
+
+String DragData::asURL(String* title) const
+{
+ return m_pasteboardHelper->urlFromPasteboard([m_platformDragData draggingPasteboard], title);
+}
+
+
+PassRefPtr<DocumentFragment> DragData::asFragment(Document* doc) const
+{
+#if 0
+ // TODO(pinkerton): Find how to get this without objc dom bindings.
+ return [m_pasteboardHelper->fragmentFromPasteboard([m_platformDragData draggingPasteboard]) _documentFragment];
+#endif
+ return NULL;
+}
+
+}
+
diff --git a/webkit/port/platform/mac/PasteboardMac.mm b/webkit/port/platform/mac/PasteboardMac.mm
new file mode 100644
index 0000000..417f74a
--- /dev/null
+++ b/webkit/port/platform/mac/PasteboardMac.mm
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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.
+ *
+ * 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 "Pasteboard.h"
+
+#import "CachedResource.h"
+#import "CharacterNames.h"
+#import "Document.h"
+#import "DocumentFragment.h"
+#import "Editor.h"
+#import "EditorClient.h"
+#import "HitTestResult.h"
+#import "Image.h"
+#import "KURL.h"
+#import "LoaderNSURLExtras.h"
+#import "MIMETypeRegistry.h"
+#import "RenderImage.h"
+#import "WebCoreNSStringExtras.h"
+#import "WebCoreSystemInterface.h"
+#import "markup.h"
+
+#import <wtf/RetainPtr.h>
+
+#include "webkit/glue/glue_util.h"
+#include "webkit/glue/webkit_glue.h"
+
+
+#if 0
+@interface NSAttributedString (AppKitSecretsIKnowAbout)
+- (id)_initWithDOMRange:(DOMRange *)domRange;
+@end
+#endif
+
+namespace WebCore {
+
+// FIXME: It's not great to have these both here and in WebKit.
+NSString *WebArchivePboardType = @"Apple Web Archive pasteboard type";
+NSString *WebSmartPastePboardType = @"NeXT smart paste pasteboard type";
+NSString *WebURLNamePboardType = @"public.url-name";
+NSString *WebURLPboardType = @"public.url";
+NSString *WebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType";
+
+#ifndef BUILDING_ON_TIGER
+static NSArray* selectionPasteboardTypes(bool canSmartCopyOrDelete, bool selectionContainsAttachments)
+{
+ if (selectionContainsAttachments) {
+ if (canSmartCopyOrDelete)
+ return [NSArray arrayWithObjects:WebSmartPastePboardType, WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
+ else
+ return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
+ } else { // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
+ if (canSmartCopyOrDelete)
+ return [NSArray arrayWithObjects:WebSmartPastePboardType, WebArchivePboardType, NSRTFPboardType, NSStringPboardType, nil];
+ else
+ return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFPboardType, NSStringPboardType, nil];
+ }
+}
+#endif
+
+static NSArray* writableTypesForURL()
+{
+ static RetainPtr<NSArray> types = nil;
+ if (!types) {
+ types = [[NSArray alloc] initWithObjects:
+ WebURLsWithTitlesPboardType,
+ NSURLPboardType,
+ WebURLPboardType,
+ WebURLNamePboardType,
+ NSStringPboardType,
+ nil];
+ }
+ return types.get();
+}
+
+static NSArray* writableTypesForImage()
+{
+ static RetainPtr<NSMutableArray> types = nil;
+ if (!types) {
+ types = [[NSMutableArray alloc] initWithObjects:NSTIFFPboardType, nil];
+ [types.get() addObjectsFromArray:writableTypesForURL()];
+ [types.get() addObject:NSRTFDPboardType];
+ }
+ return types.get();
+}
+
+Pasteboard* Pasteboard::generalPasteboard()
+{
+ static Pasteboard* pasteboard = new Pasteboard([NSPasteboard generalPasteboard]);
+ return pasteboard;
+}
+
+Pasteboard::Pasteboard(NSPasteboard* pboard)
+ : m_pasteboard(pboard)
+{
+}
+
+void Pasteboard::clear()
+{
+ [m_pasteboard.get() declareTypes:[NSArray array] owner:nil];
+}
+
+static NSAttributedString *stripAttachmentCharacters(NSAttributedString *string)
+{
+ const unichar attachmentCharacter = NSAttachmentCharacter;
+ static RetainPtr<NSString> attachmentCharacterString = [NSString stringWithCharacters:&attachmentCharacter length:1];
+ NSMutableAttributedString *result = [[string mutableCopy] autorelease];
+ NSRange attachmentRange = [[result string] rangeOfString:attachmentCharacterString.get()];
+ while (attachmentRange.location != NSNotFound) {
+ [result replaceCharactersInRange:attachmentRange withString:@""];
+ attachmentRange = [[result string] rangeOfString:attachmentCharacterString.get()];
+ }
+ return result;
+}
+
+void Pasteboard::writeSelection(NSPasteboard* pasteboard, Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame)
+{
+ if (WebArchivePboardType == nil)
+ Pasteboard::generalPasteboard(); //Initialises pasteboard types
+ ASSERT(selectedRange);
+
+ ExceptionCode ec = 0;
+ WebCore::String html = createMarkup(selectedRange, 0, AnnotateForInterchange);
+
+ NSData* htmlData = [NSData dataWithBytes:reinterpret_cast<const void*>(html.characters()) length:html.length()];
+ NSAttributedString *attributedString =
+ [[[NSAttributedString alloc] initWithHTML:htmlData documentAttributes:NULL] autorelease];
+
+#ifdef BUILDING_ON_TIGER
+ // TODO(pinkerton): I don't think we need this, since we won't be used by the
+ // OS as a web view embedded in Mail or other apps.
+ //
+ // 4930197: Mail overrides [WebHTMLView pasteboardTypesForSelection] in order to add another type to the pasteboard
+ // after WebKit does. On Tiger we must call this function so that Mail code will be executed, meaning that
+ // we can't call WebCore::Pasteboard's method for setting types.
+
+ NSArray *types = frame->editor()->client()->pasteboardTypesForSelection(frame);
+ // Don't write RTFD to the pasteboard when the copied attributed string has no attachments.
+ NSMutableArray *mutableTypes = nil;
+ if (![attributedString containsAttachments]) {
+ mutableTypes = [[types mutableCopy] autorelease];
+ [mutableTypes removeObject:NSRTFDPboardType];
+ types = mutableTypes;
+ }
+ [pasteboard declareTypes:types owner:nil];
+#else
+ NSArray *types = selectionPasteboardTypes(canSmartCopyOrDelete, [attributedString containsAttachments]);
+ [pasteboard declareTypes:types owner:nil];
+ frame->editor()->client()->didSetSelectionTypesForPasteboard();
+#endif
+
+ // Put HTML on the pasteboard.
+ if ([types containsObject:WebArchivePboardType]) {
+ [pasteboard setData:frame->editor()->client()->dataForArchivedSelection(frame) forType:WebArchivePboardType];
+ }
+
+ // Put the attributed string on the pasteboard (RTF/RTFD format).
+ if ([types containsObject:NSRTFDPboardType]) {
+ NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
+ [pasteboard setData:RTFDData forType:NSRTFDPboardType];
+ }
+ if ([types containsObject:NSRTFPboardType]) {
+ if ([attributedString containsAttachments])
+ attributedString = stripAttachmentCharacters(attributedString);
+ NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
+ [pasteboard setData:RTFData forType:NSRTFPboardType];
+ }
+
+ // Put plain string on the pasteboard.
+ if ([types containsObject:NSStringPboardType]) {
+ // Map &nbsp; to a plain old space because this is better for source code, other browsers do it,
+ // and because HTML forces you to do this any time you want two spaces in a row.
+ String text = selectedRange->text();
+ text.replace('\\', frame->backslashAsCurrencySymbol());
+ NSMutableString *s = [[[(NSString*)text copy] autorelease] mutableCopy];
+
+ NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&noBreakSpace length:1];
+ [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
+ [pasteboard setString:s forType:NSStringPboardType];
+ [s release];
+ }
+
+ if ([types containsObject:WebSmartPastePboardType]) {
+ [pasteboard setData:nil forType:WebSmartPastePboardType];
+ }
+}
+
+void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame)
+{
+ Pasteboard::writeSelection(m_pasteboard.get(), selectedRange, canSmartCopyOrDelete, frame);
+}
+
+void Pasteboard::writeURL(NSPasteboard* pasteboard, NSArray* types, const KURL& url, const String& titleStr, Frame* frame)
+{
+ if (WebArchivePboardType == nil)
+ Pasteboard::generalPasteboard(); //Initialises pasteboard types
+
+ if (types == nil) {
+ types = writableTypesForURL();
+ [pasteboard declareTypes:types owner:nil];
+ }
+
+ ASSERT(!url.isEmpty());
+
+ NSURL *cocoaURL = url.getNSURL();
+ NSString *userVisibleString = frame->editor()->client()->userVisibleString(cocoaURL);
+
+ NSString *title = (NSString*)titleStr;
+ if ([title length] == 0) {
+ title = [[cocoaURL path] lastPathComponent];
+ if ([title length] == 0)
+ title = userVisibleString;
+ }
+
+ if ([types containsObject:WebURLsWithTitlesPboardType])
+ [pasteboard setPropertyList:[NSArray arrayWithObjects:[NSArray arrayWithObject:userVisibleString],
+ [NSArray arrayWithObject:(NSString*)titleStr.stripWhiteSpace()],
+ nil]
+ forType:WebURLsWithTitlesPboardType];
+ if ([types containsObject:NSURLPboardType])
+ [cocoaURL writeToPasteboard:pasteboard];
+ if ([types containsObject:WebURLPboardType])
+ [pasteboard setString:userVisibleString forType:WebURLPboardType];
+ if ([types containsObject:WebURLNamePboardType])
+ [pasteboard setString:title forType:WebURLNamePboardType];
+ if ([types containsObject:NSStringPboardType])
+ [pasteboard setString:userVisibleString forType:NSStringPboardType];
+}
+
+void Pasteboard::writeURL(const KURL& url, const String& titleStr, Frame* frame)
+{
+ Pasteboard::writeURL(m_pasteboard.get(), nil, url, titleStr, frame);
+}
+
+static NSFileWrapper* fileWrapperForImage(CachedResource* resource, NSURL *url)
+{
+ SharedBuffer* coreData = resource->data();
+ NSData *data = [[[NSData alloc] initWithBytes:coreData->platformData()
+ length:coreData->platformDataSize()] autorelease];
+ NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
+ String coreMIMEType = resource->response().mimeType();
+ NSString *MIMEType = nil;
+ if (!coreMIMEType.isNull())
+ MIMEType = coreMIMEType;
+ [wrapper setPreferredFilename:suggestedFilenameWithMIMEType(url, MIMEType)];
+ return wrapper;
+}
+
+void Pasteboard::writeFileWrapperAsRTFDAttachment(NSFileWrapper* wrapper)
+{
+ NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:wrapper];
+
+ NSAttributedString *string = [NSAttributedString attributedStringWithAttachment:attachment];
+ [attachment release];
+
+ NSData *RTFDData = [string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
+ [m_pasteboard.get() setData:RTFDData forType:NSRTFDPboardType];
+}
+
+void Pasteboard::writeImage(Node* node, const KURL& url, const String& title)
+{
+ ASSERT(node);
+ Frame* frame = node->document()->frame();
+
+ NSURL *cocoaURL = url.getNSURL();
+ ASSERT(cocoaURL);
+
+ NSArray* types = writableTypesForImage();
+ [m_pasteboard.get() declareTypes:types owner:nil];
+ writeURL(m_pasteboard.get(), types, cocoaURL, nsStringNilIfEmpty(title), frame);
+
+ ASSERT(node->renderer() && node->renderer()->isImage());
+ RenderImage* renderer = static_cast<RenderImage*>(node->renderer());
+ CachedImage* cachedImage = static_cast<CachedImage*>(renderer->cachedImage());
+ ASSERT(cachedImage);
+ Image* image = cachedImage->image();
+ ASSERT(image);
+
+ [m_pasteboard.get() setData:[image->getNSImage() TIFFRepresentation] forType:NSTIFFPboardType];
+
+ String MIMEType = cachedImage->response().mimeType();
+ ASSERT(MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType));
+
+ writeFileWrapperAsRTFDAttachment(fileWrapperForImage(cachedImage, cocoaURL));
+}
+
+bool Pasteboard::canSmartReplace()
+{
+ return [[m_pasteboard.get() types] containsObject:WebSmartPastePboardType];
+}
+
+String Pasteboard::plainText(Frame* frame)
+{
+ NSArray *types = [m_pasteboard.get() types];
+
+ if ([types containsObject:NSStringPboardType])
+ return [m_pasteboard.get() stringForType:NSStringPboardType];
+
+ NSAttributedString *attributedString = nil;
+ NSString *string;
+
+ if ([types containsObject:NSRTFDPboardType])
+ attributedString = [[NSAttributedString alloc] initWithRTFD:[m_pasteboard.get() dataForType:NSRTFDPboardType] documentAttributes:NULL];
+ if (attributedString == nil && [types containsObject:NSRTFPboardType])
+ attributedString = [[NSAttributedString alloc] initWithRTF:[m_pasteboard.get() dataForType:NSRTFPboardType] documentAttributes:NULL];
+ if (attributedString != nil) {
+ string = [[attributedString string] copy];
+ [attributedString release];
+ return [string autorelease];
+ }
+
+ if ([types containsObject:NSFilenamesPboardType]) {
+ string = [[m_pasteboard.get() propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"];
+ if (string != nil)
+ return string;
+ }
+
+
+ if (NSURL *url = [NSURL URLFromPasteboard:m_pasteboard.get()]) {
+ // FIXME: using the editorClient to call into webkit, for now, since
+ // calling _web_userVisibleString from WebCore involves migrating a sizable web of
+ // helper code that should either be done in a separate patch or figured out in another way.
+ string = frame->editor()->client()->userVisibleString(url);
+ if ([string length] > 0)
+ return string;
+ }
+
+
+ return String();
+}
+
+PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefPtr<Range> context, bool allowPlainText, bool& chosePlainText)
+{
+ NSArray *types = [m_pasteboard.get() types];
+ chosePlainText = false;
+
+ if ([types containsObject:NSHTMLPboardType]) {
+ NSString *HTMLString = [m_pasteboard.get() stringForType:NSHTMLPboardType];
+ // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785.
+ if ([HTMLString hasPrefix:@"Version:"]) {
+ NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch];
+ if (range.location != NSNotFound) {
+ HTMLString = [HTMLString substringFromIndex:range.location];
+ }
+ }
+ if ([HTMLString length] != 0) {
+ RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(frame->document(), HTMLString, "");
+ if (fragment)
+ return fragment.release();
+ }
+ }
+
+ if (allowPlainText && [types containsObject:NSStringPboardType]) {
+ chosePlainText = true;
+ RefPtr<DocumentFragment> fragment = createFragmentFromText(context.get(), [m_pasteboard.get() stringForType:NSStringPboardType]);
+ if (fragment)
+ return fragment.release();
+ }
+
+ return 0;
+}
+
+}