summaryrefslogtreecommitdiffstats
path: root/webkit/glue/dom_operations.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/glue/dom_operations.cc')
-rw-r--r--webkit/glue/dom_operations.cc619
1 files changed, 619 insertions, 0 deletions
diff --git a/webkit/glue/dom_operations.cc b/webkit/glue/dom_operations.cc
new file mode 100644
index 0000000..82e5e3a
--- /dev/null
+++ b/webkit/glue/dom_operations.cc
@@ -0,0 +1,619 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <set>
+
+#include "base/compiler_specific.h"
+#include "base/string_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebAnimationController.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFormElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebNode.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebNodeCollection.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebNodeList.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/dom_operations.h"
+#include "webkit/glue/form_data.h"
+#include "webkit/glue/password_form_dom_manager.h"
+#include "webkit/glue/webpasswordautocompletelistener_impl.h"
+
+using WebKit::WebAnimationController;
+using WebKit::WebDocument;
+using WebKit::WebElement;
+using WebKit::WebFormElement;
+using WebKit::WebFrame;
+using WebKit::WebInputElement;
+using WebKit::WebNode;
+using WebKit::WebNodeCollection;
+using WebKit::WebNodeList;
+using WebKit::WebVector;
+using WebKit::WebView;
+
+namespace {
+
+// Structure for storage the unique set of all savable resource links for
+// making sure that no duplicated resource link in final result. The consumer
+// of the SavableResourcesUniqueCheck is responsible for keeping these pointers
+// valid for the lifetime of the SavableResourcesUniqueCheck instance.
+struct SavableResourcesUniqueCheck {
+ // Unique set of all sub resource links.
+ std::set<GURL>* resources_set;
+ // Unique set of all frame links.
+ std::set<GURL>* frames_set;
+ // Collection of all frames we go through when getting all savable resource
+ // links.
+ std::vector<WebFrame*>* frames;
+
+ SavableResourcesUniqueCheck()
+ : resources_set(NULL),
+ frames_set(NULL),
+ frames(NULL) {}
+
+ SavableResourcesUniqueCheck(std::set<GURL>* resources_set,
+ std::set<GURL>* frames_set, std::vector<WebFrame*>* frames)
+ : resources_set(resources_set),
+ frames_set(frames_set),
+ frames(frames) {}
+};
+
+// Get all savable resource links from current element. One element might
+// have more than one resource link. It is possible to have some links
+// in one CSS stylesheet.
+void GetSavableResourceLinkForElement(
+ const WebElement& element,
+ const WebDocument& current_doc,
+ SavableResourcesUniqueCheck* unique_check,
+ webkit_glue::SavableResourcesResult* result) {
+
+ // Handle frame and iframe tag.
+ if (element.hasTagName("iframe") ||
+ element.hasTagName("frame")) {
+ WebFrame* sub_frame = WebFrame::fromFrameOwnerElement(element);
+ if (sub_frame)
+ unique_check->frames->push_back(sub_frame);
+ return;
+ }
+
+ // Check whether the node has sub resource URL or not.
+ WebString value =
+ webkit_glue::GetSubResourceLinkFromElement(element);
+ if (value.isNull())
+ return;
+ // Get absolute URL.
+ GURL u = current_doc.completeURL(value);
+ // ignore invalid URL
+ if (!u.is_valid())
+ return;
+ // Ignore those URLs which are not standard protocols. Because FTP
+ // protocol does no have cache mechanism, we will skip all
+ // sub-resources if they use FTP protocol.
+ if (!u.SchemeIs("http") && !u.SchemeIs("https") && !u.SchemeIs("file"))
+ return;
+ // Ignore duplicated resource link.
+ if (!unique_check->resources_set->insert(u).second)
+ return;
+ result->resources_list->push_back(u);
+ // Insert referrer for above new resource link.
+ result->referrers_list->push_back(GURL());
+}
+
+// Get all savable resource links from current WebFrameImpl object pointer.
+void GetAllSavableResourceLinksForFrame(WebFrame* current_frame,
+ SavableResourcesUniqueCheck* unique_check,
+ webkit_glue::SavableResourcesResult* result,
+ const char** savable_schemes) {
+ // Get current frame's URL.
+ GURL current_frame_url = current_frame->url();
+
+ // If url of current frame is invalid, ignore it.
+ if (!current_frame_url.is_valid())
+ return;
+
+ // If url of current frame is not a savable protocol, ignore it.
+ bool is_valid_protocol = false;
+ for (int i = 0; savable_schemes[i] != NULL; ++i) {
+ if (current_frame_url.SchemeIs(savable_schemes[i])) {
+ is_valid_protocol = true;
+ break;
+ }
+ }
+ if (!is_valid_protocol)
+ return;
+
+ // If find same frame we have recorded, ignore it.
+ if (!unique_check->frames_set->insert(current_frame_url).second)
+ return;
+
+ // Get current using document.
+ WebDocument current_doc = current_frame->document();
+ // Go through all descent nodes.
+ WebNodeCollection all = current_doc.all();
+ // Go through all node in this frame.
+ for (WebNode node = all.firstItem(); !node.isNull();
+ node = all.nextItem()) {
+ // We only save HTML resources.
+ if (!node.isElementNode())
+ continue;
+ WebElement element = node.to<WebElement>();
+ GetSavableResourceLinkForElement(element,
+ current_doc,
+ unique_check,
+ result);
+ }
+}
+
+} // namespace
+
+namespace webkit_glue {
+
+// Map element name to a list of pointers to corresponding elements to simplify
+// form filling.
+typedef std::map<string16, WebKit::WebInputElement>
+ FormInputElementMap;
+
+// Utility struct for form lookup and autofill. When we parse the DOM to lookup
+// a form, in addition to action and origin URL's we have to compare all
+// necessary form elements. To avoid having to look these up again when we want
+// to fill the form, the FindFormElements function stores the pointers
+// in a FormElements* result, referenced to ensure they are safe to use.
+struct FormElements {
+ WebFormElement form_element;
+ FormInputElementMap input_elements;
+ FormElements() {
+ }
+};
+
+typedef std::vector<FormElements*> FormElementsList;
+
+// Internal implementation of FillForm API.
+static bool FillFormImpl(FormElements* fe, const FormData& data) {
+ if (!fe->form_element.autoComplete())
+ return false;
+
+ std::map<string16, string16> data_map;
+ for (size_t i = 0; i < data.fields.size(); i++)
+ data_map[data.fields[i].name()] = data.fields[i].value();
+
+ for (FormInputElementMap::iterator it = fe->input_elements.begin();
+ it != fe->input_elements.end(); ++it) {
+ WebKit::WebInputElement& element = it->second;
+ if (!element.value().isEmpty()) // Don't overwrite pre-filled values.
+ continue;
+ if (element.inputType() == WebInputElement::Password &&
+ (!element.isEnabledFormControl() || element.hasAttribute("readonly"))) {
+ continue; // Don't fill uneditable password fields.
+ }
+ element.setValue(data_map[it->first]);
+ element.setAutofilled(true);
+ element.dispatchFormControlChangeEvent();
+ }
+
+ return false;
+}
+
+// Helper to search the given form element for the specified input elements
+// in |data|, and add results to |result|.
+static bool FindFormInputElements(WebFormElement* fe,
+ const FormData& data,
+ FormElements* result) {
+ // Loop through the list of elements we need to find on the form in
+ // order to autofill it. If we don't find any one of them, abort
+ // processing this form; it can't be the right one.
+ for (size_t j = 0; j < data.fields.size(); j++) {
+ WebVector<WebNode> temp_elements;
+ fe->getNamedElements(data.fields[j].name(), temp_elements);
+ if (temp_elements.isEmpty()) {
+ // We didn't find a required element. This is not the right form.
+ // Make sure no input elements from a partially matched form
+ // in this iteration remain in the result set.
+ // Note: clear will remove a reference from each InputElement.
+ result->input_elements.clear();
+ return false;
+ }
+ // This element matched, add it to our temporary result. It's possible
+ // there are multiple matches, but for purposes of identifying the form
+ // one suffices and if some function needs to deal with multiple
+ // matching elements it can get at them through the FormElement*.
+ // Note: This assignment adds a reference to the InputElement.
+ result->input_elements[data.fields[j].name()] =
+ temp_elements[0].to<WebInputElement>();
+ }
+ return true;
+}
+
+// Helper to locate form elements identified by |data|.
+static void FindFormElements(WebView* view,
+ const FormData& data,
+ FormElementsList* results) {
+ DCHECK(view);
+ DCHECK(results);
+ WebFrame* main_frame = view->mainFrame();
+ if (!main_frame)
+ return;
+
+ GURL::Replacements rep;
+ rep.ClearQuery();
+ rep.ClearRef();
+
+ // Loop through each frame.
+ for (WebFrame* f = main_frame; f; f = f->traverseNext(false)) {
+ WebDocument doc = f->document();
+ if (!doc.isHTMLDocument())
+ continue;
+
+ GURL full_origin(f->url());
+ if (data.origin != full_origin.ReplaceComponents(rep))
+ continue;
+
+ WebVector<WebFormElement> forms;
+ f->forms(forms);
+
+ for (size_t i = 0; i < forms.size(); ++i) {
+ WebFormElement fe = forms[i];
+ // Action URL must match.
+ GURL full_action(f->document().completeURL(fe.action()));
+ if (data.action != full_action.ReplaceComponents(rep))
+ continue;
+
+ scoped_ptr<FormElements> curr_elements(new FormElements);
+ if (!FindFormInputElements(&fe, data, curr_elements.get()))
+ continue;
+
+ // We found the right element.
+ // Note: this assignment adds a reference to |fe|.
+ curr_elements->form_element = fe;
+ results->push_back(curr_elements.release());
+ }
+ }
+}
+
+void FillPasswordForm(WebView* view,
+ const PasswordFormFillData& data) {
+ FormElementsList forms;
+ // We own the FormElements* in forms.
+ FindFormElements(view, data.basic_data, &forms);
+ FormElementsList::iterator iter;
+ for (iter = forms.begin(); iter != forms.end(); ++iter) {
+ scoped_ptr<FormElements> form_elements(*iter);
+
+ // If wait_for_username is true, we don't want to initially fill the form
+ // until the user types in a valid username.
+ if (!data.wait_for_username)
+ FillFormImpl(form_elements.get(), data.basic_data);
+
+ // Attach autocomplete listener to enable selecting alternate logins.
+ // First, get pointers to username element.
+ WebInputElement username_element =
+ form_elements->input_elements[data.basic_data.fields[0].name()];
+
+ // Get pointer to password element. (We currently only support single
+ // password forms).
+ WebInputElement password_element =
+ form_elements->input_elements[data.basic_data.fields[1].name()];
+
+ username_element.document().frame()->registerPasswordListener(
+ username_element,
+ new WebPasswordAutocompleteListenerImpl(
+ new WebInputElementDelegate(username_element),
+ new WebInputElementDelegate(password_element),
+ data));
+ }
+}
+
+WebString GetSubResourceLinkFromElement(const WebElement& element) {
+ const char* attribute_name = NULL;
+ if (element.hasTagName("img") ||
+ element.hasTagName("script")) {
+ attribute_name = "src";
+ } else if (element.hasTagName("input")) {
+ const WebInputElement input = element.toConst<WebInputElement>();
+ if (input.inputType() == WebInputElement::Image) {
+ attribute_name = "src";
+ }
+ } else if (element.hasTagName("body") ||
+ element.hasTagName("table") ||
+ element.hasTagName("tr") ||
+ element.hasTagName("td")) {
+ attribute_name = "background";
+ } else if (element.hasTagName("blockquote") ||
+ element.hasTagName("q") ||
+ element.hasTagName("del") ||
+ element.hasTagName("ins")) {
+ attribute_name = "cite";
+ } else if (element.hasTagName("link")) {
+ // If the link element is not linked to css, ignore it.
+ if (LowerCaseEqualsASCII(element.getAttribute("type"), "text/css")) {
+ // TODO(jnd): Add support for extracting links of sub-resources which
+ // are inside style-sheet such as @import, url(), etc.
+ // See bug: http://b/issue?id=1111667.
+ attribute_name = "href";
+ }
+ }
+ if (!attribute_name)
+ return WebString();
+ WebString value = element.getAttribute(WebString::fromUTF8(attribute_name));
+ // If value has content and not start with "javascript:" then return it,
+ // otherwise return NULL.
+ if (!value.isNull() && !value.isEmpty() &&
+ !StartsWithASCII(value.utf8(), "javascript:", false))
+ return value;
+
+ return WebString();
+}
+
+// Get all savable resource links from current webview, include main
+// frame and sub-frame
+bool GetAllSavableResourceLinksForCurrentPage(WebView* view,
+ const GURL& page_url, SavableResourcesResult* result,
+ const char** savable_schemes) {
+ WebFrame* main_frame = view->mainFrame();
+ if (!main_frame)
+ return false;
+
+ std::set<GURL> resources_set;
+ std::set<GURL> frames_set;
+ std::vector<WebFrame*> frames;
+ SavableResourcesUniqueCheck unique_check(&resources_set,
+ &frames_set,
+ &frames);
+
+ GURL main_page_gurl(main_frame->url());
+
+ // Make sure we are saving same page between embedder and webkit.
+ // If page has being navigated, embedder will get three empty vector,
+ // which will make the saving page job ended.
+ if (page_url != main_page_gurl)
+ return true;
+
+ // First, process main frame.
+ frames.push_back(main_frame);
+
+ // Check all resource in this page, include sub-frame.
+ for (int i = 0; i < static_cast<int>(frames.size()); ++i) {
+ // Get current frame's all savable resource links.
+ GetAllSavableResourceLinksForFrame(frames[i], &unique_check, result,
+ savable_schemes);
+ }
+
+ // Since frame's src can also point to sub-resources link, so it is possible
+ // that some URLs in frames_list are also in resources_list. For those
+ // URLs, we will remove it from frame_list, only keep them in resources_list.
+ for (std::set<GURL>::iterator it = frames_set.begin();
+ it != frames_set.end(); ++it) {
+ // Append unique frame source to savable frame list.
+ if (resources_set.find(*it) == resources_set.end())
+ result->frames_list->push_back(*it);
+ }
+
+ return true;
+}
+
+// Sizes a single size (the width or height) from a 'sizes' attribute. A size
+// matches must match the following regex: [1-9][0-9]*.
+static int ParseSingleIconSize(const string16& text) {
+ // Size must not start with 0, and be between 0 and 9.
+ if (text.empty() || !(text[0] >= L'1' && text[0] <= L'9'))
+ return 0;
+ // Make sure all chars are from 0-9.
+ for (size_t i = 1; i < text.length(); ++i) {
+ if (!(text[i] >= L'0' && text[i] <= L'9'))
+ return 0;
+ }
+ int output;
+ if (!StringToInt(text, &output))
+ return 0;
+ return output;
+}
+
+// Parses an icon size. An icon size must match the following regex:
+// [1-9][0-9]*x[1-9][0-9]*.
+// If the input couldn't be parsed, a size with a width/height < 0 is returned.
+static gfx::Size ParseIconSize(const string16& text) {
+ std::vector<string16> sizes;
+ SplitStringDontTrim(text, L'x', &sizes);
+ if (sizes.size() != 2)
+ return gfx::Size();
+
+ return gfx::Size(ParseSingleIconSize(sizes[0]),
+ ParseSingleIconSize(sizes[1]));
+}
+
+bool ParseIconSizes(const string16& text,
+ std::vector<gfx::Size>* sizes,
+ bool* is_any) {
+ *is_any = false;
+ std::vector<string16> size_strings;
+ SplitStringAlongWhitespace(text, &size_strings);
+ for (size_t i = 0; i < size_strings.size(); ++i) {
+ if (EqualsASCII(size_strings[i], "any")) {
+ *is_any = true;
+ } else {
+ gfx::Size size = ParseIconSize(size_strings[i]);
+ if (size.width() <= 0 || size.height() <= 0)
+ return false; // Bogus size.
+ sizes->push_back(size);
+ }
+ }
+ if (*is_any && !sizes->empty()) {
+ // If is_any is true, it must occur by itself.
+ return false;
+ }
+ return (*is_any || !sizes->empty());
+}
+
+static void AddInstallIcon(const WebElement& link,
+ std::vector<WebApplicationInfo::IconInfo>* icons) {
+ WebString href = link.getAttribute("href");
+ if (href.isNull() || href.isEmpty())
+ return;
+
+ // Get complete url.
+ GURL url = link.document().completeURL(href);
+ if (!url.is_valid())
+ return;
+
+ if (!link.hasAttribute("sizes"))
+ return;
+
+ bool is_any = false;
+ std::vector<gfx::Size> icon_sizes;
+ if (!ParseIconSizes(link.getAttribute("sizes"), &icon_sizes, &is_any) ||
+ is_any ||
+ icon_sizes.size() != 1) {
+ return;
+ }
+ WebApplicationInfo::IconInfo icon_info;
+ icon_info.width = icon_sizes[0].width();
+ icon_info.height = icon_sizes[0].height();
+ icon_info.url = url;
+ icons->push_back(icon_info);
+}
+
+void GetApplicationInfo(WebView* view, WebApplicationInfo* app_info) {
+ WebFrame* main_frame = view->mainFrame();
+ if (!main_frame)
+ return;
+
+ WebDocument doc = main_frame->document();
+ if (doc.isNull())
+ return;
+
+ WebElement head = main_frame->document().head();
+ if (head.isNull())
+ return;
+
+ WebNodeList children = head.childNodes();
+ for (unsigned i = 0; i < children.length(); ++i) {
+ WebNode child = children.item(i);
+ if (!child.isElementNode())
+ continue;
+ WebElement elem = child.to<WebElement>();
+
+ if (elem.hasTagName("link")) {
+ std::string rel = elem.getAttribute("rel").utf8();
+ // "rel" attribute may use either "icon" or "shortcut icon".
+ // see also
+ // <http://en.wikipedia.org/wiki/Favicon>
+ // <http://dev.w3.org/html5/spec/Overview.html#rel-icon>
+ if (LowerCaseEqualsASCII(rel, "icon") ||
+ LowerCaseEqualsASCII(rel, "shortcut icon"))
+ AddInstallIcon(elem, &app_info->icons);
+ } else if (elem.hasTagName("meta") && elem.hasAttribute("name")) {
+ std::string name = elem.getAttribute("name").utf8();
+ WebString content = elem.getAttribute("content");
+ if (name == "application-name") {
+ app_info->title = content;
+ } else if (name == "description") {
+ app_info->description = content;
+ } else if (name == "application-url") {
+ std::string url = content.utf8();
+ GURL main_url = main_frame->url();
+ app_info->app_url = main_url.is_valid() ?
+ main_url.Resolve(url) : GURL(url);
+ if (!app_info->app_url.is_valid())
+ app_info->app_url = GURL();
+ }
+ }
+ }
+}
+
+bool PauseAnimationAtTimeOnElementWithId(WebView* view,
+ const std::string& animation_name,
+ double time,
+ const std::string& element_id) {
+ WebFrame* web_frame = view->mainFrame();
+ if (!web_frame)
+ return false;
+
+ WebAnimationController* controller = web_frame->animationController();
+ if (!controller)
+ return false;
+
+ WebElement element =
+ web_frame->document().getElementById(WebString::fromUTF8(element_id));
+ if (element.isNull())
+ return false;
+ return controller->pauseAnimationAtTime(element,
+ WebString::fromUTF8(animation_name),
+ time);
+}
+
+bool PauseTransitionAtTimeOnElementWithId(WebView* view,
+ const std::string& property_name,
+ double time,
+ const std::string& element_id) {
+ WebFrame* web_frame = view->mainFrame();
+ if (!web_frame)
+ return false;
+
+ WebAnimationController* controller = web_frame->animationController();
+ if (!controller)
+ return false;
+
+ WebElement element =
+ web_frame->document().getElementById(WebString::fromUTF8(element_id));
+ if (element.isNull())
+ return false;
+ return controller->pauseTransitionAtTime(element,
+ WebString::fromUTF8(property_name),
+ time);
+}
+
+bool ElementDoesAutoCompleteForElementWithId(WebView* view,
+ const std::string& element_id) {
+ WebFrame* web_frame = view->mainFrame();
+ if (!web_frame)
+ return false;
+
+ WebElement element = web_frame->document().getElementById(
+ WebString::fromUTF8(element_id));
+ if (element.isNull() || !element.hasTagName("input"))
+ return false;
+
+ WebInputElement input_element = element.to<WebInputElement>();
+ return input_element.autoComplete();
+}
+
+int NumberOfActiveAnimations(WebView* view) {
+ WebFrame* web_frame = view->mainFrame();
+ if (!web_frame)
+ return -1;
+
+ WebAnimationController* controller = web_frame->animationController();
+ if (!controller)
+ return -1;
+
+ return controller->numberOfActiveAnimations();
+}
+
+void GetMetaElementsWithName(WebDocument* document,
+ const string16& name,
+ std::vector<WebElement>* meta_elements) {
+ DCHECK(document);
+ DCHECK(meta_elements);
+ meta_elements->clear();
+ WebElement head = document->head();
+ if (head.isNull() || !head.hasChildNodes())
+ return;
+
+ WebNodeList children = head.childNodes();
+ for (size_t i = 0; i < children.length(); ++i) {
+ WebNode node = children.item(i);
+ if (!node.isElementNode())
+ continue;
+ WebElement element = node.to<WebElement>();
+ if (!element.hasTagName("meta"))
+ continue;
+ WebString meta_name = element.getAttribute("name");
+ if (meta_name.isNull() || meta_name != name)
+ continue;
+ meta_elements->push_back(element);
+ }
+}
+
+} // webkit_glue