| 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 121 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 132 #else | 132 #else | 
| 133     return 0; | 133     return 0; | 
| 134 #endif | 134 #endif | 
| 135 } | 135 } | 
| 136 | 136 | 
| 137 WTF::ThreadSpecific<ThreadState*>* ThreadState::s_threadSpecific = 0; | 137 WTF::ThreadSpecific<ThreadState*>* ThreadState::s_threadSpecific = 0; | 
| 138 uintptr_t ThreadState::s_mainThreadStackStart = 0; | 138 uintptr_t ThreadState::s_mainThreadStackStart = 0; | 
| 139 uintptr_t ThreadState::s_mainThreadUnderestimatedStackSize = 0; | 139 uintptr_t ThreadState::s_mainThreadUnderestimatedStackSize = 0; | 
| 140 uint8_t ThreadState::s_mainThreadStateStorage[sizeof(ThreadState)]; | 140 uint8_t ThreadState::s_mainThreadStateStorage[sizeof(ThreadState)]; | 
| 141 SafePointBarrier* ThreadState::s_safePointBarrier = 0; | 141 SafePointBarrier* ThreadState::s_safePointBarrier = 0; | 
| 142 bool ThreadState::s_inGC = false; |  | 
| 143 | 142 | 
| 144 static Mutex& threadAttachMutex() | 143 static Mutex& threadAttachMutex() | 
| 145 { | 144 { | 
| 146     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); | 145     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); | 
| 147     return mutex; | 146     return mutex; | 
| 148 } | 147 } | 
| 149 | 148 | 
| 150 static double lockingTimeout() | 149 static double lockingTimeout() | 
| 151 { | 150 { | 
| 152     // Wait time for parking all threads is at most 100 MS. | 151     // Wait time for parking all threads is at most 100 MS. | 
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 320 }; | 319 }; | 
| 321 | 320 | 
| 322 ThreadState::ThreadState() | 321 ThreadState::ThreadState() | 
| 323     : m_thread(currentThread()) | 322     : m_thread(currentThread()) | 
| 324     , m_persistents(adoptPtr(new PersistentAnchor())) | 323     , m_persistents(adoptPtr(new PersistentAnchor())) | 
| 325     , m_startOfStack(reinterpret_cast<intptr_t*>(getStackStart())) | 324     , m_startOfStack(reinterpret_cast<intptr_t*>(getStackStart())) | 
| 326     , m_endOfStack(reinterpret_cast<intptr_t*>(getStackStart())) | 325     , m_endOfStack(reinterpret_cast<intptr_t*>(getStackStart())) | 
| 327     , m_safePointScopeMarker(0) | 326     , m_safePointScopeMarker(0) | 
| 328     , m_atSafePoint(false) | 327     , m_atSafePoint(false) | 
| 329     , m_interruptors() | 328     , m_interruptors() | 
| 330     , m_gcRequested(false) |  | 
| 331     , m_didV8GCAfterLastGC(false) | 329     , m_didV8GCAfterLastGC(false) | 
| 332     , m_forcePreciseGCForTesting(false) | 330     , m_forcePreciseGCForTesting(false) | 
| 333     , m_sweepRequested(0) |  | 
| 334     , m_sweepInProgress(false) | 331     , m_sweepInProgress(false) | 
| 335     , m_noAllocationCount(0) | 332     , m_noAllocationCount(0) | 
| 336     , m_inGC(false) |  | 
| 337     , m_isTerminating(false) | 333     , m_isTerminating(false) | 
| 338     , m_shouldFlushHeapDoesNotContainCache(false) | 334     , m_shouldFlushHeapDoesNotContainCache(false) | 
| 339     , m_lowCollectionRate(false) | 335     , m_lowCollectionRate(false) | 
|  | 336     , m_gcState(NoGCScheduled) | 
| 340     , m_traceDOMWrappers(0) | 337     , m_traceDOMWrappers(0) | 
| 341 #if defined(ADDRESS_SANITIZER) | 338 #if defined(ADDRESS_SANITIZER) | 
| 342     , m_asanFakeStack(__asan_get_current_fake_stack()) | 339     , m_asanFakeStack(__asan_get_current_fake_stack()) | 
| 343 #endif | 340 #endif | 
| 344 { | 341 { | 
| 345     ASSERT(!**s_threadSpecific); | 342     ASSERT(!**s_threadSpecific); | 
| 346     **s_threadSpecific = this; | 343     **s_threadSpecific = this; | 
| 347 | 344 | 
| 348     if (isMainThread()) { | 345     if (isMainThread()) { | 
| 349         s_mainThreadStackStart = reinterpret_cast<uintptr_t>(m_startOfStack) - s
      izeof(void*); | 346         s_mainThreadStackStart = reinterpret_cast<uintptr_t>(m_startOfStack) - s
      izeof(void*); | 
| 350         s_mainThreadUnderestimatedStackSize = getUnderestimatedStackSize() - siz
      eof(void*); | 347         s_mainThreadUnderestimatedStackSize = getUnderestimatedStackSize() - siz
      eof(void*); | 
| 351     } | 348     } | 
| 352 | 349 | 
| 353     InitializeHeaps<NumberOfHeaps>::init(m_heaps, this); | 350     InitializeHeaps<NumberOfHeaps>::init(m_heaps, this); | 
| 354 | 351 | 
| 355     m_weakCallbackStack = new CallbackStack(); | 352     m_weakCallbackStack = new CallbackStack(); | 
| 356 } | 353 } | 
| 357 | 354 | 
| 358 ThreadState::~ThreadState() | 355 ThreadState::~ThreadState() | 
| 359 { | 356 { | 
| 360     checkThread(); | 357     checkThread(); | 
|  | 358     ASSERT(gcState() == NoGCScheduled); | 
| 361     delete m_weakCallbackStack; | 359     delete m_weakCallbackStack; | 
| 362     m_weakCallbackStack = 0; | 360     m_weakCallbackStack = 0; | 
| 363     for (int i = 0; i < NumberOfHeaps; i++) | 361     for (int i = 0; i < NumberOfHeaps; i++) | 
| 364         delete m_heaps[i]; | 362         delete m_heaps[i]; | 
| 365     deleteAllValues(m_interruptors); | 363     deleteAllValues(m_interruptors); | 
| 366     **s_threadSpecific = 0; | 364     **s_threadSpecific = 0; | 
| 367     if (isMainThread()) { | 365     if (isMainThread()) { | 
| 368         s_mainThreadStackStart = 0; | 366         s_mainThreadStackStart = 0; | 
| 369         s_mainThreadUnderestimatedStackSize = 0; | 367         s_mainThreadUnderestimatedStackSize = 0; | 
| 370     } | 368     } | 
| (...skipping 387 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 758         return newSize >= 4 * 1024 * 1024 && newSize > 2 * Heap::markedObjectSiz
      e(); | 756         return newSize >= 4 * 1024 * 1024 && newSize > 2 * Heap::markedObjectSiz
      e(); | 
| 759     } | 757     } | 
| 760     // Otherwise, trigger a conservative GC on a 300% increase in size, but not | 758     // Otherwise, trigger a conservative GC on a 300% increase in size, but not | 
| 761     // for less than 32MB.  We set the higher limits in this case because Oilpan | 759     // for less than 32MB.  We set the higher limits in this case because Oilpan | 
| 762     // GC might waste time to trace a lot of unused DOM wrappers or to find | 760     // GC might waste time to trace a lot of unused DOM wrappers or to find | 
| 763     // small amount of garbage. | 761     // small amount of garbage. | 
| 764     // FIXME: Is 32MB reasonable? | 762     // FIXME: Is 32MB reasonable? | 
| 765     return newSize >= 32 * 1024 * 1024 && newSize > 4 * Heap::markedObjectSize()
      ; | 763     return newSize >= 32 * 1024 * 1024 && newSize > 4 * Heap::markedObjectSize()
      ; | 
| 766 } | 764 } | 
| 767 | 765 | 
| 768 bool ThreadState::sweepRequested() | 766 void ThreadState::setGCState(GCState gcState) | 
| 769 { | 767 { | 
| 770     ASSERT(isAnyThreadInGC() || checkThread()); | 768     switch (gcState) { | 
| 771     return m_sweepRequested; | 769     case NoGCScheduled: | 
|  | 770         checkThread(); | 
|  | 771         RELEASE_ASSERT(m_gcState == Sweeping); | 
|  | 772         break; | 
|  | 773     case GCScheduled: | 
|  | 774         checkThread(); | 
|  | 775         RELEASE_ASSERT(m_gcState == NoGCScheduled || m_gcState == GCScheduled ||
       m_gcState == StoppingOtherThreads); | 
|  | 776         break; | 
|  | 777     case StoppingOtherThreads: | 
|  | 778         checkThread(); | 
|  | 779         break; | 
|  | 780     case GCRunning: | 
|  | 781         break; | 
|  | 782     case SweepScheduled: | 
|  | 783         RELEASE_ASSERT(m_gcState == GCRunning); | 
|  | 784         break; | 
|  | 785     case Sweeping: | 
|  | 786         checkThread(); | 
|  | 787         RELEASE_ASSERT(m_gcState == SweepScheduled); | 
|  | 788         break; | 
|  | 789     default: | 
|  | 790         ASSERT_NOT_REACHED(); | 
|  | 791     } | 
|  | 792     m_gcState = gcState; | 
| 772 } | 793 } | 
| 773 | 794 | 
| 774 void ThreadState::setSweepRequested() | 795 ThreadState::GCState ThreadState::gcState() const | 
| 775 { | 796 { | 
| 776     // Sweep requested is set from the thread that initiates garbage | 797     return m_gcState; | 
| 777     // collection which could be different from the thread for this |  | 
| 778     // thread state. Therefore the setting of m_sweepRequested needs a |  | 
| 779     // barrier. |  | 
| 780     atomicTestAndSetToOne(&m_sweepRequested); |  | 
| 781 } |  | 
| 782 |  | 
| 783 void ThreadState::clearSweepRequested() |  | 
| 784 { |  | 
| 785     checkThread(); |  | 
| 786     m_sweepRequested = 0; |  | 
| 787 } |  | 
| 788 |  | 
| 789 bool ThreadState::gcRequested() |  | 
| 790 { |  | 
| 791     checkThread(); |  | 
| 792     return m_gcRequested; |  | 
| 793 } |  | 
| 794 |  | 
| 795 void ThreadState::setGCRequested() |  | 
| 796 { |  | 
| 797     checkThread(); |  | 
| 798     m_gcRequested = true; |  | 
| 799 } |  | 
| 800 |  | 
| 801 void ThreadState::clearGCRequested() |  | 
| 802 { |  | 
| 803     checkThread(); |  | 
| 804     m_gcRequested = false; |  | 
| 805 } | 798 } | 
| 806 | 799 | 
| 807 void ThreadState::didV8GC() | 800 void ThreadState::didV8GC() | 
| 808 { | 801 { | 
| 809     checkThread(); | 802     checkThread(); | 
| 810     m_didV8GCAfterLastGC = true; | 803     m_didV8GCAfterLastGC = true; | 
| 811 } | 804 } | 
| 812 | 805 | 
| 813 void ThreadState::performPendingGC(StackState stackState) | 806 void ThreadState::performPendingGC(StackState stackState) | 
| 814 { | 807 { | 
| 815     if (stackState == NoHeapPointersOnStack) { | 808     if (stackState == NoHeapPointersOnStack) { | 
| 816         if (forcePreciseGCForTesting()) { | 809         if (forcePreciseGCForTesting()) { | 
| 817             setForcePreciseGCForTesting(false); | 810             setForcePreciseGCForTesting(false); | 
| 818             Heap::collectAllGarbage(); | 811             Heap::collectAllGarbage(); | 
| 819         } else if (gcRequested()) { | 812         } else if (gcState() == GCScheduled) { | 
| 820             Heap::collectGarbage(NoHeapPointersOnStack); | 813             Heap::collectGarbage(NoHeapPointersOnStack); | 
| 821         } | 814         } | 
| 822     } | 815     } | 
| 823 } | 816 } | 
| 824 | 817 | 
| 825 void ThreadState::setForcePreciseGCForTesting(bool value) | 818 void ThreadState::setForcePreciseGCForTesting(bool value) | 
| 826 { | 819 { | 
| 827     checkThread(); | 820     checkThread(); | 
| 828     m_forcePreciseGCForTesting = value; | 821     m_forcePreciseGCForTesting = value; | 
| 829 } | 822 } | 
| (...skipping 30 matching lines...) Expand all  Loading... | 
| 860 } | 853 } | 
| 861 | 854 | 
| 862 void ThreadState::flushHeapDoesNotContainCacheIfNeeded() | 855 void ThreadState::flushHeapDoesNotContainCacheIfNeeded() | 
| 863 { | 856 { | 
| 864     if (m_shouldFlushHeapDoesNotContainCache) { | 857     if (m_shouldFlushHeapDoesNotContainCache) { | 
| 865         Heap::flushHeapDoesNotContainCache(); | 858         Heap::flushHeapDoesNotContainCache(); | 
| 866         m_shouldFlushHeapDoesNotContainCache = false; | 859         m_shouldFlushHeapDoesNotContainCache = false; | 
| 867     } | 860     } | 
| 868 } | 861 } | 
| 869 | 862 | 
| 870 void ThreadState::prepareForGC() | 863 void ThreadState::preGC() | 
| 871 { | 864 { | 
| 872     for (int i = 0; i < NumberOfHeaps; i++) { | 865     for (int i = 0; i < NumberOfHeaps; i++) { | 
| 873         BaseHeap* heap = m_heaps[i]; | 866         BaseHeap* heap = m_heaps[i]; | 
| 874         heap->makeConsistentForSweeping(); | 867         heap->makeConsistentForSweeping(); | 
| 875         // If a new GC is requested before this thread got around to sweep, ie. 
      due to the | 868         // If a new GC is requested before this thread got around to sweep, ie. 
      due to the | 
| 876         // thread doing a long running operation, we clear the mark bits and mar
      k any of | 869         // thread doing a long running operation, we clear the mark bits and mar
      k any of | 
| 877         // the dead objects as dead. The latter is used to ensure the next GC ma
      rking does | 870         // the dead objects as dead. The latter is used to ensure the next GC ma
      rking does | 
| 878         // not trace already dead objects. If we trace a dead object we could en
      d up tracing | 871         // not trace already dead objects. If we trace a dead object we could en
      d up tracing | 
| 879         // into garbage or the middle of another object via the newly conservati
      vely found | 872         // into garbage or the middle of another object via the newly conservati
      vely found | 
| 880         // object. | 873         // object. | 
| 881         if (sweepRequested()) | 874         if (gcState() == ThreadState::SweepScheduled) | 
| 882             heap->markUnmarkedObjectsDead(); | 875             heap->markUnmarkedObjectsDead(); | 
| 883     } | 876     } | 
| 884     prepareRegionTree(); | 877     prepareRegionTree(); | 
| 885     setSweepRequested(); | 878     setGCState(ThreadState::GCRunning); | 
| 886     flushHeapDoesNotContainCacheIfNeeded(); | 879     flushHeapDoesNotContainCacheIfNeeded(); | 
| 887 } | 880 } | 
| 888 | 881 | 
|  | 882 void ThreadState::postGC() | 
|  | 883 { | 
|  | 884     setGCState(ThreadState::SweepScheduled); | 
|  | 885 } | 
|  | 886 | 
| 889 void ThreadState::setupHeapsForTermination() | 887 void ThreadState::setupHeapsForTermination() | 
| 890 { | 888 { | 
| 891     for (int i = 0; i < NumberOfHeaps; i++) | 889     for (int i = 0; i < NumberOfHeaps; i++) | 
| 892         m_heaps[i]->prepareHeapForTermination(); | 890         m_heaps[i]->prepareHeapForTermination(); | 
| 893 } | 891 } | 
| 894 | 892 | 
| 895 BaseHeapPage* ThreadState::pageFromAddress(Address address) | 893 BaseHeapPage* ThreadState::pageFromAddress(Address address) | 
| 896 { | 894 { | 
| 897     for (int i = 0; i < NumberOfHeaps; i++) { | 895     for (int i = 0; i < NumberOfHeaps; i++) { | 
| 898         if (BaseHeapPage* page = m_heaps[i]->pageFromAddress(address)) | 896         if (BaseHeapPage* page = m_heaps[i]->pageFromAddress(address)) | 
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1006 | 1004 | 
| 1007     ASSERT(!m_safePointStackCopy.size()); | 1005     ASSERT(!m_safePointStackCopy.size()); | 
| 1008     m_safePointStackCopy.resize(slotCount); | 1006     m_safePointStackCopy.resize(slotCount); | 
| 1009     for (size_t i = 0; i < slotCount; ++i) { | 1007     for (size_t i = 0; i < slotCount; ++i) { | 
| 1010         m_safePointStackCopy[i] = from[i]; | 1008         m_safePointStackCopy[i] = from[i]; | 
| 1011     } | 1009     } | 
| 1012 } | 1010 } | 
| 1013 | 1011 | 
| 1014 void ThreadState::performPendingSweep() | 1012 void ThreadState::performPendingSweep() | 
| 1015 { | 1013 { | 
| 1016     if (!sweepRequested()) | 1014     if (gcState() != SweepScheduled) | 
| 1017         return; | 1015         return; | 
|  | 1016     setGCState(Sweeping); | 
| 1018 | 1017 | 
| 1019 #if ENABLE(GC_PROFILE_HEAP) | 1018 #if ENABLE(GC_PROFILE_HEAP) | 
| 1020     // We snapshot the heap prior to sweeping to get numbers for both resources | 1019     // We snapshot the heap prior to sweeping to get numbers for both resources | 
| 1021     // that have been allocated since the last GC and for resources that are | 1020     // that have been allocated since the last GC and for resources that are | 
| 1022     // going to be freed. | 1021     // going to be freed. | 
| 1023     bool gcTracingEnabled; | 1022     bool gcTracingEnabled; | 
| 1024     TRACE_EVENT_CATEGORY_GROUP_ENABLED("blink_gc", &gcTracingEnabled); | 1023     TRACE_EVENT_CATEGORY_GROUP_ENABLED("blink_gc", &gcTracingEnabled); | 
| 1025     if (gcTracingEnabled) | 1024     if (gcTracingEnabled) | 
| 1026         snapshot(); | 1025         snapshot(); | 
| 1027 #endif | 1026 #endif | 
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1063             TRACE_EVENT0("blink_gc", "ThreadState::sweepFinalizedHeaps"); | 1062             TRACE_EVENT0("blink_gc", "ThreadState::sweepFinalizedHeaps"); | 
| 1064             for (int i = 0; i < NumberOfFinalizedHeaps; i++) { | 1063             for (int i = 0; i < NumberOfFinalizedHeaps; i++) { | 
| 1065                 m_heaps[FirstFinalizedHeap + i]->sweep(); | 1064                 m_heaps[FirstFinalizedHeap + i]->sweep(); | 
| 1066             } | 1065             } | 
| 1067         } | 1066         } | 
| 1068 | 1067 | 
| 1069         for (int i = 0; i < NumberOfHeaps; i++) | 1068         for (int i = 0; i < NumberOfHeaps; i++) | 
| 1070             m_heaps[i]->postSweepProcessing(); | 1069             m_heaps[i]->postSweepProcessing(); | 
| 1071     } | 1070     } | 
| 1072 | 1071 | 
| 1073     clearGCRequested(); |  | 
| 1074     m_didV8GCAfterLastGC = false; | 1072     m_didV8GCAfterLastGC = false; | 
| 1075     clearSweepRequested(); | 1073     setGCState(ThreadState::NoGCScheduled); | 
| 1076 | 1074 | 
| 1077     // If we collected less than 50% of objects, record that the collection rate | 1075     // If we collected less than 50% of objects, record that the collection rate | 
| 1078     // is low which we use to determine when to perform the next GC. | 1076     // is low which we use to determine when to perform the next GC. | 
| 1079     // FIXME: We should make m_lowCollectionRate available in non-main threads. | 1077     // FIXME: We should make m_lowCollectionRate available in non-main threads. | 
| 1080     // FIXME: Heap::markedObjectSize() may not be accurate because other threads | 1078     // FIXME: Heap::markedObjectSize() may not be accurate because other threads | 
| 1081     // may not have finished sweeping. | 1079     // may not have finished sweeping. | 
| 1082     if (isMainThread()) | 1080     if (isMainThread()) | 
| 1083         m_lowCollectionRate = Heap::markedObjectSize() > (allocatedObjectSizeBef
      oreSweeping / 2); | 1081         m_lowCollectionRate = Heap::markedObjectSize() > (allocatedObjectSizeBef
      oreSweeping / 2); | 
