1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde (at) carewolf.com) 4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit (at) nickshanks.com) 5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. 6 * Copyright (C) 2007 Alexey Proskuryakov <ap (at) webkit.org> 7 * Copyright (C) 2007, 2008 Eric Seidel <eric (at) webkit.org> 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. 10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. 11 * Copyright (C) 2012 Google Inc. All rights reserved. 12 * 13 * This library is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU Library General Public 15 * License as published by the Free Software Foundation; either 16 * version 2 of the License, or (at your option) any later version. 17 * 18 * This library is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 * Library General Public License for more details. 22 * 23 * You should have received a copy of the GNU Library General Public License 24 * along with this library; see the file COPYING.LIB. If not, write to 25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 26 * Boston, MA 02110-1301, USA. 27 */ 28 29 #include "config.h" 30 #include "core/css/resolver/FilterOperationResolver.h" 31 32 #include "core/css/CSSFilterValue.h" 33 #include "core/css/CSSMixFunctionValue.h" 34 #include "core/css/CSSParser.h" 35 #include "core/css/CSSPrimitiveValueMappings.h" 36 #include "core/css/CSSShaderValue.h" 37 #include "core/css/CSSShadowValue.h" 38 #include "core/css/resolver/TransformBuilder.h" 39 #include "core/rendering/style/StyleCustomFilterProgram.h" 40 #include "core/rendering/style/StyleShader.h" 41 #include "core/rendering/svg/ReferenceFilterBuilder.h" 42 #include "core/svg/SVGURIReference.h" 43 #include "platform/graphics/filters/custom/CustomFilterArrayParameter.h" 44 #include "platform/graphics/filters/custom/CustomFilterConstants.h" 45 #include "platform/graphics/filters/custom/CustomFilterNumberParameter.h" 46 #include "platform/graphics/filters/custom/CustomFilterOperation.h" 47 #include "platform/graphics/filters/custom/CustomFilterParameter.h" 48 #include "platform/graphics/filters/custom/CustomFilterProgramInfo.h" 49 #include "platform/graphics/filters/custom/CustomFilterTransformParameter.h" 50 51 namespace WebCore { 52 53 static FilterOperation::OperationType filterOperationForType(CSSFilterValue::FilterOperationType type) 54 { 55 switch (type) { 56 case CSSFilterValue::ReferenceFilterOperation: 57 return FilterOperation::REFERENCE; 58 case CSSFilterValue::GrayscaleFilterOperation: 59 return FilterOperation::GRAYSCALE; 60 case CSSFilterValue::SepiaFilterOperation: 61 return FilterOperation::SEPIA; 62 case CSSFilterValue::SaturateFilterOperation: 63 return FilterOperation::SATURATE; 64 case CSSFilterValue::HueRotateFilterOperation: 65 return FilterOperation::HUE_ROTATE; 66 case CSSFilterValue::InvertFilterOperation: 67 return FilterOperation::INVERT; 68 case CSSFilterValue::OpacityFilterOperation: 69 return FilterOperation::OPACITY; 70 case CSSFilterValue::BrightnessFilterOperation: 71 return FilterOperation::BRIGHTNESS; 72 case CSSFilterValue::ContrastFilterOperation: 73 return FilterOperation::CONTRAST; 74 case CSSFilterValue::BlurFilterOperation: 75 return FilterOperation::BLUR; 76 case CSSFilterValue::DropShadowFilterOperation: 77 return FilterOperation::DROP_SHADOW; 78 case CSSFilterValue::CustomFilterOperation: 79 return FilterOperation::CUSTOM; 80 case CSSFilterValue::UnknownFilterOperation: 81 return FilterOperation::NONE; 82 } 83 return FilterOperation::NONE; 84 } 85 86 static StyleShader* styleShader(CSSValue* value) 87 { 88 if (value->isShaderValue()) 89 return toCSSShaderValue(value)->cachedOrPendingShader(); 90 return 0; 91 } 92 93 static PassRefPtr<CustomFilterParameter> parseCustomFilterArrayParameter(const String& name, CSSValueList* values) 94 { 95 RefPtr<CustomFilterArrayParameter> arrayParameter = CustomFilterArrayParameter::create(name); 96 for (unsigned i = 0, length = values->length(); i < length; ++i) { 97 CSSValue* value = values->itemWithoutBoundsCheck(i); 98 if (!value->isPrimitiveValue()) 99 return 0; 100 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 101 if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_NUMBER) 102 return 0; 103 arrayParameter->addValue(primitiveValue->getDoubleValue()); 104 } 105 return arrayParameter.release(); 106 } 107 108 static PassRefPtr<CustomFilterParameter> parseCustomFilterNumberParameter(const String& name, CSSValueList* values) 109 { 110 RefPtr<CustomFilterNumberParameter> numberParameter = CustomFilterNumberParameter::create(name); 111 for (unsigned i = 0; i < values->length(); ++i) { 112 CSSValue* value = values->itemWithoutBoundsCheck(i); 113 if (!value->isPrimitiveValue()) 114 return 0; 115 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 116 if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_NUMBER) 117 return 0; 118 numberParameter->addValue(primitiveValue->getDoubleValue()); 119 } 120 return numberParameter.release(); 121 } 122 123 static PassRefPtr<CustomFilterParameter> parseCustomFilterTransformParameter(const String& name, CSSValueList* values, StyleResolverState& state) 124 { 125 RefPtr<CustomFilterTransformParameter> transformParameter = CustomFilterTransformParameter::create(name); 126 TransformOperations operations; 127 TransformBuilder::createTransformOperations(values, state.cssToLengthConversionData(), operations); 128 transformParameter->setOperations(operations); 129 return transformParameter.release(); 130 } 131 132 static PassRefPtr<CustomFilterParameter> parseCustomFilterParameter(const String& name, CSSValue* parameterValue, StyleResolverState& state) 133 { 134 // FIXME: Implement other parameters types parsing. 135 // booleans: https://bugs.webkit.org/show_bug.cgi?id=76438 136 // textures: https://bugs.webkit.org/show_bug.cgi?id=71442 137 // mat2, mat3, mat4: https://bugs.webkit.org/show_bug.cgi?id=71444 138 // Number parameters are wrapped inside a CSSValueList and all 139 // the other functions values inherit from CSSValueList. 140 if (!parameterValue->isValueList()) 141 return 0; 142 143 CSSValueList* values = toCSSValueList(parameterValue); 144 if (!values->length()) 145 return 0; 146 147 if (parameterValue->isArrayFunctionValue()) 148 return parseCustomFilterArrayParameter(name, values); 149 150 // If the first value of the list is a transform function, 151 // then we could safely assume that all the remaining items 152 // are transforms. parseCustomFilterTransformParameter will 153 // return 0 if that assumption is incorrect. 154 if (values->itemWithoutBoundsCheck(0)->isTransformValue()) 155 return parseCustomFilterTransformParameter(name, values, state); 156 157 // We can have only arrays of booleans or numbers, so use the first value to choose between those two. 158 // We need up to 4 values (all booleans or all numbers). 159 if (!values->itemWithoutBoundsCheck(0)->isPrimitiveValue() || values->length() > 4) 160 return 0; 161 162 CSSPrimitiveValue* firstPrimitiveValue = toCSSPrimitiveValue(values->itemWithoutBoundsCheck(0)); 163 if (firstPrimitiveValue->primitiveType() == CSSPrimitiveValue::CSS_NUMBER) 164 return parseCustomFilterNumberParameter(name, values); 165 166 // FIXME: Implement the boolean array parameter here. 167 // https://bugs.webkit.org/show_bug.cgi?id=76438 168 169 return 0; 170 } 171 172 static bool parseCustomFilterParameterList(CSSValue* parametersValue, CustomFilterParameterList& parameterList, StyleResolverState& state) 173 { 174 HashSet<String> knownParameterNames; 175 CSSValueListIterator parameterIterator(parametersValue); 176 for (; parameterIterator.hasMore(); parameterIterator.advance()) { 177 if (!parameterIterator.value()->isValueList()) 178 return false; 179 CSSValueListIterator iterator(parameterIterator.value()); 180 if (!iterator.isPrimitiveValue()) 181 return false; 182 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(iterator.value()); 183 if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_STRING) 184 return false; 185 186 String name = primitiveValue->getStringValue(); 187 // Do not allow duplicate parameter names. 188 if (!knownParameterNames.add(name).isNewEntry) 189 return false; 190 191 iterator.advance(); 192 193 if (!iterator.hasMore()) 194 return false; 195 196 RefPtr<CustomFilterParameter> parameter = parseCustomFilterParameter(name, iterator.value(), state); 197 if (!parameter) 198 return false; 199 parameterList.append(parameter.release()); 200 } 201 202 // Make sure we sort the parameters before passing them down to the CustomFilterOperation. 203 parameterList.sortParametersByName(); 204 205 return true; 206 } 207 208 static PassRefPtr<CustomFilterOperation> createCustomFilterOperationWithAtRuleReferenceSyntax(CSSFilterValue*) 209 { 210 // FIXME: Implement style resolution for the custom filter at-rule reference syntax. 211 return 0; 212 } 213 214 static PassRefPtr<CustomFilterProgram> createCustomFilterProgram(CSSShaderValue* vertexShader, CSSShaderValue* fragmentShader, 215 CustomFilterProgramType programType, const CustomFilterProgramMixSettings& mixSettings, CustomFilterMeshType meshType, 216 StyleResolverState& state) 217 { 218 ResourceFetcher* fetcher = state.document().fetcher(); 219 KURL vertexShaderURL = vertexShader ? vertexShader->completeURL(fetcher) : KURL(); 220 KURL fragmentShaderURL = fragmentShader ? fragmentShader->completeURL(fetcher) : KURL(); 221 // We re-resolve the custom filter style after the shaders are loaded. 222 // We always create a StyleCustomFilterProgram here, and later replace it with a program from the StyleCustomFilterProgramCache, if available. 223 StyleShader* styleVertexShader = vertexShader ? styleShader(vertexShader) : 0; 224 StyleShader* styleFragmentShader = fragmentShader ? styleShader(fragmentShader) : 0; 225 RefPtr<StyleCustomFilterProgram> program = StyleCustomFilterProgram::create(vertexShaderURL, styleVertexShader, 226 fragmentShaderURL, styleFragmentShader, programType, mixSettings, meshType); 227 state.elementStyleResources().setHasNewCustomFilterProgram(true); 228 return program.release(); 229 } 230 231 static PassRefPtr<CustomFilterOperation> createCustomFilterOperationWithInlineSyntax(CSSFilterValue* filterValue, StyleResolverState& state) 232 { 233 CSSValue* shadersValue = filterValue->itemWithoutBoundsCheck(0); 234 ASSERT_WITH_SECURITY_IMPLICATION(shadersValue->isValueList()); 235 CSSValueList* shadersList = toCSSValueList(shadersValue); 236 237 unsigned shadersListLength = shadersList->length(); 238 ASSERT(shadersListLength); 239 240 CSSShaderValue* vertexShader = 0; 241 CSSShaderValue* fragmentShader = 0; 242 243 if (shadersList->itemWithoutBoundsCheck(0)->isShaderValue()) 244 vertexShader = toCSSShaderValue(shadersList->itemWithoutBoundsCheck(0)); 245 246 CustomFilterProgramType programType = ProgramTypeBlendsElementTexture; 247 CustomFilterProgramMixSettings mixSettings; 248 249 if (shadersListLength > 1) { 250 CSSValue* fragmentShaderOrMixFunction = shadersList->itemWithoutBoundsCheck(1); 251 if (fragmentShaderOrMixFunction->isMixFunctionValue()) { 252 CSSMixFunctionValue* mixFunction = toCSSMixFunctionValue(fragmentShaderOrMixFunction); 253 CSSValueListIterator iterator(mixFunction); 254 255 ASSERT(mixFunction->length()); 256 if (iterator.value()->isShaderValue()) 257 fragmentShader = toCSSShaderValue(iterator.value()); 258 259 iterator.advance(); 260 261 ASSERT(mixFunction->length() <= 3); 262 while (iterator.hasMore()) { 263 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(iterator.value()); 264 if (CSSParser::isBlendMode(primitiveValue->getValueID())) 265 mixSettings.blendMode = *primitiveValue; 266 else if (CSSParser::isCompositeOperator(primitiveValue->getValueID())) 267 mixSettings.compositeOperator = *primitiveValue; 268 else 269 ASSERT_NOT_REACHED(); 270 iterator.advance(); 271 } 272 } else { 273 programType = ProgramTypeNoElementTexture; 274 if (fragmentShaderOrMixFunction->isShaderValue()) 275 fragmentShader = toCSSShaderValue(fragmentShaderOrMixFunction); 276 } 277 } 278 279 if (!vertexShader && !fragmentShader) 280 return 0; 281 282 unsigned meshRows = 1; 283 unsigned meshColumns = 1; 284 CustomFilterMeshType meshType = MeshTypeAttached; 285 286 CSSValue* parametersValue = 0; 287 288 if (filterValue->length() > 1) { 289 CSSValueListIterator iterator(filterValue->itemWithoutBoundsCheck(1)); 290 291 // The second value might be the mesh box or the list of parameters: 292 // If it starts with a number or any of the mesh-box identifiers it is 293 // the mesh-box list, if not it means it is the parameters list. 294 295 if (iterator.hasMore() && iterator.isPrimitiveValue()) { 296 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(iterator.value()); 297 if (primitiveValue->isNumber()) { 298 // If only one integer value is specified, it will set both 299 // the rows and the columns. 300 meshColumns = meshRows = primitiveValue->getIntValue(); 301 iterator.advance(); 302 303 // Try to match another number for the rows. 304 if (iterator.hasMore() && iterator.isPrimitiveValue()) { 305 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(iterator.value()); 306 if (primitiveValue->isNumber()) { 307 meshRows = primitiveValue->getIntValue(); 308 iterator.advance(); 309 } 310 } 311 } 312 } 313 314 if (iterator.hasMore() && iterator.isPrimitiveValue()) { 315 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(iterator.value()); 316 if (primitiveValue->getValueID() == CSSValueDetached) { 317 meshType = MeshTypeDetached; 318 iterator.advance(); 319 } 320 } 321 322 if (!iterator.index()) { 323 // If no value was consumed from the mesh value, then it is just a parameter list, meaning that we end up 324 // having just two CSSListValues: list of shaders and list of parameters. 325 ASSERT(filterValue->length() == 2); 326 parametersValue = filterValue->itemWithoutBoundsCheck(1); 327 } 328 } 329 330 if (filterValue->length() > 2 && !parametersValue) 331 parametersValue = filterValue->itemWithoutBoundsCheck(2); 332 333 CustomFilterParameterList parameterList; 334 if (parametersValue && !parseCustomFilterParameterList(parametersValue, parameterList, state)) 335 return 0; 336 337 RefPtr<CustomFilterProgram> program = createCustomFilterProgram(vertexShader, fragmentShader, programType, mixSettings, meshType, state); 338 return CustomFilterOperation::create(program.release(), parameterList, meshRows, meshColumns, meshType); 339 } 340 341 static PassRefPtr<CustomFilterOperation> createCustomFilterOperation(CSSFilterValue* filterValue, StyleResolverState& state) 342 { 343 ASSERT(filterValue->length()); 344 bool isAtRuleReferenceSyntax = filterValue->itemWithoutBoundsCheck(0)->isPrimitiveValue(); 345 return isAtRuleReferenceSyntax ? createCustomFilterOperationWithAtRuleReferenceSyntax(filterValue) : createCustomFilterOperationWithInlineSyntax(filterValue, state); 346 } 347 348 349 bool FilterOperationResolver::createFilterOperations(CSSValue* inValue, const CSSToLengthConversionData& unadjustedConversionData, FilterOperations& outOperations, StyleResolverState& state) 350 { 351 ASSERT(outOperations.isEmpty()); 352 353 if (!inValue) 354 return false; 355 356 if (inValue->isPrimitiveValue()) { 357 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(inValue); 358 if (primitiveValue->getValueID() == CSSValueNone) 359 return true; 360 } 361 362 if (!inValue->isValueList()) 363 return false; 364 365 float zoomFactor = unadjustedConversionData.zoom() * state.elementStyleResources().deviceScaleFactor(); 366 const CSSToLengthConversionData& conversionData = unadjustedConversionData.copyWithAdjustedZoom(zoomFactor); 367 FilterOperations operations; 368 for (CSSValueListIterator i = inValue; i.hasMore(); i.advance()) { 369 CSSValue* currValue = i.value(); 370 if (!currValue->isFilterValue()) 371 continue; 372 373 CSSFilterValue* filterValue = toCSSFilterValue(i.value()); 374 FilterOperation::OperationType operationType = filterOperationForType(filterValue->operationType()); 375 376 if (operationType == FilterOperation::VALIDATED_CUSTOM) { 377 // ValidatedCustomFilterOperation is not supposed to end up in the RenderStyle. 378 ASSERT_NOT_REACHED(); 379 continue; 380 } 381 if (operationType == FilterOperation::CUSTOM) { 382 RefPtr<CustomFilterOperation> operation = createCustomFilterOperation(filterValue, state); 383 if (!operation) 384 return false; 385 386 operations.operations().append(operation); 387 continue; 388 } 389 if (operationType == FilterOperation::REFERENCE) { 390 if (filterValue->length() != 1) 391 continue; 392 CSSValue* argument = filterValue->itemWithoutBoundsCheck(0); 393 394 if (!argument->isSVGDocumentValue()) 395 continue; 396 397 CSSSVGDocumentValue* svgDocumentValue = toCSSSVGDocumentValue(argument); 398 KURL url = state.document().completeURL(svgDocumentValue->url()); 399 400 RefPtr<ReferenceFilterOperation> operation = ReferenceFilterOperation::create(svgDocumentValue->url(), url.fragmentIdentifier()); 401 if (SVGURIReference::isExternalURIReference(svgDocumentValue->url(), state.document())) { 402 if (!svgDocumentValue->loadRequested()) 403 state.elementStyleResources().addPendingSVGDocument(operation.get(), svgDocumentValue); 404 else if (svgDocumentValue->cachedSVGDocument()) 405 ReferenceFilterBuilder::setDocumentResourceReference(operation.get(), adoptPtr(new DocumentResourceReference(svgDocumentValue->cachedSVGDocument()))); 406 } 407 operations.operations().append(operation); 408 continue; 409 } 410 411 // Check that all parameters are primitive values, with the 412 // exception of drop shadow which has a CSSShadowValue parameter. 413 if (operationType != FilterOperation::DROP_SHADOW) { 414 bool haveNonPrimitiveValue = false; 415 for (unsigned j = 0; j < filterValue->length(); ++j) { 416 if (!filterValue->itemWithoutBoundsCheck(j)->isPrimitiveValue()) { 417 haveNonPrimitiveValue = true; 418 break; 419 } 420 } 421 if (haveNonPrimitiveValue) 422 continue; 423 } 424 425 CSSPrimitiveValue* firstValue = filterValue->length() && filterValue->itemWithoutBoundsCheck(0)->isPrimitiveValue() ? toCSSPrimitiveValue(filterValue->itemWithoutBoundsCheck(0)) : 0; 426 switch (filterValue->operationType()) { 427 case CSSFilterValue::GrayscaleFilterOperation: 428 case CSSFilterValue::SepiaFilterOperation: 429 case CSSFilterValue::SaturateFilterOperation: { 430 double amount = 1; 431 if (filterValue->length() == 1) { 432 amount = firstValue->getDoubleValue(); 433 if (firstValue->isPercentage()) 434 amount /= 100; 435 } 436 437 operations.operations().append(BasicColorMatrixFilterOperation::create(amount, operationType)); 438 break; 439 } 440 case CSSFilterValue::HueRotateFilterOperation: { 441 double angle = 0; 442 if (filterValue->length() == 1) 443 angle = firstValue->computeDegrees(); 444 445 operations.operations().append(BasicColorMatrixFilterOperation::create(angle, operationType)); 446 break; 447 } 448 case CSSFilterValue::InvertFilterOperation: 449 case CSSFilterValue::BrightnessFilterOperation: 450 case CSSFilterValue::ContrastFilterOperation: 451 case CSSFilterValue::OpacityFilterOperation: { 452 double amount = (filterValue->operationType() == CSSFilterValue::BrightnessFilterOperation) ? 0 : 1; 453 if (filterValue->length() == 1) { 454 amount = firstValue->getDoubleValue(); 455 if (firstValue->isPercentage()) 456 amount /= 100; 457 } 458 459 operations.operations().append(BasicComponentTransferFilterOperation::create(amount, operationType)); 460 break; 461 } 462 case CSSFilterValue::BlurFilterOperation: { 463 Length stdDeviation = Length(0, Fixed); 464 if (filterValue->length() >= 1) 465 stdDeviation = firstValue->convertToLength<FixedConversion | PercentConversion>(conversionData); 466 if (stdDeviation.isUndefined()) 467 return false; 468 469 operations.operations().append(BlurFilterOperation::create(stdDeviation)); 470 break; 471 } 472 case CSSFilterValue::DropShadowFilterOperation: { 473 if (filterValue->length() != 1) 474 return false; 475 476 CSSValue* cssValue = filterValue->itemWithoutBoundsCheck(0); 477 if (!cssValue->isShadowValue()) 478 continue; 479 480 CSSShadowValue* item = toCSSShadowValue(cssValue); 481 IntPoint location(item->x->computeLength<int>(conversionData), item->y->computeLength<int>(conversionData)); 482 int blur = item->blur ? item->blur->computeLength<int>(conversionData) : 0; 483 Color shadowColor; 484 if (item->color) 485 shadowColor = state.document().textLinkColors().colorFromPrimitiveValue(item->color.get(), state.style()->color()); 486 487 operations.operations().append(DropShadowFilterOperation::create(location, blur, shadowColor.isValid() ? shadowColor : Color::transparent)); 488 break; 489 } 490 case CSSFilterValue::UnknownFilterOperation: 491 default: 492 ASSERT_NOT_REACHED(); 493 break; 494 } 495 } 496 497 outOperations = operations; 498 return true; 499 } 500 501 } // namespace WebCore 502