summaryrefslogtreecommitdiffstats
path: root/webkit/port
diff options
context:
space:
mode:
authorpinkerton@google.com <pinkerton@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-26 18:48:44 +0000
committerpinkerton@google.com <pinkerton@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-26 18:48:44 +0000
commit8be5f2bcf41943a8b38326a3a51acf5554cd5f80 (patch)
tree817e6ec82169189d001ac336a92d730e998f86c0 /webkit/port
parent3200e976b0a204ecf57e9b27a3b0249cce5eeb3b (diff)
downloadchromium_src-8be5f2bcf41943a8b38326a3a51acf5554cd5f80.zip
chromium_src-8be5f2bcf41943a8b38326a3a51acf5554cd5f80.tar.gz
chromium_src-8be5f2bcf41943a8b38326a3a51acf5554cd5f80.tar.bz2
Forking webkit sources for clipboard/drag impl on Mac. It's almost exactly
the same as before except removing anything that uses the objc DOM bindings, which we don't have. So it's like one change in each file, more or less. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@1391 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/port')
-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;
+}
+
+}