Support Samsung's SmartClip feature
Patch from Samsung to support SmartClip.
Bug: 12242896
Change-Id: I9bbca5788aa17205d158ceae1cf23f4ac11f3065
diff --git a/Source/web/WebViewImpl.cpp b/Source/web/WebViewImpl.cpp
index f123849..7312d25 100644
--- a/Source/web/WebViewImpl.cpp
+++ b/Source/web/WebViewImpl.cpp
@@ -96,12 +96,14 @@
#include "core/dom/DocumentMarkerController.h"
#include "core/dom/KeyboardEvent.h"
#include "core/dom/NodeRenderStyle.h"
+#include "core/dom/NodeTraversal.h"
#include "core/dom/Text.h"
#include "core/dom/WheelEvent.h"
#include "core/editing/Editor.h"
#include "core/editing/FrameSelection.h"
#include "core/editing/InputMethodController.h"
#include "core/editing/TextIterator.h"
+#include "core/html/HTMLFrameOwnerElement.h"
#include "core/html/HTMLInputElement.h"
#include "core/html/HTMLMediaElement.h"
#include "core/html/HTMLTextAreaElement.h"
@@ -4209,4 +4211,288 @@
|| (arguments.minZoom == arguments.maxZoom && arguments.minZoom != ViewportArguments::ValueAuto);
}
+Node* WebViewImpl::minNodeContainsNodes(Node* minNode, Node* newNode)
+{
+ if(!newNode)
+ return minNode;
+
+ if(!minNode)
+ return newNode;
+
+ IntRect minNodeRect = pixelSnappedIntRect(minNode->Node::boundingBox());
+ IntRect newNodeRect = pixelSnappedIntRect(newNode->Node::boundingBox());
+
+ Node* parentMinNode = minNode->parentNode();
+ Node* parentNewNode = newNode->parentNode();
+
+ if(minNodeRect.contains(newNodeRect) == true) {
+ if(parentMinNode && parentNewNode && parentNewNode->parentNode() == parentMinNode)
+ return parentMinNode;
+ else
+ return minNode;
+ }
+
+ if(newNodeRect.contains(minNodeRect) == true) {
+ if(parentMinNode && parentNewNode && parentMinNode->parentNode() == parentNewNode)
+ return parentNewNode;
+ else
+ return newNode;
+ }
+
+ Node* node = minNode;
+ while(node) {
+ if(node->renderer()) {
+ IntRect nodeRect = pixelSnappedIntRect(node->Node::boundingBox());
+ if(nodeRect.contains(newNodeRect) == true) {
+ return node;
+ }
+ }
+ node = node->parentNode();
+ }
+ return 0;
+}
+
+Node* WebViewImpl::findBestOverlappingNode(Node* bodyNode, const IntRect& cropRect)
+{
+ Node* node = bodyNode;
+ Node* minNode = 0;
+ IntRect resizeCropRect;
+
+ if(bodyNode)
+ resizeCropRect = bodyNode->document()->view()->windowToContents(cropRect);
+
+ while(node) {
+ IntRect nodeRect = pixelSnappedIntRect(node->Node::boundingBox());
+ bool hasAriaHiddenAttr = node->hasAttributes() && toElement(node)->getAttribute(HTMLNames::aria_hiddenAttr) == "true";
+
+ if(node->renderer() && nodeRect.width() > 0 && nodeRect.height() > 0) {
+ if(hasAriaHiddenAttr) {
+ node = NodeTraversal::nextSkippingChildren(node, bodyNode);
+ continue;
+ }
+
+ if(node->renderer()->isText() || node->renderer()->isRenderImage() || node->hasTagName(HTMLNames::iframeTag)
+ || (node->renderer()->style()->hasBackgroundImage() && !isSkipBackgroundImage(node))) {
+ if(resizeCropRect.intersects(nodeRect) == true) {
+ minNode = minNodeContainsNodes(minNode, node);
+ } else {
+ node = NodeTraversal::nextSkippingChildren(node, bodyNode);
+ continue;
+ }
+ }
+ }
+ node = NodeTraversal::next(node, bodyNode); //pNode->traverseNextNode(bodyNode);
+ }
+
+ return minNode;
+}
+
+bool WebViewImpl::isSkipBackgroundImage(Node* node) {
+ if(!node || !(node->hasTagName(HTMLNames::spanTag) || node->hasTagName(HTMLNames::divTag)))
+ return true;
+
+ RenderObject* renderer = node->renderer();
+ if(renderer && (renderer->style()->logicalHeight().isAuto() || renderer->style()->logicalWidth().isAuto()))
+ return true;
+
+ return false;
+}
+
+Node* WebViewImpl::getBodyNodeInsideIframe(Node* iframeNode) {
+ if(!iframeNode)
+ return NULL;
+
+ HTMLFrameOwnerElement* frameOwnerElement= static_cast<HTMLFrameOwnerElement*>(iframeNode);
+ Document *ownerDocument = frameOwnerElement->contentWindow()->document();
+ if (!ownerDocument)
+ return iframeNode;
+
+ Node* bodyNode = ownerDocument->body();
+ return bodyNode;
+}
+
+void WebViewImpl::saveOverlappingChildNodes(WebCore::Node* parentNode, const IntRect& cropRect,
+ Vector<OverlappingNodeInfo>* overlappingNodeInfoTable) {
+ if(!parentNode || !parentNode->firstChild())
+ return ;
+
+ IntRect resizeCropRect = parentNode->document()->view()->windowToContents(cropRect);
+ for (Node* child = parentNode->firstChild(); child; child = child->nextSibling()) {
+ IntRect childRect = pixelSnappedIntRect(child->Node::boundingBox());
+ if(resizeCropRect.intersects(childRect))
+ overlappingNodeInfoTable->append(OverlappingNodeInfo(child, childRect));
+ }
+ return ;
+}
+
+IntRect WebViewImpl::combinedRectOverlappingNodes(Vector<OverlappingNodeInfo>* overlappingNodeInfoTable) {
+ int tableSize = overlappingNodeInfoTable->size();
+ OverlappingNodeInfo& nodeInfo = (*overlappingNodeInfoTable)[0];
+ IntRect resultArea(nodeInfo.m_rect);
+
+ for (int i=1; i<tableSize; i++) {
+ nodeInfo = (*overlappingNodeInfoTable)[i];
+ resultArea.unite(nodeInfo.m_rect);
+ }
+
+ return resultArea;
+}
+
+IntRect WebViewImpl::convertRectToWindow(WebCore::Node* node, WebCore::IntRect nodeRect) {
+ nodeRect = node->document()->view()->contentsToWindow(nodeRect);
+ int x = (int)(nodeRect.x()*pageScaleFactor());
+ int y = (int)(nodeRect.y()*pageScaleFactor());
+ int w = (int)(nodeRect.width()*pageScaleFactor());
+ int h = (int)(nodeRect.height()*pageScaleFactor());
+
+ return IntRect(x,y,w,h);
+}
+
+String WebViewImpl::convertNodeInfoTableToString(Vector<OverlappingNodeInfo>* nodeInfoTable) {
+ String FieldSeparator;
+ String RowSeparator;
+ String resultStr;
+ int tableSize = nodeInfoTable->size();
+
+ FieldSeparator.append((UChar)0xFFFE);
+ RowSeparator.append((UChar)0xFFFF);
+
+ for(int i=0; i<tableSize; i++) {
+ OverlappingNodeInfo& nodeInfo = (*nodeInfoTable)[i];
+
+ String rowStr;
+ rowStr = String::number(nodeInfo.m_rect.x())
+ + FieldSeparator
+ + String::number(nodeInfo.m_rect.y())
+ + FieldSeparator
+ + String::number(nodeInfo.m_rect.width())
+ + FieldSeparator
+ + String::number(nodeInfo.m_rect.height())
+ + FieldSeparator
+ + nodeInfo.m_string
+ + RowSeparator;
+
+ resultStr.append(rowStr); //resultStr += rowStr;
+ }
+
+ return resultStr;
+}
+
+String WebViewImpl::extractTextFromNode(Node* node) {
+ String resultStr;
+ Node* currentNode = node;
+ int prevYPos = -99999;
+
+ while(currentNode != NULL) {
+ Node::NodeType nodeType = currentNode->nodeType();
+ IntRect nodeRect = pixelSnappedIntRect(currentNode->Node::boundingBox());
+
+ RenderStyle* style = currentNode->computedStyle();
+ if(!currentNode->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true) ) ||(style &&style->userSelect() == SELECT_NONE)) {
+ return resultStr;
+ }
+
+ if(currentNode->hasTagName(HTMLNames::iframeTag)) {
+ Node* iframeBodyNode= getBodyNodeInsideIframe(currentNode);
+ if(iframeBodyNode){
+ String str= extractTextFromNode(iframeBodyNode);
+ resultStr.append(str);
+ }
+ }
+
+ if(currentNode->renderer() && nodeRect.width() > 0 && nodeRect.height() > 0) {
+ if(nodeType == Node::TEXT_NODE) {
+ String nodeValue = currentNode->nodeValue();
+
+ if(nodeValue.isNull())
+ nodeValue = "";
+
+ if(nodeValue == "\n")
+ nodeValue = "";
+
+ if(nodeRect.y() != prevYPos) {
+ resultStr.append(String("\n") + nodeValue); // resultStr += String("\n") + nodeValue;
+ prevYPos = nodeRect.y();
+ } else {
+ resultStr.append(nodeValue + " "); //resultStr += nodeValue + " ";
+ }
+ }
+ }
+ currentNode = NodeTraversal::next(currentNode, node); //pCurNode->traverseNextNode(pNode);
+ }
+ return resultStr;
+}
+
+WebString WebViewImpl::extractSmartClipData(WebRect cropRect) {
+ RefPtr<Frame> frame= focusedWebCoreFrame();
+ if (!frame)
+ return WebString();
+
+ // Calculate absolute document coordinate
+ IntRect absCropRect(cropRect.x, cropRect.y, cropRect.width, cropRect.height);
+ int x =(int)(absCropRect.x()/pageScaleFactor());
+ int y =(int)(absCropRect.y()/pageScaleFactor());
+ int w =(int)(absCropRect.width()/pageScaleFactor());
+ int h =(int)(absCropRect.height()/pageScaleFactor());
+ if(absCropRect.width()>0 && w==0)//For drag&drop in smartclip
+ w =1;
+ if(absCropRect.height()>0 && h==0)//For drag&drop in smartclip
+ h =1;
+
+ IntRect resizeCropRect(x, y, w, h);
+
+
+ String resultStr("");
+ Vector<OverlappingNodeInfo>* resultNodeInfoTable = new Vector<OverlappingNodeInfo>();
+ Vector<OverlappingNodeInfo>* overlappingNodeInfoTable = new Vector<OverlappingNodeInfo>();
+
+ if(frame && resultNodeInfoTable && overlappingNodeInfoTable) {
+ Node* node =frame->document()->body();
+ Node* bestNode = findBestOverlappingNode(node, resizeCropRect);
+
+ // Set entire cropped rect & extract text which included in cropped rect.
+ if(bestNode) {
+ IntRect containerRect(0,0,0,0);
+ String collectedText(""); // Extract all text which contains in pMinContainer.
+
+ if(bestNode && bestNode->hasTagName(HTMLNames::iframeTag)) {
+ Node* bodyNodeOfIframe= getBodyNodeInsideIframe(bestNode);
+ if(bodyNodeOfIframe) {
+ Node* bestNodeInIframe = findBestOverlappingNode(bodyNodeOfIframe, resizeCropRect);
+ if(bestNodeInIframe)
+ bestNode=bestNodeInIframe;
+ }
+ }
+ saveOverlappingChildNodes(bestNode, resizeCropRect, overlappingNodeInfoTable);
+ if(overlappingNodeInfoTable->size() == 0 ||overlappingNodeInfoTable->size()==(unsigned int)(bestNode->childNodeCount())) {
+ collectedText = extractTextFromNode(bestNode);
+ containerRect = pixelSnappedIntRect(bestNode->Node::boundingBox());
+ } else {
+ int tableSize=overlappingNodeInfoTable->size();
+ for (int i=0; i<tableSize; i++)
+ {
+ OverlappingNodeInfo& nodeInfo = (*overlappingNodeInfoTable)[i];
+ collectedText.append(extractTextFromNode(nodeInfo.m_node));
+ }
+ containerRect= combinedRectOverlappingNodes(overlappingNodeInfoTable);
+ }
+ containerRect=convertRectToWindow(bestNode,containerRect);
+ resultNodeInfoTable->append(OverlappingNodeInfo(bestNode, containerRect, collectedText));
+ // Converts CroppedNodeInfo array to string which will transfer to Java layer.
+ resultStr = convertNodeInfoTableToString(resultNodeInfoTable);
+ }
+ else
+ {
+ resultStr ="";
+ }
+ }
+
+ if (resultNodeInfoTable)
+ delete resultNodeInfoTable;
+ if (overlappingNodeInfoTable)
+ delete overlappingNodeInfoTable;
+
+ return resultStr;
+}
+
} // namespace WebKit
diff --git a/Source/web/WebViewImpl.h b/Source/web/WebViewImpl.h
index 5e3d321..6c817a5 100644
--- a/Source/web/WebViewImpl.h
+++ b/Source/web/WebViewImpl.h
@@ -567,7 +567,29 @@
// Exposed for tests.
WebVector<WebCompositionUnderline> compositionUnderlines() const;
+ class OverlappingNodeInfo
+ {
+ public:
+ WebCore::Node* m_node;
+ WebCore::IntRect m_rect;
+ WTF::String m_string;
+
+ OverlappingNodeInfo(WebCore::Node* node, WebCore::IntRect rect) { m_node = node; m_rect = rect; }
+ OverlappingNodeInfo(WebCore::Node* node, WebCore::IntRect rect, WTF::String string) { m_node = node; m_rect = rect; m_string = string; }
+ };
+
+ WebString extractSmartClipData(WebRect cropRect);
+
private:
+ WebCore::Node* minNodeContainsNodes(WebCore::Node* minNode, WebCore::Node* newNode);
+ WebCore::Node* findBestOverlappingNode(WebCore::Node* bodyNode, const WebCore::IntRect& cropRect);
+ bool isSkipBackgroundImage(WebCore::Node* node);
+ void saveOverlappingChildNodes(WebCore::Node* parentNode, const WebCore::IntRect& cropRect, Vector<OverlappingNodeInfo>* overlappingNodeInfoTable);
+ WebCore::Node* getBodyNodeInsideIframe(WebCore::Node* iframeNode);
+ WebCore::IntRect combinedRectOverlappingNodes(Vector<OverlappingNodeInfo>* overlappingNodeInfoTable);
+ WebCore::IntRect convertRectToWindow(WebCore::Node* node, WebCore::IntRect nodeRect);
+ WTF::String convertNodeInfoTableToString(Vector<OverlappingNodeInfo>* nodeInfoTable);
+ WTF::String extractTextFromNode(WebCore::Node* pNode);
void refreshPageScaleFactorAfterLayout();
void setUserAgentPageScaleConstraints(WebCore::PageScaleConstraints newConstraints);
float clampPageScaleFactorToLimits(float) const;
diff --git a/public/web/WebView.h b/public/web/WebView.h
index 1c770e4..d95bbbb 100644
--- a/public/web/WebView.h
+++ b/public/web/WebView.h
@@ -523,6 +523,8 @@
// Testing functionality for TestRunner ---------------------------------
+ virtual WebString extractSmartClipData(WebRect checkRect) = 0;
+
protected:
~WebView() {}
};