Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "webkit/glue/dom_operations.h"
      6 
      7 #include <set>
      8 
      9 #include "base/compiler_specific.h"
     10 #include "base/logging.h"
     11 #include "base/string_util.h"
     12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAnimationController.h"
     13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
     14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
     15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
     16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
     17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
     18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeCollection.h"
     19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeList.h"
     20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
     21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebVector.h"
     22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
     23 
     24 using WebKit::WebAnimationController;
     25 using WebKit::WebDocument;
     26 using WebKit::WebElement;
     27 using WebKit::WebFrame;
     28 using WebKit::WebInputElement;
     29 using WebKit::WebNode;
     30 using WebKit::WebNodeCollection;
     31 using WebKit::WebNodeList;
     32 using WebKit::WebString;
     33 using WebKit::WebVector;
     34 using WebKit::WebView;
     35 
     36 namespace {
     37 
     38 // Structure for storage the unique set of all savable resource links for
     39 // making sure that no duplicated resource link in final result. The consumer
     40 // of the SavableResourcesUniqueCheck is responsible for keeping these pointers
     41 // valid for the lifetime of the SavableResourcesUniqueCheck instance.
     42 struct SavableResourcesUniqueCheck {
     43   // Unique set of all sub resource links.
     44   std::set<GURL>* resources_set;
     45   // Unique set of all frame links.
     46   std::set<GURL>* frames_set;
     47   // Collection of all frames we go through when getting all savable resource
     48   // links.
     49   std::vector<WebFrame*>* frames;
     50 
     51   SavableResourcesUniqueCheck()
     52       : resources_set(NULL),
     53         frames_set(NULL),
     54         frames(NULL) {}
     55 
     56   SavableResourcesUniqueCheck(std::set<GURL>* resources_set,
     57       std::set<GURL>* frames_set, std::vector<WebFrame*>* frames)
     58       : resources_set(resources_set),
     59         frames_set(frames_set),
     60         frames(frames) {}
     61 };
     62 
     63 // Get all savable resource links from current element. One element might
     64 // have more than one resource link. It is possible to have some links
     65 // in one CSS stylesheet.
     66 void GetSavableResourceLinkForElement(
     67     const WebElement& element,
     68     const WebDocument& current_doc,
     69     SavableResourcesUniqueCheck* unique_check,
     70     webkit_glue::SavableResourcesResult* result) {
     71 
     72   // Handle frame and iframe tag.
     73   if (element.hasTagName("iframe") ||
     74       element.hasTagName("frame")) {
     75     WebFrame* sub_frame = WebFrame::fromFrameOwnerElement(element);
     76     if (sub_frame)
     77       unique_check->frames->push_back(sub_frame);
     78     return;
     79   }
     80 
     81   // Check whether the node has sub resource URL or not.
     82   WebString value =
     83       webkit_glue::GetSubResourceLinkFromElement(element);
     84   if (value.isNull())
     85     return;
     86   // Get absolute URL.
     87   GURL u = current_doc.completeURL(value);
     88   // ignore invalid URL
     89   if (!u.is_valid())
     90     return;
     91   // Ignore those URLs which are not standard protocols. Because FTP
     92   // protocol does no have cache mechanism, we will skip all
     93   // sub-resources if they use FTP protocol.
     94   if (!u.SchemeIs("http") && !u.SchemeIs("https") && !u.SchemeIs("file"))
     95     return;
     96   // Ignore duplicated resource link.
     97   if (!unique_check->resources_set->insert(u).second)
     98     return;
     99   result->resources_list->push_back(u);
    100   // Insert referrer for above new resource link.
    101   result->referrers_list->push_back(GURL());
    102 }
    103 
    104 // Get all savable resource links from current WebFrameImpl object pointer.
    105 void GetAllSavableResourceLinksForFrame(WebFrame* current_frame,
    106     SavableResourcesUniqueCheck* unique_check,
    107     webkit_glue::SavableResourcesResult* result,
    108     const char** savable_schemes) {
    109   // Get current frame's URL.
    110   GURL current_frame_url = current_frame->url();
    111 
    112   // If url of current frame is invalid, ignore it.
    113   if (!current_frame_url.is_valid())
    114     return;
    115 
    116   // If url of current frame is not a savable protocol, ignore it.
    117   bool is_valid_protocol = false;
    118   for (int i = 0; savable_schemes[i] != NULL; ++i) {
    119     if (current_frame_url.SchemeIs(savable_schemes[i])) {
    120       is_valid_protocol = true;
    121       break;
    122     }
    123   }
    124   if (!is_valid_protocol)
    125     return;
    126 
    127   // If find same frame we have recorded, ignore it.
    128   if (!unique_check->frames_set->insert(current_frame_url).second)
    129     return;
    130 
    131   // Get current using document.
    132   WebDocument current_doc = current_frame->document();
    133   // Go through all descent nodes.
    134   WebNodeCollection all = current_doc.all();
    135   // Go through all node in this frame.
    136   for (WebNode node = all.firstItem(); !node.isNull();
    137        node = all.nextItem()) {
    138     // We only save HTML resources.
    139     if (!node.isElementNode())
    140       continue;
    141     WebElement element = node.to<WebElement>();
    142     GetSavableResourceLinkForElement(element,
    143                                      current_doc,
    144                                      unique_check,
    145                                      result);
    146   }
    147 }
    148 
    149 }  // namespace
    150 
    151 namespace webkit_glue {
    152 
    153 WebString GetSubResourceLinkFromElement(const WebElement& element) {
    154   const char* attribute_name = NULL;
    155   if (element.hasTagName("img") ||
    156       element.hasTagName("script")) {
    157     attribute_name = "src";
    158   } else if (element.hasTagName("input")) {
    159     const WebInputElement input = element.toConst<WebInputElement>();
    160     if (input.isImageButton()) {
    161       attribute_name = "src";
    162     }
    163   } else if (element.hasTagName("body") ||
    164              element.hasTagName("table") ||
    165              element.hasTagName("tr") ||
    166              element.hasTagName("td")) {
    167     attribute_name = "background";
    168   } else if (element.hasTagName("blockquote") ||
    169              element.hasTagName("q") ||
    170              element.hasTagName("del") ||
    171              element.hasTagName("ins")) {
    172     attribute_name = "cite";
    173   } else if (element.hasTagName("link")) {
    174     // If the link element is not linked to css, ignore it.
    175     if (LowerCaseEqualsASCII(element.getAttribute("type"), "text/css")) {
    176       // TODO(jnd): Add support for extracting links of sub-resources which
    177       // are inside style-sheet such as @import, url(), etc.
    178       // See bug: http://b/issue?id=1111667.
    179       attribute_name = "href";
    180     }
    181   }
    182   if (!attribute_name)
    183     return WebString();
    184   WebString value = element.getAttribute(WebString::fromUTF8(attribute_name));
    185   // If value has content and not start with "javascript:" then return it,
    186   // otherwise return NULL.
    187   if (!value.isNull() && !value.isEmpty() &&
    188       !StartsWithASCII(value.utf8(), "javascript:", false))
    189     return value;
    190 
    191   return WebString();
    192 }
    193 
    194 // Get all savable resource links from current webview, include main
    195 // frame and sub-frame
    196 bool GetAllSavableResourceLinksForCurrentPage(WebView* view,
    197     const GURL& page_url, SavableResourcesResult* result,
    198     const char** savable_schemes) {
    199   WebFrame* main_frame = view->mainFrame();
    200   if (!main_frame)
    201     return false;
    202 
    203   std::set<GURL> resources_set;
    204   std::set<GURL> frames_set;
    205   std::vector<WebFrame*> frames;
    206   SavableResourcesUniqueCheck unique_check(&resources_set,
    207                                            &frames_set,
    208                                            &frames);
    209 
    210   GURL main_page_gurl(main_frame->url());
    211 
    212   // Make sure we are saving same page between embedder and webkit.
    213   // If page has being navigated, embedder will get three empty vector,
    214   // which will make the saving page job ended.
    215   if (page_url != main_page_gurl)
    216     return true;
    217 
    218   // First, process main frame.
    219   frames.push_back(main_frame);
    220 
    221   // Check all resource in this page, include sub-frame.
    222   for (int i = 0; i < static_cast<int>(frames.size()); ++i) {
    223     // Get current frame's all savable resource links.
    224     GetAllSavableResourceLinksForFrame(frames[i], &unique_check, result,
    225                                        savable_schemes);
    226   }
    227 
    228   // Since frame's src can also point to sub-resources link, so it is possible
    229   // that some URLs in frames_list are also in resources_list. For those
    230   // URLs, we will remove it from frame_list, only keep them in resources_list.
    231   for (std::set<GURL>::iterator it = frames_set.begin();
    232        it != frames_set.end(); ++it) {
    233     // Append unique frame source to savable frame list.
    234     if (resources_set.find(*it) == resources_set.end())
    235       result->frames_list->push_back(*it);
    236   }
    237 
    238   return true;
    239 }
    240 
    241 bool PauseAnimationAtTimeOnElementWithId(WebView* view,
    242                                          const std::string& animation_name,
    243                                          double time,
    244                                          const std::string& element_id) {
    245   WebFrame* web_frame = view->mainFrame();
    246   if (!web_frame)
    247     return false;
    248 
    249   WebAnimationController* controller = web_frame->animationController();
    250   if (!controller)
    251     return false;
    252 
    253   WebElement element =
    254     web_frame->document().getElementById(WebString::fromUTF8(element_id));
    255   if (element.isNull())
    256     return false;
    257   return controller->pauseAnimationAtTime(element,
    258                                           WebString::fromUTF8(animation_name),
    259                                           time);
    260 }
    261 
    262 bool PauseTransitionAtTimeOnElementWithId(WebView* view,
    263                                           const std::string& property_name,
    264                                           double time,
    265                                           const std::string& element_id) {
    266   WebFrame* web_frame = view->mainFrame();
    267   if (!web_frame)
    268     return false;
    269 
    270   WebAnimationController* controller = web_frame->animationController();
    271   if (!controller)
    272     return false;
    273 
    274   WebElement element =
    275       web_frame->document().getElementById(WebString::fromUTF8(element_id));
    276   if (element.isNull())
    277     return false;
    278   return controller->pauseTransitionAtTime(element,
    279                                            WebString::fromUTF8(property_name),
    280                                            time);
    281 }
    282 
    283 bool ElementDoesAutoCompleteForElementWithId(WebView* view,
    284                                              const std::string& element_id) {
    285   WebFrame* web_frame = view->mainFrame();
    286   if (!web_frame)
    287     return false;
    288 
    289   WebElement element = web_frame->document().getElementById(
    290       WebString::fromUTF8(element_id));
    291   if (element.isNull() || !element.hasTagName("input"))
    292     return false;
    293 
    294   WebInputElement input_element = element.to<WebInputElement>();
    295   return input_element.autoComplete();
    296 }
    297 
    298 int NumberOfActiveAnimations(WebView* view) {
    299   WebFrame* web_frame = view->mainFrame();
    300   if (!web_frame)
    301     return -1;
    302 
    303   WebAnimationController* controller = web_frame->animationController();
    304   if (!controller)
    305     return -1;
    306 
    307   return controller->numberOfActiveAnimations();
    308 }
    309 
    310 void GetMetaElementsWithAttribute(WebDocument* document,
    311                                   const string16& attribute_name,
    312                                   const string16& attribute_value,
    313                                   std::vector<WebElement>* meta_elements) {
    314   DCHECK(document);
    315   DCHECK(meta_elements);
    316   meta_elements->clear();
    317   WebElement head = document->head();
    318   if (head.isNull() || !head.hasChildNodes())
    319     return;
    320 
    321   WebNodeList children = head.childNodes();
    322   for (size_t i = 0; i < children.length(); ++i) {
    323     WebNode node = children.item(i);
    324     if (!node.isElementNode())
    325       continue;
    326     WebElement element = node.to<WebElement>();
    327     if (!element.hasTagName("meta"))
    328       continue;
    329     WebString value = element.getAttribute(attribute_name);
    330     if (value.isNull() || value != attribute_value)
    331       continue;
    332     meta_elements->push_back(element);
    333   }
    334 }
    335 
    336 }  // webkit_glue
    337