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() {}
 };