| /* |
| * Copyright (C) 2006-2016 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 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 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 "Editor.h" |
| |
| #if PLATFORM(MAC) |
| |
| #import "Blob.h" |
| #import "CSSPrimitiveValueMappings.h" |
| #import "CSSValuePool.h" |
| #import "Color.h" |
| #import "ColorCocoa.h" |
| #import "ColorSerialization.h" |
| #import "DataTransfer.h" |
| #import "DocumentFragment.h" |
| #import "Editing.h" |
| #import "EditorClient.h" |
| #import "FontAttributes.h" |
| #import "FontShadow.h" |
| #import "Frame.h" |
| #import "FrameView.h" |
| #import "HTMLConverter.h" |
| #import "HTMLElement.h" |
| #import "HTMLNames.h" |
| #import "LegacyNSPasteboardTypes.h" |
| #import "LegacyWebArchive.h" |
| #import "PagePasteboardContext.h" |
| #import "Pasteboard.h" |
| #import "PasteboardStrategy.h" |
| #import "PlatformStrategies.h" |
| #import "RenderBlock.h" |
| #import "RenderImage.h" |
| #import "RuntimeApplicationChecks.h" |
| #import "RuntimeEnabledFeatures.h" |
| #import "SharedBuffer.h" |
| #import "StyleProperties.h" |
| #import "WebContentReader.h" |
| #import "WebNSAttributedStringExtras.h" |
| #import "markup.h" |
| #import <AppKit/AppKit.h> |
| #import <wtf/RetainPtr.h> |
| #import <wtf/cocoa/NSURLExtras.h> |
| |
| namespace WebCore { |
| |
| void Editor::showFontPanel() |
| { |
| auto* client = this->client(); |
| if (!client || !client->canShowFontPanel()) |
| return; |
| |
| [[NSFontManager sharedFontManager] orderFrontFontPanel:nil]; |
| } |
| |
| void Editor::showStylesPanel() |
| { |
| [[NSFontManager sharedFontManager] orderFrontStylesPanel:nil]; |
| } |
| |
| void Editor::showColorPanel() |
| { |
| [[NSApplication sharedApplication] orderFrontColorPanel:nil]; |
| } |
| |
| void Editor::pasteWithPasteboard(Pasteboard* pasteboard, OptionSet<PasteOption> options) |
| { |
| auto range = selectedRange(); |
| |
| ALLOW_DEPRECATED_DECLARATIONS_BEGIN |
| // FIXME: How can this hard-coded pasteboard name be right, given that the passed-in pasteboard has a name? |
| client()->setInsertionPasteboard(NSGeneralPboard); |
| ALLOW_DEPRECATED_DECLARATIONS_END |
| |
| bool chosePlainText; |
| RefPtr<DocumentFragment> fragment = webContentFromPasteboard(*pasteboard, *range, options.contains(PasteOption::AllowPlainText), chosePlainText); |
| |
| if (fragment && options.contains(PasteOption::AsQuotation)) |
| quoteFragmentForPasting(*fragment); |
| |
| if (fragment && shouldInsertFragment(*fragment, range, EditorInsertAction::Pasted)) |
| pasteAsFragment(fragment.releaseNonNull(), canSmartReplaceWithPasteboard(*pasteboard), false, options.contains(PasteOption::IgnoreMailBlockquote) ? MailBlockquoteHandling::IgnoreBlockquote : MailBlockquoteHandling::RespectBlockquote ); |
| |
| if (auto* client = this->client()) |
| client->setInsertionPasteboard(String()); |
| } |
| |
| void Editor::platformCopyFont() |
| { |
| Pasteboard pasteboard(PagePasteboardContext::create(m_document.pageID()), NSPasteboardNameFont); |
| |
| auto fontSampleString = adoptNS([[NSAttributedString alloc] initWithString:@"x" attributes:fontAttributesAtSelectionStart().createDictionary().get()]); |
| auto fontData = RetainPtr([fontSampleString RTFFromRange:NSMakeRange(0, [fontSampleString length]) documentAttributes:@{ }]); |
| |
| PasteboardBuffer pasteboardBuffer; |
| pasteboardBuffer.contentOrigin = m_document.originIdentifierForPasteboard(); |
| pasteboardBuffer.type = legacyFontPasteboardType(); |
| pasteboardBuffer.data = SharedBuffer::create(fontData.get()); |
| pasteboard.write(pasteboardBuffer); |
| } |
| |
| void Editor::platformPasteFont() |
| { |
| Pasteboard pasteboard(PagePasteboardContext::create(m_document.pageID()), NSPasteboardNameFont); |
| |
| client()->setInsertionPasteboard(pasteboard.name()); |
| |
| RetainPtr<NSData> fontData; |
| if (auto buffer = pasteboard.readBuffer(std::nullopt, legacyFontPasteboardType())) |
| fontData = buffer->createNSData(); |
| auto fontSampleString = adoptNS([[NSAttributedString alloc] initWithRTF:fontData.get() documentAttributes:nil]); |
| auto fontAttributes = RetainPtr([fontSampleString fontAttributesInRange:NSMakeRange(0, 1)]); |
| |
| auto style = MutableStyleProperties::create(); |
| |
| Color backgroundColor; |
| if (NSColor *nsBackgroundColor = dynamic_objc_cast<NSColor>([fontAttributes objectForKey:NSBackgroundColorAttributeName])) |
| backgroundColor = colorFromCocoaColor(nsBackgroundColor); |
| if (!backgroundColor.isValid()) |
| backgroundColor = Color::transparentBlack; |
| style->setProperty(CSSPropertyBackgroundColor, CSSPrimitiveValue::create(backgroundColor)); |
| |
| if (NSFont *font = dynamic_objc_cast<NSFont>([fontAttributes objectForKey:NSFontAttributeName])) { |
| // FIXME: Need more sophisticated escaping code if we want to handle family names |
| // with characters like single quote or backslash in their names. |
| style->setProperty(CSSPropertyFontFamily, [NSString stringWithFormat:@"'%@'", [font familyName]]); |
| style->setProperty(CSSPropertyFontSize, CSSPrimitiveValue::create([font pointSize], CSSUnitType::CSS_PX)); |
| // FIXME: Map to the entire range of CSS weight values. |
| style->setProperty(CSSPropertyFontWeight, ([NSFontManager.sharedFontManager weightOfFont:font] >= 7) ? CSSValueBold : CSSValueNormal); |
| style->setProperty(CSSPropertyFontStyle, ([NSFontManager.sharedFontManager traitsOfFont:font] & NSItalicFontMask) ? CSSValueItalic : CSSValueNormal); |
| } else { |
| style->setProperty(CSSPropertyFontFamily, "Helvetica"_s); |
| style->setProperty(CSSPropertyFontSize, CSSPrimitiveValue::create(12, CSSUnitType::CSS_PX)); |
| style->setProperty(CSSPropertyFontWeight, CSSValueNormal); |
| style->setProperty(CSSPropertyFontStyle, CSSValueNormal); |
| } |
| |
| Color foregroundColor; |
| if (NSColor *nsForegroundColor = dynamic_objc_cast<NSColor>([fontAttributes objectForKey:NSForegroundColorAttributeName])) { |
| foregroundColor = colorFromCocoaColor(nsForegroundColor); |
| if (!foregroundColor.isValid()) |
| foregroundColor = Color::transparentBlack; |
| } else |
| foregroundColor = Color::black; |
| style->setProperty(CSSPropertyColor, CSSPrimitiveValue::create(foregroundColor)); |
| |
| FontShadow fontShadow; |
| if (NSShadow *nsFontShadow = dynamic_objc_cast<NSShadow>([fontAttributes objectForKey:NSShadowAttributeName])) |
| fontShadow = fontShadowFromNSShadow(nsFontShadow); |
| style->setProperty(CSSPropertyTextShadow, serializationForCSS(fontShadow)); |
| |
| auto superscriptStyle = [[fontAttributes objectForKey:NSSuperscriptAttributeName] intValue]; |
| style->setProperty(CSSPropertyVerticalAlign, (superscriptStyle > 0) ? CSSValueSuper : ((superscriptStyle < 0) ? CSSValueSub : CSSValueBaseline)); |
| |
| // FIXME: Underline wins here if we have both (see bug 3790443). |
| auto underlineStyle = [[fontAttributes objectForKey:NSUnderlineStyleAttributeName] intValue]; |
| auto strikethroughStyle = [[fontAttributes objectForKey:NSStrikethroughStyleAttributeName] intValue]; |
| style->setProperty(CSSPropertyWebkitTextDecorationsInEffect, (underlineStyle != NSUnderlineStyleNone) ? CSSValueUnderline : ((strikethroughStyle != NSUnderlineStyleNone) ? CSSValueLineThrough : CSSValueNone)); |
| |
| applyStyleToSelection(style.ptr(), EditAction::PasteFont); |
| |
| client()->setInsertionPasteboard(String()); |
| } |
| |
| RefPtr<SharedBuffer> Editor::imageInWebArchiveFormat(Element& imageElement) |
| { |
| auto archive = LegacyWebArchive::create(imageElement); |
| if (!archive) |
| return nullptr; |
| return SharedBuffer::create(archive->rawDataRepresentation().get()); |
| } |
| |
| RefPtr<SharedBuffer> Editor::dataSelectionForPasteboard(const String& pasteboardType) |
| { |
| // FIXME: The interface to this function is awkward. We'd probably be better off with three separate functions. As of this writing, this is only used in WebKit2 to implement the method -[WKView writeSelectionToPasteboard:types:], which is only used to support OS X services. |
| |
| // FIXME: Does this function really need to use adjustedSelectionRange()? Because writeSelectionToPasteboard() just uses selectedRange(). This is the only function in WebKit that uses adjustedSelectionRange. |
| if (!canCopy()) |
| return nullptr; |
| |
| ALLOW_DEPRECATED_DECLARATIONS_BEGIN |
| if (pasteboardType == WebArchivePboardType || pasteboardType == String(kUTTypeWebArchive)) |
| return selectionInWebArchiveFormat(); |
| ALLOW_DEPRECATED_DECLARATIONS_END |
| |
| if (pasteboardType == String(legacyRTFDPasteboardType())) |
| return dataInRTFDFormat(attributedString(*adjustedSelectionRange()).string.get()); |
| |
| if (pasteboardType == String(legacyRTFPasteboardType())) { |
| auto string = attributedString(*adjustedSelectionRange()).string; |
| // FIXME: Why is this stripping needed here, but not in writeSelectionToPasteboard? |
| if ([string containsAttachments]) |
| string = attributedStringByStrippingAttachmentCharacters(string.get()); |
| return dataInRTFFormat(string.get()); |
| } |
| |
| return nullptr; |
| } |
| |
| static void getImage(Element& imageElement, RefPtr<Image>& image, CachedImage*& cachedImage) |
| { |
| auto* renderer = imageElement.renderer(); |
| if (!is<RenderImage>(renderer)) |
| return; |
| |
| CachedImage* tentativeCachedImage = downcast<RenderImage>(*renderer).cachedImage(); |
| if (!tentativeCachedImage || tentativeCachedImage->errorOccurred()) |
| return; |
| |
| image = tentativeCachedImage->imageForRenderer(renderer); |
| if (!image) |
| return; |
| |
| cachedImage = tentativeCachedImage; |
| } |
| |
| void Editor::selectionWillChange() |
| { |
| if (!hasComposition() || ignoreSelectionChanges() || m_document.selection().isNone() || !m_document.hasLivingRenderTree()) |
| return; |
| |
| cancelComposition(); |
| client()->canceledComposition(); |
| } |
| |
| String Editor::plainTextFromPasteboard(const PasteboardPlainText& text) |
| { |
| auto string = text.text; |
| |
| // FIXME: It's not clear this is 100% correct since we know -[NSURL URLWithString:] does not handle |
| // all the same cases we handle well in the URL code for creating an NSURL. |
| if (text.isURL) |
| string = WTF::userVisibleString([NSURL URLWithString:string]); |
| |
| // FIXME: WTF should offer a non-Mac-specific way to convert string to precomposed form so we can do it for all platforms. |
| return [(NSString *)string precomposedStringWithCanonicalMapping]; |
| } |
| |
| void Editor::writeImageToPasteboard(Pasteboard& pasteboard, Element& imageElement, const URL& url, const String& title) |
| { |
| PasteboardImage pasteboardImage; |
| |
| CachedImage* cachedImage = nullptr; |
| getImage(imageElement, pasteboardImage.image, cachedImage); |
| if (!pasteboardImage.image) |
| return; |
| ASSERT(cachedImage); |
| |
| if (!pasteboard.isStatic()) |
| pasteboardImage.dataInWebArchiveFormat = imageInWebArchiveFormat(imageElement); |
| |
| if (auto imageRange = makeRangeSelectingNode(imageElement)) |
| pasteboardImage.dataInHTMLFormat = serializePreservingVisualAppearance(VisibleSelection { *imageRange }, ResolveURLs::YesExcludingLocalFileURLsForPrivacy, SerializeComposedTree::Yes); |
| |
| pasteboardImage.url.url = url; |
| pasteboardImage.url.title = title; |
| pasteboardImage.url.userVisibleForm = WTF::userVisibleString(pasteboardImage.url.url); |
| if (auto* buffer = cachedImage->resourceBuffer()) |
| pasteboardImage.resourceData = buffer->makeContiguous(); |
| pasteboardImage.resourceMIMEType = cachedImage->response().mimeType(); |
| |
| pasteboard.write(pasteboardImage); |
| } |
| |
| } // namespace WebCore |
| |
| #endif // PLATFORM(MAC) |