blob: 453dea333bfb80257916221a7d1845a35eae4f41 [file] [log] [blame]
// Copyright 2015 Google Inc. 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 "cobalt/dom/font_face_updater.h"
#include <string>
#include "cobalt/cssom/css_font_face_rule.h"
#include "cobalt/cssom/css_media_rule.h"
#include "cobalt/cssom/css_rule_list.h"
#include "cobalt/cssom/css_style_rule.h"
#include "cobalt/cssom/css_style_sheet.h"
#include "cobalt/cssom/font_style_value.h"
#include "cobalt/cssom/font_weight_value.h"
#include "cobalt/cssom/keyword_value.h"
#include "cobalt/cssom/local_src_value.h"
#include "cobalt/cssom/property_list_value.h"
#include "cobalt/cssom/property_value_visitor.h"
#include "cobalt/cssom/string_value.h"
#include "cobalt/cssom/style_sheet_list.h"
#include "cobalt/cssom/url_src_value.h"
#include "cobalt/cssom/url_value.h"
namespace cobalt {
namespace dom {
namespace {
////////////////////////////////////////////////////////////////////////////////
// FontFaceProvider
////////////////////////////////////////////////////////////////////////////////
// Visit a single font face rule, generating a FontFaceStyleSet::Entry from its
// values and verifying that it is valid.
// TODO: Handle unicode ranges.
class FontFaceProvider : public cssom::NotReachedPropertyValueVisitor {
public:
explicit FontFaceProvider(const GURL& base_url) : base_url_(base_url) {}
void VisitFontStyle(cssom::FontStyleValue* font_style) OVERRIDE;
void VisitFontWeight(cssom::FontWeightValue* font_weight) OVERRIDE;
void VisitKeyword(cssom::KeywordValue* keyword) OVERRIDE;
void VisitLocalSrc(cssom::LocalSrcValue* local_src) OVERRIDE;
void VisitPropertyList(cssom::PropertyListValue* property_list) OVERRIDE;
void VisitString(cssom::StringValue* percentage) OVERRIDE;
void VisitUnicodeRange(cssom::UnicodeRangeValue* unicode_range) OVERRIDE;
void VisitUrlSrc(cssom::UrlSrcValue* url_src) OVERRIDE;
void VisitURL(cssom::URLValue* url) OVERRIDE;
bool IsFontFaceValid() const;
const std::string& font_family() const { return font_family_; }
const FontFaceStyleSet::Entry& style_set_entry() const {
return style_set_entry_;
}
private:
const GURL& base_url_;
std::string font_family_;
FontFaceStyleSet::Entry style_set_entry_;
DISALLOW_COPY_AND_ASSIGN(FontFaceProvider);
};
void FontFaceProvider::VisitFontStyle(cssom::FontStyleValue* font_style) {
style_set_entry_.style.slant =
font_style->value() == cssom::FontStyleValue::kItalic
? render_tree::FontStyle::kItalicSlant
: render_tree::FontStyle::kUprightSlant;
}
void FontFaceProvider::VisitFontWeight(cssom::FontWeightValue* font_weight) {
switch (font_weight->value()) {
case cssom::FontWeightValue::kThinAka100:
style_set_entry_.style.weight = render_tree::FontStyle::kThinWeight;
break;
case cssom::FontWeightValue::kExtraLightAka200:
style_set_entry_.style.weight = render_tree::FontStyle::kExtraLightWeight;
break;
case cssom::FontWeightValue::kLightAka300:
style_set_entry_.style.weight = render_tree::FontStyle::kLightWeight;
break;
case cssom::FontWeightValue::kNormalAka400:
style_set_entry_.style.weight = render_tree::FontStyle::kNormalWeight;
break;
case cssom::FontWeightValue::kMediumAka500:
style_set_entry_.style.weight = render_tree::FontStyle::kMediumWeight;
break;
case cssom::FontWeightValue::kSemiBoldAka600:
style_set_entry_.style.weight = render_tree::FontStyle::kSemiBoldWeight;
break;
case cssom::FontWeightValue::kBoldAka700:
style_set_entry_.style.weight = render_tree::FontStyle::kBoldWeight;
break;
case cssom::FontWeightValue::kExtraBoldAka800:
style_set_entry_.style.weight = render_tree::FontStyle::kExtraBoldWeight;
break;
case cssom::FontWeightValue::kBlackAka900:
style_set_entry_.style.weight = render_tree::FontStyle::kBlackWeight;
break;
}
}
void FontFaceProvider::VisitKeyword(cssom::KeywordValue* keyword) {
switch (keyword->value()) {
case cssom::KeywordValue::kCursive:
case cssom::KeywordValue::kFantasy:
case cssom::KeywordValue::kMonospace:
case cssom::KeywordValue::kSansSerif:
case cssom::KeywordValue::kSerif:
font_family_ = keyword->ToString();
break;
// Inherit and Initial are valid font-family values. However, they are
// meaningless in the context of an @font-face rule, as a font-family will
// never attempt a lookup with that value. Clear the font-family name if
// they are encountered so that the font face rule won't be created.
case cssom::KeywordValue::kInherit:
case cssom::KeywordValue::kInitial:
font_family_.clear();
break;
case cssom::KeywordValue::kAbsolute:
case cssom::KeywordValue::kAlternate:
case cssom::KeywordValue::kAlternateReverse:
case cssom::KeywordValue::kAuto:
case cssom::KeywordValue::kBackwards:
case cssom::KeywordValue::kBaseline:
case cssom::KeywordValue::kBlock:
case cssom::KeywordValue::kBoth:
case cssom::KeywordValue::kBottom:
case cssom::KeywordValue::kBreakWord:
case cssom::KeywordValue::kCenter:
case cssom::KeywordValue::kClip:
case cssom::KeywordValue::kContain:
case cssom::KeywordValue::kCover:
case cssom::KeywordValue::kCurrentColor:
case cssom::KeywordValue::kEllipsis:
case cssom::KeywordValue::kEnd:
case cssom::KeywordValue::kFixed:
case cssom::KeywordValue::kForwards:
case cssom::KeywordValue::kHidden:
case cssom::KeywordValue::kInfinite:
case cssom::KeywordValue::kInline:
case cssom::KeywordValue::kInlineBlock:
case cssom::KeywordValue::kLeft:
case cssom::KeywordValue::kLineThrough:
case cssom::KeywordValue::kMiddle:
case cssom::KeywordValue::kMonoscopic:
case cssom::KeywordValue::kNone:
case cssom::KeywordValue::kNoRepeat:
case cssom::KeywordValue::kNormal:
case cssom::KeywordValue::kNoWrap:
case cssom::KeywordValue::kPre:
case cssom::KeywordValue::kPreLine:
case cssom::KeywordValue::kPreWrap:
case cssom::KeywordValue::kRelative:
case cssom::KeywordValue::kRepeat:
case cssom::KeywordValue::kReverse:
case cssom::KeywordValue::kRight:
case cssom::KeywordValue::kSolid:
case cssom::KeywordValue::kStart:
case cssom::KeywordValue::kStatic:
case cssom::KeywordValue::kStereoscopicLeftRight:
case cssom::KeywordValue::kStereoscopicTopBottom:
case cssom::KeywordValue::kTop:
case cssom::KeywordValue::kUppercase:
case cssom::KeywordValue::kVisible:
default:
NOTREACHED();
}
}
void FontFaceProvider::VisitLocalSrc(cssom::LocalSrcValue* local_src) {
style_set_entry_.sources.push_back(FontFaceSource(local_src->value()));
}
void FontFaceProvider::VisitPropertyList(
cssom::PropertyListValue* property_list) {
for (size_t i = 0; i < property_list->value().size(); ++i) {
property_list->value()[i]->Accept(this);
}
}
void FontFaceProvider::VisitString(cssom::StringValue* string) {
font_family_ = string->value();
}
void FontFaceProvider::VisitUnicodeRange(
cssom::UnicodeRangeValue* /*unicode_range*/) {
NOTIMPLEMENTED()
<< "FontFaceProvider::UnicodeRange support not implemented yet.";
}
// Check for a supported format. If no format hints are supplied, then the user
// agent should download the font resource.
// https://www.w3.org/TR/css3-fonts/#descdef-src
void FontFaceProvider::VisitUrlSrc(cssom::UrlSrcValue* url_src) {
if (url_src->format().empty() || url_src->format() == "truetype" ||
url_src->format() == "opentype" || url_src->format() == "woff") {
url_src->url()->Accept(this);
}
}
// The URL may be relative, in which case it is resolved relative to the
// location of the style sheet containing the @font-face rule.
// https://www.w3.org/TR/css3-fonts/#descdef-src
void FontFaceProvider::VisitURL(cssom::URLValue* url) {
GURL gurl = url->is_absolute() ? GURL(url->value()) : url->Resolve(base_url_);
if (gurl.is_valid()) {
style_set_entry_.sources.push_back(FontFaceSource(gurl));
}
}
// The font family and src are required for the @font-face rule to be valid.
// https://www.w3.org/TR/css3-fonts/#descdef-font-family
// https://www.w3.org/TR/css3-fonts/#descdef-src
bool FontFaceProvider::IsFontFaceValid() const {
return !font_family_.empty() && !style_set_entry_.sources.empty();
}
} // namespace
//////////////////////////////////////////////////////////////////////////
// FontFaceUpdater
//////////////////////////////////////////////////////////////////////////
FontFaceUpdater::FontFaceUpdater(const GURL& document_base_url,
FontCache* const cache)
: document_base_url_(document_base_url),
cache_(cache),
font_face_map_(new FontCache::FontFaceMap()) {}
FontFaceUpdater::~FontFaceUpdater() {
cache_->SetFontFaceMap(font_face_map_.Pass());
}
void FontFaceUpdater::ProcessCSSStyleSheet(
const scoped_refptr<cssom::CSSStyleSheet>& style_sheet) {
if (style_sheet && style_sheet->css_rules()) {
style_sheet->css_rules()->Accept(this);
}
}
void FontFaceUpdater::ProcessStyleSheetList(
const scoped_refptr<cssom::StyleSheetList>& style_sheet_list) {
for (unsigned int style_sheet_index = 0;
style_sheet_index < style_sheet_list->length(); ++style_sheet_index) {
scoped_refptr<cssom::CSSStyleSheet> style_sheet =
style_sheet_list->Item(style_sheet_index)->AsCSSStyleSheet();
// ProcessCSSStyleSheet handles a NULL CSSStyleSheet.
ProcessCSSStyleSheet(style_sheet);
}
}
void FontFaceUpdater::VisitCSSFontFaceRule(
cssom::CSSFontFaceRule* css_font_face_rule) {
// The URL may be relative, in which case it is resolved relative to the
// location of the style sheet containing the @font-face rule.
// https://www.w3.org/TR/css3-fonts/#descdef-src
// In the case where the style sheet URL is empty, treat the document base URL
// as the style sheet's URL. While the spec is not clear on the expected
// behavior for this case, this mimics WebKit's functionality.
const GURL& style_sheet_url =
css_font_face_rule->parent_style_sheet()->LocationUrl();
const GURL& base_url =
!style_sheet_url.is_empty() ? style_sheet_url : document_base_url_;
FontFaceProvider font_face_provider(base_url);
if (css_font_face_rule->data()->family()) {
css_font_face_rule->data()->family()->Accept(&font_face_provider);
}
if (css_font_face_rule->data()->src()) {
css_font_face_rule->data()->src()->Accept(&font_face_provider);
}
if (css_font_face_rule->data()->style()) {
css_font_face_rule->data()->style()->Accept(&font_face_provider);
}
if (css_font_face_rule->data()->weight()) {
css_font_face_rule->data()->weight()->Accept(&font_face_provider);
}
if (font_face_provider.IsFontFaceValid()) {
(*font_face_map_)[font_face_provider.font_family()].AddEntry(
font_face_provider.style_set_entry());
}
}
} // namespace dom
} // namespace cobalt