1 | /* |
2 | * Copyright (C) 2010 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 "InjectedBundleNodeHandle.h" |
28 | |
29 | #include "InjectedBundleRangeHandle.h" |
30 | #include "ShareableBitmap.h" |
31 | #include "WebFrame.h" |
32 | #include "WebFrameLoaderClient.h" |
33 | #include "WebImage.h" |
34 | #include <JavaScriptCore/APICast.h> |
35 | #include <WebCore/Document.h> |
36 | #include <WebCore/Frame.h> |
37 | #include <WebCore/FrameLoader.h> |
38 | #include <WebCore/FrameView.h> |
39 | #include <WebCore/GraphicsContext.h> |
40 | #include <WebCore/HTMLFrameElement.h> |
41 | #include <WebCore/HTMLIFrameElement.h> |
42 | #include <WebCore/HTMLInputElement.h> |
43 | #include <WebCore/HTMLNames.h> |
44 | #include <WebCore/HTMLSelectElement.h> |
45 | #include <WebCore/HTMLTableCellElement.h> |
46 | #include <WebCore/HTMLTextAreaElement.h> |
47 | #include <WebCore/IntRect.h> |
48 | #include <WebCore/JSNode.h> |
49 | #include <WebCore/Node.h> |
50 | #include <WebCore/Page.h> |
51 | #include <WebCore/Position.h> |
52 | #include <WebCore/Range.h> |
53 | #include <WebCore/RenderObject.h> |
54 | #include <WebCore/VisiblePosition.h> |
55 | #include <wtf/HashMap.h> |
56 | #include <wtf/NeverDestroyed.h> |
57 | #include <wtf/text/WTFString.h> |
58 | |
59 | namespace WebKit { |
60 | using namespace WebCore; |
61 | using namespace HTMLNames; |
62 | |
63 | typedef HashMap<Node*, InjectedBundleNodeHandle*> DOMNodeHandleCache; |
64 | |
65 | static DOMNodeHandleCache& domNodeHandleCache() |
66 | { |
67 | static NeverDestroyed<DOMNodeHandleCache> cache; |
68 | return cache; |
69 | } |
70 | |
71 | RefPtr<InjectedBundleNodeHandle> InjectedBundleNodeHandle::getOrCreate(JSContextRef, JSObjectRef object) |
72 | { |
73 | Node* node = JSNode::toWrapped(*toJS(object)->vm(), toJS(object)); |
74 | return getOrCreate(node); |
75 | } |
76 | |
77 | RefPtr<InjectedBundleNodeHandle> InjectedBundleNodeHandle::getOrCreate(Node* node) |
78 | { |
79 | if (!node) |
80 | return nullptr; |
81 | |
82 | return InjectedBundleNodeHandle::getOrCreate(*node); |
83 | } |
84 | |
85 | Ref<InjectedBundleNodeHandle> InjectedBundleNodeHandle::getOrCreate(Node& node) |
86 | { |
87 | DOMNodeHandleCache::AddResult result = domNodeHandleCache().add(&node, nullptr); |
88 | if (!result.isNewEntry) |
89 | return Ref<InjectedBundleNodeHandle>(*result.iterator->value); |
90 | |
91 | Ref<InjectedBundleNodeHandle> nodeHandle = InjectedBundleNodeHandle::create(node); |
92 | result.iterator->value = nodeHandle.ptr(); |
93 | return nodeHandle; |
94 | } |
95 | |
96 | Ref<InjectedBundleNodeHandle> InjectedBundleNodeHandle::create(Node& node) |
97 | { |
98 | return adoptRef(*new InjectedBundleNodeHandle(node)); |
99 | } |
100 | |
101 | InjectedBundleNodeHandle::InjectedBundleNodeHandle(Node& node) |
102 | : m_node(node) |
103 | { |
104 | } |
105 | |
106 | InjectedBundleNodeHandle::~InjectedBundleNodeHandle() |
107 | { |
108 | domNodeHandleCache().remove(m_node.ptr()); |
109 | } |
110 | |
111 | Node* InjectedBundleNodeHandle::coreNode() |
112 | { |
113 | return m_node.ptr(); |
114 | } |
115 | |
116 | Ref<InjectedBundleNodeHandle> InjectedBundleNodeHandle::document() |
117 | { |
118 | return getOrCreate(m_node->document()); |
119 | } |
120 | |
121 | // Additional DOM Operations |
122 | // Note: These should only be operations that are not exposed to JavaScript. |
123 | |
124 | IntRect InjectedBundleNodeHandle::elementBounds() |
125 | { |
126 | if (!is<Element>(m_node)) |
127 | return IntRect(); |
128 | |
129 | return downcast<Element>(m_node.get()).boundsInRootViewSpace(); |
130 | } |
131 | |
132 | IntRect InjectedBundleNodeHandle::renderRect(bool* isReplaced) |
133 | { |
134 | return m_node->pixelSnappedRenderRect(isReplaced); |
135 | } |
136 | |
137 | static RefPtr<WebImage> imageForRect(FrameView* frameView, const IntRect& paintingRect, const Optional<float>& bitmapWidth, SnapshotOptions options) |
138 | { |
139 | if (paintingRect.isEmpty()) |
140 | return nullptr; |
141 | |
142 | float bitmapScaleFactor; |
143 | IntSize bitmapSize; |
144 | if (bitmapWidth) { |
145 | bitmapScaleFactor = bitmapWidth.value() / paintingRect.width(); |
146 | bitmapSize = roundedIntSize(FloatSize(bitmapWidth.value(), paintingRect.height() * bitmapScaleFactor)); |
147 | } else { |
148 | bitmapScaleFactor = 1; |
149 | bitmapSize = paintingRect.size(); |
150 | } |
151 | |
152 | float deviceScaleFactor = frameView->frame().page()->deviceScaleFactor(); |
153 | bitmapSize.scale(deviceScaleFactor); |
154 | |
155 | if (bitmapSize.isEmpty()) |
156 | return nullptr; |
157 | |
158 | auto snapshot = WebImage::create(bitmapSize, snapshotOptionsToImageOptions(options)); |
159 | if (!snapshot) |
160 | return nullptr; |
161 | |
162 | auto graphicsContext = snapshot->bitmap().createGraphicsContext(); |
163 | graphicsContext->clearRect(IntRect(IntPoint(), bitmapSize)); |
164 | graphicsContext->applyDeviceScaleFactor(deviceScaleFactor); |
165 | graphicsContext->scale(bitmapScaleFactor); |
166 | graphicsContext->translate(-paintingRect.location()); |
167 | |
168 | FrameView::SelectionInSnapshot shouldPaintSelection = FrameView::IncludeSelection; |
169 | if (options & SnapshotOptionsExcludeSelectionHighlighting) |
170 | shouldPaintSelection = FrameView::ExcludeSelection; |
171 | |
172 | auto paintBehavior = frameView->paintBehavior() | PaintBehavior::FlattenCompositingLayers | PaintBehavior::Snapshotting; |
173 | if (options & SnapshotOptionsForceBlackText) |
174 | paintBehavior.add(PaintBehavior::ForceBlackText); |
175 | if (options & SnapshotOptionsForceWhiteText) |
176 | paintBehavior.add(PaintBehavior::ForceWhiteText); |
177 | |
178 | auto oldPaintBehavior = frameView->paintBehavior(); |
179 | frameView->setPaintBehavior(paintBehavior); |
180 | frameView->paintContentsForSnapshot(*graphicsContext.get(), paintingRect, shouldPaintSelection, FrameView::DocumentCoordinates); |
181 | frameView->setPaintBehavior(oldPaintBehavior); |
182 | |
183 | return snapshot; |
184 | } |
185 | |
186 | RefPtr<WebImage> InjectedBundleNodeHandle::renderedImage(SnapshotOptions options, bool shouldExcludeOverflow, const Optional<float>& bitmapWidth) |
187 | { |
188 | Frame* frame = m_node->document().frame(); |
189 | if (!frame) |
190 | return nullptr; |
191 | |
192 | FrameView* frameView = frame->view(); |
193 | if (!frameView) |
194 | return nullptr; |
195 | |
196 | m_node->document().updateLayout(); |
197 | |
198 | RenderObject* renderer = m_node->renderer(); |
199 | if (!renderer) |
200 | return nullptr; |
201 | |
202 | IntRect paintingRect; |
203 | if (shouldExcludeOverflow) |
204 | paintingRect = renderer->absoluteBoundingBoxRectIgnoringTransforms(); |
205 | else { |
206 | LayoutRect topLevelRect; |
207 | paintingRect = snappedIntRect(renderer->paintingRootRect(topLevelRect)); |
208 | } |
209 | |
210 | frameView->setNodeToDraw(m_node.ptr()); |
211 | auto image = imageForRect(frameView, paintingRect, bitmapWidth, options); |
212 | frameView->setNodeToDraw(0); |
213 | |
214 | return image; |
215 | } |
216 | |
217 | RefPtr<InjectedBundleRangeHandle> InjectedBundleNodeHandle::visibleRange() |
218 | { |
219 | VisiblePosition start = firstPositionInNode(m_node.ptr()); |
220 | VisiblePosition end = lastPositionInNode(m_node.ptr()); |
221 | |
222 | RefPtr<Range> range = makeRange(start, end); |
223 | return InjectedBundleRangeHandle::getOrCreate(range.get()); |
224 | } |
225 | |
226 | void InjectedBundleNodeHandle::setHTMLInputElementValueForUser(const String& value) |
227 | { |
228 | if (!is<HTMLInputElement>(m_node)) |
229 | return; |
230 | |
231 | downcast<HTMLInputElement>(m_node.get()).setValueForUser(value); |
232 | } |
233 | |
234 | void InjectedBundleNodeHandle::setHTMLInputElementSpellcheckEnabled(bool enabled) |
235 | { |
236 | if (!is<HTMLInputElement>(m_node)) |
237 | return; |
238 | |
239 | downcast<HTMLInputElement>(m_node.get()).setSpellcheckDisabledExceptTextReplacement(!enabled); |
240 | } |
241 | |
242 | bool InjectedBundleNodeHandle::isHTMLInputElementAutoFilled() const |
243 | { |
244 | if (!is<HTMLInputElement>(m_node)) |
245 | return false; |
246 | |
247 | return downcast<HTMLInputElement>(m_node.get()).isAutoFilled(); |
248 | } |
249 | |
250 | void InjectedBundleNodeHandle::setHTMLInputElementAutoFilled(bool filled) |
251 | { |
252 | if (!is<HTMLInputElement>(m_node)) |
253 | return; |
254 | |
255 | downcast<HTMLInputElement>(m_node.get()).setAutoFilled(filled); |
256 | } |
257 | |
258 | bool InjectedBundleNodeHandle::isHTMLInputElementAutoFillButtonEnabled() const |
259 | { |
260 | if (!is<HTMLInputElement>(m_node)) |
261 | return false; |
262 | |
263 | return downcast<HTMLInputElement>(m_node.get()).autoFillButtonType() != AutoFillButtonType::None; |
264 | } |
265 | |
266 | void InjectedBundleNodeHandle::setHTMLInputElementAutoFillButtonEnabled(AutoFillButtonType autoFillButtonType) |
267 | { |
268 | if (!is<HTMLInputElement>(m_node)) |
269 | return; |
270 | |
271 | downcast<HTMLInputElement>(m_node.get()).setShowAutoFillButton(autoFillButtonType); |
272 | } |
273 | |
274 | AutoFillButtonType InjectedBundleNodeHandle::htmlInputElementAutoFillButtonType() const |
275 | { |
276 | if (!is<HTMLInputElement>(m_node)) |
277 | return AutoFillButtonType::None; |
278 | return downcast<HTMLInputElement>(m_node.get()).autoFillButtonType(); |
279 | } |
280 | |
281 | AutoFillButtonType InjectedBundleNodeHandle::htmlInputElementLastAutoFillButtonType() const |
282 | { |
283 | if (!is<HTMLInputElement>(m_node)) |
284 | return AutoFillButtonType::None; |
285 | return downcast<HTMLInputElement>(m_node.get()).lastAutoFillButtonType(); |
286 | } |
287 | |
288 | bool InjectedBundleNodeHandle::isAutoFillAvailable() const |
289 | { |
290 | if (!is<HTMLInputElement>(m_node)) |
291 | return false; |
292 | |
293 | return downcast<HTMLInputElement>(m_node.get()).isAutoFillAvailable(); |
294 | } |
295 | |
296 | void InjectedBundleNodeHandle::setAutoFillAvailable(bool autoFillAvailable) |
297 | { |
298 | if (!is<HTMLInputElement>(m_node)) |
299 | return; |
300 | |
301 | downcast<HTMLInputElement>(m_node.get()).setAutoFillAvailable(autoFillAvailable); |
302 | } |
303 | |
304 | IntRect InjectedBundleNodeHandle::htmlInputElementAutoFillButtonBounds() |
305 | { |
306 | if (!is<HTMLInputElement>(m_node)) |
307 | return IntRect(); |
308 | |
309 | auto autoFillButton = downcast<HTMLInputElement>(m_node.get()).autoFillButtonElement(); |
310 | if (!autoFillButton) |
311 | return IntRect(); |
312 | |
313 | return autoFillButton->boundsInRootViewSpace(); |
314 | } |
315 | |
316 | bool InjectedBundleNodeHandle::htmlInputElementLastChangeWasUserEdit() |
317 | { |
318 | if (!is<HTMLInputElement>(m_node)) |
319 | return false; |
320 | |
321 | return downcast<HTMLInputElement>(m_node.get()).lastChangeWasUserEdit(); |
322 | } |
323 | |
324 | bool InjectedBundleNodeHandle::htmlTextAreaElementLastChangeWasUserEdit() |
325 | { |
326 | if (!is<HTMLTextAreaElement>(m_node)) |
327 | return false; |
328 | |
329 | return downcast<HTMLTextAreaElement>(m_node.get()).lastChangeWasUserEdit(); |
330 | } |
331 | |
332 | bool InjectedBundleNodeHandle::isTextField() const |
333 | { |
334 | if (!is<HTMLInputElement>(m_node)) |
335 | return false; |
336 | |
337 | return downcast<HTMLInputElement>(m_node.get()).isTextField(); |
338 | } |
339 | |
340 | bool InjectedBundleNodeHandle::isSelectElement() const |
341 | { |
342 | return is<HTMLSelectElement>(m_node); |
343 | } |
344 | |
345 | RefPtr<InjectedBundleNodeHandle> InjectedBundleNodeHandle::htmlTableCellElementCellAbove() |
346 | { |
347 | if (!is<HTMLTableCellElement>(m_node)) |
348 | return nullptr; |
349 | |
350 | return getOrCreate(downcast<HTMLTableCellElement>(m_node.get()).cellAbove()); |
351 | } |
352 | |
353 | RefPtr<WebFrame> InjectedBundleNodeHandle::documentFrame() |
354 | { |
355 | if (!m_node->isDocumentNode()) |
356 | return nullptr; |
357 | |
358 | Frame* frame = downcast<Document>(m_node.get()).frame(); |
359 | if (!frame) |
360 | return nullptr; |
361 | |
362 | return WebFrame::fromCoreFrame(*frame); |
363 | } |
364 | |
365 | RefPtr<WebFrame> InjectedBundleNodeHandle::htmlFrameElementContentFrame() |
366 | { |
367 | if (!is<HTMLFrameElement>(m_node)) |
368 | return nullptr; |
369 | |
370 | Frame* frame = downcast<HTMLFrameElement>(m_node.get()).contentFrame(); |
371 | if (!frame) |
372 | return nullptr; |
373 | |
374 | return WebFrame::fromCoreFrame(*frame); |
375 | } |
376 | |
377 | RefPtr<WebFrame> InjectedBundleNodeHandle::htmlIFrameElementContentFrame() |
378 | { |
379 | if (!is<HTMLIFrameElement>(m_node)) |
380 | return nullptr; |
381 | |
382 | Frame* frame = downcast<HTMLIFrameElement>(m_node.get()).contentFrame(); |
383 | if (!frame) |
384 | return nullptr; |
385 | |
386 | return WebFrame::fromCoreFrame(*frame); |
387 | } |
388 | |
389 | } // namespace WebKit |
390 | |