 Chromium Code Reviews
 Chromium Code Reviews Issue 393163003:
  Add coalescing timer to ResourceScheduler. CL 2 from BUG=128035.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@rsblank
    
  
    Issue 393163003:
  Add coalescing timer to ResourceScheduler. CL 2 from BUG=128035.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@rsblank| OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "content/browser/loader/resource_scheduler.h" | 5 #include "content/browser/loader/resource_scheduler.h" | 
| 6 | 6 | 
| 7 #include "base/memory/scoped_vector.h" | 7 #include "base/memory/scoped_vector.h" | 
| 8 #include "base/message_loop/message_loop.h" | 8 #include "base/message_loop/message_loop.h" | 
| 9 #include "base/strings/string_number_conversions.h" | 9 #include "base/strings/string_number_conversions.h" | 
| 10 #include "base/timer/mock_timer.h" | |
| 11 #include "base/timer/timer.h" | |
| 10 #include "content/browser/browser_thread_impl.h" | 12 #include "content/browser/browser_thread_impl.h" | 
| 11 #include "content/browser/loader/resource_dispatcher_host_impl.h" | 13 #include "content/browser/loader/resource_dispatcher_host_impl.h" | 
| 12 #include "content/browser/loader/resource_message_filter.h" | 14 #include "content/browser/loader/resource_message_filter.h" | 
| 13 #include "content/browser/loader/resource_request_info_impl.h" | 15 #include "content/browser/loader/resource_request_info_impl.h" | 
| 14 #include "content/common/resource_messages.h" | 16 #include "content/common/resource_messages.h" | 
| 15 #include "content/public/browser/resource_context.h" | 17 #include "content/public/browser/resource_context.h" | 
| 16 #include "content/public/browser/resource_controller.h" | 18 #include "content/public/browser/resource_controller.h" | 
| 17 #include "content/public/browser/resource_throttle.h" | 19 #include "content/public/browser/resource_throttle.h" | 
| 18 #include "content/public/common/process_type.h" | 20 #include "content/public/common/process_type.h" | 
| 19 #include "content/public/common/resource_type.h" | 21 #include "content/public/common/resource_type.h" | 
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 130 | 132 | 
| 131 FakeResourceContext context_; | 133 FakeResourceContext context_; | 
| 132 }; | 134 }; | 
| 133 | 135 | 
| 134 class ResourceSchedulerTest : public testing::Test { | 136 class ResourceSchedulerTest : public testing::Test { | 
| 135 protected: | 137 protected: | 
| 136 ResourceSchedulerTest() | 138 ResourceSchedulerTest() | 
| 137 : next_request_id_(0), | 139 : next_request_id_(0), | 
| 138 ui_thread_(BrowserThread::UI, &message_loop_), | 140 ui_thread_(BrowserThread::UI, &message_loop_), | 
| 139 io_thread_(BrowserThread::IO, &message_loop_) { | 141 io_thread_(BrowserThread::IO, &message_loop_) { | 
| 142 mock_timer_ = new base::MockTimer(true, true); | |
| 143 scheduler_.set_timer_for_testing(scoped_ptr<base::Timer>(mock_timer_)); | |
| 144 | |
| 145 // TODO(aiolos): remove when throttling and coalescing have both landed | |
| 146 scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */, | |
| 147 false /* should_coalesce */); | |
| 140 | 148 | 
| 141 scheduler_.OnClientCreated(kChildId, kRouteId); | 149 scheduler_.OnClientCreated(kChildId, kRouteId); | 
| 142 scheduler_.OnVisibilityChanged(kChildId, kRouteId, true); | 150 scheduler_.OnVisibilityChanged(kChildId, kRouteId, true); | 
| 143 scheduler_.OnClientCreated(kBackgroundChildId, kBackgroundRouteId); | 151 scheduler_.OnClientCreated(kBackgroundChildId, kBackgroundRouteId); | 
| 144 context_.set_http_server_properties(http_server_properties_.GetWeakPtr()); | 152 context_.set_http_server_properties(http_server_properties_.GetWeakPtr()); | 
| 145 } | 153 } | 
| 146 | 154 | 
| 147 virtual ~ResourceSchedulerTest() { | 155 virtual ~ResourceSchedulerTest() { | 
| 148 scheduler_.OnClientDeleted(kChildId, kRouteId); | 156 scheduler_.OnClientDeleted(kChildId, kRouteId); | 
| 149 scheduler_.OnClientDeleted(kBackgroundChildId, kBackgroundRouteId); | 157 scheduler_.OnClientDeleted(kBackgroundChildId, kBackgroundRouteId); | 
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 250 scoped_refptr<FakeResourceMessageFilter> filter( | 258 scoped_refptr<FakeResourceMessageFilter> filter( | 
| 251 new FakeResourceMessageFilter(kChildId)); | 259 new FakeResourceMessageFilter(kChildId)); | 
| 252 const ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest( | 260 const ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest( | 
| 253 request->url_request()); | 261 request->url_request()); | 
| 254 const GlobalRequestID& id = info->GetGlobalRequestID(); | 262 const GlobalRequestID& id = info->GetGlobalRequestID(); | 
| 255 ResourceHostMsg_DidChangePriority msg(id.request_id, new_priority, | 263 ResourceHostMsg_DidChangePriority msg(id.request_id, new_priority, | 
| 256 intra_priority); | 264 intra_priority); | 
| 257 rdh_.OnMessageReceived(msg, filter.get()); | 265 rdh_.OnMessageReceived(msg, filter.get()); | 
| 258 } | 266 } | 
| 259 | 267 | 
| 268 void FireCoalescingTimer() { | |
| 269 EXPECT_TRUE(mock_timer_->IsRunning()); | |
| 270 mock_timer_->Fire(); | |
| 271 } | |
| 272 | |
| 260 int next_request_id_; | 273 int next_request_id_; | 
| 261 base::MessageLoopForIO message_loop_; | 274 base::MessageLoopForIO message_loop_; | 
| 262 BrowserThreadImpl ui_thread_; | 275 BrowserThreadImpl ui_thread_; | 
| 263 BrowserThreadImpl io_thread_; | 276 BrowserThreadImpl io_thread_; | 
| 264 ResourceDispatcherHostImpl rdh_; | 277 ResourceDispatcherHostImpl rdh_; | 
| 265 ResourceScheduler scheduler_; | 278 ResourceScheduler scheduler_; | 
| 279 base::MockTimer* mock_timer_; | |
| 266 net::HttpServerPropertiesImpl http_server_properties_; | 280 net::HttpServerPropertiesImpl http_server_properties_; | 
| 267 net::TestURLRequestContext context_; | 281 net::TestURLRequestContext context_; | 
| 268 }; | 282 }; | 
| 269 | 283 | 
| 270 TEST_F(ResourceSchedulerTest, OneIsolatedLowRequest) { | 284 TEST_F(ResourceSchedulerTest, OneIsolatedLowRequest) { | 
| 271 scoped_ptr<TestRequest> request(NewRequest("http://host/1", net::LOWEST)); | 285 scoped_ptr<TestRequest> request(NewRequest("http://host/1", net::LOWEST)); | 
| 272 EXPECT_TRUE(request->started()); | 286 EXPECT_TRUE(request->started()); | 
| 273 } | 287 } | 
| 274 | 288 | 
| 275 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilIdle) { | 289 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilIdle) { | 
| (...skipping 1468 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1744 kBackgroundRouteId)); | 1758 kBackgroundRouteId)); | 
| 1745 EXPECT_EQ(ResourceScheduler::THROTTLED, | 1759 EXPECT_EQ(ResourceScheduler::THROTTLED, | 
| 1746 scheduler_.GetClientStateForTesting(kBackgroundChildId2, | 1760 scheduler_.GetClientStateForTesting(kBackgroundChildId2, | 
| 1747 kBackgroundRouteId2)); | 1761 kBackgroundRouteId2)); | 
| 1748 EXPECT_EQ(ResourceScheduler::ACTIVE_AND_LOADING, | 1762 EXPECT_EQ(ResourceScheduler::ACTIVE_AND_LOADING, | 
| 1749 scheduler_.GetClientStateForTesting(kChildId, kRouteId)); | 1763 scheduler_.GetClientStateForTesting(kChildId, kRouteId)); | 
| 1750 | 1764 | 
| 1751 scheduler_.OnClientDeleted(kBackgroundChildId2, kBackgroundRouteId2); | 1765 scheduler_.OnClientDeleted(kBackgroundChildId2, kBackgroundRouteId2); | 
| 1752 } | 1766 } | 
| 1753 | 1767 | 
| 1768 TEST_F(ResourceSchedulerTest, CoalescedClientCreationStartsTimer) { | |
| 1769 scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */, | |
| 1770 true /* should_coalesce */); | |
| 1771 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1772 scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true); | |
| 1773 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1774 scheduler_.OnLoadingStateChanged( | |
| 1775 kBackgroundChildId, kBackgroundRouteId, true); | |
| 1776 EXPECT_EQ(ResourceScheduler::COALESCED, | |
| 1777 scheduler_.GetClientStateForTesting(kBackgroundChildId, | |
| 1778 kBackgroundRouteId)); | |
| 1779 EXPECT_TRUE(mock_timer_->IsRunning()); | |
| 1780 } | |
| 1781 | |
| 1782 TEST_F(ResourceSchedulerTest, LastCoalescedClientDeletionStopsTimer) { | |
| 1783 scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */, | |
| 1784 true /* should_coalesce */); | |
| 1785 scheduler_.OnClientCreated(kBackgroundChildId2, kBackgroundRouteId2); | |
| 1786 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1787 scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true); | |
| 1788 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1789 scheduler_.OnLoadingStateChanged( | |
| 1790 kBackgroundChildId, kBackgroundRouteId, true); | |
| 1791 EXPECT_EQ(ResourceScheduler::COALESCED, | |
| 1792 scheduler_.GetClientStateForTesting(kBackgroundChildId, | |
| 1793 kBackgroundRouteId)); | |
| 1794 scheduler_.OnLoadingStateChanged( | |
| 1795 kBackgroundChildId2, kBackgroundRouteId2, true); | |
| 1796 EXPECT_EQ(ResourceScheduler::COALESCED, | |
| 1797 scheduler_.GetClientStateForTesting(kBackgroundChildId2, | |
| 1798 kBackgroundRouteId2)); | |
| 1799 EXPECT_TRUE(mock_timer_->IsRunning()); | |
| 1800 | |
| 1801 scheduler_.OnClientDeleted(kBackgroundChildId, kBackgroundRouteId); | |
| 1802 EXPECT_TRUE(mock_timer_->IsRunning()); | |
| 1803 | |
| 1804 scheduler_.OnClientDeleted(kBackgroundChildId2, kBackgroundRouteId2); | |
| 1805 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1806 | |
| 1807 // To avoid errors on test tear down. | |
| 1808 scheduler_.OnClientCreated(kBackgroundChildId, kBackgroundRouteId); | |
| 1809 } | |
| 1810 | |
| 1811 TEST_F(ResourceSchedulerTest, LastCoalescedClientStartsLoadingStopsTimer) { | |
| 1812 scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */, | |
| 1813 true /* should_coalesce */); | |
| 1814 scheduler_.OnClientCreated(kBackgroundChildId2, kBackgroundRouteId2); | |
| 1815 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1816 scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true); | |
| 1817 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1818 scheduler_.OnLoadingStateChanged( | |
| 1819 kBackgroundChildId, kBackgroundRouteId, true); | |
| 1820 EXPECT_EQ(ResourceScheduler::COALESCED, | |
| 1821 scheduler_.GetClientStateForTesting(kBackgroundChildId, | |
| 1822 kBackgroundRouteId)); | |
| 1823 scheduler_.OnLoadingStateChanged( | |
| 1824 kBackgroundChildId2, kBackgroundRouteId2, true); | |
| 1825 EXPECT_EQ(ResourceScheduler::COALESCED, | |
| 1826 scheduler_.GetClientStateForTesting(kBackgroundChildId2, | |
| 1827 kBackgroundRouteId2)); | |
| 1828 EXPECT_TRUE(mock_timer_->IsRunning()); | |
| 1829 | |
| 1830 scheduler_.OnLoadingStateChanged( | |
| 1831 kBackgroundChildId, kBackgroundRouteId, false); | |
| 1832 EXPECT_TRUE(mock_timer_->IsRunning()); | |
| 1833 | |
| 1834 scheduler_.OnLoadingStateChanged( | |
| 1835 kBackgroundChildId2, kBackgroundRouteId2, false); | |
| 1836 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1837 | |
| 1838 // To avoid errors on test tear down. | |
| 
mmenke
2014/07/25 16:33:27
nit:  Style guide encourages complete sentences.
 | |
