1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc.
6 * All rights reserved.
7 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
8 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
9 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved.
10 * (http://www.torchmobile.com/)
11 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
12 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
13 * Copyright (C) 2012 Google Inc. All rights reserved.
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Library General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Library General Public License for more details.
24 *
25 * You should have received a copy of the GNU Library General Public License
26 * along with this library; see the file COPYING.LIB. If not, write to
27 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 * Boston, MA 02110-1301, USA.
29 */
30
31 #include "third_party/blink/renderer/core/css/resolver/filter_operation_resolver.h"
32
33 #include "third_party/blink/renderer/core/css/css_function_value.h"
34 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
35 #include "third_party/blink/renderer/core/css/css_uri_value.h"
36 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
37 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
38 #include "third_party/blink/renderer/core/frame/web_feature.h"
39 #include "third_party/blink/renderer/core/style/computed_style.h"
40 #include "third_party/blink/renderer/platform/heap/heap.h"
41 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
42
43 namespace blink {
44
45 static const float kOffScreenCanvasEmFontSize = 16.0;
46 static const float kOffScreenCanvasRemFontSize = 16.0;
47
FilterOperationForType(CSSValueID type)48 FilterOperation::OperationType FilterOperationResolver::FilterOperationForType(
49 CSSValueID type) {
50 switch (type) {
51 case CSSValueID::kGrayscale:
52 return FilterOperation::GRAYSCALE;
53 case CSSValueID::kSepia:
54 return FilterOperation::SEPIA;
55 case CSSValueID::kSaturate:
56 return FilterOperation::SATURATE;
57 case CSSValueID::kHueRotate:
58 return FilterOperation::HUE_ROTATE;
59 case CSSValueID::kInvert:
60 return FilterOperation::INVERT;
61 case CSSValueID::kOpacity:
62 return FilterOperation::OPACITY;
63 case CSSValueID::kBrightness:
64 return FilterOperation::BRIGHTNESS;
65 case CSSValueID::kContrast:
66 return FilterOperation::CONTRAST;
67 case CSSValueID::kBlur:
68 return FilterOperation::BLUR;
69 case CSSValueID::kDropShadow:
70 return FilterOperation::DROP_SHADOW;
71 default:
72 NOTREACHED();
73 // FIXME: We shouldn't have a type None since we never create them
74 return FilterOperation::NONE;
75 }
76 }
77
CountFilterUse(FilterOperation::OperationType operation_type,const Document & document)78 static void CountFilterUse(FilterOperation::OperationType operation_type,
79 const Document& document) {
80 // This variable is always reassigned, but MSVC thinks it might be left
81 // uninitialized.
82 WebFeature feature = WebFeature::kNumberOfFeatures;
83 switch (operation_type) {
84 case FilterOperation::NONE:
85 case FilterOperation::BOX_REFLECT:
86 NOTREACHED();
87 return;
88 case FilterOperation::REFERENCE:
89 feature = WebFeature::kCSSFilterReference;
90 break;
91 case FilterOperation::GRAYSCALE:
92 feature = WebFeature::kCSSFilterGrayscale;
93 break;
94 case FilterOperation::SEPIA:
95 feature = WebFeature::kCSSFilterSepia;
96 break;
97 case FilterOperation::SATURATE:
98 feature = WebFeature::kCSSFilterSaturate;
99 break;
100 case FilterOperation::HUE_ROTATE:
101 feature = WebFeature::kCSSFilterHueRotate;
102 break;
103 case FilterOperation::INVERT:
104 feature = WebFeature::kCSSFilterInvert;
105 break;
106 case FilterOperation::OPACITY:
107 feature = WebFeature::kCSSFilterOpacity;
108 break;
109 case FilterOperation::BRIGHTNESS:
110 feature = WebFeature::kCSSFilterBrightness;
111 break;
112 case FilterOperation::CONTRAST:
113 feature = WebFeature::kCSSFilterContrast;
114 break;
115 case FilterOperation::BLUR:
116 feature = WebFeature::kCSSFilterBlur;
117 break;
118 case FilterOperation::DROP_SHADOW:
119 feature = WebFeature::kCSSFilterDropShadow;
120 break;
121 };
122 document.CountUse(feature);
123 }
124
ResolveNumericArgumentForFunction(const CSSFunctionValue & filter)125 double FilterOperationResolver::ResolveNumericArgumentForFunction(
126 const CSSFunctionValue& filter) {
127 switch (filter.FunctionType()) {
128 case CSSValueID::kGrayscale:
129 case CSSValueID::kSepia:
130 case CSSValueID::kSaturate:
131 case CSSValueID::kInvert:
132 case CSSValueID::kBrightness:
133 case CSSValueID::kContrast:
134 case CSSValueID::kOpacity: {
135 double amount = 1;
136 if (filter.length() == 1) {
137 const CSSPrimitiveValue& value = To<CSSPrimitiveValue>(filter.Item(0));
138 amount = value.GetDoubleValue();
139 if (value.IsPercentage())
140 amount /= 100;
141 }
142 return amount;
143 }
144 case CSSValueID::kHueRotate: {
145 double angle = 0;
146 if (filter.length() == 1) {
147 const CSSPrimitiveValue& value = To<CSSPrimitiveValue>(filter.Item(0));
148 angle = value.ComputeDegrees();
149 }
150 return angle;
151 }
152 default:
153 return 0;
154 }
155 }
156
CreateFilterOperations(StyleResolverState & state,const CSSValue & in_value)157 FilterOperations FilterOperationResolver::CreateFilterOperations(
158 StyleResolverState& state,
159 const CSSValue& in_value) {
160 FilterOperations operations;
161
162 if (auto* in_identifier_value = DynamicTo<CSSIdentifierValue>(in_value)) {
163 DCHECK_EQ(in_identifier_value->GetValueID(), CSSValueID::kNone);
164 return operations;
165 }
166
167 const CSSToLengthConversionData& conversion_data =
168 state.CssToLengthConversionData();
169
170 for (auto& curr_value : To<CSSValueList>(in_value)) {
171 if (const auto* url_value =
172 DynamicTo<cssvalue::CSSURIValue>(curr_value.Get())) {
173 CountFilterUse(FilterOperation::REFERENCE, state.GetDocument());
174
175 SVGResource* resource =
176 state.GetElementStyleResources().GetSVGResourceFromValue(
177 state.GetTreeScope(), *url_value,
178 ElementStyleResources::kAllowExternalResource);
179 operations.Operations().push_back(
180 MakeGarbageCollected<ReferenceFilterOperation>(
181 url_value->ValueForSerialization(), resource));
182 continue;
183 }
184
185 const auto* filter_value = To<CSSFunctionValue>(curr_value.Get());
186 FilterOperation::OperationType operation_type =
187 FilterOperationForType(filter_value->FunctionType());
188 CountFilterUse(operation_type, state.GetDocument());
189 DCHECK_LE(filter_value->length(), 1u);
190 switch (filter_value->FunctionType()) {
191 case CSSValueID::kGrayscale:
192 case CSSValueID::kSepia:
193 case CSSValueID::kSaturate:
194 case CSSValueID::kHueRotate: {
195 operations.Operations().push_back(
196 MakeGarbageCollected<BasicColorMatrixFilterOperation>(
197 ResolveNumericArgumentForFunction(*filter_value),
198 operation_type));
199 break;
200 }
201 case CSSValueID::kInvert:
202 case CSSValueID::kBrightness:
203 case CSSValueID::kContrast:
204 case CSSValueID::kOpacity: {
205 operations.Operations().push_back(
206 MakeGarbageCollected<BasicComponentTransferFilterOperation>(
207 ResolveNumericArgumentForFunction(*filter_value),
208 operation_type));
209 break;
210 }
211 case CSSValueID::kBlur: {
212 Length std_deviation = Length::Fixed(0);
213 if (filter_value->length() >= 1) {
214 const CSSPrimitiveValue* first_value =
215 DynamicTo<CSSPrimitiveValue>(filter_value->Item(0));
216 std_deviation = first_value->ConvertToLength(conversion_data);
217 }
218 operations.Operations().push_back(
219 MakeGarbageCollected<BlurFilterOperation>(std_deviation));
220 break;
221 }
222 case CSSValueID::kDropShadow: {
223 ShadowData shadow = StyleBuilderConverter::ConvertShadow(
224 conversion_data, &state, filter_value->Item(0));
225 // TODO(fs): Resolve 'currentcolor' when constructing the filter chain.
226 if (shadow.GetColor().IsCurrentColor()) {
227 shadow.OverrideColor(state.Style()->GetCurrentColor());
228 }
229 operations.Operations().push_back(
230 MakeGarbageCollected<DropShadowFilterOperation>(shadow));
231 break;
232 }
233 default:
234 NOTREACHED();
235 break;
236 }
237 }
238
239 return operations;
240 }
241
CreateOffscreenFilterOperations(const CSSValue & in_value,const Font & font)242 FilterOperations FilterOperationResolver::CreateOffscreenFilterOperations(
243 const CSSValue& in_value,
244 const Font& font) {
245 FilterOperations operations;
246
247 if (auto* in_identifier_value = DynamicTo<CSSIdentifierValue>(in_value)) {
248 DCHECK_EQ(in_identifier_value->GetValueID(), CSSValueID::kNone);
249 return operations;
250 }
251
252 // TODO(layout-dev): Should document zoom factor apply for offscreen canvas?
253 float zoom = 1.0f;
254 CSSToLengthConversionData::FontSizes font_sizes(
255 kOffScreenCanvasEmFontSize, kOffScreenCanvasRemFontSize, &font, zoom);
256 CSSToLengthConversionData::ViewportSize viewport_size(0, 0);
257 CSSToLengthConversionData conversion_data(nullptr, // ComputedStyle
258 font_sizes, viewport_size,
259 1); // zoom
260
261 for (auto& curr_value : To<CSSValueList>(in_value)) {
262 if (curr_value->IsURIValue())
263 continue;
264
265 const auto* filter_value = To<CSSFunctionValue>(curr_value.Get());
266 FilterOperation::OperationType operation_type =
267 FilterOperationForType(filter_value->FunctionType());
268 // TODO(fserb): Take an ExecutionContext argument to this function,
269 // so we can have workers using UseCounter as well.
270 // countFilterUse(operationType, state.document());
271 DCHECK_LE(filter_value->length(), 1u);
272 switch (filter_value->FunctionType()) {
273 case CSSValueID::kGrayscale:
274 case CSSValueID::kSepia:
275 case CSSValueID::kSaturate:
276 case CSSValueID::kHueRotate: {
277 operations.Operations().push_back(
278 MakeGarbageCollected<BasicColorMatrixFilterOperation>(
279 ResolveNumericArgumentForFunction(*filter_value),
280 operation_type));
281 break;
282 }
283 case CSSValueID::kInvert:
284 case CSSValueID::kBrightness:
285 case CSSValueID::kContrast:
286 case CSSValueID::kOpacity: {
287 operations.Operations().push_back(
288 MakeGarbageCollected<BasicComponentTransferFilterOperation>(
289 ResolveNumericArgumentForFunction(*filter_value),
290 operation_type));
291 break;
292 }
293 case CSSValueID::kBlur: {
294 Length std_deviation = Length::Fixed(0);
295 if (filter_value->length() >= 1) {
296 const CSSPrimitiveValue* first_value =
297 DynamicTo<CSSPrimitiveValue>(filter_value->Item(0));
298 std_deviation = first_value->ConvertToLength(conversion_data);
299 }
300 operations.Operations().push_back(
301 MakeGarbageCollected<BlurFilterOperation>(std_deviation));
302 break;
303 }
304 case CSSValueID::kDropShadow: {
305 ShadowData shadow = StyleBuilderConverter::ConvertShadow(
306 conversion_data, nullptr, filter_value->Item(0));
307 // For offscreen canvas, the default color is always black.
308 if (shadow.GetColor().IsCurrentColor()) {
309 shadow.OverrideColor(Color::kBlack);
310 }
311 operations.Operations().push_back(
312 MakeGarbageCollected<DropShadowFilterOperation>(shadow));
313 break;
314 }
315 default:
316 NOTREACHED();
317 break;
318 }
319 }
320 return operations;
321 }
322
323 } // namespace blink
324