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