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