2 * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #import "ArchiveResource.h"
30 #import "CSSValueList.h"
31 #import "CSSValuePool.h"
32 #import "CachedResourceLoader.h"
34 #import "DocumentFragment.h"
35 #import "DocumentLoader.h"
37 #import "EditingStyle.h"
38 #import "EditorClient.h"
40 #import "FrameSelection.h"
41 #import "HTMLConverter.h"
42 #import "HTMLImageElement.h"
43 #import "HTMLSpanElement.h"
44 #import "LegacyWebArchive.h"
45 #import "NSAttributedStringSPI.h"
46 #import "Pasteboard.h"
47 #import "RenderElement.h"
48 #import "RenderStyle.h"
49 #import "SoftLinking.h"
51 #import <wtf/BlockObjCExceptions.h>
54 SOFT_LINK_PRIVATE_FRAMEWORK(WebKitLegacy)
58 SOFT_LINK_FRAMEWORK_IN_UMBRELLA(WebKit, WebKitLegacy)
61 // FIXME: Get rid of this and change NSAttributedString conversion so it doesn't use WebKitLegacy (cf. rdar://problem/30597352).
62 SOFT_LINK(WebKitLegacy, _WebCreateFragment, void, (WebCore::Document& document, NSAttributedString *string, WebCore::FragmentAndResources& result), (document, string, result))
66 void Editor::getTextDecorationAttributesRespectingTypingStyle(const RenderStyle& style, NSMutableDictionary* result) const
68 RefPtr<EditingStyle> typingStyle = m_frame.selection().typingStyle();
69 if (typingStyle && typingStyle->style()) {
70 RefPtr<CSSValue> value = typingStyle->style()->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
71 if (value && value->isValueList()) {
72 CSSValueList& valueList = downcast<CSSValueList>(*value);
73 if (valueList.hasValue(CSSValuePool::singleton().createIdentifierValue(CSSValueLineThrough).ptr()))
74 [result setObject:@(NSUnderlineStyleSingle) forKey:NSStrikethroughStyleAttributeName];
75 if (valueList.hasValue(CSSValuePool::singleton().createIdentifierValue(CSSValueUnderline).ptr()))
76 [result setObject:@(NSUnderlineStyleSingle) forKey:NSUnderlineStyleAttributeName];
79 int decoration = style.textDecorationsInEffect();
80 if (decoration & TextDecorationLineThrough)
81 [result setObject:@(NSUnderlineStyleSingle) forKey:NSStrikethroughStyleAttributeName];
82 if (decoration & TextDecorationUnderline)
83 [result setObject:@(NSUnderlineStyleSingle) forKey:NSUnderlineStyleAttributeName];
87 RetainPtr<NSDictionary> Editor::fontAttributesForSelectionStart() const
90 auto* style = styleForSelectionStart(&m_frame, nodeToRemove);
94 RetainPtr<NSMutableDictionary> attributes = adoptNS([[NSMutableDictionary alloc] init]);
96 if (auto ctFont = style->fontCascade().primaryFont().getCTFont())
97 [attributes setObject:(id)ctFont forKey:NSFontAttributeName];
99 // FIXME: Why would we not want to retrieve these attributes on iOS?
101 if (style->visitedDependentColor(CSSPropertyBackgroundColor).isVisible())
102 [attributes setObject:nsColor(style->visitedDependentColor(CSSPropertyBackgroundColor)) forKey:NSBackgroundColorAttributeName];
104 if (style->visitedDependentColor(CSSPropertyColor).isValid() && !Color::isBlackColor(style->visitedDependentColor(CSSPropertyColor)))
105 [attributes setObject:nsColor(style->visitedDependentColor(CSSPropertyColor)) forKey:NSForegroundColorAttributeName];
107 const ShadowData* shadowData = style->textShadow();
109 RetainPtr<NSShadow> platformShadow = adoptNS([[NSShadow alloc] init]);
110 [platformShadow setShadowOffset:NSMakeSize(shadowData->x(), shadowData->y())];
111 [platformShadow setShadowBlurRadius:shadowData->radius()];
112 [platformShadow setShadowColor:nsColor(shadowData->color())];
113 [attributes setObject:platformShadow.get() forKey:NSShadowAttributeName];
116 int superscriptInt = 0;
117 switch (style->verticalAlign()) {
120 case BASELINE_MIDDLE:
135 [attributes setObject:@(superscriptInt) forKey:NSSuperscriptAttributeName];
138 getTextDecorationAttributesRespectingTypingStyle(*style, attributes.get());
141 nodeToRemove->remove();
146 FragmentAndResources Editor::createFragment(NSAttributedString *string)
148 // FIXME: The algorithm to convert an attributed string into HTML should be implemented here in WebCore.
149 // For now, though, we call into WebKitLegacy, which in turn calls into AppKit/TextKit.
150 FragmentAndResources result;
151 _WebCreateFragment(*m_frame.document(), string, result);
155 static RefPtr<SharedBuffer> archivedDataForAttributedString(NSAttributedString *attributedString)
157 if (!attributedString.length)
160 return SharedBuffer::wrapNSData([NSKeyedArchiver archivedDataWithRootObject:attributedString]);
163 void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
165 NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
167 PasteboardWebContent content;
168 content.canSmartCopyOrDelete = canSmartCopyOrDelete();
169 content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
170 content.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
171 content.dataInRTFFormat = dataInRTFFormat(attributedString);
172 content.dataInAttributedStringFormat = archivedDataForAttributedString(attributedString);
173 // FIXME: Why don't we want this on iOS?
175 content.dataInHTMLFormat = selectionInHTMLFormat();
177 content.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
178 client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
180 pasteboard.write(content);
183 void Editor::writeSelection(PasteboardWriterData& pasteboardWriterData)
185 NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
187 PasteboardWriterData::WebContent webContent;
188 webContent.canSmartCopyOrDelete = canSmartCopyOrDelete();
189 webContent.dataInWebArchiveFormat = selectionInWebArchiveFormat();
190 webContent.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
191 webContent.dataInRTFFormat = dataInRTFFormat(attributedString);
192 webContent.dataInAttributedStringFormat = archivedDataForAttributedString(attributedString);
193 // FIXME: Why don't we want this on iOS?
195 webContent.dataInHTMLFormat = selectionInHTMLFormat();
197 webContent.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
198 client()->getClientPasteboardDataForRange(selectedRange().get(), webContent.clientTypes, webContent.clientData);
200 pasteboardWriterData.setWebContent(WTFMove(webContent));
203 RefPtr<SharedBuffer> Editor::selectionInWebArchiveFormat()
205 RefPtr<LegacyWebArchive> archive = LegacyWebArchive::createFromSelection(&m_frame);
208 return SharedBuffer::wrapCFData(archive->rawDataRepresentation().get());
211 // FIXME: Makes no sense that selectedTextForDataTransfer always includes alt text, but stringSelectionForPasteboard does not.
212 // This was left in a bad state when selectedTextForDataTransfer was added. Need to look over clients and fix this.
213 String Editor::stringSelectionForPasteboard()
216 return emptyString();
217 String text = selectedText();
218 text.replace(noBreakSpace, ' ');
222 String Editor::stringSelectionForPasteboardWithImageAltText()
225 return emptyString();
226 String text = selectedTextForDataTransfer();
227 text.replace(noBreakSpace, ' ');
231 void Editor::replaceSelectionWithAttributedString(NSAttributedString *attributedString, MailBlockquoteHandling mailBlockquoteHandling)
233 if (m_frame.selection().isNone())
236 if (m_frame.selection().selection().isContentRichlyEditable()) {
237 RefPtr<DocumentFragment> fragment = createFragmentAndAddResources(attributedString);
238 if (fragment && shouldInsertFragment(fragment, selectedRange(), EditorInsertAction::Pasted))
239 pasteAsFragment(fragment.releaseNonNull(), false, false, mailBlockquoteHandling);
241 String text = attributedString.string;
242 if (shouldInsertText(text, selectedRange().get(), EditorInsertAction::Pasted))
243 pasteAsPlainText(text, false);
247 RefPtr<DocumentFragment> Editor::createFragmentForImageResourceAndAddResource(RefPtr<ArchiveResource>&& resource)
252 // FIXME: Why is this different?
254 String resourceURL = resource->url().string();
256 NSURL *URL = resource->url();
257 String resourceURL = URL.isFileURL ? URL.absoluteString : resource->url();
260 if (DocumentLoader* loader = m_frame.loader().documentLoader())
261 loader->addArchiveResource(resource.releaseNonNull());
263 auto imageElement = HTMLImageElement::create(*m_frame.document());
264 imageElement->setAttributeWithoutSynchronization(HTMLNames::srcAttr, resourceURL);
266 auto fragment = m_frame.document()->createDocumentFragment();
267 fragment->appendChild(imageElement);
269 return WTFMove(fragment);
272 RefPtr<SharedBuffer> Editor::dataInRTFDFormat(NSAttributedString *string)
274 NSUInteger length = string.length;
278 BEGIN_BLOCK_OBJC_EXCEPTIONS;
279 return SharedBuffer::wrapNSData([string RTFDFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
280 END_BLOCK_OBJC_EXCEPTIONS;
285 RefPtr<SharedBuffer> Editor::dataInRTFFormat(NSAttributedString *string)
287 NSUInteger length = string.length;
291 BEGIN_BLOCK_OBJC_EXCEPTIONS;
292 return SharedBuffer::wrapNSData([string RTFFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
293 END_BLOCK_OBJC_EXCEPTIONS;
298 RefPtr<DocumentFragment> Editor::createFragmentAndAddResources(NSAttributedString *string)
300 if (!m_frame.page() || !m_frame.document())
303 auto& document = *m_frame.document();
304 if (!document.isHTMLDocument() || !string)
307 bool wasDeferringCallbacks = m_frame.page()->defersLoading();
308 if (!wasDeferringCallbacks)
309 m_frame.page()->setDefersLoading(true);
311 auto& cachedResourceLoader = document.cachedResourceLoader();
312 bool wasImagesEnabled = cachedResourceLoader.imagesEnabled();
313 if (wasImagesEnabled)
314 cachedResourceLoader.setImagesEnabled(false);
316 auto fragmentAndResources = createFragment(string);
318 if (auto* loader = m_frame.loader().documentLoader()) {
319 for (auto& resource : fragmentAndResources.resources)
320 loader->addArchiveResource(WTFMove(resource));
323 if (wasImagesEnabled)
324 cachedResourceLoader.setImagesEnabled(true);
325 if (!wasDeferringCallbacks)
326 m_frame.page()->setDefersLoading(false);
328 return WTFMove(fragmentAndResources.fragment);