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