1 | /* |
2 | * Copyright (C) 2015-2018 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. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "HTMLAttachmentElement.h" |
28 | |
29 | #if ENABLE(ATTACHMENT_ELEMENT) |
30 | |
31 | #include "DOMURL.h" |
32 | #include "Document.h" |
33 | #include "Editor.h" |
34 | #include "File.h" |
35 | #include "Frame.h" |
36 | #include "HTMLImageElement.h" |
37 | #include "HTMLNames.h" |
38 | #include "MIMETypeRegistry.h" |
39 | #include "RenderAttachment.h" |
40 | #include "SharedBuffer.h" |
41 | #include <pal/FileSizeFormatter.h> |
42 | #include <wtf/IsoMallocInlines.h> |
43 | #include <wtf/UUID.h> |
44 | #include <wtf/URLParser.h> |
45 | |
46 | #if PLATFORM(COCOA) |
47 | #include "UTIUtilities.h" |
48 | #endif |
49 | |
50 | namespace WebCore { |
51 | |
52 | WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLAttachmentElement); |
53 | |
54 | using namespace HTMLNames; |
55 | |
56 | HTMLAttachmentElement::HTMLAttachmentElement(const QualifiedName& tagName, Document& document) |
57 | : HTMLElement(tagName, document) |
58 | { |
59 | ASSERT(hasTagName(attachmentTag)); |
60 | } |
61 | |
62 | HTMLAttachmentElement::~HTMLAttachmentElement() = default; |
63 | |
64 | Ref<HTMLAttachmentElement> HTMLAttachmentElement::create(const QualifiedName& tagName, Document& document) |
65 | { |
66 | return adoptRef(*new HTMLAttachmentElement(tagName, document)); |
67 | } |
68 | |
69 | RenderPtr<RenderElement> HTMLAttachmentElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) |
70 | { |
71 | return createRenderer<RenderAttachment>(*this, WTFMove(style)); |
72 | } |
73 | |
74 | const String& HTMLAttachmentElement::getAttachmentIdentifier(HTMLImageElement& image) |
75 | { |
76 | if (auto attachment = image.attachmentElement()) |
77 | return attachment->uniqueIdentifier(); |
78 | |
79 | auto& document = image.document(); |
80 | auto attachment = create(HTMLNames::attachmentTag, document); |
81 | auto& identifier = attachment->ensureUniqueIdentifier(); |
82 | |
83 | document.registerAttachmentIdentifier(identifier); |
84 | image.setAttachmentElement(WTFMove(attachment)); |
85 | |
86 | return identifier; |
87 | } |
88 | |
89 | void HTMLAttachmentElement::copyNonAttributePropertiesFromElement(const Element& source) |
90 | { |
91 | m_uniqueIdentifier = downcast<HTMLAttachmentElement>(source).uniqueIdentifier(); |
92 | HTMLElement::copyNonAttributePropertiesFromElement(source); |
93 | } |
94 | |
95 | URL HTMLAttachmentElement::archiveResourceURL(const String& identifier) |
96 | { |
97 | auto resourceURL = URL({ }, "applewebdata://attachment/"_s ); |
98 | resourceURL.setPath(identifier); |
99 | return resourceURL; |
100 | } |
101 | |
102 | File* HTMLAttachmentElement::file() const |
103 | { |
104 | return m_file.get(); |
105 | } |
106 | |
107 | URL HTMLAttachmentElement::blobURL() const |
108 | { |
109 | return { { }, attributeWithoutSynchronization(HTMLNames::webkitattachmentbloburlAttr).string() }; |
110 | } |
111 | |
112 | void HTMLAttachmentElement::setFile(RefPtr<File>&& file, UpdateDisplayAttributes updateAttributes) |
113 | { |
114 | m_file = WTFMove(file); |
115 | |
116 | if (updateAttributes == UpdateDisplayAttributes::Yes) { |
117 | if (m_file) { |
118 | setAttributeWithoutSynchronization(HTMLNames::titleAttr, m_file->name()); |
119 | setAttributeWithoutSynchronization(HTMLNames::subtitleAttr, fileSizeDescription(m_file->size())); |
120 | setAttributeWithoutSynchronization(HTMLNames::typeAttr, m_file->type()); |
121 | } else { |
122 | removeAttribute(HTMLNames::titleAttr); |
123 | removeAttribute(HTMLNames::subtitleAttr); |
124 | removeAttribute(HTMLNames::typeAttr); |
125 | } |
126 | } |
127 | |
128 | if (auto* renderer = this->renderer()) |
129 | renderer->invalidate(); |
130 | } |
131 | |
132 | Node::InsertedIntoAncestorResult HTMLAttachmentElement::insertedIntoAncestor(InsertionType type, ContainerNode& ancestor) |
133 | { |
134 | auto result = HTMLElement::insertedIntoAncestor(type, ancestor); |
135 | if (type.connectedToDocument) |
136 | document().didInsertAttachmentElement(*this); |
137 | return result; |
138 | } |
139 | |
140 | void HTMLAttachmentElement::removedFromAncestor(RemovalType type, ContainerNode& ancestor) |
141 | { |
142 | HTMLElement::removedFromAncestor(type, ancestor); |
143 | if (type.disconnectedFromDocument) |
144 | document().didRemoveAttachmentElement(*this); |
145 | } |
146 | |
147 | const String& HTMLAttachmentElement::ensureUniqueIdentifier() |
148 | { |
149 | if (m_uniqueIdentifier.isEmpty()) |
150 | m_uniqueIdentifier = createCanonicalUUIDString(); |
151 | return m_uniqueIdentifier; |
152 | } |
153 | |
154 | bool HTMLAttachmentElement::hasEnclosingImage() const |
155 | { |
156 | return is<HTMLImageElement>(shadowHost()); |
157 | } |
158 | |
159 | void HTMLAttachmentElement::parseAttribute(const QualifiedName& name, const AtomicString& value) |
160 | { |
161 | if (name == progressAttr || name == subtitleAttr || name == titleAttr || name == typeAttr) { |
162 | if (auto* renderer = this->renderer()) |
163 | renderer->invalidate(); |
164 | } |
165 | |
166 | HTMLElement::parseAttribute(name, value); |
167 | } |
168 | |
169 | String HTMLAttachmentElement::attachmentTitle() const |
170 | { |
171 | auto& title = attributeWithoutSynchronization(titleAttr); |
172 | if (!title.isEmpty()) |
173 | return title; |
174 | return m_file ? m_file->name() : String(); |
175 | } |
176 | |
177 | String HTMLAttachmentElement::attachmentTitleForDisplay() const |
178 | { |
179 | auto title = attachmentTitle(); |
180 | |
181 | auto indexOfLastDot = title.reverseFind('.'); |
182 | if (indexOfLastDot == notFound) |
183 | return title; |
184 | |
185 | String name = title.left(indexOfLastDot); |
186 | String extension = title.substring(indexOfLastDot); |
187 | |
188 | StringBuilder builder; |
189 | builder.append(leftToRightMark); |
190 | builder.append(firstStrongIsolate); |
191 | builder.append(name); |
192 | builder.append(popDirectionalIsolate); |
193 | builder.append(extension); |
194 | |
195 | return builder.toString(); |
196 | } |
197 | |
198 | String HTMLAttachmentElement::attachmentType() const |
199 | { |
200 | return attributeWithoutSynchronization(typeAttr); |
201 | } |
202 | |
203 | String HTMLAttachmentElement::attachmentPath() const |
204 | { |
205 | return attributeWithoutSynchronization(webkitattachmentpathAttr); |
206 | } |
207 | |
208 | void HTMLAttachmentElement::updateAttributes(Optional<uint64_t>&& newFileSize, const String& newContentType, const String& newFilename) |
209 | { |
210 | if (!newFilename.isNull()) |
211 | setAttributeWithoutSynchronization(HTMLNames::titleAttr, newFilename); |
212 | else |
213 | removeAttribute(HTMLNames::titleAttr); |
214 | |
215 | if (!newContentType.isNull()) |
216 | setAttributeWithoutSynchronization(HTMLNames::typeAttr, newContentType); |
217 | else |
218 | removeAttribute(HTMLNames::typeAttr); |
219 | |
220 | if (newFileSize) |
221 | setAttributeWithoutSynchronization(HTMLNames::subtitleAttr, fileSizeDescription(*newFileSize)); |
222 | else |
223 | removeAttribute(HTMLNames::subtitleAttr); |
224 | |
225 | if (auto* renderer = this->renderer()) |
226 | renderer->invalidate(); |
227 | } |
228 | |
229 | static bool mimeTypeIsSuitableForInlineImageAttachment(const String& mimeType) |
230 | { |
231 | return MIMETypeRegistry::isSupportedImageMIMEType(mimeType) || MIMETypeRegistry::isPDFMIMEType(mimeType); |
232 | } |
233 | |
234 | void HTMLAttachmentElement::updateEnclosingImageWithData(const String& contentType, Ref<SharedBuffer>&& data) |
235 | { |
236 | auto* hostElement = shadowHost(); |
237 | if (!is<HTMLImageElement>(hostElement) || !data->size()) |
238 | return; |
239 | |
240 | String mimeType = contentType; |
241 | #if PLATFORM(COCOA) |
242 | if (isDeclaredUTI(contentType)) |
243 | mimeType = MIMETypeFromUTI(contentType); |
244 | #endif |
245 | |
246 | if (!mimeTypeIsSuitableForInlineImageAttachment(mimeType)) |
247 | return; |
248 | |
249 | hostElement->setAttributeWithoutSynchronization(HTMLNames::srcAttr, DOMURL::createObjectURL(document(), Blob::create(WTFMove(data), mimeType))); |
250 | } |
251 | |
252 | } // namespace WebCore |
253 | |
254 | #endif // ENABLE(ATTACHMENT_ELEMENT) |
255 | |