Unreviewed, rolling out r223889.
[WebKit-https.git] / Source / WebCore / editing / cocoa / EditorCocoa.mm
1 /*
2  * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #import "config.h"
27 #import "Editor.h"
28
29 #import "ArchiveResource.h"
30 #import "CSSValueList.h"
31 #import "CSSValuePool.h"
32 #import "CachedResourceLoader.h"
33 #import "ColorMac.h"
34 #import "DocumentFragment.h"
35 #import "DocumentLoader.h"
36 #import "Editing.h"
37 #import "EditingStyle.h"
38 #import "EditorClient.h"
39 #import "FontCascade.h"
40 #import "Frame.h"
41 #import "FrameLoader.h"
42 #import "FrameSelection.h"
43 #import "HTMLConverter.h"
44 #import "HTMLImageElement.h"
45 #import "HTMLSpanElement.h"
46 #import "LegacyWebArchive.h"
47 #import "Pasteboard.h"
48 #import "RenderElement.h"
49 #import "RenderStyle.h"
50 #import "Text.h"
51 #import "WebContentReader.h"
52 #import "WebCoreNSURLExtras.h"
53 #import "markup.h"
54 #import <pal/spi/cocoa/NSAttributedStringSPI.h>
55 #import <wtf/BlockObjCExceptions.h>
56
57 namespace WebCore {
58
59 void Editor::getTextDecorationAttributesRespectingTypingStyle(const RenderStyle& style, NSMutableDictionary* result) const
60 {
61     RefPtr<EditingStyle> typingStyle = m_frame.selection().typingStyle();
62     if (typingStyle && typingStyle->style()) {
63         RefPtr<CSSValue> value = typingStyle->style()->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
64         if (value && value->isValueList()) {
65             CSSValueList& valueList = downcast<CSSValueList>(*value);
66             if (valueList.hasValue(CSSValuePool::singleton().createIdentifierValue(CSSValueLineThrough).ptr()))
67                 [result setObject:@(NSUnderlineStyleSingle) forKey:NSStrikethroughStyleAttributeName];
68             if (valueList.hasValue(CSSValuePool::singleton().createIdentifierValue(CSSValueUnderline).ptr()))
69                 [result setObject:@(NSUnderlineStyleSingle) forKey:NSUnderlineStyleAttributeName];
70         }
71     } else {
72         int decoration = style.textDecorationsInEffect();
73         if (decoration & TextDecorationLineThrough)
74             [result setObject:@(NSUnderlineStyleSingle) forKey:NSStrikethroughStyleAttributeName];
75         if (decoration & TextDecorationUnderline)
76             [result setObject:@(NSUnderlineStyleSingle) forKey:NSUnderlineStyleAttributeName];
77     }
78 }
79
80 RetainPtr<NSDictionary> Editor::fontAttributesForSelectionStart() const
81 {
82     Node* nodeToRemove;
83     auto* style = styleForSelectionStart(&m_frame, nodeToRemove);
84     if (!style)
85         return nil;
86
87     RetainPtr<NSMutableDictionary> attributes = adoptNS([[NSMutableDictionary alloc] init]);
88
89     if (auto ctFont = style->fontCascade().primaryFont().getCTFont())
90         [attributes setObject:(id)ctFont forKey:NSFontAttributeName];
91
92     // FIXME: Why would we not want to retrieve these attributes on iOS?
93 #if PLATFORM(MAC)
94     if (style->visitedDependentColor(CSSPropertyBackgroundColor).isVisible())
95         [attributes setObject:nsColor(style->visitedDependentColor(CSSPropertyBackgroundColor)) forKey:NSBackgroundColorAttributeName];
96
97     if (style->visitedDependentColor(CSSPropertyColor).isValid() && !Color::isBlackColor(style->visitedDependentColor(CSSPropertyColor)))
98         [attributes setObject:nsColor(style->visitedDependentColor(CSSPropertyColor)) forKey:NSForegroundColorAttributeName];
99
100     const ShadowData* shadowData = style->textShadow();
101     if (shadowData) {
102         RetainPtr<NSShadow> platformShadow = adoptNS([[NSShadow alloc] init]);
103         [platformShadow setShadowOffset:NSMakeSize(shadowData->x(), shadowData->y())];
104         [platformShadow setShadowBlurRadius:shadowData->radius()];
105         [platformShadow setShadowColor:nsColor(shadowData->color())];
106         [attributes setObject:platformShadow.get() forKey:NSShadowAttributeName];
107     }
108
109     int superscriptInt = 0;
110     switch (style->verticalAlign()) {
111     case BASELINE:
112     case BOTTOM:
113     case BASELINE_MIDDLE:
114     case LENGTH:
115     case MIDDLE:
116     case TEXT_BOTTOM:
117     case TEXT_TOP:
118     case TOP:
119         break;
120     case SUB:
121         superscriptInt = -1;
122         break;
123     case SUPER:
124         superscriptInt = 1;
125         break;
126     }
127     if (superscriptInt)
128         [attributes setObject:@(superscriptInt) forKey:NSSuperscriptAttributeName];
129 #endif
130
131     getTextDecorationAttributesRespectingTypingStyle(*style, attributes.get());
132
133     if (nodeToRemove)
134         nodeToRemove->remove();
135
136     return attributes;
137 }
138
139 static RefPtr<SharedBuffer> archivedDataForAttributedString(NSAttributedString *attributedString)
140 {
141     if (!attributedString.length)
142         return nullptr;
143
144     return SharedBuffer::create([NSKeyedArchiver archivedDataWithRootObject:attributedString]);
145 }
146
147 String Editor::selectionInHTMLFormat()
148 {
149     if (auto range = selectedRange())
150         return createMarkup(*range, nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs);
151     return { };
152 }
153
154 void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
155 {
156     NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
157
158     PasteboardWebContent content;
159     content.contentOrigin = m_frame.document()->originIdentifierForPasteboard();
160     content.canSmartCopyOrDelete = canSmartCopyOrDelete();
161     content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
162     content.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
163     content.dataInRTFFormat = dataInRTFFormat(attributedString);
164     content.dataInAttributedStringFormat = archivedDataForAttributedString(attributedString);
165     content.dataInHTMLFormat = selectionInHTMLFormat();
166     content.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
167     client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
168
169     pasteboard.write(content);
170 }
171
172 void Editor::writeSelection(PasteboardWriterData& pasteboardWriterData)
173 {
174     NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
175
176     PasteboardWriterData::WebContent webContent;
177     webContent.contentOrigin = m_frame.document()->originIdentifierForPasteboard();
178     webContent.canSmartCopyOrDelete = canSmartCopyOrDelete();
179     webContent.dataInWebArchiveFormat = selectionInWebArchiveFormat();
180     webContent.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
181     webContent.dataInRTFFormat = dataInRTFFormat(attributedString);
182     webContent.dataInAttributedStringFormat = archivedDataForAttributedString(attributedString);
183     webContent.dataInHTMLFormat = selectionInHTMLFormat();
184     webContent.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
185     client()->getClientPasteboardDataForRange(selectedRange().get(), webContent.clientTypes, webContent.clientData);
186
187     pasteboardWriterData.setWebContent(WTFMove(webContent));
188 }
189
190 RefPtr<SharedBuffer> Editor::selectionInWebArchiveFormat()
191 {
192     RefPtr<LegacyWebArchive> archive = LegacyWebArchive::createFromSelection(&m_frame);
193     if (!archive)
194         return nullptr;
195     return SharedBuffer::create(archive->rawDataRepresentation().get());
196 }
197
198 // FIXME: Makes no sense that selectedTextForDataTransfer always includes alt text, but stringSelectionForPasteboard does not.
199 // This was left in a bad state when selectedTextForDataTransfer was added. Need to look over clients and fix this.
200 String Editor::stringSelectionForPasteboard()
201 {
202     if (!canCopy())
203         return emptyString();
204     String text = selectedText();
205     text.replace(noBreakSpace, ' ');
206     return text;
207 }
208
209 String Editor::stringSelectionForPasteboardWithImageAltText()
210 {
211     if (!canCopy())
212         return emptyString();
213     String text = selectedTextForDataTransfer();
214     text.replace(noBreakSpace, ' ');
215     return text;
216 }
217
218 void Editor::replaceSelectionWithAttributedString(NSAttributedString *attributedString, MailBlockquoteHandling mailBlockquoteHandling)
219 {
220     if (m_frame.selection().isNone())
221         return;
222
223     if (m_frame.selection().selection().isContentRichlyEditable()) {
224         RefPtr<DocumentFragment> fragment = createFragmentAndAddResources(m_frame, attributedString);
225         if (fragment && shouldInsertFragment(*fragment, selectedRange().get(), EditorInsertAction::Pasted))
226             pasteAsFragment(fragment.releaseNonNull(), false, false, mailBlockquoteHandling);
227     } else {
228         String text = attributedString.string;
229         if (shouldInsertText(text, selectedRange().get(), EditorInsertAction::Pasted))
230             pasteAsPlainText(text, false);
231     }
232 }
233
234 String Editor::userVisibleString(const URL& url)
235 {
236     return WebCore::userVisibleString(url);
237 }
238
239 RefPtr<SharedBuffer> Editor::dataInRTFDFormat(NSAttributedString *string)
240 {
241     NSUInteger length = string.length;
242     if (!length)
243         return nullptr;
244
245     BEGIN_BLOCK_OBJC_EXCEPTIONS;
246     return SharedBuffer::create([string RTFDFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
247     END_BLOCK_OBJC_EXCEPTIONS;
248
249     return nullptr;
250 }
251
252 RefPtr<SharedBuffer> Editor::dataInRTFFormat(NSAttributedString *string)
253 {
254     NSUInteger length = string.length;
255     if (!length)
256         return nullptr;
257
258     BEGIN_BLOCK_OBJC_EXCEPTIONS;
259     return SharedBuffer::create([string RTFFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
260     END_BLOCK_OBJC_EXCEPTIONS;
261
262     return nullptr;
263 }
264
265 }