Remove PassRefPtr from "loader" directory of WebCore
[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 "EditingStyle.h"
37 #import "EditorClient.h"
38 #import "Frame.h"
39 #import "FrameSelection.h"
40 #import "HTMLConverter.h"
41 #import "HTMLImageElement.h"
42 #import "HTMLSpanElement.h"
43 #import "LegacyWebArchive.h"
44 #import "NSAttributedStringSPI.h"
45 #import "Pasteboard.h"
46 #import "RenderElement.h"
47 #import "RenderStyle.h"
48 #import "SoftLinking.h"
49 #import "Text.h"
50 #import "htmlediting.h"
51 #import <wtf/BlockObjCExceptions.h>
52
53 #if PLATFORM(IOS)
54 SOFT_LINK_PRIVATE_FRAMEWORK(WebKitLegacy)
55 #endif
56
57 #if PLATFORM(MAC)
58 SOFT_LINK_FRAMEWORK_IN_UMBRELLA(WebKit, WebKitLegacy)
59 #endif
60
61 SOFT_LINK(WebKitLegacy, _WebCreateFragment, void, (WebCore::Document& document, NSAttributedString *string, WebCore::FragmentAndResources& result), (document, string, result))
62
63 namespace WebCore {
64
65 void Editor::getTextDecorationAttributesRespectingTypingStyle(const RenderStyle& style, NSMutableDictionary* result) const
66 {
67     RefPtr<EditingStyle> typingStyle = m_frame.selection().typingStyle();
68     if (typingStyle && typingStyle->style()) {
69         RefPtr<CSSValue> value = typingStyle->style()->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
70         if (value && value->isValueList()) {
71             CSSValueList& valueList = downcast<CSSValueList>(*value);
72             if (valueList.hasValue(CSSValuePool::singleton().createIdentifierValue(CSSValueLineThrough).ptr()))
73                 [result setObject:@(NSUnderlineStyleSingle) forKey:NSStrikethroughStyleAttributeName];
74             if (valueList.hasValue(CSSValuePool::singleton().createIdentifierValue(CSSValueUnderline).ptr()))
75                 [result setObject:@(NSUnderlineStyleSingle) forKey:NSUnderlineStyleAttributeName];
76         }
77     } else {
78         int decoration = style.textDecorationsInEffect();
79         if (decoration & TextDecorationLineThrough)
80             [result setObject:@(NSUnderlineStyleSingle) forKey:NSStrikethroughStyleAttributeName];
81         if (decoration & TextDecorationUnderline)
82             [result setObject:@(NSUnderlineStyleSingle) forKey:NSUnderlineStyleAttributeName];
83     }
84 }
85
86 RetainPtr<NSDictionary> Editor::fontAttributesForSelectionStart() const
87 {
88     Node* nodeToRemove;
89     auto* style = styleForSelectionStart(&m_frame, nodeToRemove);
90     if (!style)
91         return nil;
92
93     RetainPtr<NSMutableDictionary> attributes = adoptNS([[NSMutableDictionary alloc] init]);
94
95     if (auto ctFont = style->fontCascade().primaryFont().getCTFont())
96         [attributes setObject:(id)ctFont forKey:NSFontAttributeName];
97
98     // FIXME: Why would we not want to retrieve these attributes on iOS?
99 #if PLATFORM(MAC)
100     if (style->visitedDependentColor(CSSPropertyBackgroundColor).isVisible())
101         [attributes setObject:nsColor(style->visitedDependentColor(CSSPropertyBackgroundColor)) forKey:NSBackgroundColorAttributeName];
102
103     if (style->visitedDependentColor(CSSPropertyColor).isValid() && !Color::isBlackColor(style->visitedDependentColor(CSSPropertyColor)))
104         [attributes setObject:nsColor(style->visitedDependentColor(CSSPropertyColor)) forKey:NSForegroundColorAttributeName];
105
106     const ShadowData* shadowData = style->textShadow();
107     if (shadowData) {
108         RetainPtr<NSShadow> platformShadow = adoptNS([[NSShadow alloc] init]);
109         [platformShadow setShadowOffset:NSMakeSize(shadowData->x(), shadowData->y())];
110         [platformShadow setShadowBlurRadius:shadowData->radius()];
111         [platformShadow setShadowColor:nsColor(shadowData->color())];
112         [attributes setObject:platformShadow.get() forKey:NSShadowAttributeName];
113     }
114
115     int superscriptInt = 0;
116     switch (style->verticalAlign()) {
117     case BASELINE:
118     case BOTTOM:
119     case BASELINE_MIDDLE:
120     case LENGTH:
121     case MIDDLE:
122     case TEXT_BOTTOM:
123     case TEXT_TOP:
124     case TOP:
125         break;
126     case SUB:
127         superscriptInt = -1;
128         break;
129     case SUPER:
130         superscriptInt = 1;
131         break;
132     }
133     if (superscriptInt)
134         [attributes setObject:@(superscriptInt) forKey:NSSuperscriptAttributeName];
135 #endif
136
137     getTextDecorationAttributesRespectingTypingStyle(*style, attributes.get());
138
139     if (nodeToRemove)
140         nodeToRemove->remove();
141
142     return attributes;
143 }
144
145 FragmentAndResources Editor::createFragment(NSAttributedString *string)
146 {
147     // FIXME: The algorithm to convert an attributed string into HTML should be implemented here in WebCore.
148     // For now, though, we call into WebKitLegacy, which in turn calls into AppKit/TextKit.
149     FragmentAndResources result;
150     _WebCreateFragment(*m_frame.document(), string, result);
151     return result;
152 }
153
154 void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
155 {
156     NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
157
158     PasteboardWebContent content;
159     content.canSmartCopyOrDelete = canSmartCopyOrDelete();
160     content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
161     content.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
162     content.dataInRTFFormat = dataInRTFFormat(attributedString);
163     // FIXME: Why don't we want this on iOS?
164 #if PLATFORM(MAC)
165     content.dataInHTMLFormat = selectionInHTMLFormat();
166 #endif
167     content.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
168     client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
169
170     pasteboard.write(content);
171 }
172
173 RefPtr<SharedBuffer> Editor::selectionInWebArchiveFormat()
174 {
175     RefPtr<LegacyWebArchive> archive = LegacyWebArchive::createFromSelection(&m_frame);
176     if (!archive)
177         return nullptr;
178     return SharedBuffer::wrapCFData(archive->rawDataRepresentation().get());
179 }
180
181 // FIXME: Makes no sense that selectedTextForDataTransfer always includes alt text, but stringSelectionForPasteboard does not.
182 // This was left in a bad state when selectedTextForDataTransfer was added. Need to look over clients and fix this.
183 String Editor::stringSelectionForPasteboard()
184 {
185     if (!canCopy())
186         return emptyString();
187     String text = selectedText();
188     text.replace(noBreakSpace, ' ');
189     return text;
190 }
191
192 String Editor::stringSelectionForPasteboardWithImageAltText()
193 {
194     if (!canCopy())
195         return emptyString();
196     String text = selectedTextForDataTransfer();
197     text.replace(noBreakSpace, ' ');
198     return text;
199 }
200
201 void Editor::replaceSelectionWithAttributedString(NSAttributedString *attributedString, MailBlockquoteHandling mailBlockquoteHandling)
202 {
203     if (m_frame.selection().isNone())
204         return;
205
206     if (m_frame.selection().selection().isContentRichlyEditable()) {
207         RefPtr<DocumentFragment> fragment = createFragmentAndAddResources(attributedString);
208         if (fragment && shouldInsertFragment(fragment, selectedRange(), EditorInsertAction::Pasted))
209             pasteAsFragment(fragment.releaseNonNull(), false, false, mailBlockquoteHandling);
210     } else {
211         String text = attributedString.string;
212         if (shouldInsertText(text, selectedRange().get(), EditorInsertAction::Pasted))
213             pasteAsPlainText(text, false);
214     }
215 }
216
217 RefPtr<DocumentFragment> Editor::createFragmentForImageResourceAndAddResource(RefPtr<ArchiveResource>&& resource)
218 {
219     if (!resource)
220         return nullptr;
221
222     // FIXME: Why is this different?
223 #if PLATFORM(MAC)
224     String resourceURL = resource->url().string();
225 #else
226     NSURL *URL = resource->url();
227     String resourceURL = URL.isFileURL ? URL.absoluteString : resource->url();
228 #endif
229
230     if (DocumentLoader* loader = m_frame.loader().documentLoader())
231         loader->addArchiveResource(resource.releaseNonNull());
232
233     auto imageElement = HTMLImageElement::create(*m_frame.document());
234     imageElement->setAttributeWithoutSynchronization(HTMLNames::srcAttr, resourceURL);
235
236     auto fragment = m_frame.document()->createDocumentFragment();
237     fragment->appendChild(imageElement);
238     
239     return WTFMove(fragment);
240 }
241
242 RefPtr<SharedBuffer> Editor::dataInRTFDFormat(NSAttributedString *string)
243 {
244     NSUInteger length = string.length;
245     if (!length)
246         return nullptr;
247
248     BEGIN_BLOCK_OBJC_EXCEPTIONS;
249     return SharedBuffer::wrapNSData([string RTFDFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
250     END_BLOCK_OBJC_EXCEPTIONS;
251
252     return nullptr;
253 }
254
255 RefPtr<SharedBuffer> Editor::dataInRTFFormat(NSAttributedString *string)
256 {
257     NSUInteger length = string.length;
258     if (!length)
259         return nullptr;
260
261     BEGIN_BLOCK_OBJC_EXCEPTIONS;
262     return SharedBuffer::wrapNSData([string RTFFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
263     END_BLOCK_OBJC_EXCEPTIONS;
264
265     return nullptr;
266 }
267
268 RefPtr<DocumentFragment> Editor::createFragmentAndAddResources(NSAttributedString *string)
269 {
270     if (!m_frame.page() || !m_frame.document())
271         return nullptr;
272
273     auto& document = *m_frame.document();
274     if (!document.isHTMLDocument() || !string)
275         return nullptr;
276
277     bool wasDeferringCallbacks = m_frame.page()->defersLoading();
278     if (!wasDeferringCallbacks)
279         m_frame.page()->setDefersLoading(true);
280
281     auto& cachedResourceLoader = document.cachedResourceLoader();
282     bool wasImagesEnabled = cachedResourceLoader.imagesEnabled();
283     if (wasImagesEnabled)
284         cachedResourceLoader.setImagesEnabled(false);
285
286     auto fragmentAndResources = createFragment(string);
287
288     if (auto* loader = m_frame.loader().documentLoader()) {
289         for (auto& resource : fragmentAndResources.resources)
290             loader->addArchiveResource(WTFMove(resource));
291     }
292
293     if (wasImagesEnabled)
294         cachedResourceLoader.setImagesEnabled(true);
295     if (!wasDeferringCallbacks)
296         m_frame.page()->setDefersLoading(false);
297     
298     return WTFMove(fragmentAndResources.fragment);
299 }
300
301 }