| /* |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * (C) 2001 Dirk Mueller (mueller@kde.org) |
| * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights |
| * reserved. |
| * Copyright (C) 2009 Rob Buis (rwlbuis@gmail.com) |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "core/html/HTMLLinkElement.h" |
| |
| #include "bindings/core/v8/ScriptEventListener.h" |
| #include "core/CoreInitializer.h" |
| #include "core/HTMLNames.h" |
| #include "core/dom/Attribute.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/TaskRunnerHelper.h" |
| #include "core/events/Event.h" |
| #include "core/frame/LocalFrameClient.h" |
| #include "core/frame/UseCounter.h" |
| #include "core/html/CrossOriginAttribute.h" |
| #include "core/html/LinkManifest.h" |
| #include "core/html/imports/LinkImport.h" |
| #include "core/inspector/ConsoleMessage.h" |
| #include "core/loader/NetworkHintsInterface.h" |
| #include "core/origin_trials/OriginTrials.h" |
| #include "platform/weborigin/SecurityPolicy.h" |
| #include "public/platform/WebIconSizesParser.h" |
| #include "public/platform/WebSize.h" |
| |
| namespace blink { |
| |
| using namespace HTMLNames; |
| |
| inline HTMLLinkElement::HTMLLinkElement(Document& document, |
| bool created_by_parser) |
| : HTMLElement(linkTag, document), |
| link_loader_(LinkLoader::Create(this)), |
| sizes_(DOMTokenList::Create(*this, HTMLNames::sizesAttr)), |
| rel_list_(RelList::Create(this)), |
| created_by_parser_(created_by_parser) {} |
| |
| HTMLLinkElement* HTMLLinkElement::Create(Document& document, |
| bool created_by_parser) { |
| return new HTMLLinkElement(document, created_by_parser); |
| } |
| |
| HTMLLinkElement::~HTMLLinkElement() {} |
| |
| void HTMLLinkElement::ParseAttribute( |
| const AttributeModificationParams& params) { |
| const QualifiedName& name = params.name; |
| const AtomicString& value = params.new_value; |
| if (name == relAttr) { |
| rel_attribute_ = LinkRelAttribute(value); |
| rel_list_->DidUpdateAttributeValue(params.old_value, value); |
| Process(); |
| } else if (name == hrefAttr) { |
| // Log href attribute before logging resource fetching in process(). |
| LogUpdateAttributeIfIsolatedWorldAndInDocument("link", params); |
| Process(); |
| } else if (name == typeAttr) { |
| type_ = value; |
| Process(); |
| } else if (name == asAttr) { |
| as_ = value; |
| Process(); |
| } else if (name == referrerpolicyAttr) { |
| referrer_policy_ = kReferrerPolicyDefault; |
| if (!value.IsNull()) { |
| SecurityPolicy::ReferrerPolicyFromString( |
| value, kDoNotSupportReferrerPolicyLegacyKeywords, &referrer_policy_); |
| UseCounter::Count(GetDocument(), |
| WebFeature::kHTMLLinkElementReferrerPolicyAttribute); |
| } |
| } else if (name == sizesAttr) { |
| sizes_->DidUpdateAttributeValue(params.old_value, value); |
| WebVector<WebSize> web_icon_sizes = |
| WebIconSizesParser::ParseIconSizes(value); |
| icon_sizes_.resize(web_icon_sizes.size()); |
| for (size_t i = 0; i < web_icon_sizes.size(); ++i) |
| icon_sizes_[i] = web_icon_sizes[i]; |
| Process(); |
| } else if (name == mediaAttr) { |
| media_ = value.DeprecatedLower(); |
| Process(); |
| } else if (name == scopeAttr) { |
| scope_ = value; |
| Process(); |
| } else if (name == disabledAttr) { |
| UseCounter::Count(GetDocument(), WebFeature::kHTMLLinkElementDisabled); |
| if (LinkStyle* link = GetLinkStyle()) |
| link->SetDisabledState(!value.IsNull()); |
| } else { |
| if (name == titleAttr) { |
| if (LinkStyle* link = GetLinkStyle()) |
| link->SetSheetTitle(value); |
| } |
| |
| HTMLElement::ParseAttribute(params); |
| } |
| } |
| |
| bool HTMLLinkElement::ShouldLoadLink() { |
| const KURL& href = GetNonEmptyURLAttribute(hrefAttr); |
| return (IsInDocumentTree() || |
| (isConnected() && rel_attribute_.IsStyleSheet())) && |
| !href.PotentiallyDanglingMarkup(); |
| } |
| |
| bool HTMLLinkElement::LoadLink(const String& type, |
| const String& as, |
| const String& media, |
| ReferrerPolicy referrer_policy, |
| const KURL& url) { |
| return link_loader_->LoadLink(rel_attribute_, |
| GetCrossOriginAttributeValue(FastGetAttribute( |
| HTMLNames::crossoriginAttr)), |
| type, as, media, referrer_policy, url, |
| GetDocument(), NetworkHintsInterfaceImpl()); |
| } |
| |
| LinkResource* HTMLLinkElement::LinkResourceToProcess() { |
| if (!ShouldLoadLink()) { |
| DCHECK(!GetLinkStyle() || !GetLinkStyle()->HasSheet()); |
| // TODO(yoav): Ideally, the element's error event would be fired here. |
| return nullptr; |
| } |
| |
| if (!link_) { |
| if (rel_attribute_.IsImport()) { |
| link_ = LinkImport::Create(this); |
| } else if (rel_attribute_.IsManifest()) { |
| link_ = LinkManifest::Create(this); |
| } else if (rel_attribute_.IsServiceWorker() && |
| OriginTrials::linkServiceWorkerEnabled(GetExecutionContext())) { |
| if (GetDocument().GetFrame()) { |
| link_ = CoreInitializer::GetInstance().CreateServiceWorkerLinkResource( |
| this); |
| } |
| } else { |
| LinkStyle* link = LinkStyle::Create(this); |
| if (FastHasAttribute(disabledAttr)) { |
| UseCounter::Count(GetDocument(), WebFeature::kHTMLLinkElementDisabled); |
| link->SetDisabledState(true); |
| } |
| link_ = link; |
| } |
| } |
| |
| return link_.Get(); |
| } |
| |
| LinkStyle* HTMLLinkElement::GetLinkStyle() const { |
| if (!link_ || link_->GetType() != LinkResource::kStyle) |
| return nullptr; |
| return static_cast<LinkStyle*>(link_.Get()); |
| } |
| |
| LinkImport* HTMLLinkElement::GetLinkImport() const { |
| if (!link_ || link_->GetType() != LinkResource::kImport) |
| return nullptr; |
| return static_cast<LinkImport*>(link_.Get()); |
| } |
| |
| Document* HTMLLinkElement::import() const { |
| if (LinkImport* link = GetLinkImport()) |
| return link->ImportedDocument(); |
| return nullptr; |
| } |
| |
| void HTMLLinkElement::Process() { |
| if (LinkResource* link = LinkResourceToProcess()) |
| link->Process(); |
| } |
| |
| Node::InsertionNotificationRequest HTMLLinkElement::InsertedInto( |
| ContainerNode* insertion_point) { |
| HTMLElement::InsertedInto(insertion_point); |
| LogAddElementIfIsolatedWorldAndInDocument("link", relAttr, hrefAttr); |
| if (!insertion_point->isConnected()) |
| return kInsertionDone; |
| DCHECK(isConnected()); |
| if (!ShouldLoadLink() && IsInShadowTree()) { |
| String message = "HTML element <link> is ignored in shadow tree."; |
| GetDocument().AddConsoleMessage(ConsoleMessage::Create( |
| kJSMessageSource, kWarningMessageLevel, message)); |
| return kInsertionDone; |
| } |
| |
| GetDocument().GetStyleEngine().AddStyleSheetCandidateNode(*this); |
| |
| Process(); |
| |
| if (link_) |
| link_->OwnerInserted(); |
| |
| return kInsertionDone; |
| } |
| |
| void HTMLLinkElement::RemovedFrom(ContainerNode* insertion_point) { |
| // Store the result of isConnected() here before Node::removedFrom(..) clears |
| // the flags. |
| bool was_connected = isConnected(); |
| HTMLElement::RemovedFrom(insertion_point); |
| if (!insertion_point->isConnected()) |
| return; |
| |
| link_loader_->Abort(); |
| |
| if (!was_connected) { |
| DCHECK(!GetLinkStyle() || !GetLinkStyle()->HasSheet()); |
| return; |
| } |
| GetDocument().GetStyleEngine().RemoveStyleSheetCandidateNode( |
| *this, *insertion_point); |
| if (link_) |
| link_->OwnerRemoved(); |
| } |
| |
| void HTMLLinkElement::FinishParsingChildren() { |
| created_by_parser_ = false; |
| HTMLElement::FinishParsingChildren(); |
| } |
| |
| bool HTMLLinkElement::HasActivationBehavior() const { |
| // TODO(tkent): Implement activation behavior. crbug.com/422732. |
| return false; |
| } |
| |
| bool HTMLLinkElement::StyleSheetIsLoading() const { |
| return GetLinkStyle() && GetLinkStyle()->StyleSheetIsLoading(); |
| } |
| |
| void HTMLLinkElement::LinkLoaded() { |
| DispatchEvent(Event::Create(EventTypeNames::load)); |
| } |
| |
| void HTMLLinkElement::LinkLoadingErrored() { |
| DispatchEvent(Event::Create(EventTypeNames::error)); |
| } |
| |
| void HTMLLinkElement::DidStartLinkPrerender() { |
| DispatchEvent(Event::Create(EventTypeNames::webkitprerenderstart)); |
| } |
| |
| void HTMLLinkElement::DidStopLinkPrerender() { |
| DispatchEvent(Event::Create(EventTypeNames::webkitprerenderstop)); |
| } |
| |
| void HTMLLinkElement::DidSendLoadForLinkPrerender() { |
| DispatchEvent(Event::Create(EventTypeNames::webkitprerenderload)); |
| } |
| |
| void HTMLLinkElement::DidSendDOMContentLoadedForLinkPrerender() { |
| DispatchEvent(Event::Create(EventTypeNames::webkitprerenderdomcontentloaded)); |
| } |
| |
| RefPtr<WebTaskRunner> HTMLLinkElement::GetLoadingTaskRunner() { |
| return TaskRunnerHelper::Get(TaskType::kNetworking, &GetDocument()); |
| } |
| |
| bool HTMLLinkElement::SheetLoaded() { |
| DCHECK(GetLinkStyle()); |
| return GetLinkStyle()->SheetLoaded(); |
| } |
| |
| void HTMLLinkElement::NotifyLoadedSheetAndAllCriticalSubresources( |
| LoadedSheetErrorStatus error_status) { |
| DCHECK(GetLinkStyle()); |
| GetLinkStyle()->NotifyLoadedSheetAndAllCriticalSubresources(error_status); |
| } |
| |
| void HTMLLinkElement::DispatchPendingEvent( |
| std::unique_ptr<IncrementLoadEventDelayCount> count) { |
| DCHECK(link_); |
| if (link_->HasLoaded()) |
| LinkLoaded(); |
| else |
| LinkLoadingErrored(); |
| |
| // Checks Document's load event synchronously here for performance. |
| // This is safe because dispatchPendingEvent() is called asynchronously. |
| count->ClearAndCheckLoadEvent(); |
| } |
| |
| void HTMLLinkElement::ScheduleEvent() { |
| TaskRunnerHelper::Get(TaskType::kDOMManipulation, &GetDocument()) |
| ->PostTask(BLINK_FROM_HERE, |
| WTF::Bind(&HTMLLinkElement::DispatchPendingEvent, |
| WrapPersistent(this), |
| WTF::Passed(IncrementLoadEventDelayCount::Create( |
| GetDocument())))); |
| } |
| |
| void HTMLLinkElement::StartLoadingDynamicSheet() { |
| DCHECK(GetLinkStyle()); |
| GetLinkStyle()->StartLoadingDynamicSheet(); |
| } |
| |
| bool HTMLLinkElement::IsURLAttribute(const Attribute& attribute) const { |
| return attribute.GetName().LocalName() == hrefAttr || |
| HTMLElement::IsURLAttribute(attribute); |
| } |
| |
| bool HTMLLinkElement::HasLegalLinkAttribute(const QualifiedName& name) const { |
| return name == hrefAttr || HTMLElement::HasLegalLinkAttribute(name); |
| } |
| |
| const QualifiedName& HTMLLinkElement::SubResourceAttributeName() const { |
| // If the link element is not css, ignore it. |
| if (DeprecatedEqualIgnoringCase(getAttribute(typeAttr), "text/css")) { |
| // FIXME: Add support for extracting links of sub-resources which |
| // are inside style-sheet such as @import, @font-face, url(), etc. |
| return hrefAttr; |
| } |
| return HTMLElement::SubResourceAttributeName(); |
| } |
| |
| KURL HTMLLinkElement::Href() const { |
| const String& url = getAttribute(hrefAttr); |
| if (url.IsEmpty()) |
| return KURL(); |
| return GetDocument().CompleteURL(url); |
| } |
| |
| const AtomicString& HTMLLinkElement::Rel() const { |
| return getAttribute(relAttr); |
| } |
| |
| const AtomicString& HTMLLinkElement::GetType() const { |
| return getAttribute(typeAttr); |
| } |
| |
| bool HTMLLinkElement::Async() const { |
| return FastHasAttribute(HTMLNames::asyncAttr); |
| } |
| |
| IconType HTMLLinkElement::GetIconType() const { |
| return rel_attribute_.GetIconType(); |
| } |
| |
| const Vector<IntSize>& HTMLLinkElement::IconSizes() const { |
| return icon_sizes_; |
| } |
| |
| DOMTokenList* HTMLLinkElement::sizes() const { |
| return sizes_.Get(); |
| } |
| |
| DEFINE_TRACE(HTMLLinkElement) { |
| visitor->Trace(link_); |
| visitor->Trace(sizes_); |
| visitor->Trace(link_loader_); |
| visitor->Trace(rel_list_); |
| HTMLElement::Trace(visitor); |
| LinkLoaderClient::Trace(visitor); |
| } |
| |
| DEFINE_TRACE_WRAPPERS(HTMLLinkElement) { |
| visitor->TraceWrappers(rel_list_); |
| HTMLElement::TraceWrappers(visitor); |
| } |
| |
| } // namespace blink |