OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 25 matching lines...) Expand all Loading... |
36 #include "core/dom/NodeTraversal.h" | 36 #include "core/dom/NodeTraversal.h" |
37 #include "core/frame/LocalDOMWindow.h" | 37 #include "core/frame/LocalDOMWindow.h" |
38 #include "core/frame/FrameView.h" | 38 #include "core/frame/FrameView.h" |
39 #include "core/html/HTMLFrameOwnerElement.h" | 39 #include "core/html/HTMLFrameOwnerElement.h" |
40 #include "core/layout/LayoutObject.h" | 40 #include "core/layout/LayoutObject.h" |
41 #include "core/page/Page.h" | 41 #include "core/page/Page.h" |
42 #include "wtf/text/StringBuilder.h" | 42 #include "wtf/text/StringBuilder.h" |
43 | 43 |
44 namespace blink { | 44 namespace blink { |
45 | 45 |
46 static IntRect applyScaleWithoutCollapsingToZero(const IntRect& rect, float scal
e) | 46 static IntRect convertToContentCoordinatesWithoutCollapsingToZero(const IntRect&
rectInViewport, const FrameView* view) |
47 { | 47 { |
48 IntRect result = rect; | 48 IntRect rectInContents = view->viewportToContents(rectInViewport); |
49 result.scale(scale); | 49 if (rectInViewport.width() > 0 && !rectInContents.width()) |
50 if (rect.width() > 0 && !result.width()) | 50 rectInContents.setWidth(1); |
51 result.setWidth(1); | 51 if (rectInViewport.height() > 0 && !rectInContents.height()) |
52 if (rect.height() > 0 && !result.height()) | 52 rectInContents.setHeight(1); |
53 result.setHeight(1); | 53 return rectInContents; |
54 return result; | |
55 } | 54 } |
56 | 55 |
57 static Node* nodeInsideFrame(Node* node) | 56 static Node* nodeInsideFrame(Node* node) |
58 { | 57 { |
59 if (node->isFrameOwnerElement()) | 58 if (node->isFrameOwnerElement()) |
60 return toHTMLFrameOwnerElement(node)->contentDocument(); | 59 return toHTMLFrameOwnerElement(node)->contentDocument(); |
61 return nullptr; | 60 return nullptr; |
62 } | 61 } |
63 | 62 |
64 IntRect SmartClipData::rect() const | 63 IntRect SmartClipData::rectInViewport() const |
65 { | 64 { |
66 return m_rect; | 65 return m_rectInViewport; |
67 } | 66 } |
68 | 67 |
69 const String& SmartClipData::clipData() const | 68 const String& SmartClipData::clipData() const |
70 { | 69 { |
71 return m_string; | 70 return m_string; |
72 } | 71 } |
73 | 72 |
74 SmartClip::SmartClip(PassRefPtrWillBeRawPtr<LocalFrame> frame) | 73 SmartClip::SmartClip(PassRefPtrWillBeRawPtr<LocalFrame> frame) |
75 : m_frame(frame) | 74 : m_frame(frame) |
76 { | 75 { |
77 } | 76 } |
78 | 77 |
79 SmartClipData SmartClip::dataForRect(const IntRect& cropRect) | 78 SmartClipData SmartClip::dataForRect(const IntRect& cropRectInViewport) |
80 { | 79 { |
81 IntRect resizedCropRect = applyScaleWithoutCollapsingToZero(cropRect, 1 / pa
geScaleFactor()); | 80 Node* bestNode = findBestOverlappingNode(m_frame->document(), cropRectInView
port); |
82 | |
83 Node* bestNode = findBestOverlappingNode(m_frame->document(), resizedCropRec
t); | |
84 if (!bestNode) | 81 if (!bestNode) |
85 return SmartClipData(); | 82 return SmartClipData(); |
86 | 83 |
87 if (Node* nodeFromFrame = nodeInsideFrame(bestNode)) { | 84 if (Node* nodeFromFrame = nodeInsideFrame(bestNode)) { |
88 // FIXME: This code only hit-tests a single iframe. It seems like we oug
ht support nested frames. | 85 // FIXME: This code only hit-tests a single iframe. It seems like we oug
ht support nested frames. |
89 if (Node* bestNodeInFrame = findBestOverlappingNode(nodeFromFrame, resiz
edCropRect)) | 86 if (Node* bestNodeInFrame = findBestOverlappingNode(nodeFromFrame, cropR
ectInViewport)) |
90 bestNode = bestNodeInFrame; | 87 bestNode = bestNodeInFrame; |
91 } | 88 } |
92 | 89 |
93 WillBeHeapVector<RawPtrWillBeMember<Node>> hitNodes; | 90 WillBeHeapVector<RawPtrWillBeMember<Node>> hitNodes; |
94 collectOverlappingChildNodes(bestNode, resizedCropRect, hitNodes); | 91 collectOverlappingChildNodes(bestNode, cropRectInViewport, hitNodes); |
95 | 92 |
96 if (hitNodes.isEmpty() || hitNodes.size() == bestNode->countChildren()) { | 93 if (hitNodes.isEmpty() || hitNodes.size() == bestNode->countChildren()) { |
97 hitNodes.clear(); | 94 hitNodes.clear(); |
98 hitNodes.append(bestNode); | 95 hitNodes.append(bestNode); |
99 } | 96 } |
100 | 97 |
101 // Unite won't work with the empty rect, so we initialize to the first rect. | 98 // Unite won't work with the empty rect, so we initialize to the first rect. |
102 IntRect unitedRects = hitNodes[0]->pixelSnappedBoundingBox(); | 99 IntRect unitedRects = hitNodes[0]->pixelSnappedBoundingBox(); |
103 StringBuilder collectedText; | 100 StringBuilder collectedText; |
104 for (size_t i = 0; i < hitNodes.size(); ++i) { | 101 for (size_t i = 0; i < hitNodes.size(); ++i) { |
105 collectedText.append(extractTextFromNode(hitNodes[i])); | 102 collectedText.append(extractTextFromNode(hitNodes[i])); |
106 unitedRects.unite(hitNodes[i]->pixelSnappedBoundingBox()); | 103 unitedRects.unite(hitNodes[i]->pixelSnappedBoundingBox()); |
107 } | 104 } |
108 | 105 |
109 return SmartClipData(bestNode, convertRectToWindow(unitedRects), collectedTe
xt.toString()); | 106 return SmartClipData(bestNode, m_frame->document()->view()->contentsToViewpo
rt(unitedRects), collectedText.toString()); |
110 } | 107 } |
111 | 108 |
112 float SmartClip::pageScaleFactor() | 109 float SmartClip::pageScaleFactor() |
113 { | 110 { |
114 return m_frame->page()->pageScaleFactor(); | 111 return m_frame->page()->pageScaleFactor(); |
115 } | 112 } |
116 | 113 |
117 // This function is a bit of a mystery. If you understand what it does, please | 114 // This function is a bit of a mystery. If you understand what it does, please |
118 // consider adding a more descriptive name. | 115 // consider adding a more descriptive name. |
119 Node* SmartClip::minNodeContainsNodes(Node* minNode, Node* newNode) | 116 Node* SmartClip::minNodeContainsNodes(Node* minNode, Node* newNode) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
152 if (nodeRect.contains(newNodeRect)) { | 149 if (nodeRect.contains(newNodeRect)) { |
153 return node; | 150 return node; |
154 } | 151 } |
155 } | 152 } |
156 node = node->parentNode(); | 153 node = node->parentNode(); |
157 } | 154 } |
158 | 155 |
159 return nullptr; | 156 return nullptr; |
160 } | 157 } |
161 | 158 |
162 Node* SmartClip::findBestOverlappingNode(Node* rootNode, const IntRect& cropRect
) | 159 Node* SmartClip::findBestOverlappingNode(Node* rootNode, const IntRect& cropRect
InViewport) |
163 { | 160 { |
164 if (!rootNode) | 161 if (!rootNode) |
165 return nullptr; | 162 return nullptr; |
166 | 163 |
167 IntRect resizedCropRect = rootNode->document().view()->windowToContents(crop
Rect); | 164 IntRect resizedCropRect = convertToContentCoordinatesWithoutCollapsingToZero
(cropRectInViewport, rootNode->document().view()); |
168 | 165 |
169 Node* node = rootNode; | 166 Node* node = rootNode; |
170 Node* minNode = nullptr; | 167 Node* minNode = nullptr; |
171 | 168 |
172 while (node) { | 169 while (node) { |
173 IntRect nodeRect = node->pixelSnappedBoundingBox(); | 170 IntRect nodeRect = node->pixelSnappedBoundingBox(); |
174 | 171 |
175 if (node->isElementNode() && equalIgnoringCase(toElement(node)->fastGetA
ttribute(HTMLNames::aria_hiddenAttr), "true")) { | 172 if (node->isElementNode() && equalIgnoringCase(toElement(node)->fastGetA
ttribute(HTMLNames::aria_hiddenAttr), "true")) { |
176 node = NodeTraversal::nextSkippingChildren(*node, rootNode); | 173 node = NodeTraversal::nextSkippingChildren(*node, rootNode); |
177 continue; | 174 continue; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 // image out of a CSS background, you're probably going to specify a height | 208 // image out of a CSS background, you're probably going to specify a height |
212 // or a width. On the other hand, if we've got a legit background image, | 209 // or a width. On the other hand, if we've got a legit background image, |
213 // it's very likely the height or the width will be set to auto. | 210 // it's very likely the height or the width will be set to auto. |
214 LayoutObject* renderer = node->layoutObject(); | 211 LayoutObject* renderer = node->layoutObject(); |
215 if (renderer && (renderer->style()->logicalHeight().isAuto() || renderer->st
yle()->logicalWidth().isAuto())) | 212 if (renderer && (renderer->style()->logicalHeight().isAuto() || renderer->st
yle()->logicalWidth().isAuto())) |
216 return true; | 213 return true; |
217 | 214 |
218 return false; | 215 return false; |
219 } | 216 } |
220 | 217 |
221 void SmartClip::collectOverlappingChildNodes(Node* parentNode, const IntRect& cr
opRect, WillBeHeapVector<RawPtrWillBeMember<Node>>& hitNodes) | 218 void SmartClip::collectOverlappingChildNodes(Node* parentNode, const IntRect& cr
opRectInViewport, WillBeHeapVector<RawPtrWillBeMember<Node>>& hitNodes) |
222 { | 219 { |
223 if (!parentNode) | 220 if (!parentNode) |
224 return; | 221 return; |
225 IntRect resizedCropRect = parentNode->document().view()->windowToContents(cr
opRect); | 222 IntRect resizedCropRect = convertToContentCoordinatesWithoutCollapsingToZero
(cropRectInViewport, parentNode->document().view()); |
226 for (Node* child = parentNode->firstChild(); child; child = child->nextSibli
ng()) { | 223 for (Node* child = parentNode->firstChild(); child; child = child->nextSibli
ng()) { |
227 IntRect childRect = child->pixelSnappedBoundingBox(); | 224 IntRect childRect = child->pixelSnappedBoundingBox(); |
228 if (resizedCropRect.intersects(childRect)) | 225 if (resizedCropRect.intersects(childRect)) |
229 hitNodes.append(child); | 226 hitNodes.append(child); |
230 } | 227 } |
231 } | 228 } |
232 | 229 |
233 IntRect SmartClip::convertRectToWindow(const IntRect& nodeRect) | |
234 { | |
235 IntRect result = m_frame->document()->view()->contentsToWindow(nodeRect); | |
236 result.scale(pageScaleFactor()); | |
237 return result; | |
238 } | |
239 | |
240 String SmartClip::extractTextFromNode(Node* node) | 230 String SmartClip::extractTextFromNode(Node* node) |
241 { | 231 { |
242 // Science has proven that no text nodes are ever positioned at y == -99999. | 232 // Science has proven that no text nodes are ever positioned at y == -99999. |
243 int prevYPos = -99999; | 233 int prevYPos = -99999; |
244 | 234 |
245 StringBuilder result; | 235 StringBuilder result; |
246 for (Node& currentNode : NodeTraversal::inclusiveDescendantsOf(*node)) { | 236 for (Node& currentNode : NodeTraversal::inclusiveDescendantsOf(*node)) { |
247 const LayoutStyle* style = currentNode.computedStyle(); | 237 const LayoutStyle* style = currentNode.computedStyle(); |
248 if (style && style->userSelect() == SELECT_NONE) | 238 if (style && style->userSelect() == SELECT_NONE) |
249 continue; | 239 continue; |
(...skipping 18 matching lines...) Expand all Loading... |
268 | 258 |
269 result.append(nodeValue); | 259 result.append(nodeValue); |
270 } | 260 } |
271 } | 261 } |
272 } | 262 } |
273 | 263 |
274 return result.toString(); | 264 return result.toString(); |
275 } | 265 } |
276 | 266 |
277 } // namespace blink | 267 } // namespace blink |
OLD | NEW |