/* * 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 "config.h" #include "core/html/HTMLLinkElement.h" #include "core/HTMLNames.h" #include "core/css/MediaList.h" #include "core/css/MediaQueryEvaluator.h" #include "core/css/StyleSheetContents.h" #include "core/css/resolver/StyleResolver.h" #include "core/dom/Attribute.h" #include "core/dom/Document.h" #include "core/dom/StyleEngine.h" #include "core/events/Event.h" #include "core/events/EventSender.h" #include "core/fetch/FetchRequest.h" #include "core/fetch/ResourceFetcher.h" #include "core/frame/FrameView.h" #include "core/frame/LocalFrame.h" #include "core/html/imports/LinkImport.h" #include "core/html/parser/HTMLParserIdioms.h" #include "core/loader/FrameLoaderClient.h" #include "core/rendering/style/RenderStyle.h" #include "core/rendering/style/StyleInheritedData.h" #include "platform/RuntimeEnabledFeatures.h" #include "wtf/StdLibExtras.h" namespace blink { template static void parseSizes(const CharacterType* value, unsigned length, Vector& iconSizes) { enum State { ParseStart, ParseWidth, ParseHeight }; int width = 0; unsigned start = 0; unsigned i = 0; State state = ParseStart; bool invalid = false; for (; i < length; ++i) { if (state == ParseWidth) { if (value[i] == 'x' || value[i] == 'X') { if (i == start) { invalid = true; break; } width = charactersToInt(value + start, i - start); start = i + 1; state = ParseHeight; } else if (value[i] < '0' || value[i] > '9') { invalid = true; break; } } else if (state == ParseHeight) { if (value[i] == ' ') { if (i == start) { invalid = true; break; } int height = charactersToInt(value + start, i - start); iconSizes.append(IntSize(width, height)); start = i + 1; state = ParseStart; } else if (value[i] < '0' || value[i] > '9') { invalid = true; break; } } else if (state == ParseStart) { if (value[i] >= '0' && value[i] <= '9') { start = i; state = ParseWidth; } else if (value[i] != ' ') { invalid = true; break; } } } if (invalid || state == ParseWidth || (state == ParseHeight && start == i)) { iconSizes.clear(); return; } if (state == ParseHeight && i > start) { int height = charactersToInt(value + start, i - start); iconSizes.append(IntSize(width, height)); } } static LinkEventSender& linkLoadEventSender() { DEFINE_STATIC_LOCAL(LinkEventSender, sharedLoadEventSender, (EventTypeNames::load)); return sharedLoadEventSender; } void HTMLLinkElement::parseSizesAttribute(const AtomicString& value, Vector& iconSizes) { ASSERT(iconSizes.isEmpty()); if (value.isEmpty()) return; if (value.is8Bit()) parseSizes(value.characters8(), value.length(), iconSizes); else parseSizes(value.characters16(), value.length(), iconSizes); } inline HTMLLinkElement::HTMLLinkElement(Document& document, bool createdByParser) : HTMLElement(HTMLNames::linkTag, document) , m_sizes(DOMSettableTokenList::create()) , m_createdByParser(createdByParser) , m_isInShadowTree(false) { ScriptWrappable::init(this); } PassRefPtr HTMLLinkElement::create(Document& document, bool createdByParser) { return adoptRef(new HTMLLinkElement(document, createdByParser)); } HTMLLinkElement::~HTMLLinkElement() { #if !ENABLE(OILPAN) m_link.clear(); if (inDocument()) document().styleEngine()->removeStyleSheetCandidateNode(this); #endif linkLoadEventSender().cancelEvent(this); } void HTMLLinkElement::parseAttribute(const QualifiedName& name, const AtomicString& value) { if (name == HTMLNames::relAttr) { m_relAttribute = LinkRelAttribute(value); process(); } else if (name == HTMLNames::hrefAttr) { process(); } else if (name == HTMLNames::typeAttr) { m_type = value; process(); } else if (name == HTMLNames::sizesAttr) { m_sizes->setValue(value); parseSizesAttribute(value, m_iconSizes); process(); } else if (name == HTMLNames::mediaAttr) { m_media = value.string().lower(); process(); } else { HTMLElement::parseAttribute(name, value); } } LinkResource* HTMLLinkElement::linkResourceToProcess() { bool visible = inDocument() && !m_isInShadowTree; if (!visible) return 0; if (!m_link && m_relAttribute.isImport()) m_link = LinkImport::create(this); return m_link.get(); } LinkImport* HTMLLinkElement::linkImport() const { if (!m_link || m_link->type() != LinkResource::Import) return 0; return static_cast(m_link.get()); } Document* HTMLLinkElement::import() const { if (LinkImport* link = linkImport()) return link->importedDocument(); return 0; } void HTMLLinkElement::process() { if (LinkResource* link = linkResourceToProcess()) link->process(); } Node::InsertionNotificationRequest HTMLLinkElement::insertedInto(ContainerNode* insertionPoint) { HTMLElement::insertedInto(insertionPoint); if (!insertionPoint->inDocument()) return InsertionDone; m_isInShadowTree = isInShadowTree(); if (m_isInShadowTree) return InsertionDone; document().styleEngine()->addStyleSheetCandidateNode(this, m_createdByParser); process(); if (m_link) m_link->ownerInserted(); return InsertionDone; } void HTMLLinkElement::removedFrom(ContainerNode* insertionPoint) { HTMLElement::removedFrom(insertionPoint); if (!insertionPoint->inDocument()) return; if (m_isInShadowTree) return; document().styleEngine()->removeStyleSheetCandidateNode(this); RefPtr removedSheet = sheet(); if (m_link) m_link->ownerRemoved(); document().removedStyleSheet(removedSheet.get()); } void HTMLLinkElement::finishParsingChildren() { m_createdByParser = false; HTMLElement::finishParsingChildren(); } void HTMLLinkElement::dispatchPendingLoadEvents() { linkLoadEventSender().dispatchPendingEvents(); } void HTMLLinkElement::dispatchPendingEvent(LinkEventSender* eventSender) { ASSERT_UNUSED(eventSender, eventSender == &linkLoadEventSender()); ASSERT(m_link); // FIXME(sky): Remove } void HTMLLinkElement::scheduleEvent() { linkLoadEventSender().dispatchEventSoon(this); } bool HTMLLinkElement::isURLAttribute(const Attribute& attribute) const { return attribute.name().localName() == HTMLNames::hrefAttr || HTMLElement::isURLAttribute(attribute); } bool HTMLLinkElement::hasLegalLinkAttribute(const QualifiedName& name) const { return name == HTMLNames::hrefAttr || HTMLElement::hasLegalLinkAttribute(name); } const QualifiedName& HTMLLinkElement::subResourceAttributeName() const { // If the link element is not css, ignore it. if (equalIgnoringCase(getAttribute(HTMLNames::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 HTMLNames::hrefAttr; } return HTMLElement::subResourceAttributeName(); } KURL HTMLLinkElement::href() const { return document().completeURL(getAttribute(HTMLNames::hrefAttr)); } const AtomicString& HTMLLinkElement::rel() const { return getAttribute(HTMLNames::relAttr); } const AtomicString& HTMLLinkElement::type() const { return getAttribute(HTMLNames::typeAttr); } bool HTMLLinkElement::async() const { return hasAttribute(HTMLNames::asyncAttr); } String HTMLLinkElement::as() const { return stripLeadingAndTrailingHTMLSpaces(getAttribute(HTMLNames::asAttr)); } const Vector& HTMLLinkElement::iconSizes() const { return m_iconSizes; } DOMSettableTokenList* HTMLLinkElement::sizes() const { return m_sizes.get(); } void HTMLLinkElement::trace(Visitor* visitor) { visitor->trace(m_link); visitor->trace(m_sizes); HTMLElement::trace(visitor); } } // namespace blink