| 1084 | 1082 | 
| 1085     if (Platform::current()) { | 1083     if (Platform::current()) { | 
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1144         if (entry.value(entry.key, visitor)) | 1142         if (entry.value(entry.key, visitor)) | 
| 1145             deadObjects.append(entry.key); | 1143             deadObjects.append(entry.key); | 
| 1146     } | 1144     } | 
| 1147     // FIXME: removeAll is inefficient.  It can shrink repeatedly. | 1145     // FIXME: removeAll is inefficient.  It can shrink repeatedly. | 
| 1148     m_preFinalizers.removeAll(deadObjects); | 1146     m_preFinalizers.removeAll(deadObjects); | 
| 1149 } | 1147 } | 
| 1150 | 1148 | 
| 1151 #if ENABLE(GC_PROFILE_MARKING) | 1149 #if ENABLE(GC_PROFILE_MARKING) | 
| 1152 const GCInfo* ThreadState::findGCInfoFromAllThreads(Address address) | 1150 const GCInfo* ThreadState::findGCInfoFromAllThreads(Address address) | 
| 1153 { | 1151 { | 
| 1154     bool needLockForIteration = !isAnyThreadInGC(); | 1152     bool needLockForIteration = !Heap::isInGC(); | 
| 1155     if (needLockForIteration) | 1153     if (needLockForIteration) | 
| 1156         threadAttachMutex().lock(); | 1154         threadAttachMutex().lock(); | 
| 1157 | 1155 | 
| 1158     ThreadState::AttachedThreadStateSet& threads = attachedThreads(); | 1156     ThreadState::AttachedThreadStateSet& threads = attachedThreads(); | 
| 1159     for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end
       = threads.end(); it != end; ++it) { | 1157     for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end
       = threads.end(); it != end; ++it) { | 
| 1160         if (const GCInfo* gcInfo = (*it)->findGCInfo(address)) { | 1158         if (const GCInfo* gcInfo = (*it)->findGCInfo(address)) { | 
| 1161             if (needLockForIteration) | 1159             if (needLockForIteration) | 
| 1162                 threadAttachMutex().unlock(); | 1160                 threadAttachMutex().unlock(); | 
| 1163             return gcInfo; | 1161             return gcInfo; | 
| 1164         } | 1162         } | 
| 1165     } | 1163     } | 
| 1166     if (needLockForIteration) | 1164     if (needLockForIteration) | 
| 1167         threadAttachMutex().unlock(); | 1165         threadAttachMutex().unlock(); | 
| 1168     return 0; | 1166     return 0; | 
| 1169 } | 1167 } | 
| 1170 #endif | 1168 #endif | 
| 1171 | 1169 | 
| 1172 } // namespace blink | 1170 } // namespace blink | 
| OLD | NEW | 
|---|