#include "config.h"
#include "core/html/HTMLLinkElement.h"
-#include "HTMLNames.h"
-#include "RuntimeEnabledFeatures.h"
-#include "bindings/v8/ScriptEventListener.h"
+#include "bindings/core/v8/ScriptEventListener.h"
+#include "bindings/core/v8/V8DOMActivityLogger.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/dom/StyleEngine.h"
#include "core/fetch/CSSStyleSheetResource.h"
#include "core/fetch/FetchRequest.h"
#include "core/fetch/ResourceFetcher.h"
-#include "core/html/LinkImport.h"
+#include "core/frame/FrameView.h"
+#include "core/frame/LocalFrame.h"
+#include "core/frame/SubresourceIntegrity.h"
+#include "core/frame/csp/ContentSecurityPolicy.h"
+#include "core/html/LinkManifest.h"
+#include "core/html/imports/LinkImport.h"
#include "core/loader/FrameLoader.h"
#include "core/loader/FrameLoaderClient.h"
-#include "core/frame/ContentSecurityPolicy.h"
-#include "core/frame/Frame.h"
-#include "core/frame/FrameView.h"
+#include "core/rendering/style/StyleInheritedData.h"
+#include "platform/RuntimeEnabledFeatures.h"
#include "wtf/StdLibExtras.h"
-namespace WebCore {
+namespace blink {
using namespace HTMLNames;
+template <typename CharacterType>
+static void parseSizes(const CharacterType* value, unsigned length, Vector<IntSize>& 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;
}
-inline HTMLLinkElement::HTMLLinkElement(const QualifiedName& tagName, Document& document, bool createdByParser)
- : HTMLElement(tagName, document)
+void HTMLLinkElement::parseSizesAttribute(const AtomicString& value, Vector<IntSize>& 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(linkTag, document)
, m_linkLoader(this)
, m_sizes(DOMSettableTokenList::create())
, m_createdByParser(createdByParser)
, m_isInShadowTree(false)
- , m_beforeLoadRecurseCount(0)
{
- ASSERT(hasTagName(linkTag));
- ScriptWrappable::init(this);
}
-PassRefPtr<HTMLLinkElement> HTMLLinkElement::create(const QualifiedName& tagName, Document& document, bool createdByParser)
+PassRefPtrWillBeRawPtr<HTMLLinkElement> HTMLLinkElement::create(Document& document, bool createdByParser)
{
- return adoptRef(new HTMLLinkElement(tagName, document, createdByParser));
+ return adoptRefWillBeNoop(new HTMLLinkElement(document, createdByParser));
}
HTMLLinkElement::~HTMLLinkElement()
{
+#if !ENABLE(OILPAN)
m_link.clear();
if (inDocument())
document().styleEngine()->removeStyleSheetCandidateNode(this);
+#endif
linkLoadEventSender().cancelEvent(this);
}
process();
} else if (name == sizesAttr) {
m_sizes->setValue(value);
+ parseSizesAttribute(value, m_iconSizes);
process();
} else if (name == mediaAttr) {
- m_media = value.string().lower();
+ m_media = value.lower();
process();
} else if (name == disabledAttr) {
if (LinkStyle* link = linkStyle())
link->setDisabledState(!value.isNull());
- } else if (name == onbeforeloadAttr)
- setAttributeEventListener(EventTypeNames::beforeload, createAttributeEventListener(this, name, value));
- else {
+ } else {
if (name == titleAttr) {
if (LinkStyle* link = linkStyle())
link->setSheetTitle(value);
bool HTMLLinkElement::shouldLoadLink()
{
- bool continueLoad = true;
- RefPtr<Document> originalDocument(document());
- int recursionRank = ++m_beforeLoadRecurseCount;
- if (!dispatchBeforeLoadEvent(getNonEmptyURLAttribute(hrefAttr)))
- continueLoad = false;
-
- // A beforeload handler might have removed us from the document or changed the document.
- if (continueLoad && (!inDocument() || document() != originalDocument))
- continueLoad = false;
-
- // If the beforeload handler recurses into the link element by mutating it, we should only
- // let the latest (innermost) mutation occur.
- if (recursionRank != m_beforeLoadRecurseCount)
- continueLoad = false;
-
- if (recursionRank == 1)
- m_beforeLoadRecurseCount = 0;
+ return inDocument();
+}
- return continueLoad;
+bool HTMLLinkElement::loadLink(const String& type, const KURL& url)
+{
+ return m_linkLoader.loadLink(m_relAttribute, fastGetAttribute(HTMLNames::crossoriginAttr), type, url, document());
}
LinkResource* HTMLLinkElement::linkResourceToProcess()
bool visible = inDocument() && !m_isInShadowTree;
if (!visible) {
ASSERT(!linkStyle() || !linkStyle()->hasSheet());
- return 0;
+ return nullptr;
}
if (!m_link) {
- if (m_relAttribute.isImport() && RuntimeEnabledFeatures::htmlImportsEnabled())
+ if (m_relAttribute.isImport()) {
m_link = LinkImport::create(this);
- else {
- RefPtr<LinkStyle> link = LinkStyle::create(this);
- if (fastHasAttribute(disabledAttr))
+ } else if (m_relAttribute.isManifest()) {
+ m_link = LinkManifest::create(this);
+ } else {
+ OwnPtrWillBeRawPtr<LinkStyle> link = LinkStyle::create(this);
+ if (fastHasAttribute(disabledAttr) || m_relAttribute.isTransitionExitingStylesheet())
link->setDisabledState(true);
m_link = link.release();
}
LinkStyle* HTMLLinkElement::linkStyle() const
{
if (!m_link || m_link->type() != LinkResource::Style)
- return 0;
+ return nullptr;
return static_cast<LinkStyle*>(m_link.get());
}
LinkImport* HTMLLinkElement::linkImport() const
{
if (!m_link || m_link->type() != LinkResource::Import)
- return 0;
+ return nullptr;
return static_cast<LinkImport*>(m_link.get());
}
{
if (LinkImport* link = linkImport())
return link->importedDocument();
- return 0;
+ return nullptr;
}
void HTMLLinkElement::process()
link->process();
}
+void HTMLLinkElement::enableIfExitTransitionStyle()
+{
+ if (m_relAttribute.isTransitionExitingStylesheet()) {
+ if (LinkStyle* link = linkStyle())
+ link->setDisabledState(false);
+ }
+}
+
Node::InsertionNotificationRequest HTMLLinkElement::insertedInto(ContainerNode* insertionPoint)
{
+ if (insertionPoint->inDocument()) {
+ V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
+ if (activityLogger) {
+ Vector<String> argv;
+ argv.append("link");
+ argv.append(fastGetAttribute(relAttr));
+ argv.append(fastGetAttribute(hrefAttr));
+ activityLogger->logEvent("blinkAddElement", argv.size(), argv.data());
+ }
+ }
HTMLElement::insertedInto(insertionPoint);
if (!insertionPoint->inDocument())
return InsertionDone;
m_isInShadowTree = isInShadowTree();
- if (m_isInShadowTree)
+ if (m_isInShadowTree) {
+ String message = "HTML element <link> is ignored in shadow tree.";
+ document().addConsoleMessage(ConsoleMessage::create(JSMessageSource, WarningMessageLevel, message));
return InsertionDone;
+ }
document().styleEngine()->addStyleSheetCandidateNode(this, m_createdByParser);
process();
+
+ if (m_link)
+ m_link->ownerInserted();
+
return InsertionDone;
}
}
document().styleEngine()->removeStyleSheetCandidateNode(this);
- RefPtr<StyleSheet> removedSheet = sheet();
+ RefPtrWillBeRawPtr<StyleSheet> removedSheet = sheet();
if (m_link)
m_link->ownerRemoved();
- if (document().isActive())
- document().removedStyleSheet(removedSheet.get());
+ document().removedStyleSheet(removedSheet.get());
}
void HTMLLinkElement::finishParsingChildren()
return attribute.name().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 (equalIgnoringCase(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
{
return document().completeURL(getAttribute(hrefAttr));
}
-String HTMLLinkElement::rel() const
+const AtomicString& HTMLLinkElement::rel() const
{
return getAttribute(relAttr);
}
-String HTMLLinkElement::target() const
+const AtomicString& HTMLLinkElement::type() const
{
- return getAttribute(targetAttr);
+ return getAttribute(typeAttr);
}
-String HTMLLinkElement::type() const
+bool HTMLLinkElement::async() const
{
- return getAttribute(typeAttr);
+ return fastHasAttribute(HTMLNames::asyncAttr);
}
IconType HTMLLinkElement::iconType() const
return m_relAttribute.iconType();
}
-String HTMLLinkElement::iconSizes() const
+const Vector<IntSize>& HTMLLinkElement::iconSizes() const
{
- return m_sizes->toString();
+ return m_iconSizes;
}
-void HTMLLinkElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
+DOMSettableTokenList* HTMLLinkElement::sizes() const
{
- HTMLElement::addSubresourceAttributeURLs(urls);
-
- // Favicons are handled by a special case in LegacyWebArchive::create()
- if (m_relAttribute.iconType() != InvalidIcon)
- return;
-
- if (!m_relAttribute.isStyleSheet())
- return;
-
- // Append the URL of this link element.
- addSubresourceURL(urls, href());
+ return m_sizes.get();
+}
- // Walk the URLs linked by the linked-to stylesheet.
- if (CSSStyleSheet* styleSheet = const_cast<HTMLLinkElement*>(this)->sheet())
- styleSheet->contents()->addSubresourceStyleURLs(urls);
+void HTMLLinkElement::trace(Visitor* visitor)
+{
+ visitor->trace(m_link);
+ visitor->trace(m_sizes);
+ HTMLElement::trace(visitor);
}
-DOMSettableTokenList* HTMLLinkElement::sizes() const
+void HTMLLinkElement::attributeWillChange(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
{
- return m_sizes.get();
+ if (name == hrefAttr && inDocument()) {
+ V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
+ if (activityLogger) {
+ Vector<String> argv;
+ argv.append("link");
+ argv.append(hrefAttr.toString());
+ argv.append(oldValue);
+ argv.append(newValue);
+ activityLogger->logEvent("blinkSetAttribute", argv.size(), argv.data());
+ }
+ }
+ HTMLElement::attributeWillChange(name, oldValue, newValue);
}
-PassRefPtr<LinkStyle> LinkStyle::create(HTMLLinkElement* owner)
+PassOwnPtrWillBeRawPtr<LinkStyle> LinkStyle::create(HTMLLinkElement* owner)
{
- return adoptRef(new LinkStyle(owner));
+ return adoptPtrWillBeNoop(new LinkStyle(owner));
}
LinkStyle::LinkStyle(HTMLLinkElement* owner)
LinkStyle::~LinkStyle()
{
+#if !ENABLE(OILPAN)
if (m_sheet)
m_sheet->clearOwnerNode();
-
- if (m_resource)
- m_resource->removeClient(this);
+#endif
}
Document& LinkStyle::document()
if (!m_owner->inDocument()) {
ASSERT(!m_sheet);
return;
+ }
+ if (!SubresourceIntegrity::CheckSubresourceIntegrity(*m_owner, cachedStyleSheet->sheetText(), KURL(KURL(), href))) {
+ m_loading = false;
+ removePendingSheet();
+ return;
}
+
// Completing the sheet load may cause scripts to execute.
- RefPtr<Node> protector(m_owner);
+ RefPtrWillBeRawPtr<Node> protector(m_owner.get());
- CSSParserContext parserContext(m_owner->document(), baseURL, charset);
+ CSSParserContext parserContext(m_owner->document(), 0, baseURL, charset);
- if (RefPtr<StyleSheetContents> restoredSheet = const_cast<CSSStyleSheetResource*>(cachedStyleSheet)->restoreParsedStyleSheet(parserContext)) {
+ if (RefPtrWillBeRawPtr<StyleSheetContents> restoredSheet = const_cast<CSSStyleSheetResource*>(cachedStyleSheet)->restoreParsedStyleSheet(parserContext)) {
ASSERT(restoredSheet->isCacheable());
ASSERT(!restoredSheet->isLoading());
m_sheet->setTitle(m_owner->title());
m_loading = false;
- sheetLoaded();
- notifyLoadedSheetAndAllCriticalSubresources(false);
+ restoredSheet->checkLoaded();
return;
}
- RefPtr<StyleSheetContents> styleSheet = StyleSheetContents::create(href, parserContext);
+ RefPtrWillBeRawPtr<StyleSheetContents> styleSheet = StyleSheetContents::create(href, parserContext);
if (m_sheet)
clearSheet();
+
m_sheet = CSSStyleSheet::create(styleSheet, m_owner);
m_sheet->setMediaQueries(MediaQuerySet::create(m_owner->media()));
m_sheet->setTitle(m_owner->title());
ASSERT(m_sheet);
ASSERT(m_sheet->ownerNode() == m_owner);
m_sheet->clearOwnerNode();
- m_sheet = 0;
+ m_sheet = nullptr;
}
bool LinkStyle::styleSheetIsLoading() const
m_owner->document().styleEngine()->addPendingSheet();
}
-void LinkStyle::removePendingSheet(RemovePendingSheetNotificationType notification)
+void LinkStyle::removePendingSheet()
{
PendingSheetType type = m_pendingSheetType;
m_pendingSheetType = None;
// Document::removePendingSheet() triggers the style selector recalc for blocking sheets.
// FIXME: We don't have enough knowledge at this point to know if we're adding or removing a sheet
// so we can't call addedStyleSheet() or removedStyleSheet().
- m_owner->document().styleResolverChanged(RecalcStyleImmediately);
+ m_owner->document().styleResolverChanged();
return;
}
- m_owner->document().styleEngine()->removePendingSheet(m_owner,
- notification == RemovePendingSheetNotifyImmediately
- ? StyleEngine::RemovePendingSheetNotifyImmediately
- : StyleEngine::RemovePendingSheetNotifyLater);
+ m_owner->document().styleEngine()->removePendingSheet(m_owner);
}
void LinkStyle::setDisabledState(bool disabled)
process();
} else {
// FIXME: We don't have enough knowledge here to know if we should call addedStyleSheet() or removedStyleSheet().
- m_owner->document().styleResolverChanged(RecalcStyleDeferred);
+ m_owner->document().styleResolverChanged();
}
}
}
return;
if (!document().contentSecurityPolicy()->allowImageFromSource(builder.url()))
return;
- if (document().frame())
+ if (document().frame() && document().frame()->loader().client())
document().frame()->loader().client()->dispatchDidChangeIcons(m_owner->relAttribute().iconType());
}
if (!m_owner->loadLink(type, builder.url()))
return;
- if ((m_disabledState != Disabled) && m_owner->relAttribute().isStyleSheet()
- && document().frame() && builder.url().isValid()) {
+ if ((m_disabledState != Disabled) && (m_owner->relAttribute().isStyleSheet() || m_owner->relAttribute().isTransitionExitingStylesheet())
+ && shouldLoadResource() && builder.url().isValid()) {
- if (m_resource) {
+ if (resource()) {
removePendingSheet();
- m_resource->removeClient(this);
- m_resource = 0;
+ clearResource();
}
if (!m_owner->shouldLoadLink())
m_loading = true;
bool mediaQueryMatches = true;
- if (!m_owner->media().isEmpty()) {
- RefPtr<RenderStyle> documentStyle = StyleResolver::styleForDocument(document());
- RefPtr<MediaQuerySet> media = MediaQuerySet::create(m_owner->media());
- MediaQueryEvaluator evaluator(document().frame()->view()->mediaType(), document().frame(), documentStyle.get());
+ LocalFrame* frame = loadingFrame();
+ if (!m_owner->media().isEmpty() && frame && frame->document()) {
+ RefPtr<RenderStyle> documentStyle = StyleResolver::styleForDocument(*frame->document());
+ RefPtrWillBeRawPtr<MediaQuerySet> media = MediaQuerySet::create(m_owner->media());
+ MediaQueryEvaluator evaluator(frame);
mediaQueryMatches = evaluator.eval(media.get());
}
// Load stylesheets that are not needed for the rendering immediately with low priority.
FetchRequest request = builder.build(blocking);
- m_resource = document().fetcher()->fetchCSSStyleSheet(request);
+ AtomicString crossOriginMode = m_owner->fastGetAttribute(HTMLNames::crossoriginAttr);
+ if (!crossOriginMode.isNull())
+ request.setCrossOriginAccessControl(document().securityOrigin(), crossOriginMode);
+ setResource(document().fetcher()->fetchCSSStyleSheet(request));
- if (m_resource)
- m_resource->addClient(this);
- else {
+ if (!resource()) {
// The request may have been denied if (for example) the stylesheet is local and the document is remote.
m_loading = false;
removePendingSheet();
}
} else if (m_sheet) {
// we no longer contain a stylesheet, e.g. perhaps rel or type was changed
- RefPtr<StyleSheet> removedSheet = m_sheet;
+ RefPtrWillBeRawPtr<StyleSheet> removedSheet = m_sheet.get();
clearSheet();
document().removedStyleSheet(removedSheet.get());
}
clearSheet();
if (styleSheetIsLoading())
- removePendingSheet(RemovePendingSheetNotifyLater);
+ removePendingSheet();
+}
+
+void LinkStyle::trace(Visitor* visitor)
+{
+ visitor->trace(m_sheet);
+ LinkResource::trace(visitor);
}
-} // namespace WebCore
+} // namespace blink