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
59namespace WebKit {
60using namespace WebCore;
61using namespace HTMLNames;
62
63typedef HashMap<Node*, InjectedBundleNodeHandle*> DOMNodeHandleCache;
64
65static DOMNodeHandleCache& domNodeHandleCache()
66{
67 static NeverDestroyed<DOMNodeHandleCache> cache;
68 return cache;
69}
70
71RefPtr<InjectedBundleNodeHandle> InjectedBundleNodeHandle::getOrCreate(JSContextRef, JSObjectRef object)
72{
73 Node* node = JSNode::toWrapped(*toJS(object)->vm(), toJS(object));
74 return getOrCreate(node);
75}
76
77RefPtr<InjectedBundleNodeHandle> InjectedBundleNodeHandle::getOrCreate(Node* node)
78{
79 if (!node)
80 return nullptr;
81
82 return InjectedBundleNodeHandle::getOrCreate(*node);
83}
84
85Ref<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
96Ref<InjectedBundleNodeHandle> InjectedBundleNodeHandle::create(Node& node)
97{
98 return adoptRef(*new InjectedBundleNodeHandle(node));
99}
100
101InjectedBundleNodeHandle::InjectedBundleNodeHandle(Node& node)
102 : m_node(node)
103{
104}
105
106InjectedBundleNodeHandle::~InjectedBundleNodeHandle()
107{
108 domNodeHandleCache().remove(m_node.ptr());
109}
110
111Node* InjectedBundleNodeHandle::coreNode()
112{
113 return m_node.ptr();
114}
115
116Ref<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
124IntRect InjectedBundleNodeHandle::elementBounds()
125{
126 if (!is<Element>(m_node))
127 return IntRect();
128
129 return downcast<Element>(m_node.get()).boundsInRootViewSpace();
130}
131
132IntRect InjectedBundleNodeHandle::renderRect(bool* isReplaced)
133{
134 return m_node->pixelSnappedRenderRect(isReplaced);
135}
136
137static 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
186RefPtr<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
217RefPtr<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
226void 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
234void InjectedBundleNodeHandle::setHTMLInputElementSpellcheckEnabled(bool enabled)
235{
236 if (!is<HTMLInputElement>(m_node))
237 return;
238
239 downcast<HTMLInputElement>(m_node.get()).setSpellcheckDisabledExceptTextReplacement(!enabled);
240}
241
242bool InjectedBundleNodeHandle::isHTMLInputElementAutoFilled() const
243{
244 if (!is<HTMLInputElement>(m_node))
245 return false;
246
247 return downcast<HTMLInputElement>(m_node.get()).isAutoFilled();
248}
249
250void InjectedBundleNodeHandle::setHTMLInputElementAutoFilled(bool filled)
251{
252 if (!is<HTMLInputElement>(m_node))
253 return;
254
255 downcast<HTMLInputElement>(m_node.get()).setAutoFilled(filled);
256}
257
258bool 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
266void InjectedBundleNodeHandle::setHTMLInputElementAutoFillButtonEnabled(AutoFillButtonType autoFillButtonType)
267{
268 if (!is<HTMLInputElement>(m_node))
269 return;
270
271 downcast<HTMLInputElement>(m_node.get()).setShowAutoFillButton(autoFillButtonType);
272}
273
274AutoFillButtonType InjectedBundleNodeHandle::htmlInputElementAutoFillButtonType() const
275{
276 if (!is<HTMLInputElement>(m_node))
277 return AutoFillButtonType::None;
278 return downcast<HTMLInputElement>(m_node.get()).autoFillButtonType();
279}
280
281AutoFillButtonType InjectedBundleNodeHandle::htmlInputElementLastAutoFillButtonType() const
282{
283 if (!is<HTMLInputElement>(m_node))
284 return AutoFillButtonType::None;
285 return downcast<HTMLInputElement>(m_node.get()).lastAutoFillButtonType();
286}
287
288bool InjectedBundleNodeHandle::isAutoFillAvailable() const
289{
290 if (!is<HTMLInputElement>(m_node))
291 return false;
292
293 return downcast<HTMLInputElement>(m_node.get()).isAutoFillAvailable();
294}
295
296void InjectedBundleNodeHandle::setAutoFillAvailable(bool autoFillAvailable)
297{
298 if (!is<HTMLInputElement>(m_node))
299 return;
300
301 downcast<HTMLInputElement>(m_node.get()).setAutoFillAvailable(autoFillAvailable);
302}
303
304IntRect 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
316bool InjectedBundleNodeHandle::htmlInputElementLastChangeWasUserEdit()
317{
318 if (!is<HTMLInputElement>(m_node))
319 return false;
320
321 return downcast<HTMLInputElement>(m_node.get()).lastChangeWasUserEdit();
322}
323
324bool InjectedBundleNodeHandle::htmlTextAreaElementLastChangeWasUserEdit()
325{
326 if (!is<HTMLTextAreaElement>(m_node))
327 return false;
328
329 return downcast<HTMLTextAreaElement>(m_node.get()).lastChangeWasUserEdit();
330}
331
332bool InjectedBundleNodeHandle::isTextField() const
333{
334 if (!is<HTMLInputElement>(m_node))
335 return false;
336
337 return downcast<HTMLInputElement>(m_node.get()).isTextField();
338}
339
340bool InjectedBundleNodeHandle::isSelectElement() const
341{
342 return is<HTMLSelectElement>(m_node);
343}
344
345RefPtr<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
353RefPtr<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
365RefPtr<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
377RefPtr<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