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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
45 { | 45 { |
46 static unsigned next = 0; | 46 static unsigned next = 0; |
47 return ++next; | 47 return ++next; |
48 } | 48 } |
49 | 49 |
50 } | 50 } |
51 | 51 |
52 PassRefPtr<AnimationPlayer> AnimationPlayer::create(ExecutionContext* executionC
ontext, AnimationTimeline& timeline, AnimationNode* content) | 52 PassRefPtr<AnimationPlayer> AnimationPlayer::create(ExecutionContext* executionC
ontext, AnimationTimeline& timeline, AnimationNode* content) |
53 { | 53 { |
54 RefPtr<AnimationPlayer> player = adoptRef(new AnimationPlayer(executionConte
xt, timeline, content)); | 54 RefPtr<AnimationPlayer> player = adoptRef(new AnimationPlayer(executionConte
xt, timeline, content)); |
55 timeline.document()->compositorPendingAnimations().add(player.get()); | 55 timeline.document()->pendingAnimations().add(player.get()); |
56 player->suspendIfNeeded(); | 56 player->suspendIfNeeded(); |
57 return player.release(); | 57 return player.release(); |
58 } | 58 } |
59 | 59 |
60 AnimationPlayer::AnimationPlayer(ExecutionContext* executionContext, AnimationTi
meline& timeline, AnimationNode* content) | 60 AnimationPlayer::AnimationPlayer(ExecutionContext* executionContext, AnimationTi
meline& timeline, AnimationNode* content) |
61 : ActiveDOMObject(executionContext) | 61 : ActiveDOMObject(executionContext) |
62 , m_playbackRate(1) | 62 , m_playbackRate(1) |
63 , m_startTime(nullValue()) | 63 , m_startTime(nullValue()) |
64 , m_holdTime(0) | 64 , m_holdTime(0) |
65 , m_sequenceNumber(nextSequenceNumber()) | 65 , m_sequenceNumber(nextSequenceNumber()) |
66 , m_content(content) | 66 , m_content(content) |
67 , m_timeline(&timeline) | 67 , m_timeline(&timeline) |
68 , m_paused(false) | 68 , m_paused(false) |
69 , m_held(true) | 69 , m_held(true) |
70 , m_isPausedForTesting(false) | 70 , m_isPausedForTesting(false) |
71 , m_outdated(true) | 71 , m_outdated(true) |
72 , m_finished(false) | 72 , m_finished(false) |
73 , m_compositorState(nullptr) | 73 , m_pending(true) |
74 , m_compositorPending(true) | |
75 , m_currentTimePending(false) | 74 , m_currentTimePending(false) |
76 { | 75 { |
77 if (m_content) { | 76 if (m_content) { |
78 if (m_content->player()) | 77 if (m_content->player()) |
79 m_content->player()->cancel(); | 78 m_content->player()->cancel(); |
80 m_content->attach(this); | 79 m_content->attach(this); |
81 } | 80 } |
82 } | 81 } |
83 | 82 |
84 AnimationPlayer::~AnimationPlayer() | 83 AnimationPlayer::~AnimationPlayer() |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
154 } | 153 } |
155 | 154 |
156 double AnimationPlayer::currentTimeInternal() | 155 double AnimationPlayer::currentTimeInternal() |
157 { | 156 { |
158 updateCurrentTimingState(TimingUpdateOnDemand); | 157 updateCurrentTimingState(TimingUpdateOnDemand); |
159 if (m_held) | 158 if (m_held) |
160 return m_holdTime; | 159 return m_holdTime; |
161 return calculateCurrentTime(); | 160 return calculateCurrentTime(); |
162 } | 161 } |
163 | 162 |
164 void AnimationPlayer::preCommit(bool startOnCompositor) | 163 void AnimationPlayer::preCommit() |
165 { | 164 { |
166 if (m_compositorState && m_compositorState->pendingAction == Start) { | 165 if (!playing()) { |
167 // Still waiting for a start time. | |
168 return; | |
169 } | |
170 | |
171 bool softChange = m_compositorState && (paused() || m_compositorState->playb
ackRate != m_playbackRate); | |
172 bool hardChange = m_compositorState && (m_compositorState->sourceChanged ||
(m_compositorState->startTime != m_startTime && !std::isnan(m_compositorState->s
tartTime) && !std::isnan(m_startTime))); | |
173 | |
174 // FIXME: softChange && !hardChange should generate a Pause/ThenStart, | |
175 // not a Cancel, but we can't communicate these to the compositor yet. | |
176 | |
177 bool changed = softChange || hardChange; | |
178 bool shouldCancel = (!playing() && m_compositorState) || changed; | |
179 bool shouldStart = playing() && (!m_compositorState || changed); | |
180 | |
181 if (shouldCancel) { | |
182 cancelAnimationOnCompositor(); | |
183 m_compositorState = nullptr; | |
184 | |
185 } | |
186 | |
187 if (!shouldStart) { | |
188 m_currentTimePending = false; | 166 m_currentTimePending = false; |
189 } | 167 } |
190 | |
191 if (shouldStart && startOnCompositor && maybeStartAnimationOnCompositor()) { | |
192 m_compositorState = adoptPtr(new CompositorState(*this)); | |
193 } | |
194 } | 168 } |
195 | 169 |
196 void AnimationPlayer::postCommit(double timelineTime) | 170 void AnimationPlayer::postCommit(double timelineTime) |
197 { | 171 { |
198 m_compositorPending = false; | 172 m_pending = false; |
199 | |
200 if (!m_compositorState || m_compositorState->pendingAction == None) | |
201 return; | |
202 | |
203 switch (m_compositorState->pendingAction) { | |
204 case Start: | |
205 if (!std::isnan(m_compositorState->startTime)) { | |
206 ASSERT(m_startTime == m_compositorState->startTime); | |
207 m_compositorState->pendingAction = None; | |
208 } | |
209 break; | |
210 case Pause: | |
211 case PauseThenStart: | |
212 ASSERT(std::isnan(m_startTime)); | |
213 m_compositorState->pendingAction = None; | |
214 setCurrentTimeInternal((timelineTime - m_compositorState->startTime) * m
_playbackRate, TimingUpdateForAnimationFrame); | |
215 m_currentTimePending = false; | |
216 break; | |
217 default: | |
218 ASSERT_NOT_REACHED(); | |
219 } | |
220 } | 173 } |
221 | 174 |
222 void AnimationPlayer::notifyCompositorStartTime(double timelineTime) | 175 void AnimationPlayer::notifyCompositorStartTime(double timelineTime) |
223 { | 176 { |
224 if (m_compositorState) { | |
225 ASSERT(m_compositorState->pendingAction == Start); | |
226 ASSERT(std::isnan(m_compositorState->startTime)); | |
227 | |
228 double initialCompositorHoldTime = m_compositorState->holdTime; | |
229 m_compositorState->pendingAction = None; | |
230 m_compositorState->startTime = timelineTime; | |
231 | |
232 if (paused() || m_compositorState->playbackRate != m_playbackRate || m_c
ompositorState->sourceChanged) { | |
233 // Paused state, playback rate, or source changed while starting. | |
234 setCompositorPending(); | |
235 } | |
236 | |
237 if (m_startTime == timelineTime) { | |
238 // The start time was set to the incoming compositor start time. | |
239 // Unlikely, but possible. | |
240 // FIXME: Depending on what changed above this might still be pendin
g. | |
241 // Maybe... | |
242 m_currentTimePending = false; | |
243 return; | |
244 } | |
245 | |
246 if (!std::isnan(m_startTime) || currentTimeInternal() != initialComposit
orHoldTime) { | |
247 // A new start time or current time was set while starting. | |
248 setCompositorPending(); | |
249 return; | |
250 } | |
251 } | |
252 | |
253 if (playing()) { | 177 if (playing()) { |
254 ASSERT(std::isnan(m_startTime)); | 178 ASSERT(std::isnan(m_startTime)); |
255 ASSERT(m_held); | 179 ASSERT(m_held); |
256 | 180 |
257 if (m_playbackRate == 0) { | 181 if (m_playbackRate == 0) { |
258 setStartTimeInternal(timelineTime); | 182 setStartTimeInternal(timelineTime); |
259 } else { | 183 } else { |
260 setStartTimeInternal(timelineTime + currentTimeInternal() / -m_playb
ackRate); | 184 setStartTimeInternal(timelineTime + currentTimeInternal() / -m_playb
ackRate); |
261 } | 185 } |
262 | 186 |
(...skipping 16 matching lines...) Expand all Loading... |
279 if (isNull(m_startTime) || !m_timeline) | 203 if (isNull(m_startTime) || !m_timeline) |
280 return 0; | 204 return 0; |
281 return (m_timeline->effectiveTime() - m_startTime) * m_playbackRate; | 205 return (m_timeline->effectiveTime() - m_startTime) * m_playbackRate; |
282 } | 206 } |
283 | 207 |
284 void AnimationPlayer::setCurrentTime(double newCurrentTime) | 208 void AnimationPlayer::setCurrentTime(double newCurrentTime) |
285 { | 209 { |
286 if (!std::isfinite(newCurrentTime)) | 210 if (!std::isfinite(newCurrentTime)) |
287 return; | 211 return; |
288 | 212 |
289 setCompositorPending(); | 213 setPending(); |
290 | 214 |
291 // Setting current time while pending forces a start time. | 215 // Setting current time while pending forces a start time. |
292 if (m_currentTimePending) { | 216 if (m_currentTimePending) { |
293 m_startTime = 0; | 217 m_startTime = 0; |
294 m_currentTimePending = false; | 218 m_currentTimePending = false; |
295 } | 219 } |
296 | 220 |
297 setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand); | 221 setCurrentTimeInternal(newCurrentTime / 1000, TimingUpdateOnDemand); |
298 } | 222 } |
299 | 223 |
300 void AnimationPlayer::setStartTime(double startTime) | 224 void AnimationPlayer::setStartTime(double startTime) |
301 { | 225 { |
302 if (m_paused) // FIXME: Should this throw an exception? | 226 if (m_paused) // FIXME: Should this throw an exception? |
303 return; | 227 return; |
304 if (!std::isfinite(startTime)) | 228 if (!std::isfinite(startTime)) |
305 return; | 229 return; |
306 if (startTime == m_startTime) | 230 if (startTime == m_startTime) |
307 return; | 231 return; |
308 | 232 |
309 setCompositorPending(); | 233 setPending(); |
310 m_currentTimePending = false; | 234 m_currentTimePending = false; |
311 setStartTimeInternal(startTime / 1000); | 235 setStartTimeInternal(startTime / 1000); |
312 } | 236 } |
313 | 237 |
314 void AnimationPlayer::setStartTimeInternal(double newStartTime) | 238 void AnimationPlayer::setStartTimeInternal(double newStartTime) |
315 { | 239 { |
316 ASSERT(!m_paused); | 240 ASSERT(!m_paused); |
317 ASSERT(std::isfinite(newStartTime)); | 241 ASSERT(std::isfinite(newStartTime)); |
318 ASSERT(newStartTime != m_startTime); | 242 ASSERT(newStartTime != m_startTime); |
319 | 243 |
(...skipping 21 matching lines...) Expand all Loading... |
341 // infinity until start time is set. | 265 // infinity until start time is set. |
342 m_timeline->wake(); | 266 m_timeline->wake(); |
343 } | 267 } |
344 } | 268 } |
345 | 269 |
346 void AnimationPlayer::setSource(AnimationNode* newSource) | 270 void AnimationPlayer::setSource(AnimationNode* newSource) |
347 { | 271 { |
348 if (m_content == newSource) | 272 if (m_content == newSource) |
349 return; | 273 return; |
350 | 274 |
351 setCompositorPending(true); | 275 setPending(); |
352 | 276 |
353 double storedCurrentTime = currentTimeInternal(); | 277 double storedCurrentTime = currentTimeInternal(); |
354 if (m_content) | 278 if (m_content) |
355 m_content->detach(); | 279 m_content->detach(); |
356 m_content = newSource; | 280 m_content = newSource; |
357 if (newSource) { | 281 if (newSource) { |
358 // FIXME: This logic needs to be updated once groups are implemented | 282 // FIXME: This logic needs to be updated once groups are implemented |
359 if (newSource->player()) | 283 if (newSource->player()) |
360 newSource->player()->cancel(); | 284 newSource->player()->cancel(); |
361 newSource->attach(this); | 285 newSource->attach(this); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 if (finished()) | 318 if (finished()) |
395 return Finished; | 319 return Finished; |
396 return Running; | 320 return Running; |
397 } | 321 } |
398 | 322 |
399 void AnimationPlayer::pause() | 323 void AnimationPlayer::pause() |
400 { | 324 { |
401 if (m_paused) | 325 if (m_paused) |
402 return; | 326 return; |
403 if (playing()) { | 327 if (playing()) { |
404 setCompositorPending(); | 328 setPending(); |
405 m_currentTimePending = true; | 329 m_currentTimePending = true; |
406 } | 330 } |
407 m_paused = true; | 331 m_paused = true; |
408 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); | 332 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); |
409 } | 333 } |
410 | 334 |
411 void AnimationPlayer::unpause() | 335 void AnimationPlayer::unpause() |
412 { | 336 { |
413 if (!m_paused) | 337 if (!m_paused) |
414 return; | 338 return; |
415 setCompositorPending(); | 339 setPending(); |
416 m_currentTimePending = true; | 340 m_currentTimePending = true; |
417 unpauseInternal(); | 341 unpauseInternal(); |
418 } | 342 } |
419 | 343 |
420 void AnimationPlayer::unpauseInternal() | 344 void AnimationPlayer::unpauseInternal() |
421 { | 345 { |
422 if (!m_paused) | 346 if (!m_paused) |
423 return; | 347 return; |
424 m_paused = false; | 348 m_paused = false; |
425 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); | 349 setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); |
426 } | 350 } |
427 | 351 |
428 void AnimationPlayer::play() | 352 void AnimationPlayer::play() |
429 { | 353 { |
430 if (!playing()) | 354 if (!playing()) |
431 m_startTime = nullValue(); | 355 m_startTime = nullValue(); |
432 | 356 |
433 setCompositorPending(); | 357 setPending(); |
434 unpauseInternal(); | 358 unpauseInternal(); |
435 if (!m_content) | 359 if (!m_content) |
436 return; | 360 return; |
437 double currentTime = this->currentTimeInternal(); | 361 double currentTime = this->currentTimeInternal(); |
438 if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd())) | 362 if (m_playbackRate > 0 && (currentTime < 0 || currentTime >= sourceEnd())) |
439 setCurrentTimeInternal(0, TimingUpdateOnDemand); | 363 setCurrentTimeInternal(0, TimingUpdateOnDemand); |
440 else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd(
))) | 364 else if (m_playbackRate < 0 && (currentTime <= 0 || currentTime > sourceEnd(
))) |
441 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); | 365 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); |
442 m_finished = false; | 366 m_finished = false; |
443 } | 367 } |
(...skipping 19 matching lines...) Expand all Loading... |
463 void AnimationPlayer::finish(ExceptionState& exceptionState) | 387 void AnimationPlayer::finish(ExceptionState& exceptionState) |
464 { | 388 { |
465 if (!m_playbackRate) { | 389 if (!m_playbackRate) { |
466 return; | 390 return; |
467 } | 391 } |
468 if (m_playbackRate > 0 && sourceEnd() == std::numeric_limits<double>::infini
ty()) { | 392 if (m_playbackRate > 0 && sourceEnd() == std::numeric_limits<double>::infini
ty()) { |
469 exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has
source content whose end time is infinity."); | 393 exceptionState.throwDOMException(InvalidStateError, "AnimationPlayer has
source content whose end time is infinity."); |
470 return; | 394 return; |
471 } | 395 } |
472 if (playing()) { | 396 if (playing()) { |
473 setCompositorPending(); | 397 setPending(); |
474 } | 398 } |
475 if (m_playbackRate < 0) { | 399 if (m_playbackRate < 0) { |
476 setCurrentTimeInternal(0, TimingUpdateOnDemand); | 400 setCurrentTimeInternal(0, TimingUpdateOnDemand); |
477 } else { | 401 } else { |
478 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); | 402 setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); |
479 } | 403 } |
480 ASSERT(finished()); | 404 ASSERT(finished()); |
481 } | 405 } |
482 | 406 |
483 const AtomicString& AnimationPlayer::interfaceName() const | 407 const AtomicString& AnimationPlayer::interfaceName() const |
(...skipping 24 matching lines...) Expand all Loading... |
508 return EventTargetWithInlineData::dispatchEvent(event); | 432 return EventTargetWithInlineData::dispatchEvent(event); |
509 } | 433 } |
510 | 434 |
511 void AnimationPlayer::setPlaybackRate(double playbackRate) | 435 void AnimationPlayer::setPlaybackRate(double playbackRate) |
512 { | 436 { |
513 if (!std::isfinite(playbackRate)) | 437 if (!std::isfinite(playbackRate)) |
514 return; | 438 return; |
515 if (playbackRate == m_playbackRate) | 439 if (playbackRate == m_playbackRate) |
516 return; | 440 return; |
517 | 441 |
518 setCompositorPending(); | 442 setPending(); |
519 if (!finished() && !paused()) | 443 if (!finished() && !paused()) |
520 m_currentTimePending = true; | 444 m_currentTimePending = true; |
521 | 445 |
522 double storedCurrentTime = currentTimeInternal(); | 446 double storedCurrentTime = currentTimeInternal(); |
523 if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && play
backRate <= 0)) | 447 if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && play
backRate <= 0)) |
524 m_finished = false; | 448 m_finished = false; |
525 | 449 |
526 m_playbackRate = playbackRate; | 450 m_playbackRate = playbackRate; |
527 m_startTime = std::numeric_limits<double>::quiet_NaN(); | 451 m_startTime = std::numeric_limits<double>::quiet_NaN(); |
528 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); | 452 setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); |
529 } | 453 } |
530 | 454 |
531 void AnimationPlayer::setOutdated() | 455 void AnimationPlayer::setOutdated() |
532 { | 456 { |
533 m_outdated = true; | 457 m_outdated = true; |
534 if (m_timeline) | 458 if (m_timeline) |
535 m_timeline->setOutdatedAnimationPlayer(this); | 459 m_timeline->setOutdatedAnimationPlayer(this); |
536 } | 460 } |
537 | 461 |
538 bool AnimationPlayer::canStartAnimationOnCompositor() | 462 void AnimationPlayer::setPending() |
539 { | 463 { |
540 // FIXME: Need compositor support for playback rate != 1. | 464 if (!m_pending) { |
541 if (playbackRate() != 1) | 465 m_pending = true; |
542 return false; | 466 timeline()->document()->pendingAnimations().add(this); |
543 | |
544 return m_timeline && m_content && m_content->isAnimation() && playing(); | |
545 } | |
546 | |
547 bool AnimationPlayer::maybeStartAnimationOnCompositor() | |
548 { | |
549 if (!canStartAnimationOnCompositor()) | |
550 return false; | |
551 | |
552 double startTime = timeline()->zeroTime() + startTimeInternal(); | |
553 double timeOffset = 0; | |
554 if (std::isnan(startTime)) { | |
555 timeOffset = currentTimeInternal(); | |
556 } | |
557 return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(startTi
me, timeOffset); | |
558 } | |
559 | |
560 void AnimationPlayer::setCompositorPending(bool sourceChanged) | |
561 { | |
562 // FIXME: Animation could notify this directly? | |
563 if (!hasActiveAnimationsOnCompositor()) { | |
564 m_compositorState.release(); | |
565 } | |
566 if (!m_compositorPending) { | |
567 m_compositorPending = true; | |
568 if (sourceChanged && m_compositorState) | |
569 m_compositorState->sourceChanged = true; | |
570 timeline()->document()->compositorPendingAnimations().add(this); | |
571 } | 467 } |
572 } | 468 } |
573 | 469 |
574 bool AnimationPlayer::hasActiveAnimationsOnCompositor() | |
575 { | |
576 if (!m_content || !m_content->isAnimation()) | |
577 return false; | |
578 | |
579 return toAnimation(m_content.get())->hasActiveAnimationsOnCompositor(); | |
580 } | |
581 | |
582 void AnimationPlayer::cancelAnimationOnCompositor() | |
583 { | |
584 if (hasActiveAnimationsOnCompositor()) | |
585 toAnimation(m_content.get())->cancelAnimationOnCompositor(); | |
586 } | |
587 | |
588 bool AnimationPlayer::update(TimingUpdateReason reason) | 470 bool AnimationPlayer::update(TimingUpdateReason reason) |
589 { | 471 { |
590 if (!m_timeline) | 472 if (!m_timeline) |
591 return false; | 473 return false; |
592 | 474 |
593 updateCurrentTimingState(reason); | 475 updateCurrentTimingState(reason); |
594 m_outdated = false; | 476 m_outdated = false; |
595 | 477 |
596 if (m_content) { | 478 if (m_content) { |
597 double inheritedTime = isNull(m_timeline->currentTimeInternal()) ? nullV
alue() : currentTimeInternal(); | 479 double inheritedTime = isNull(m_timeline->currentTimeInternal()) ? nullV
alue() : currentTimeInternal(); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
641 { | 523 { |
642 if (eventType == EventTypeNames::finish) | 524 if (eventType == EventTypeNames::finish) |
643 UseCounter::count(executionContext(), UseCounter::AnimationPlayerFinishE
vent); | 525 UseCounter::count(executionContext(), UseCounter::AnimationPlayerFinishE
vent); |
644 return EventTargetWithInlineData::addEventListener(eventType, listener, useC
apture); | 526 return EventTargetWithInlineData::addEventListener(eventType, listener, useC
apture); |
645 } | 527 } |
646 | 528 |
647 void AnimationPlayer::pauseForTesting(double pauseTime) | 529 void AnimationPlayer::pauseForTesting(double pauseTime) |
648 { | 530 { |
649 RELEASE_ASSERT(!paused()); | 531 RELEASE_ASSERT(!paused()); |
650 setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand); | 532 setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand); |
651 if (hasActiveAnimationsOnCompositor()) | |
652 toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(curre
ntTimeInternal()); | |
653 m_isPausedForTesting = true; | 533 m_isPausedForTesting = true; |
654 pause(); | 534 pause(); |
655 } | 535 } |
656 | 536 |
657 } // namespace | 537 } // namespace |
OLD | NEW |