Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(56)

Side by Side Diff: third_party/WebKit/Source/core/html/HTMLLinkElement.cpp

Issue 2426513003: Refactor LinkStyle out of HTMLLinkElement (Closed)
Patch Set: Review comment Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org) 4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights 5 * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights
6 * reserved. 6 * reserved.
7 * Copyright (C) 2009 Rob Buis (rwlbuis@gmail.com) 7 * Copyright (C) 2009 Rob Buis (rwlbuis@gmail.com)
8 * Copyright (C) 2011 Google Inc. All rights reserved. 8 * Copyright (C) 2011 Google Inc. All rights reserved.
9 * 9 *
10 * This library is free software; you can redistribute it and/or 10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public 11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either 12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version. 13 * version 2 of the License, or (at your option) any later version.
14 * 14 *
15 * This library is distributed in the hope that it will be useful, 15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details. 18 * Library General Public License for more details.
19 * 19 *
20 * You should have received a copy of the GNU Library General Public License 20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to 21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA. 23 * Boston, MA 02110-1301, USA.
24 */ 24 */
25 25
26 #include "core/html/HTMLLinkElement.h" 26 #include "core/html/HTMLLinkElement.h"
27 27
28 #include "bindings/core/v8/ScriptEventListener.h" 28 #include "bindings/core/v8/ScriptEventListener.h"
29 #include "core/HTMLNames.h" 29 #include "core/HTMLNames.h"
30 #include "core/css/MediaList.h"
31 #include "core/css/MediaQueryEvaluator.h"
32 #include "core/css/StyleSheetContents.h"
33 #include "core/css/resolver/StyleResolver.h"
34 #include "core/dom/Attribute.h" 30 #include "core/dom/Attribute.h"
35 #include "core/dom/Document.h" 31 #include "core/dom/Document.h"
36 #include "core/dom/StyleEngine.h"
37 #include "core/dom/TaskRunnerHelper.h" 32 #include "core/dom/TaskRunnerHelper.h"
38 #include "core/events/Event.h" 33 #include "core/events/Event.h"
39 #include "core/fetch/CSSStyleSheetResource.h"
40 #include "core/fetch/FetchRequest.h"
41 #include "core/fetch/ResourceFetcher.h"
42 #include "core/frame/FrameView.h"
43 #include "core/frame/LocalFrame.h"
44 #include "core/frame/SubresourceIntegrity.h"
45 #include "core/frame/UseCounter.h" 34 #include "core/frame/UseCounter.h"
46 #include "core/frame/csp/ContentSecurityPolicy.h"
47 #include "core/html/CrossOriginAttribute.h" 35 #include "core/html/CrossOriginAttribute.h"
48 #include "core/html/LinkManifest.h" 36 #include "core/html/LinkManifest.h"
49 #include "core/html/imports/LinkImport.h" 37 #include "core/html/imports/LinkImport.h"
50 #include "core/inspector/ConsoleMessage.h" 38 #include "core/inspector/ConsoleMessage.h"
51 #include "core/loader/FrameLoader.h"
52 #include "core/loader/FrameLoaderClient.h" 39 #include "core/loader/FrameLoaderClient.h"
53 #include "core/loader/NetworkHintsInterface.h" 40 #include "core/loader/NetworkHintsInterface.h"
54 #include "core/origin_trials/OriginTrials.h" 41 #include "core/origin_trials/OriginTrials.h"
55 #include "core/style/StyleInheritedData.h"
56 #include "platform/ContentType.h"
57 #include "platform/Histogram.h"
58 #include "platform/MIMETypeRegistry.h"
59 #include "platform/RuntimeEnabledFeatures.h"
60 #include "public/platform/WebIconSizesParser.h" 42 #include "public/platform/WebIconSizesParser.h"
61 #include "public/platform/WebSize.h" 43 #include "public/platform/WebSize.h"
62 #include "wtf/StdLibExtras.h"
63 44
64 namespace blink { 45 namespace blink {
65 46
66 using namespace HTMLNames; 47 using namespace HTMLNames;
67 48
68 static bool styleSheetTypeIsSupported(const String& type) {
69 String trimmedType = ContentType(type).type();
70 return trimmedType.isEmpty() ||
71 MIMETypeRegistry::isSupportedStyleSheetMIMEType(trimmedType);
72 }
73
74 inline HTMLLinkElement::HTMLLinkElement(Document& document, 49 inline HTMLLinkElement::HTMLLinkElement(Document& document,
75 bool createdByParser) 50 bool createdByParser)
76 : HTMLElement(linkTag, document), 51 : HTMLElement(linkTag, document),
77 m_linkLoader(LinkLoader::create(this)), 52 m_linkLoader(LinkLoader::create(this)),
78 m_sizes(DOMTokenList::create(this)), 53 m_sizes(DOMTokenList::create(this)),
79 m_relList(RelList::create(this)), 54 m_relList(RelList::create(this)),
80 m_createdByParser(createdByParser) {} 55 m_createdByParser(createdByParser) {}
81 56
82 HTMLLinkElement* HTMLLinkElement::create(Document& document, 57 HTMLLinkElement* HTMLLinkElement::create(Document& document,
83 bool createdByParser) { 58 bool createdByParser) {
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after
377 HTMLElement::trace(visitor); 352 HTMLElement::trace(visitor);
378 LinkLoaderClient::trace(visitor); 353 LinkLoaderClient::trace(visitor);
379 DOMTokenListObserver::trace(visitor); 354 DOMTokenListObserver::trace(visitor);
380 } 355 }
381 356
382 DEFINE_TRACE_WRAPPERS(HTMLLinkElement) { 357 DEFINE_TRACE_WRAPPERS(HTMLLinkElement) {
383 visitor->traceWrappers(m_relList); 358 visitor->traceWrappers(m_relList);
384 HTMLElement::traceWrappers(visitor); 359 HTMLElement::traceWrappers(visitor);
385 } 360 }
386 361
387 LinkStyle* LinkStyle::create(HTMLLinkElement* owner) {
388 return new LinkStyle(owner);
389 }
390
391 LinkStyle::LinkStyle(HTMLLinkElement* owner)
392 : LinkResource(owner),
393 m_disabledState(Unset),
394 m_pendingSheetType(None),
395 m_loading(false),
396 m_firedLoad(false),
397 m_loadedSheet(false),
398 m_fetchFollowingCORS(false) {}
399
400 LinkStyle::~LinkStyle() {}
401
402 Document& LinkStyle::document() {
403 return m_owner->document();
404 }
405
406 enum StyleSheetCacheStatus {
407 StyleSheetNewEntry,
408 StyleSheetInDiskCache,
409 StyleSheetInMemoryCache,
410 StyleSheetCacheStatusCount,
411 };
412
413 void LinkStyle::setCSSStyleSheet(
414 const String& href,
415 const KURL& baseURL,
416 const String& charset,
417 const CSSStyleSheetResource* cachedStyleSheet) {
418 if (!m_owner->isConnected()) {
419 // While the stylesheet is asynchronously loading, the owner can be
420 // disconnected from a document.
421 // In that case, cancel any processing on the loaded content.
422 m_loading = false;
423 removePendingSheet();
424 if (m_sheet)
425 clearSheet();
426 return;
427 }
428
429 // See the comment in PendingScript.cpp about why this check is necessary
430 // here, instead of in the resource fetcher. https://crbug.com/500701.
431 if (!cachedStyleSheet->errorOccurred() &&
432 !m_owner->fastGetAttribute(HTMLNames::integrityAttr).isEmpty() &&
433 !cachedStyleSheet->integrityMetadata().isEmpty()) {
434 ResourceIntegrityDisposition disposition =
435 cachedStyleSheet->integrityDisposition();
436
437 if (disposition == ResourceIntegrityDisposition::NotChecked &&
438 !cachedStyleSheet->loadFailedOrCanceled()) {
439 bool checkResult;
440
441 // cachedStyleSheet->resourceBuffer() can be nullptr on load success.
442 // If response size == 0.
443 const char* data = nullptr;
444 size_t size = 0;
445 if (cachedStyleSheet->resourceBuffer()) {
446 data = cachedStyleSheet->resourceBuffer()->data();
447 size = cachedStyleSheet->resourceBuffer()->size();
448 }
449 checkResult = SubresourceIntegrity::CheckSubresourceIntegrity(
450 *m_owner, data, size, KURL(baseURL, href), *cachedStyleSheet);
451 disposition = checkResult ? ResourceIntegrityDisposition::Passed
452 : ResourceIntegrityDisposition::Failed;
453
454 // TODO(kouhei): Remove this const_cast crbug.com/653502
455 const_cast<CSSStyleSheetResource*>(cachedStyleSheet)
456 ->setIntegrityDisposition(disposition);
457 }
458
459 if (disposition == ResourceIntegrityDisposition::Failed) {
460 m_loading = false;
461 removePendingSheet();
462 notifyLoadedSheetAndAllCriticalSubresources(
463 Node::ErrorOccurredLoadingSubresource);
464 return;
465 }
466 }
467
468 CSSParserContext parserContext(m_owner->document(), nullptr, baseURL,
469 charset);
470
471 DEFINE_STATIC_LOCAL(EnumerationHistogram, restoredCachedStyleSheetHistogram,
472 ("Blink.RestoredCachedStyleSheet", 2));
473 DEFINE_STATIC_LOCAL(
474 EnumerationHistogram, restoredCachedStyleSheet2Histogram,
475 ("Blink.RestoredCachedStyleSheet2", StyleSheetCacheStatusCount));
476
477 if (StyleSheetContents* restoredSheet =
478 const_cast<CSSStyleSheetResource*>(cachedStyleSheet)
479 ->restoreParsedStyleSheet(parserContext)) {
480 DCHECK(restoredSheet->isCacheableForResource());
481 DCHECK(!restoredSheet->isLoading());
482
483 if (m_sheet)
484 clearSheet();
485 m_sheet = CSSStyleSheet::create(restoredSheet, *m_owner);
486 m_sheet->setMediaQueries(MediaQuerySet::create(m_owner->media()));
487 if (m_owner->isInDocumentTree())
488 setSheetTitle(m_owner->title());
489 setCrossOriginStylesheetStatus(m_sheet.get());
490
491 m_loading = false;
492 restoredSheet->checkLoaded();
493
494 restoredCachedStyleSheetHistogram.count(true);
495 restoredCachedStyleSheet2Histogram.count(StyleSheetInMemoryCache);
496 return;
497 }
498 restoredCachedStyleSheetHistogram.count(false);
499 StyleSheetCacheStatus cacheStatus = cachedStyleSheet->response().wasCached()
500 ? StyleSheetInDiskCache
501 : StyleSheetNewEntry;
502 restoredCachedStyleSheet2Histogram.count(cacheStatus);
503
504 StyleSheetContents* styleSheet =
505 StyleSheetContents::create(href, parserContext);
506
507 if (m_sheet)
508 clearSheet();
509
510 m_sheet = CSSStyleSheet::create(styleSheet, *m_owner);
511 m_sheet->setMediaQueries(MediaQuerySet::create(m_owner->media()));
512 if (m_owner->isInDocumentTree())
513 setSheetTitle(m_owner->title());
514 setCrossOriginStylesheetStatus(m_sheet.get());
515
516 styleSheet->parseAuthorStyleSheet(cachedStyleSheet,
517 m_owner->document().getSecurityOrigin());
518
519 m_loading = false;
520 styleSheet->notifyLoadedSheet(cachedStyleSheet);
521 styleSheet->checkLoaded();
522
523 if (styleSheet->isCacheableForResource())
524 const_cast<CSSStyleSheetResource*>(cachedStyleSheet)
525 ->saveParsedStyleSheet(styleSheet);
526 clearResource();
527 }
528
529 bool LinkStyle::sheetLoaded() {
530 if (!styleSheetIsLoading()) {
531 removePendingSheet();
532 return true;
533 }
534 return false;
535 }
536
537 void LinkStyle::notifyLoadedSheetAndAllCriticalSubresources(
538 Node::LoadedSheetErrorStatus errorStatus) {
539 if (m_firedLoad)
540 return;
541 m_loadedSheet = (errorStatus == Node::NoErrorLoadingSubresource);
542 if (m_owner)
543 m_owner->scheduleEvent();
544 m_firedLoad = true;
545 }
546
547 void LinkStyle::startLoadingDynamicSheet() {
548 DCHECK_LT(m_pendingSheetType, Blocking);
549 addPendingSheet(Blocking);
550 }
551
552 void LinkStyle::clearSheet() {
553 DCHECK(m_sheet);
554 DCHECK_EQ(m_sheet->ownerNode(), m_owner);
555 m_sheet.release()->clearOwnerNode();
556 }
557
558 bool LinkStyle::styleSheetIsLoading() const {
559 if (m_loading)
560 return true;
561 if (!m_sheet)
562 return false;
563 return m_sheet->contents()->isLoading();
564 }
565
566 void LinkStyle::addPendingSheet(PendingSheetType type) {
567 if (type <= m_pendingSheetType)
568 return;
569 m_pendingSheetType = type;
570
571 if (m_pendingSheetType == NonBlocking)
572 return;
573 m_owner->document().styleEngine().addPendingSheet(m_styleEngineContext);
574 }
575
576 void LinkStyle::removePendingSheet() {
577 DCHECK(m_owner);
578 PendingSheetType type = m_pendingSheetType;
579 m_pendingSheetType = None;
580
581 if (type == None)
582 return;
583 if (type == NonBlocking) {
584 // Tell StyleEngine to re-compute styleSheets of this m_owner's treescope.
585 m_owner->document().styleEngine().modifiedStyleSheetCandidateNode(*m_owner);
586 return;
587 }
588
589 m_owner->document().styleEngine().removePendingSheet(*m_owner,
590 m_styleEngineContext);
591 }
592
593 void LinkStyle::setDisabledState(bool disabled) {
594 LinkStyle::DisabledState oldDisabledState = m_disabledState;
595 m_disabledState = disabled ? Disabled : EnabledViaScript;
596 if (oldDisabledState != m_disabledState) {
597 // If we change the disabled state while the sheet is still loading, then we
598 // have to perform three checks:
599 if (styleSheetIsLoading()) {
600 // Check #1: The sheet becomes disabled while loading.
601 if (m_disabledState == Disabled)
602 removePendingSheet();
603
604 // Check #2: An alternate sheet becomes enabled while it is still loading.
605 if (m_owner->relAttribute().isAlternate() &&
606 m_disabledState == EnabledViaScript)
607 addPendingSheet(Blocking);
608
609 // Check #3: A main sheet becomes enabled while it was still loading and
610 // after it was disabled via script. It takes really terrible code to make
611 // this happen (a double toggle for no reason essentially). This happens
612 // on virtualplastic.net, which manages to do about 12 enable/disables on
613 // only 3 sheets. :)
614 if (!m_owner->relAttribute().isAlternate() &&
615 m_disabledState == EnabledViaScript && oldDisabledState == Disabled)
616 addPendingSheet(Blocking);
617
618 // If the sheet is already loading just bail.
619 return;
620 }
621
622 if (m_sheet) {
623 m_sheet->setDisabled(disabled);
624 return;
625 }
626
627 if (m_disabledState == EnabledViaScript && m_owner->shouldProcessStyle())
628 process();
629 }
630 }
631
632 void LinkStyle::setCrossOriginStylesheetStatus(CSSStyleSheet* sheet) {
633 if (m_fetchFollowingCORS && resource() && !resource()->errorOccurred()) {
634 // Record the security origin the CORS access check succeeded at, if cross
635 // origin. Only origins that are script accessible to it may access the
636 // stylesheet's rules.
637 sheet->setAllowRuleAccessFromOrigin(
638 m_owner->document().getSecurityOrigin());
639 }
640 m_fetchFollowingCORS = false;
641 }
642
643 void LinkStyle::process() {
644 DCHECK(m_owner->shouldProcessStyle());
645 String type = m_owner->typeValue().lower();
646 String as = m_owner->asValue().lower();
647 String media = m_owner->media().lower();
648 LinkRequestBuilder builder(m_owner);
649
650 if (m_owner->relAttribute().getIconType() != InvalidIcon &&
651 builder.url().isValid() && !builder.url().isEmpty()) {
652 if (!m_owner->shouldLoadLink())
653 return;
654 if (!document().getSecurityOrigin()->canDisplay(builder.url()))
655 return;
656 if (!document().contentSecurityPolicy()->allowImageFromSource(
657 builder.url()))
658 return;
659 if (document().frame() && document().frame()->loader().client())
660 document().frame()->loader().client()->dispatchDidChangeIcons(
661 m_owner->relAttribute().getIconType());
662 }
663
664 if (!m_owner->loadLink(type, as, media, builder.url()))
665 return;
666
667 if (m_disabledState != Disabled && m_owner->relAttribute().isStyleSheet() &&
668 styleSheetTypeIsSupported(type) && shouldLoadResource() &&
669 builder.url().isValid()) {
670 if (resource()) {
671 removePendingSheet();
672 clearResource();
673 clearFetchFollowingCORS();
674 }
675
676 if (!m_owner->shouldLoadLink())
677 return;
678
679 m_loading = true;
680
681 String title = m_owner->title();
682 if (!title.isEmpty() && !m_owner->isAlternate() &&
683 m_disabledState != EnabledViaScript && m_owner->isInDocumentTree())
684 document().styleEngine().setPreferredStylesheetSetNameIfNotSet(
685 title, StyleEngine::DontUpdateActiveSheets);
686
687 bool mediaQueryMatches = true;
688 LocalFrame* frame = loadingFrame();
689 if (!m_owner->media().isEmpty() && frame) {
690 MediaQuerySet* media = MediaQuerySet::create(m_owner->media());
691 MediaQueryEvaluator evaluator(frame);
692 mediaQueryMatches = evaluator.eval(media);
693 }
694
695 // Don't hold up layout tree construction and script execution on
696 // stylesheets that are not needed for the layout at the moment.
697 bool blocking = mediaQueryMatches && !m_owner->isAlternate() &&
698 m_owner->isCreatedByParser();
699 addPendingSheet(blocking ? Blocking : NonBlocking);
700
701 // Load stylesheets that are not needed for the layout immediately with low
702 // priority. When the link element is created by scripts, load the
703 // stylesheets asynchronously but in high priority.
704 bool lowPriority = !mediaQueryMatches || m_owner->isAlternate();
705 FetchRequest request = builder.build(lowPriority);
706 CrossOriginAttributeValue crossOrigin = crossOriginAttributeValue(
707 m_owner->fastGetAttribute(HTMLNames::crossoriginAttr));
708 if (crossOrigin != CrossOriginAttributeNotSet) {
709 request.setCrossOriginAccessControl(document().getSecurityOrigin(),
710 crossOrigin);
711 setFetchFollowingCORS();
712 }
713
714 String integrityAttr = m_owner->fastGetAttribute(HTMLNames::integrityAttr);
715 if (!integrityAttr.isEmpty()) {
716 IntegrityMetadataSet metadataSet;
717 SubresourceIntegrity::parseIntegrityAttribute(integrityAttr, metadataSet);
718 request.setIntegrityMetadata(metadataSet);
719 }
720 setResource(CSSStyleSheetResource::fetch(request, document().fetcher()));
721
722 if (m_loading && !resource()) {
723 // The request may have been denied if (for example) the stylesheet is
724 // local and the document is remote, or if there was a Content Security
725 // Policy Failure. setCSSStyleSheet() can be called synchronuosly in
726 // setResource() and thus resource() is null and |m_loading| is false in
727 // such cases even if the request succeeds.
728 m_loading = false;
729 removePendingSheet();
730 notifyLoadedSheetAndAllCriticalSubresources(
731 Node::ErrorOccurredLoadingSubresource);
732 }
733 } else if (m_sheet) {
734 // we no longer contain a stylesheet, e.g. perhaps rel or type was changed
735 StyleSheet* removedSheet = m_sheet.get();
736 clearSheet();
737 document().styleEngine().setNeedsActiveStyleUpdate(removedSheet,
738 FullStyleUpdate);
739 }
740 }
741
742 void LinkStyle::setSheetTitle(
743 const String& title,
744 StyleEngine::ActiveSheetsUpdate updateActiveSheets) {
745 if (!m_owner->isInDocumentTree() || !m_owner->relAttribute().isStyleSheet())
746 return;
747
748 if (m_sheet)
749 m_sheet->setTitle(title);
750
751 if (title.isEmpty() || !isUnset() || m_owner->isAlternate())
752 return;
753
754 KURL href = m_owner->getNonEmptyURLAttribute(hrefAttr);
755 if (href.isValid() && !href.isEmpty())
756 document().styleEngine().setPreferredStylesheetSetNameIfNotSet(
757 title, updateActiveSheets);
758 }
759
760 void LinkStyle::ownerRemoved() {
761 if (m_sheet)
762 clearSheet();
763
764 if (styleSheetIsLoading())
765 removePendingSheet();
766 }
767
768 DEFINE_TRACE(LinkStyle) {
769 visitor->trace(m_sheet);
770 LinkResource::trace(visitor);
771 ResourceOwner<StyleSheetResource>::trace(visitor);
772 }
773
774 } // namespace blink 362 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/html/HTMLLinkElement.h ('k') | third_party/WebKit/Source/core/html/LinkStyle.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698