Add support for using the current text selection as the find string on iOS
[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 "FontAttributes.h"
40 #import "FontCascade.h"
41 #import "Frame.h"
42 #import "FrameLoader.h"
43 #import "FrameSelection.h"
44 #import "HTMLAttachmentElement.h"
45 #import "HTMLConverter.h"
46 #import "HTMLImageElement.h"
47 #import "HTMLSpanElement.h"
48 #import "LegacyNSPasteboardTypes.h"
49 #import "LegacyWebArchive.h"
50 #import "Pasteboard.h"
51 #import "PasteboardStrategy.h"
52 #import "PlatformStrategies.h"
53 #import "RenderElement.h"
54 #import "RenderStyle.h"
55 #import "Settings.h"
56 #import "Text.h"
57 #import "WebContentReader.h"
58 #import "markup.h"
59 #import <pal/spi/cocoa/NSAttributedStringSPI.h>
60 #import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
61 #import <pal/system/Sound.h>
62 #import <wtf/BlockObjCExceptions.h>
63 #import <wtf/cocoa/NSURLExtras.h>
64
65 namespace WebCore {
66
67 void Editor::platformFontAttributesAtSelectionStart(FontAttributes& attributes, const RenderStyle& style) const
68 {
69     if (auto ctFont = style.fontCascade().primaryFont().getCTFont())
70         attributes.font = (__bridge id)ctFont;
71 }
72
73 static RefPtr<SharedBuffer> archivedDataForAttributedString(NSAttributedString *attributedString)
74 {
75     if (!attributedString.length)
76         return nullptr;
77
78     return SharedBuffer::create(securelyArchivedDataWithRootObject(attributedString));
79 }
80
81 String Editor::selectionInHTMLFormat()
82 {
83     return serializePreservingVisualAppearance(m_frame.selection().selection(), ResolveURLs::YesExcludingLocalFileURLsForPrivacy,
84         m_frame.settings().selectionAcrossShadowBoundariesEnabled() ? SerializeComposedTree::Yes : SerializeComposedTree::No);
85 }
86
87 #if ENABLE(ATTACHMENT_ELEMENT)
88
89 void Editor::getPasteboardTypesAndDataForAttachment(Element& element, Vector<String>& outTypes, Vector<RefPtr<SharedBuffer>>& outData)
90 {
91     auto& document = element.document();
92     auto elementRange = Range::create(document, { &element, Position::PositionIsBeforeAnchor }, { &element, Position::PositionIsAfterAnchor });
93     client()->getClientPasteboardDataForRange(elementRange.ptr(), outTypes, outData);
94
95     outTypes.append(PasteboardCustomData::cocoaType());
96     outData.append(PasteboardCustomData { document.originIdentifierForPasteboard(), { }, { }, { } }.createSharedBuffer());
97
98     if (auto archive = LegacyWebArchive::create(elementRange.ptr())) {
99         if (auto webArchiveData = archive->rawDataRepresentation()) {
100             outTypes.append(WebArchivePboardType);
101             outData.append(SharedBuffer::create(webArchiveData.get()));
102         }
103     }
104 }
105
106 #endif
107
108 void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
109 {
110     NSAttributedString *attributedString = attributedStringFromSelection(m_frame.selection().selection());
111
112     PasteboardWebContent content;
113     content.contentOrigin = m_frame.document()->originIdentifierForPasteboard();
114     content.canSmartCopyOrDelete = canSmartCopyOrDelete();
115     content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
116     content.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
117     content.dataInRTFFormat = dataInRTFFormat(attributedString);
118     content.dataInAttributedStringFormat = archivedDataForAttributedString(attributedString);
119     content.dataInHTMLFormat = selectionInHTMLFormat();
120     content.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
121     client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
122
123     pasteboard.write(content);
124 }
125
126 void Editor::writeSelection(PasteboardWriterData& pasteboardWriterData)
127 {
128     NSAttributedString *attributedString = attributedStringFromSelection(m_frame.selection().selection());
129
130     PasteboardWriterData::WebContent webContent;
131     webContent.contentOrigin = m_frame.document()->originIdentifierForPasteboard();
132     webContent.canSmartCopyOrDelete = canSmartCopyOrDelete();
133     webContent.dataInWebArchiveFormat = selectionInWebArchiveFormat();
134     webContent.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
135     webContent.dataInRTFFormat = dataInRTFFormat(attributedString);
136     webContent.dataInAttributedStringFormat = archivedDataForAttributedString(attributedString);
137     webContent.dataInHTMLFormat = selectionInHTMLFormat();
138     webContent.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
139     client()->getClientPasteboardDataForRange(selectedRange().get(), webContent.clientTypes, webContent.clientData);
140
141     pasteboardWriterData.setWebContent(WTFMove(webContent));
142 }
143
144 RefPtr<SharedBuffer> Editor::selectionInWebArchiveFormat()
145 {
146     auto archive = LegacyWebArchive::createFromSelection(&m_frame);
147     if (!archive)
148         return nullptr;
149     return SharedBuffer::create(archive->rawDataRepresentation().get());
150 }
151
152 // FIXME: Makes no sense that selectedTextForDataTransfer always includes alt text, but stringSelectionForPasteboard does not.
153 // This was left in a bad state when selectedTextForDataTransfer was added. Need to look over clients and fix this.
154 String Editor::stringSelectionForPasteboard()
155 {
156     if (!canCopy())
157         return emptyString();
158     String text = selectedText();
159     text.replace(noBreakSpace, ' ');
160     return text;
161 }
162
163 String Editor::stringSelectionForPasteboardWithImageAltText()
164 {
165     if (!canCopy())
166         return emptyString();
167     String text = selectedTextForDataTransfer();
168     text.replace(noBreakSpace, ' ');
169     return text;
170 }
171
172 void Editor::replaceSelectionWithAttributedString(NSAttributedString *attributedString, MailBlockquoteHandling mailBlockquoteHandling)
173 {
174     if (m_frame.selection().isNone())
175         return;
176
177     if (m_frame.selection().selection().isContentRichlyEditable()) {
178         if (auto fragment = createFragmentAndAddResources(m_frame, attributedString)) {
179             if (shouldInsertFragment(*fragment, selectedRange().get(), EditorInsertAction::Pasted))
180                 pasteAsFragment(fragment.releaseNonNull(), false, false, mailBlockquoteHandling);
181         }
182     } else {
183         String text = attributedString.string;
184         if (shouldInsertText(text, selectedRange().get(), EditorInsertAction::Pasted))
185             pasteAsPlainText(text, false);
186     }
187 }
188
189 String Editor::userVisibleString(const URL& url)
190 {
191     return WTF::userVisibleString(url);
192 }
193
194 RefPtr<SharedBuffer> Editor::dataInRTFDFormat(NSAttributedString *string)
195 {
196     NSUInteger length = string.length;
197     if (!length)
198         return nullptr;
199
200     BEGIN_BLOCK_OBJC_EXCEPTIONS;
201     return SharedBuffer::create([string RTFDFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
202     END_BLOCK_OBJC_EXCEPTIONS;
203
204     return nullptr;
205 }
206
207 RefPtr<SharedBuffer> Editor::dataInRTFFormat(NSAttributedString *string)
208 {
209     NSUInteger length = string.length;
210     if (!length)
211         return nullptr;
212
213     BEGIN_BLOCK_OBJC_EXCEPTIONS;
214     return SharedBuffer::create([string RTFFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
215     END_BLOCK_OBJC_EXCEPTIONS;
216
217     return nullptr;
218 }
219
220 // FIXME: Should give this function a name that makes it clear it adds resources to the document loader as a side effect.
221 // Or refactor so it does not do that.
222 RefPtr<DocumentFragment> Editor::webContentFromPasteboard(Pasteboard& pasteboard, Range& context, bool allowPlainText, bool& chosePlainText)
223 {
224     WebContentReader reader(m_frame, context, allowPlainText);
225     pasteboard.read(reader);
226     chosePlainText = reader.madeFragmentFromPlainText;
227     return WTFMove(reader.fragment);
228 }
229
230 void Editor::takeFindStringFromSelection()
231 {
232     if (!canCopyExcludingStandaloneImages()) {
233         PAL::systemBeep();
234         return;
235     }
236
237     auto stringFromSelection = m_frame.displayStringModifiedByEncoding(selectedTextForDataTransfer());
238 #if PLATFORM(MAC)
239     Vector<String> types;
240     types.append(String(legacyStringPasteboardType()));
241     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
242     platformStrategies()->pasteboardStrategy()->setTypes(types, NSFindPboard);
243     platformStrategies()->pasteboardStrategy()->setStringForType(WTFMove(stringFromSelection), legacyStringPasteboardType(), NSFindPboard);
244     ALLOW_DEPRECATED_DECLARATIONS_END
245 #else
246     if (auto* client = this->client()) {
247         // Since the find pasteboard doesn't exist on iOS, WebKit maintains its own notion of the latest find string,
248         // which SPI clients may respect when presenting find-in-page UI.
249         client->updateStringForFind(stringFromSelection);
250     }
251 #endif
252 }
253
254 }