summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/css/active_style_sheets.cc
blob: 1359ffeda38e15abea7714483866ddc55d711461 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright 2016 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 "third_party/blink/renderer/core/css/active_style_sheets.h"

#include "third_party/blink/renderer/core/css/css_style_sheet.h"
#include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h"
#include "third_party/blink/renderer/core/css/rule_set.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
#include "third_party/blink/renderer/core/dom/container_node.h"

namespace blink {

ActiveSheetsChange CompareActiveStyleSheets(
    const ActiveStyleSheetVector& old_style_sheets,
    const ActiveStyleSheetVector& new_style_sheets,
    HeapHashSet<Member<RuleSet>>& changed_rule_sets) {
  unsigned new_style_sheet_count = new_style_sheets.size();
  unsigned old_style_sheet_count = old_style_sheets.size();

  unsigned min_count = std::min(new_style_sheet_count, old_style_sheet_count);
  unsigned index = 0;

  // Walk the common prefix of stylesheets. If the stylesheet rules were
  // modified since last time, add them to the list of changed rulesets.
  for (; index < min_count &&
         new_style_sheets[index].first == old_style_sheets[index].first;
       index++) {
    if (new_style_sheets[index].second == old_style_sheets[index].second)
      continue;

    if (new_style_sheets[index].second)
      changed_rule_sets.insert(new_style_sheets[index].second);
    if (old_style_sheets[index].second)
      changed_rule_sets.insert(old_style_sheets[index].second);
  }

  // If we add a sheet for which the media attribute currently doesn't match, we
  // have a null RuleSet and there's no need to do any style invalidation.
  // However, we need to tell the StyleEngine to re-collect viewport and device
  // dependent media query results so that we can correctly update active style
  // sheets when such media query evaluations change.
  bool adds_non_matching_mq = false;

  if (index == old_style_sheet_count) {
    // The old stylesheet vector is a prefix of the new vector in terms of
    // StyleSheets. If none of the RuleSets changed, we only need to add the new
    // sheets to the ScopedStyleResolver (ActiveSheetsAppended).
    bool rule_sets_changed_in_common_prefix = !changed_rule_sets.IsEmpty();
    for (; index < new_style_sheet_count; index++) {
      if (new_style_sheets[index].second)
        changed_rule_sets.insert(new_style_sheets[index].second);
      else if (new_style_sheets[index].first->HasMediaQueryResults())
        adds_non_matching_mq = true;
    }
    if (rule_sets_changed_in_common_prefix)
      return kActiveSheetsChanged;
    if (changed_rule_sets.IsEmpty() && !adds_non_matching_mq)
      return kNoActiveSheetsChanged;
    return kActiveSheetsAppended;
  }

  if (index == new_style_sheet_count) {
    // Sheets removed from the end.
    for (; index < old_style_sheet_count; index++) {
      if (old_style_sheets[index].second)
        changed_rule_sets.insert(old_style_sheets[index].second);
      else if (old_style_sheets[index].first->HasMediaQueryResults())
        adds_non_matching_mq = true;
    }
    return changed_rule_sets.IsEmpty() && !adds_non_matching_mq
               ? kNoActiveSheetsChanged
               : kActiveSheetsChanged;
  }

  DCHECK_LT(index, old_style_sheet_count);
  DCHECK_LT(index, new_style_sheet_count);

  // Both the new and old active stylesheet vectors have stylesheets following
  // the common prefix. Figure out which were added or removed by sorting the
  // merged vector of old and new sheets.

  ActiveStyleSheetVector merged_sorted;
  merged_sorted.ReserveCapacity(old_style_sheet_count + new_style_sheet_count -
                                2 * index);
  merged_sorted.AppendRange(old_style_sheets.begin() + index,
                            old_style_sheets.end());
  merged_sorted.AppendRange(new_style_sheets.begin() + index,
                            new_style_sheets.end());

  std::sort(merged_sorted.begin(), merged_sorted.end());

  auto* merged_iterator = merged_sorted.begin();
  while (merged_iterator != merged_sorted.end()) {
    const auto& sheet1 = *merged_iterator++;
    if (merged_iterator == merged_sorted.end() ||
        (*merged_iterator).first != sheet1.first) {
      // Sheet either removed or inserted.
      if (sheet1.second)
        changed_rule_sets.insert(sheet1.second);
      else if (sheet1.first->HasMediaQueryResults())
        adds_non_matching_mq = true;
      continue;
    }

    // Sheet present in both old and new.
    const auto& sheet2 = *merged_iterator++;

    if (sheet1.second == sheet2.second)
      continue;

    // Active rules for the given stylesheet changed.
    // DOM, CSSOM, or media query changes.
    if (sheet1.second)
      changed_rule_sets.insert(sheet1.second);
    if (sheet2.second)
      changed_rule_sets.insert(sheet2.second);
  }
  return changed_rule_sets.IsEmpty() && !adds_non_matching_mq
             ? kNoActiveSheetsChanged
             : kActiveSheetsChanged;
}

bool ClearMediaQueryDependentRuleSets(
    const ActiveStyleSheetVector& active_style_sheets) {
  bool needs_active_style_update = false;
  for (const auto& active_sheet : active_style_sheets) {
    if (const MediaQuerySet* media_queries =
            active_sheet.first->MediaQueries()) {
      if (!media_queries->QueryVector().IsEmpty())
        needs_active_style_update = true;
    }
    StyleSheetContents* contents = active_sheet.first->Contents();
    if (contents->HasMediaQueries()) {
      needs_active_style_update = true;
      contents->ClearRuleSet();
    }
  }
  return needs_active_style_update;
}

}  // namespace blink