Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 203 | 203 |
| 204 } // namespace | 204 } // namespace |
| 205 | 205 |
| 206 CSSAnimations::CSSAnimations() | 206 CSSAnimations::CSSAnimations() |
| 207 { | 207 { |
| 208 } | 208 } |
| 209 | 209 |
| 210 const AtomicString CSSAnimations::getAnimationNameForInspector(const AnimationPl ayer& player) | 210 const AtomicString CSSAnimations::getAnimationNameForInspector(const AnimationPl ayer& player) |
| 211 { | 211 { |
| 212 for (const auto& it : m_animations) { | 212 for (const auto& it : m_animations) { |
| 213 if (it.value->sequenceNumber() == player.sequenceNumber()) | 213 if (it.value.player->sequenceNumber() == player.sequenceNumber()) |
| 214 return it.key; | 214 return it.key; |
| 215 } | 215 } |
| 216 return nullAtom; | 216 return nullAtom; |
| 217 } | 217 } |
| 218 | 218 |
| 219 bool CSSAnimations::isTransitionAnimationForInspector(const AnimationPlayer& pla yer) const | 219 bool CSSAnimations::isTransitionAnimationForInspector(const AnimationPlayer& pla yer) const |
| 220 { | 220 { |
| 221 for (const auto& it : m_transitions) { | 221 for (const auto& it : m_transitions) { |
| 222 if (it.value.player->sequenceNumber() == player.sequenceNumber()) | 222 if (it.value.player->sequenceNumber() == player.sequenceNumber()) |
| 223 return true; | 223 return true; |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 241 | 241 |
| 242 #if !ENABLE(ASSERT) | 242 #if !ENABLE(ASSERT) |
| 243 // If we're in an animation style change, no animations can have started, be en cancelled or changed play state. | 243 // If we're in an animation style change, no animations can have started, be en cancelled or changed play state. |
| 244 // When ASSERT is enabled, we verify this optimization. | 244 // When ASSERT is enabled, we verify this optimization. |
| 245 if (activeAnimations && activeAnimations->isAnimationStyleChange()) | 245 if (activeAnimations && activeAnimations->isAnimationStyleChange()) |
| 246 return; | 246 return; |
| 247 #endif | 247 #endif |
| 248 | 248 |
| 249 const CSSAnimationData* animationData = style.animations(); | 249 const CSSAnimationData* animationData = style.animations(); |
| 250 const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->c ssAnimations() : nullptr; | 250 const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->c ssAnimations() : nullptr; |
| 251 const Element* elementForScoping = animatingElement ? animatingElement : &el ement; | |
| 251 | 252 |
| 252 HashSet<AtomicString> inactive; | 253 HashSet<AtomicString> inactive; |
| 253 if (cssAnimations) { | 254 if (cssAnimations) { |
| 254 for (const auto& entry : cssAnimations->m_animations) | 255 for (const auto& entry : cssAnimations->m_animations) |
| 255 inactive.add(entry.key); | 256 inactive.add(entry.key); |
| 256 } | 257 } |
| 257 | 258 |
| 258 if (style.display() != NONE) { | 259 if (style.display() != NONE) { |
| 259 for (size_t i = 0; animationData && i < animationData->nameList().size() ; ++i) { | 260 for (size_t i = 0; animationData && i < animationData->nameList().size() ; ++i) { |
| 260 AtomicString animationName(animationData->nameList()[i]); | 261 AtomicString animationName(animationData->nameList()[i]); |
| 261 if (animationName == CSSAnimationData::initialName()) | 262 if (animationName == CSSAnimationData::initialName()) |
| 262 continue; | 263 continue; |
| 263 | 264 |
| 264 const bool isPaused = CSSTimingData::getRepeated(animationData->play StateList(), i) == AnimPlayStatePaused; | 265 const bool isPaused = CSSTimingData::getRepeated(animationData->play StateList(), i) == AnimPlayStatePaused; |
| 265 | 266 |
| 266 Timing timing = animationData->convertToTiming(i); | 267 Timing timing = animationData->convertToTiming(i); |
| 268 Timing specifiedTiming = timing; | |
| 267 RefPtr<TimingFunction> keyframeTimingFunction = timing.timingFunctio n; | 269 RefPtr<TimingFunction> keyframeTimingFunction = timing.timingFunctio n; |
| 268 timing.timingFunction = Timing::defaults().timingFunction; | 270 timing.timingFunction = Timing::defaults().timingFunction; |
| 269 | 271 |
| 272 RefPtrWillBeRawPtr<StyleRuleKeyframes> keyframesRule = resolver->fin dKeyframesRule(elementForScoping, animationName); | |
| 273 if (!keyframesRule) | |
| 274 continue; // Cancel the animation if there's no style rule for i t. | |
| 275 | |
| 270 if (cssAnimations) { | 276 if (cssAnimations) { |
| 271 AnimationMap::const_iterator existing(cssAnimations->m_animation s.find(animationName)); | 277 AnimationMap::const_iterator existing(cssAnimations->m_animation s.find(animationName)); |
| 272 if (existing != cssAnimations->m_animations.end()) { | 278 if (existing != cssAnimations->m_animations.end()) { |
| 273 inactive.remove(animationName); | 279 inactive.remove(animationName); |
| 274 | 280 |
| 275 AnimationPlayer* player = existing->value.get(); | 281 const RunningAnimation& runningAnimation = existing->value; |
| 282 AnimationPlayer* player = runningAnimation.player.get(); | |
| 276 | 283 |
| 277 // FIXME: Should handle changes in the timing function. | 284 ASSERT(keyframesRule); |
| 278 if (timing != player->source()->specifiedTiming()) { | 285 if (keyframesRule != runningAnimation.styleRule || keyframes Rule->version() != runningAnimation.styleRuleVersion || runningAnimation.specifi edTiming != specifiedTiming) { |
| 279 ASSERT(!activeAnimations || !activeAnimations->isAnimati onStyleChange()); | |
| 280 | |
| 281 AnimatableValueKeyframeVector resolvedKeyframes; | 286 AnimatableValueKeyframeVector resolvedKeyframes; |
| 282 resolveKeyframes(resolver, animatingElement, element, st yle, parentStyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes ); | 287 resolveKeyframes(resolver, animatingElement, element, st yle, parentStyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes ); |
| 283 | 288 |
| 284 update->updateAnimationTiming(player, InertAnimation::cr eate(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), | 289 update->updateAnimation(animationName, player, InertAnim ation::create(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), |
| 285 timing, isPaused, player->currentTimeInternal()), ti ming); | 290 timing, isPaused, player->currentTimeInternal()), sp ecifiedTiming, keyframesRule); |
| 286 } | 291 } |
| 287 | 292 |
| 288 if (isPaused != player->paused()) { | 293 if (isPaused != player->paused()) { |
| 289 ASSERT(!activeAnimations || !activeAnimations->isAnimati onStyleChange()); | 294 ASSERT(!activeAnimations || !activeAnimations->isAnimati onStyleChange()); |
| 290 update->toggleAnimationPaused(animationName); | 295 update->toggleAnimationPaused(animationName); |
| 291 } | 296 } |
| 292 | 297 |
| 293 continue; | 298 continue; |
| 294 } | 299 } |
| 295 } | 300 } |
| 296 | 301 |
| 297 AnimatableValueKeyframeVector resolvedKeyframes; | 302 AnimatableValueKeyframeVector resolvedKeyframes; |
| 298 resolveKeyframes(resolver, animatingElement, element, style, parentS tyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes); | 303 resolveKeyframes(resolver, animatingElement, element, style, parentS tyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes); |
| 299 if (!resolvedKeyframes.isEmpty()) { | 304 if (!resolvedKeyframes.isEmpty()) { |
| 300 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleC hange()); | 305 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleC hange()); |
| 301 update->startAnimation(animationName, InertAnimation::create(Ani matableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused, 0) ); | 306 update->startAnimation(animationName, InertAnimation::create(Ani matableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused, 0) , specifiedTiming, keyframesRule); |
| 302 } | 307 } |
| 303 } | 308 } |
| 304 } | 309 } |
| 305 | 310 |
| 306 ASSERT(inactive.isEmpty() || cssAnimations); | 311 ASSERT(inactive.isEmpty() || cssAnimations); |
| 307 for (const AtomicString& animationName : inactive) { | 312 for (const AtomicString& animationName : inactive) { |
| 308 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()) ; | 313 ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()) ; |
| 309 update->cancelAnimation(animationName, *cssAnimations->m_animations.get( animationName)); | 314 update->cancelAnimation(animationName, *cssAnimations->m_animations.get( animationName).player); |
| 310 } | 315 } |
| 311 } | 316 } |
| 312 | 317 |
| 313 void CSSAnimations::maybeApplyPendingUpdate(Element* element) | 318 void CSSAnimations::maybeApplyPendingUpdate(Element* element) |
| 314 { | 319 { |
| 315 if (!m_pendingUpdate) { | 320 if (!m_pendingUpdate) { |
| 316 m_previousActiveInterpolationsForAnimations.clear(); | 321 m_previousActiveInterpolationsForAnimations.clear(); |
| 317 return; | 322 return; |
| 318 } | 323 } |
| 319 | 324 |
| 320 OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = m_pendingUpdate.release(); | 325 OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = m_pendingUpdate.release(); |
| 321 | 326 |
| 322 m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolation sForAnimations()); | 327 m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolation sForAnimations()); |
| 323 | 328 |
| 324 // FIXME: cancelling, pausing, unpausing animations all query compositingSta te, which is not necessarily up to date here | 329 // FIXME: cancelling, pausing, unpausing animations all query compositingSta te, which is not necessarily up to date here |
| 325 // since we call this from recalc style. | 330 // since we call this from recalc style. |
| 326 // https://code.google.com/p/chromium/issues/detail?id=339847 | 331 // https://code.google.com/p/chromium/issues/detail?id=339847 |
| 327 DisableCompositingQueryAsserts disabler; | 332 DisableCompositingQueryAsserts disabler; |
| 328 | 333 |
| 329 for (const AtomicString& animationName : update->cancelledAnimationNames()) { | 334 for (const AtomicString& animationName : update->cancelledAnimationNames()) { |
| 330 RefPtrWillBeRawPtr<AnimationPlayer> player = m_animations.take(animation Name); | 335 RefPtrWillBeRawPtr<AnimationPlayer> player = m_animations.take(animation Name).player; |
| 331 player->cancel(); | 336 player->cancel(); |
| 332 player->update(TimingUpdateOnDemand); | 337 player->update(TimingUpdateOnDemand); |
| 333 } | 338 } |
| 334 | 339 |
| 335 for (const AtomicString& animationName : update->animationsWithPauseToggled( )) { | 340 for (const AtomicString& animationName : update->animationsWithPauseToggled( )) { |
| 336 AnimationPlayer* player = m_animations.get(animationName); | 341 AnimationPlayer* player = m_animations.get(animationName).player.get(); |
| 337 if (player->paused()) | 342 if (player->paused()) |
| 338 player->unpause(); | 343 player->unpause(); |
| 339 else | 344 else |
| 340 player->pause(); | 345 player->pause(); |
| 341 if (player->outdated()) | 346 if (player->outdated()) |
| 342 player->update(TimingUpdateOnDemand); | 347 player->update(TimingUpdateOnDemand); |
| 343 } | 348 } |
| 344 | 349 |
| 345 for (const auto& timingUpdate : update->animationsWithTimingUpdates()) { | 350 for (const auto& entry : update->animationsWithUpdates()) { |
| 346 timingUpdate.player->source()->updateSpecifiedTiming(timingUpdate.newTim ing); | 351 Animation* animation = toAnimation(entry.player->source()); |
| 347 timingUpdate.player->update(TimingUpdateOnDemand); | 352 |
| 353 animation->setEffect(entry.animation->effect()); | |
| 354 animation->updateSpecifiedTiming(entry.animation->specifiedTiming()); | |
| 355 | |
| 356 m_animations.find(entry.name)->value.update(entry); | |
| 348 } | 357 } |
| 349 | 358 |
| 350 for (const auto& entry : update->newAnimations()) { | 359 for (const auto& entry : update->newAnimations()) { |
| 351 const InertAnimation* inertAnimation = entry.animation.get(); | 360 const InertAnimation* inertAnimation = entry.animation.get(); |
| 352 OwnPtrWillBeRawPtr<AnimationEventDelegate> eventDelegate = adoptPtrWillB eNoop(new AnimationEventDelegate(element, entry.name)); | 361 OwnPtrWillBeRawPtr<AnimationEventDelegate> eventDelegate = adoptPtrWillB eNoop(new AnimationEventDelegate(element, entry.name)); |
| 353 RefPtrWillBeRawPtr<Animation> animation = Animation::create(element, ine rtAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPrio rity, eventDelegate.release()); | 362 RefPtrWillBeRawPtr<Animation> animation = Animation::create(element, ine rtAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPrio rity, eventDelegate.release()); |
| 354 animation->setName(inertAnimation->name()); | 363 animation->setName(inertAnimation->name()); |
| 355 RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timelin e().play(animation.get()); | 364 RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timelin e().play(animation.get()); |
| 356 if (inertAnimation->paused()) | 365 if (inertAnimation->paused()) |
| 357 player->pause(); | 366 player->pause(); |
| 358 player->update(TimingUpdateOnDemand); | 367 player->update(TimingUpdateOnDemand); |
| 359 m_animations.set(entry.name, player.get()); | 368 |
| 369 m_animations.set(entry.name, RunningAnimation(player, entry)); | |
|
esprehn
2015/01/30 02:50:28
We should probably store pointers to RunningAnimat
shend
2015/02/01 23:49:13
Done.
| |
| 360 } | 370 } |
| 361 | 371 |
| 362 // Transitions that are run on the compositor only update main-thread state | 372 // Transitions that are run on the compositor only update main-thread state |
| 363 // lazily. However, we need the new state to know what the from state shoud | 373 // lazily. However, we need the new state to know what the from state shoud |
| 364 // be when transitions are retargeted. Instead of triggering complete style | 374 // be when transitions are retargeted. Instead of triggering complete style |
| 365 // recalculation, we find these cases by searching for new transitions that | 375 // recalculation, we find these cases by searching for new transitions that |
| 366 // have matching cancelled animation property IDs on the compositor. | 376 // have matching cancelled animation property IDs on the compositor. |
| 367 WillBeHeapHashMap<CSSPropertyID, std::pair<RefPtrWillBeMember<Animation>, do uble>> retargetedCompositorTransitions; | 377 WillBeHeapHashMap<CSSPropertyID, std::pair<RefPtrWillBeMember<Animation>, do uble>> retargetedCompositorTransitions; |
| 368 for (CSSPropertyID id : update->cancelledTransitions()) { | 378 for (CSSPropertyID id : update->cancelledTransitions()) { |
| 369 ASSERT(m_transitions.contains(id)); | 379 ASSERT(m_transitions.contains(id)); |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 557 // ASSERT(player.playStateInternal() == AnimationPlayer::Finishe d || !(activeAnimations && activeAnimations->isAnimationStyleChange())); | 567 // ASSERT(player.playStateInternal() == AnimationPlayer::Finishe d || !(activeAnimations && activeAnimations->isAnimationStyleChange())); |
| 558 update->cancelTransition(id); | 568 update->cancelTransition(id); |
| 559 } | 569 } |
| 560 } | 570 } |
| 561 } | 571 } |
| 562 } | 572 } |
| 563 | 573 |
| 564 void CSSAnimations::cancel() | 574 void CSSAnimations::cancel() |
| 565 { | 575 { |
| 566 for (const auto& entry : m_animations) { | 576 for (const auto& entry : m_animations) { |
| 567 entry.value->cancel(); | 577 entry.value.player->cancel(); |
| 568 entry.value->update(TimingUpdateOnDemand); | 578 entry.value.player->update(TimingUpdateOnDemand); |
| 569 } | 579 } |
| 570 | 580 |
| 571 for (const auto& entry : m_transitions) { | 581 for (const auto& entry : m_transitions) { |
| 572 entry.value.player->cancel(); | 582 entry.value.player->cancel(); |
| 573 entry.value.player->update(TimingUpdateOnDemand); | 583 entry.value.player->update(TimingUpdateOnDemand); |
| 574 } | 584 } |
| 575 | 585 |
| 576 m_animations.clear(); | 586 m_animations.clear(); |
| 577 m_transitions.clear(); | 587 m_transitions.clear(); |
| 578 m_pendingUpdate = nullptr; | 588 m_pendingUpdate = nullptr; |
| 579 } | 589 } |
| 580 | 590 |
| 581 void CSSAnimations::calculateAnimationActiveInterpolations(CSSAnimationUpdate* u pdate, const Element* animatingElement, double timelineCurrentTime) | 591 void CSSAnimations::calculateAnimationActiveInterpolations(CSSAnimationUpdate* u pdate, const Element* animatingElement, double timelineCurrentTime) |
| 582 { | 592 { |
| 583 ActiveAnimations* activeAnimations = animatingElement ? animatingElement->ac tiveAnimations() : nullptr; | 593 ActiveAnimations* activeAnimations = animatingElement ? animatingElement->ac tiveAnimations() : nullptr; |
| 584 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defau ltStack() : nullptr; | 594 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defau ltStack() : nullptr; |
| 585 | 595 |
| 586 if (update->newAnimations().isEmpty() && update->suppressedAnimationAnimatio nPlayers().isEmpty()) { | 596 if (update->newAnimations().isEmpty() && update->suppressedAnimationAnimatio nPlayers().isEmpty()) { |
| 587 WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation>> acti veInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStac k, 0, 0, Animation::DefaultPriority, timelineCurrentTime)); | 597 WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation>> acti veInterpolationsForAnimations(AnimationStack::activeInterpolations(animationStac k, 0, 0, Animation::DefaultPriority, timelineCurrentTime)); |
| 588 update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAn imations); | 598 update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAn imations); |
| 589 return; | 599 return; |
| 590 } | 600 } |
| 591 | 601 |
| 592 WillBeHeapVector<RawPtrWillBeMember<InertAnimation>> newAnimations; | 602 WillBeHeapVector<RawPtrWillBeMember<InertAnimation>> newAnimations; |
| 593 for (const auto& newAnimation : update->newAnimations()) | 603 for (const auto& newAnimation : update->newAnimations()) |
| 594 newAnimations.append(newAnimation.animation.get()); | 604 newAnimations.append(newAnimation.animation.get()); |
| 595 for (const auto& updatedAnimation : update->animationsWithTimingUpdates()) | 605 for (const auto& updatedAnimation : update->animationsWithUpdates()) |
| 596 newAnimations.append(updatedAnimation.animation.get()); // Animations wi th timing updates use a temporary InertAnimation for the current frame. | 606 newAnimations.append(updatedAnimation.animation.get()); // Animations wi th updates use a temporary InertAnimation for the current frame. |
| 597 | 607 |
| 598 WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation>> activeIn terpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, & newAnimations, &update->suppressedAnimationAnimationPlayers(), Animation::Defaul tPriority, timelineCurrentTime)); | 608 WillBeHeapHashMap<CSSPropertyID, RefPtrWillBeMember<Interpolation>> activeIn terpolationsForAnimations(AnimationStack::activeInterpolations(animationStack, & newAnimations, &update->suppressedAnimationAnimationPlayers(), Animation::Defaul tPriority, timelineCurrentTime)); |
| 599 update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimat ions); | 609 update->adoptActiveInterpolationsForAnimations(activeInterpolationsForAnimat ions); |
| 600 } | 610 } |
| 601 | 611 |
| 602 void CSSAnimations::calculateTransitionActiveInterpolations(CSSAnimationUpdate* update, const Element* animatingElement, double timelineCurrentTime) | 612 void CSSAnimations::calculateTransitionActiveInterpolations(CSSAnimationUpdate* update, const Element* animatingElement, double timelineCurrentTime) |
| 603 { | 613 { |
| 604 ActiveAnimations* activeAnimations = animatingElement ? animatingElement->ac tiveAnimations() : nullptr; | 614 ActiveAnimations* activeAnimations = animatingElement ? animatingElement->ac tiveAnimations() : nullptr; |
| 605 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defau ltStack() : nullptr; | 615 AnimationStack* animationStack = activeAnimations ? &activeAnimations->defau ltStack() : nullptr; |
| 606 | 616 |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 772 } | 782 } |
| 773 | 783 |
| 774 void CSSAnimationUpdate::trace(Visitor* visitor) | 784 void CSSAnimationUpdate::trace(Visitor* visitor) |
| 775 { | 785 { |
| 776 #if ENABLE(OILPAN) | 786 #if ENABLE(OILPAN) |
| 777 visitor->trace(m_newTransitions); | 787 visitor->trace(m_newTransitions); |
| 778 visitor->trace(m_activeInterpolationsForAnimations); | 788 visitor->trace(m_activeInterpolationsForAnimations); |
| 779 visitor->trace(m_activeInterpolationsForTransitions); | 789 visitor->trace(m_activeInterpolationsForTransitions); |
| 780 visitor->trace(m_newAnimations); | 790 visitor->trace(m_newAnimations); |
| 781 visitor->trace(m_suppressedAnimationPlayers); | 791 visitor->trace(m_suppressedAnimationPlayers); |
| 782 visitor->trace(m_animationsWithTimingUpdates); | 792 visitor->trace(m_animationsWithUpdates); |
| 783 #endif | 793 #endif |
| 784 } | 794 } |
| 785 | 795 |
| 786 } // namespace blink | 796 } // namespace blink |
| OLD | NEW |