| 1839 scheduler_.OnClientDeleted(kBackgroundChildId2, kBackgroundRouteId2); | |
| 1840 } | |
| 1841 | |
| 1842 TEST_F(ResourceSchedulerTest, LastCoalescedClientBecomesVisibleStopsTimer) { | |
| 1843 scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */, | |
| 1844 true /* should_coalesce */); | |
| 1845 scheduler_.OnClientCreated(kBackgroundChildId2, kBackgroundRouteId2); | |
| 1846 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1847 scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true); | |
| 1848 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1849 scheduler_.OnLoadingStateChanged( | |
| 1850 kBackgroundChildId, kBackgroundRouteId, true); | |
| 1851 EXPECT_EQ(ResourceScheduler::COALESCED, | |
| 1852 scheduler_.GetClientStateForTesting(kBackgroundChildId, | |
| 1853 kBackgroundRouteId)); | |
| 1854 scheduler_.OnLoadingStateChanged( | |
| 1855 kBackgroundChildId2, kBackgroundRouteId2, true); | |
| 1856 EXPECT_EQ(ResourceScheduler::COALESCED, | |
| 1857 scheduler_.GetClientStateForTesting(kBackgroundChildId2, | |
| 1858 kBackgroundRouteId2)); | |
| 1859 EXPECT_TRUE(mock_timer_->IsRunning()); | |
| 1860 | |
| 1861 scheduler_.OnVisibilityChanged(kBackgroundChildId, kBackgroundRouteId, true); | |
| 1862 EXPECT_TRUE(mock_timer_->IsRunning()); | |
| 1863 | |
| 1864 scheduler_.OnVisibilityChanged( | |
| 1865 kBackgroundChildId2, kBackgroundRouteId2, true); | |
| 1866 EXPECT_FALSE(mock_timer_->IsRunning()); | |
| 1867 | |
| 1868 // To avoid errors on test tear down. | |
| 1869 scheduler_.OnClientDeleted(kBackgroundChildId2, kBackgroundRouteId2); | |
| 1870 } | |
| 1871 | |
| 1872 TEST_F(ResourceSchedulerTest, CoalescedRequestsIssueOnTimer) { | |
| 1873 scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */, | |
| 1874 true /* should_coalesce */); | |
| 1875 scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true); | |
| 1876 scheduler_.OnLoadingStateChanged( | |
| 1877 kBackgroundChildId, kBackgroundRouteId, true); | |
| 1878 EXPECT_EQ(ResourceScheduler::COALESCED, | |
| 1879 scheduler_.GetClientStateForTesting(kBackgroundChildId, | |
| 1880 kBackgroundRouteId)); | |
| 1881 EXPECT_TRUE(scheduler_.active_clients_loaded()); | |
| 1882 | |
| 1883 scoped_ptr<TestRequest> high( | |
| 1884 NewBackgroundRequest("http://host/high", net::HIGHEST)); | |
| 1885 scoped_ptr<TestRequest> low( | |
| 1886 NewBackgroundRequest("http://host/low", net::LOWEST)); | |
| 1887 EXPECT_FALSE(high->started()); | |
| 1888 EXPECT_FALSE(low->started()); | |
| 1889 | |
| 1890 FireCoalescingTimer(); | |
| 1891 | |
| 1892 EXPECT_TRUE(high->started()); | |
| 1893 EXPECT_TRUE(low->started()); | |
| 1894 } | |
| 1895 | |
| 1896 TEST_F(ResourceSchedulerTest, CoalescedRequestsUnthrottleCorrectlyOnTimer) { | |
| 1897 scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */, | |
| 1898 true /* should_coalesce */); | |
| 1899 scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true); | |
| 1900 scheduler_.OnLoadingStateChanged( | |
| 1901 kBackgroundChildId, kBackgroundRouteId, true); | |
| 1902 EXPECT_EQ(ResourceScheduler::COALESCED, | |
| 1903 scheduler_.GetClientStateForTesting(kBackgroundChildId, | |
| 1904 kBackgroundRouteId)); | |
| 1905 EXPECT_TRUE(scheduler_.active_clients_loaded()); | |
| 1906 | |
| 1907 scoped_ptr<TestRequest> high( | |
| 1908 NewBackgroundRequest("http://host/high", net::HIGHEST)); | |
| 1909 scoped_ptr<TestRequest> high2( | |
| 1910 NewBackgroundRequest("http://host/high", net::HIGHEST)); | |
| 1911 scoped_ptr<TestRequest> high3( | |
| 1912 NewBackgroundRequest("http://host/high", net::HIGHEST)); | |
| 1913 scoped_ptr<TestRequest> high4( | |
| 1914 NewBackgroundRequest("http://host/high", net::HIGHEST)); | |
| 1915 scoped_ptr<TestRequest> low( | |
| 1916 NewBackgroundRequest("http://host/low", net::LOWEST)); | |
| 1917 scoped_ptr<TestRequest> low2( | |
| 1918 NewBackgroundRequest("http://host/low", net::LOWEST)); | |
| 1919 scoped_ptr<TestRequest> low3( | |
| 1920 NewBackgroundRequest("http://host/low", net::LOWEST)); | |
| 1921 scoped_ptr<TestRequest> low4( | |
| 1922 NewBackgroundRequest("http://host/low", net::LOWEST)); | |
| 1923 | |
| 1924 http_server_properties_.SetSupportsSpdy(net::HostPortPair("spdyhost", 443), | |
| 1925 true); | |
| 1926 scoped_ptr<TestRequest> low_spdy( | |
| 1927 NewBackgroundRequest("https://spdyhost/low", net::LOW)); | |
| 1928 scoped_ptr<TestRequest> sync_request( | |
| 1929 NewBackgroundSyncRequest("http://host/req", net::LOW)); | |
| 1930 scoped_ptr<TestRequest> non_http_request( | |
| 1931 NewBackgroundRequest("chrome-extension://req", net::LOW)); | |
| 1932 | |
| 1933 // Sync requests should issue immediately. | |
| 1934 EXPECT_TRUE(sync_request->started()); | |
| 1935 // Non-http(s) requests should issue immediately. | |
| 1936 EXPECT_TRUE(non_http_request->started()); | |
| 1937 // Nothing else should issue without a timer fire. | |
| 1938 EXPECT_FALSE(high->started()); | |
| 1939 EXPECT_FALSE(high2->started()); | |
| 1940 EXPECT_FALSE(high3->started()); | |
| 1941 EXPECT_FALSE(high4->started()); | |
| 1942 EXPECT_FALSE(low->started()); | |
| 1943 EXPECT_FALSE(low2->started()); | |
| 1944 EXPECT_FALSE(low3->started()); | |
| 1945 EXPECT_FALSE(low4->started()); | |
| 1946 EXPECT_FALSE(low_spdy->started()); | |
| 1947 | |
| 1948 FireCoalescingTimer(); | |
| 1949 | |
| 1950 // All high priority requests should issue. | |
| 1951 EXPECT_TRUE(high->started()); | |
| 1952 EXPECT_TRUE(high2->started()); | |
| 1953 EXPECT_TRUE(high3->started()); | |
| 1954 EXPECT_TRUE(high4->started()); | |
| 1955 // There should only be one net::LOWEST priority request issued with | |
| 1956 // non-delayable requests in flight. | |
| 1957 EXPECT_TRUE(low->started()); | |
| 1958 EXPECT_FALSE(low2->started()); | |
| 1959 EXPECT_FALSE(low3->started()); | |
| 1960 EXPECT_FALSE(low4->started()); | |
| 1961 // Spdy-Enable requests should issue regardless of priority. | |
| 1962 EXPECT_TRUE(low_spdy->started()); | |
| 1963 } | |
| 1964 | |
| 1965 TEST_F(ResourceSchedulerTest, CoalescedRequestsWaitForNextTimer) { | |
| 1966 scheduler_.SetThrottleOptionsForTesting(true /* should_throttle */, | |
| 1967 true /* should_coalesce */); | |
| 1968 scheduler_.OnLoadingStateChanged(kChildId, kRouteId, true); | |
| 1969 scheduler_.OnLoadingStateChanged( | |
| 1970 kBackgroundChildId, kBackgroundRouteId, true); | |
| 1971 | |
| 1972 EXPECT_EQ(ResourceScheduler::COALESCED, | |
| 1973 scheduler_.GetClientStateForTesting(kBackgroundChildId, | |
| 1974 kBackgroundRouteId)); | |
| 1975 EXPECT_TRUE(scheduler_.active_clients_loaded()); | |
| 1976 | |
| 1977 scoped_ptr<TestRequest> high( | |
| 1978 NewBackgroundRequest("http://host/high", net::HIGHEST)); | |
| 1979 EXPECT_FALSE(high->started()); | |
| 1980 | |
| 1981 FireCoalescingTimer(); | |
| 1982 | |
| 1983 scoped_ptr<TestRequest> high2( | |
| 1984 NewBackgroundRequest("http://host/high2", net::HIGHEST)); | |
| 1985 scoped_ptr<TestRequest> low( | |
| 1986 NewBackgroundRequest("http://host/low", net::LOWEST)); | |
| 1987 | |
| 1988 EXPECT_TRUE(high->started()); | |
| 1989 EXPECT_FALSE(high2->started()); | |
| 1990 EXPECT_FALSE(low->started()); | |
| 1991 | |
| 1992 FireCoalescingTimer(); | |
| 1993 | |
| 1994 EXPECT_TRUE(high->started()); | |
| 1995 EXPECT_TRUE(high2->started()); | |
| 1996 EXPECT_TRUE(low->started()); | |
| 1997 } | |
| 
mmenke
2014/07/25 16:33:27
Good job on the tests, but two cases we don't seem
 | |
| 1998 | |
| 1754 } // unnamed namespace | 1999 } // unnamed namespace | 
| 1755 | 2000 | 
| 1756 } // namespace content | 2001 } // namespace content | 
| OLD | NEW |