blob: edd0825b123654779b9e0c3a37b2d569c5cc4a5c [file] [log] [blame]
// 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