| // Copyright 2014 The Cobalt Authors. All Rights Reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include <memory> | |
| #include "cobalt/cssom/css_rule_list.h" | |
| #include "cobalt/cssom/css_media_rule.h" | |
| #include "cobalt/cssom/css_parser.h" | |
| #include "cobalt/cssom/css_rule_style_declaration.h" | |
| #include "cobalt/cssom/css_style_declaration.h" | |
| #include "cobalt/cssom/css_style_rule.h" | |
| #include "cobalt/cssom/css_style_sheet.h" | |
| #include "cobalt/cssom/length_value.h" | |
| #include "cobalt/cssom/media_feature.h" | |
| #include "cobalt/cssom/media_feature_keyword_value.h" | |
| #include "cobalt/cssom/media_feature_names.h" | |
| #include "cobalt/cssom/media_list.h" | |
| #include "cobalt/cssom/media_query.h" | |
| #include "cobalt/cssom/selector.h" | |
| #include "cobalt/cssom/style_sheet_list.h" | |
| #include "cobalt/cssom/testing/mock_css_parser.h" | |
| #include "testing/gmock/include/gmock/gmock.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| using testing::_; | |
| using testing::Return; | |
| namespace cobalt { | |
| namespace cssom { | |
| class MockMutationObserver : public MutationObserver { | |
| public: | |
| MOCK_METHOD0(OnCSSMutation, void()); | |
| }; | |
| class CSSStyleSheetTest : public ::testing::Test { | |
| protected: | |
| CSSStyleSheetTest() : css_style_sheet_(new CSSStyleSheet(&css_parser_)) { | |
| css_style_sheet_->SetOriginClean(true); | |
| StyleSheetVector style_sheets; | |
| style_sheets.push_back(css_style_sheet_); | |
| style_sheet_list_ = new StyleSheetList(style_sheets, &mutation_observer_); | |
| } | |
| ~CSSStyleSheetTest() override {} | |
| const scoped_refptr<CSSStyleSheet> css_style_sheet_; | |
| scoped_refptr<StyleSheetList> style_sheet_list_; | |
| MockMutationObserver mutation_observer_; | |
| testing::MockCSSParser css_parser_; | |
| }; | |
| TEST_F(CSSStyleSheetTest, InsertRule) { | |
| const std::string css_text = "div { font-size: 100px; color: #0047ab; }"; | |
| EXPECT_CALL(css_parser_, ParseRule(css_text, _)) | |
| .WillOnce(Return(scoped_refptr<CSSRule>())); | |
| css_style_sheet_->InsertRuleSameOrigin(css_text, 0); | |
| } | |
| TEST_F(CSSStyleSheetTest, CSSRuleListIsCached) { | |
| scoped_refptr<CSSRuleList> rule_list_1 = | |
| css_style_sheet_->css_rules_same_origin(); | |
| scoped_refptr<CSSRuleList> rule_list_2 = | |
| css_style_sheet_->css_rules_same_origin(); | |
| ASSERT_EQ(rule_list_1, rule_list_2); | |
| } | |
| TEST_F(CSSStyleSheetTest, CSSRuleListIsLive) { | |
| scoped_refptr<CSSRuleList> rule_list = | |
| css_style_sheet_->css_rules_same_origin(); | |
| ASSERT_EQ(0, rule_list->length()); | |
| ASSERT_FALSE(rule_list->Item(0).get()); | |
| scoped_refptr<CSSStyleRule> rule = | |
| new CSSStyleRule(Selectors(), new CSSRuleStyleDeclaration(NULL)); | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(1); | |
| rule_list->AppendCSSRule(rule); | |
| css_style_sheet_->set_css_rules(rule_list); | |
| ASSERT_EQ(1, rule_list->length()); | |
| ASSERT_EQ(rule, rule_list->Item(0)); | |
| ASSERT_FALSE(rule_list->Item(1).get()); | |
| ASSERT_EQ(rule_list, css_style_sheet_->css_rules_same_origin()); | |
| } | |
| TEST_F(CSSStyleSheetTest, CSSMutationIsReportedAtStyleSheetList) { | |
| // A call to OnCSSMutation on the CSSStyleSheet should result in a call to | |
| // OnCSSMutation in the MutationObserver registered with the StyleSheetList | |
| // that is the parent of the CSSStyleSheet. | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(1); | |
| css_style_sheet_->OnCSSMutation(); | |
| } | |
| TEST_F(CSSStyleSheetTest, CSSMutationIsRecordedAfterMediaRuleAddition) { | |
| // When a CSSMediaRule is added to a CSSStyleSheet, it should result in a call | |
| // to OnCSSMutation() in the MutationObserver after the next call to | |
| // EvaluateMediaRules(), even when called with the same media parameters as | |
| // before. That also tests that OnMediaRuleMutation() should be called and | |
| // that the flag it sets should be honored. | |
| scoped_refptr<CSSRuleList> rule_list = | |
| css_style_sheet_->css_rules_same_origin(); | |
| // A CSSMediaRule with no expression always evaluates to true. | |
| scoped_refptr<CSSMediaRule> rule = | |
| new CSSMediaRule(new MediaList(), new CSSRuleList()); | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(0); | |
| css_style_sheet_->EvaluateMediaRules(ViewportSize(1920, 1080)); | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(1); | |
| rule_list->AppendCSSRule(rule); | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(1); | |
| css_style_sheet_->EvaluateMediaRules(ViewportSize(1920, 1080)); | |
| } | |
| TEST_F(CSSStyleSheetTest, CSSMutationIsRecordedForAddingFalseMediaRule) { | |
| // Adding a CSSMediaRule that is false should result in a call to | |
| // OnCSSMutation() only when the rule is added, for the added MediaRule | |
| // itself. It should not call OnCSSMutation when it's evaluated, because no | |
| // rules are added or removed at that time for a new rule that is false. | |
| scoped_refptr<MediaQuery> media_query(new MediaQuery(false)); | |
| scoped_refptr<MediaList> media_list(new MediaList()); | |
| media_list->Append(media_query); | |
| scoped_refptr<CSSMediaRule> rule = | |
| new CSSMediaRule(media_list, new CSSRuleList()); | |
| scoped_refptr<CSSRuleList> rule_list = | |
| css_style_sheet_->css_rules_same_origin(); | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(1); | |
| rule_list->AppendCSSRule(rule); | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(0); | |
| css_style_sheet_->EvaluateMediaRules(ViewportSize(1920, 1080)); | |
| } | |
| TEST_F(CSSStyleSheetTest, CSSMutationIsRecordedAfterMediaValueChanges) { | |
| // Changing a media value (width or height) should result in a call to | |
| // OnCSSMutation() if the media rule condition value changes. | |
| // We first need to build a CSSMediaRule that holds a media at-rule that | |
| // can change value. We choose '(orientation:landscape)'. | |
| scoped_refptr<MediaFeatureKeywordValue> property = | |
| MediaFeatureKeywordValue::GetLandscape(); | |
| scoped_refptr<MediaFeature> media_feature( | |
| new MediaFeature(kOrientationMediaFeature, property)); | |
| media_feature->set_operator(kEquals); | |
| std::unique_ptr<MediaFeatures> media_features(new MediaFeatures); | |
| media_features->push_back(media_feature); | |
| scoped_refptr<MediaQuery> media_query( | |
| new MediaQuery(true, std::move(media_features))); | |
| scoped_refptr<MediaList> media_list(new MediaList()); | |
| media_list->Append(media_query); | |
| scoped_refptr<CSSMediaRule> rule = | |
| new CSSMediaRule(media_list, new CSSRuleList()); | |
| // This should result in a call to OnCSSMutation(), because a media rule is | |
| // added to the style sheet. | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(1); | |
| css_style_sheet_->css_rules_same_origin()->AppendCSSRule(rule); | |
| // This should result in a call to OnCSSMutation(), because the added media | |
| // rule evaluates to true, so its rule list needs to be traversed for the next | |
| // rule matching. | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(1); | |
| css_style_sheet_->EvaluateMediaRules(ViewportSize(1920, 1080)); | |
| // This should not result in a call to OnCSSMutation(), because changing the | |
| // width to 1280 does not change the CSSMediaRule condition. | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(0); | |
| css_style_sheet_->EvaluateMediaRules(ViewportSize(1280, 1080)); | |
| // This should result in a call to OnCSSMutation(), because changing the width | |
| // to 640 makes the CSSMediaRule condition change. The display orientation is | |
| // now Portrait instead of Landscape. | |
| EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(1); | |
| css_style_sheet_->EvaluateMediaRules(ViewportSize(640, 1080)); | |
| } | |
| } // namespace cssom | |
| } // namespace cobalt |