Chromium Code Reviews| 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/renderer_host/render_widget_host_view_android.h" | 5 #include "content/browser/renderer_host/render_widget_host_view_android.h" |
| 6 | 6 |
| 7 #include <android/bitmap.h> | 7 #include <android/bitmap.h> |
| 8 | 8 |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 964 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 975 void RenderWidgetHostViewAndroid::CopyFromCompositingSurface( | 975 void RenderWidgetHostViewAndroid::CopyFromCompositingSurface( |
| 976 const gfx::Rect& src_subrect, | 976 const gfx::Rect& src_subrect, |
| 977 const gfx::Size& dst_size, | 977 const gfx::Size& dst_size, |
| 978 const ReadbackRequestCallback& callback, | 978 const ReadbackRequestCallback& callback, |
| 979 const SkColorType preferred_color_type) { | 979 const SkColorType preferred_color_type) { |
| 980 TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::CopyFromCompositingSurface"); | 980 TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::CopyFromCompositingSurface"); |
| 981 if (!host_ || !IsSurfaceAvailableForCopy()) { | 981 if (!host_ || !IsSurfaceAvailableForCopy()) { |
| 982 callback.Run(SkBitmap(), READBACK_SURFACE_UNAVAILABLE); | 982 callback.Run(SkBitmap(), READBACK_SURFACE_UNAVAILABLE); |
| 983 return; | 983 return; |
| 984 } | 984 } |
| 985 if (!content_view_core_ || !(content_view_core_->GetWindowAndroid())) { | 985 if (!content_view_core_ || !(view_.GetWindowAndroid())) { |
|
boliu
2016/10/31 01:45:52
don't need the cvc check if it's not used
Jinsuk Kim
2016/10/31 02:01:16
Done.
| |
| 986 callback.Run(SkBitmap(), READBACK_FAILED); | 986 callback.Run(SkBitmap(), READBACK_FAILED); |
| 987 return; | 987 return; |
| 988 } | 988 } |
| 989 | 989 |
| 990 base::TimeTicks start_time = base::TimeTicks::Now(); | 990 base::TimeTicks start_time = base::TimeTicks::Now(); |
| 991 const display::Display& display = | 991 const display::Display& display = |
| 992 display::Screen::GetScreen()->GetPrimaryDisplay(); | 992 display::Screen::GetScreen()->GetPrimaryDisplay(); |
| 993 float device_scale_factor = display.device_scale_factor(); | 993 float device_scale_factor = display.device_scale_factor(); |
| 994 gfx::Size dst_size_in_pixel = | 994 gfx::Size dst_size_in_pixel = |
| 995 gfx::ConvertRectToPixel(device_scale_factor, gfx::Rect(dst_size)).size(); | 995 gfx::ConvertRectToPixel(device_scale_factor, gfx::Rect(dst_size)).size(); |
| 996 gfx::Rect src_subrect_in_pixel = | 996 gfx::Rect src_subrect_in_pixel = |
| 997 gfx::ConvertRectToPixel(device_scale_factor, src_subrect); | 997 gfx::ConvertRectToPixel(device_scale_factor, src_subrect); |
| 998 | 998 |
| 999 if (!using_browser_compositor_) { | 999 if (!using_browser_compositor_) { |
| 1000 SynchronousCopyContents(src_subrect_in_pixel, dst_size_in_pixel, callback, | 1000 SynchronousCopyContents(src_subrect_in_pixel, dst_size_in_pixel, callback, |
| 1001 preferred_color_type); | 1001 preferred_color_type); |
| 1002 UMA_HISTOGRAM_TIMES("Compositing.CopyFromSurfaceTimeSynchronous", | 1002 UMA_HISTOGRAM_TIMES("Compositing.CopyFromSurfaceTimeSynchronous", |
| 1003 base::TimeTicks::Now() - start_time); | 1003 base::TimeTicks::Now() - start_time); |
| 1004 return; | 1004 return; |
| 1005 } | 1005 } |
| 1006 | 1006 |
| 1007 ui::WindowAndroidCompositor* compositor = | 1007 ui::WindowAndroidCompositor* compositor = |
| 1008 content_view_core_->GetWindowAndroid()->GetCompositor(); | 1008 view_.GetWindowAndroid()->GetCompositor(); |
| 1009 DCHECK(compositor); | 1009 DCHECK(compositor); |
| 1010 DCHECK(delegated_frame_host_); | 1010 DCHECK(delegated_frame_host_); |
| 1011 scoped_refptr<PendingReadbackLock> readback_lock( | 1011 scoped_refptr<PendingReadbackLock> readback_lock( |
| 1012 g_pending_readback_lock ? g_pending_readback_lock | 1012 g_pending_readback_lock ? g_pending_readback_lock |
| 1013 : new PendingReadbackLock); | 1013 : new PendingReadbackLock); |
| 1014 delegated_frame_host_->RequestCopyOfSurface( | 1014 delegated_frame_host_->RequestCopyOfSurface( |
| 1015 compositor, src_subrect_in_pixel, | 1015 compositor, src_subrect_in_pixel, |
| 1016 base::Bind(&PrepareTextureCopyOutputResult, dst_size_in_pixel, | 1016 base::Bind(&PrepareTextureCopyOutputResult, dst_size_in_pixel, |
| 1017 preferred_color_type, start_time, callback, readback_lock)); | 1017 preferred_color_type, start_time, callback, readback_lock)); |
| 1018 } | 1018 } |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1208 } | 1208 } |
| 1209 } | 1209 } |
| 1210 | 1210 |
| 1211 bool RenderWidgetHostViewAndroid::SupportsAnimation() const { | 1211 bool RenderWidgetHostViewAndroid::SupportsAnimation() const { |
| 1212 // The synchronous (WebView) compositor does not have a proper browser | 1212 // The synchronous (WebView) compositor does not have a proper browser |
| 1213 // compositor with which to drive animations. | 1213 // compositor with which to drive animations. |
| 1214 return using_browser_compositor_; | 1214 return using_browser_compositor_; |
| 1215 } | 1215 } |
| 1216 | 1216 |
| 1217 void RenderWidgetHostViewAndroid::SetNeedsAnimate() { | 1217 void RenderWidgetHostViewAndroid::SetNeedsAnimate() { |
| 1218 DCHECK(content_view_core_ && content_view_core_->GetWindowAndroid()); | 1218 DCHECK(view_.GetWindowAndroid()); |
| 1219 DCHECK(using_browser_compositor_); | 1219 DCHECK(using_browser_compositor_); |
| 1220 content_view_core_->GetWindowAndroid()->SetNeedsAnimate(); | 1220 view_.GetWindowAndroid()->SetNeedsAnimate(); |
| 1221 } | 1221 } |
| 1222 | 1222 |
| 1223 void RenderWidgetHostViewAndroid::MoveCaret(const gfx::PointF& position) { | 1223 void RenderWidgetHostViewAndroid::MoveCaret(const gfx::PointF& position) { |
| 1224 MoveCaret(gfx::Point(position.x(), position.y())); | 1224 MoveCaret(gfx::Point(position.x(), position.y())); |
| 1225 } | 1225 } |
| 1226 | 1226 |
| 1227 void RenderWidgetHostViewAndroid::MoveRangeSelectionExtent( | 1227 void RenderWidgetHostViewAndroid::MoveRangeSelectionExtent( |
| 1228 const gfx::PointF& extent) { | 1228 const gfx::PointF& extent) { |
| 1229 DCHECK(content_view_core_); | 1229 DCHECK(content_view_core_); |
| 1230 content_view_core_->MoveRangeSelectionExtent(extent); | 1230 content_view_core_->MoveRangeSelectionExtent(extent); |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1422 } | 1422 } |
| 1423 | 1423 |
| 1424 void RenderWidgetHostViewAndroid::RequestVSyncUpdate(uint32_t requests) { | 1424 void RenderWidgetHostViewAndroid::RequestVSyncUpdate(uint32_t requests) { |
| 1425 bool should_request_vsync = !outstanding_vsync_requests_ && requests; | 1425 bool should_request_vsync = !outstanding_vsync_requests_ && requests; |
| 1426 outstanding_vsync_requests_ |= requests; | 1426 outstanding_vsync_requests_ |= requests; |
| 1427 | 1427 |
| 1428 // Note that if we're not currently observing the root window, outstanding | 1428 // Note that if we're not currently observing the root window, outstanding |
| 1429 // vsync requests will be pushed if/when we resume observing in | 1429 // vsync requests will be pushed if/when we resume observing in |
| 1430 // |StartObservingRootWindow()|. | 1430 // |StartObservingRootWindow()|. |
| 1431 if (observing_root_window_ && should_request_vsync) { | 1431 if (observing_root_window_ && should_request_vsync) { |
| 1432 ui::WindowAndroid* windowAndroid = content_view_core_->GetWindowAndroid(); | 1432 ui::WindowAndroid* windowAndroid = view_.GetWindowAndroid(); |
| 1433 DCHECK(windowAndroid); | 1433 DCHECK(windowAndroid); |
| 1434 // TODO(boliu): This check should be redundant with | 1434 // TODO(boliu): This check should be redundant with |
| 1435 // |observing_root_window_| check above. However we are receiving trickle | 1435 // |observing_root_window_| check above. However we are receiving trickle |
| 1436 // of crash reports (crbug.com/639868) with no root cause. Should | 1436 // of crash reports (crbug.com/639868) with no root cause. Should |
| 1437 // investigate more when time allows what corner case is missed. | 1437 // investigate more when time allows what corner case is missed. |
| 1438 if (windowAndroid) | 1438 if (windowAndroid) |
| 1439 windowAndroid->RequestVSyncUpdate(); | 1439 windowAndroid->RequestVSyncUpdate(); |
| 1440 } | 1440 } |
| 1441 } | 1441 } |
| 1442 | 1442 |
| 1443 void RenderWidgetHostViewAndroid::StartObservingRootWindow() { | 1443 void RenderWidgetHostViewAndroid::StartObservingRootWindow() { |
| 1444 DCHECK(content_view_core_); | 1444 DCHECK(content_view_core_); |
| 1445 // TODO(yusufo): This will need to have a better fallback for cases where | 1445 // TODO(yusufo): This will need to have a better fallback for cases where |
| 1446 // setContentViewCore is called with a valid ContentViewCore without a window. | 1446 // setContentViewCore is called with a valid ContentViewCore without a window. |
| 1447 DCHECK(content_view_core_->GetWindowAndroid()); | 1447 DCHECK(view_.GetWindowAndroid()); |
| 1448 DCHECK(is_showing_); | 1448 DCHECK(is_showing_); |
| 1449 if (observing_root_window_) | 1449 if (observing_root_window_) |
| 1450 return; | 1450 return; |
| 1451 | 1451 |
| 1452 observing_root_window_ = true; | 1452 observing_root_window_ = true; |
| 1453 if (host_) | 1453 if (host_) |
| 1454 host_->Send(new ViewMsg_SetBeginFramePaused(host_->GetRoutingID(), false)); | 1454 host_->Send(new ViewMsg_SetBeginFramePaused(host_->GetRoutingID(), false)); |
| 1455 content_view_core_->GetWindowAndroid()->AddObserver(this); | 1455 view_.GetWindowAndroid()->AddObserver(this); |
| 1456 | 1456 |
| 1457 // Clear existing vsync requests to allow a request to the new window. | 1457 // Clear existing vsync requests to allow a request to the new window. |
| 1458 uint32_t outstanding_vsync_requests = outstanding_vsync_requests_; | 1458 uint32_t outstanding_vsync_requests = outstanding_vsync_requests_; |
| 1459 outstanding_vsync_requests_ = 0; | 1459 outstanding_vsync_requests_ = 0; |
| 1460 RequestVSyncUpdate(outstanding_vsync_requests); | 1460 RequestVSyncUpdate(outstanding_vsync_requests); |
| 1461 | 1461 |
| 1462 ui::WindowAndroidCompositor* compositor = | 1462 ui::WindowAndroidCompositor* compositor = |
| 1463 content_view_core_->GetWindowAndroid()->GetCompositor(); | 1463 view_.GetWindowAndroid()->GetCompositor(); |
| 1464 if (compositor) { | 1464 if (compositor) { |
| 1465 delegated_frame_host_->RegisterFrameSinkHierarchy( | 1465 delegated_frame_host_->RegisterFrameSinkHierarchy( |
| 1466 compositor->GetFrameSinkId()); | 1466 compositor->GetFrameSinkId()); |
| 1467 } | 1467 } |
| 1468 } | 1468 } |
| 1469 | 1469 |
| 1470 void RenderWidgetHostViewAndroid::StopObservingRootWindow() { | 1470 void RenderWidgetHostViewAndroid::StopObservingRootWindow() { |
| 1471 if (!content_view_core_ || !(content_view_core_->GetWindowAndroid())) { | 1471 if (!(view_.GetWindowAndroid())) { |
| 1472 DCHECK(!observing_root_window_); | 1472 DCHECK(!observing_root_window_); |
| 1473 return; | 1473 return; |
| 1474 } | 1474 } |
| 1475 | 1475 |
| 1476 if (!observing_root_window_) | 1476 if (!observing_root_window_) |
| 1477 return; | 1477 return; |
| 1478 | 1478 |
| 1479 // Reset window state variables to their defaults. | 1479 // Reset window state variables to their defaults. |
| 1480 is_window_activity_started_ = true; | 1480 is_window_activity_started_ = true; |
| 1481 is_window_visible_ = true; | 1481 is_window_visible_ = true; |
| 1482 observing_root_window_ = false; | 1482 observing_root_window_ = false; |
| 1483 if (host_) | 1483 if (host_) |
| 1484 host_->Send(new ViewMsg_SetBeginFramePaused(host_->GetRoutingID(), true)); | 1484 host_->Send(new ViewMsg_SetBeginFramePaused(host_->GetRoutingID(), true)); |
| 1485 content_view_core_->GetWindowAndroid()->RemoveObserver(this); | 1485 view_.GetWindowAndroid()->RemoveObserver(this); |
| 1486 // If the DFH has already been destroyed, it will have cleaned itself up. | 1486 // If the DFH has already been destroyed, it will have cleaned itself up. |
| 1487 // This happens in some WebView cases. | 1487 // This happens in some WebView cases. |
| 1488 if (delegated_frame_host_) | 1488 if (delegated_frame_host_) |
| 1489 delegated_frame_host_->UnregisterFrameSinkHierarchy(); | 1489 delegated_frame_host_->UnregisterFrameSinkHierarchy(); |
| 1490 } | 1490 } |
| 1491 | 1491 |
| 1492 void RenderWidgetHostViewAndroid::SendBeginFrame(base::TimeTicks frame_time, | 1492 void RenderWidgetHostViewAndroid::SendBeginFrame(base::TimeTicks frame_time, |
| 1493 base::TimeDelta vsync_period) { | 1493 base::TimeDelta vsync_period) { |
| 1494 TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::SendBeginFrame", | 1494 TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::SendBeginFrame", |
| 1495 "frame_time_us", frame_time.ToInternalValue()); | 1495 "frame_time_us", frame_time.ToInternalValue()); |
| 1496 | 1496 |
| 1497 // Synchronous compositor does not use deadline-based scheduling. | 1497 // Synchronous compositor does not use deadline-based scheduling. |
| 1498 // TODO(brianderson): Replace this hardcoded deadline after Android | 1498 // TODO(brianderson): Replace this hardcoded deadline after Android |
| 1499 // switches to Surfaces and the Browser's commit isn't in the critcal path. | 1499 // switches to Surfaces and the Browser's commit isn't in the critcal path. |
| 1500 base::TimeTicks deadline = | 1500 base::TimeTicks deadline = |
| 1501 sync_compositor_ ? base::TimeTicks() : frame_time + (vsync_period * 0.6); | 1501 sync_compositor_ ? base::TimeTicks() : frame_time + (vsync_period * 0.6); |
| 1502 host_->Send(new ViewMsg_BeginFrame( | 1502 host_->Send(new ViewMsg_BeginFrame( |
| 1503 host_->GetRoutingID(), | 1503 host_->GetRoutingID(), |
| 1504 cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, | 1504 cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, |
| 1505 vsync_period, cc::BeginFrameArgs::NORMAL))); | 1505 vsync_period, cc::BeginFrameArgs::NORMAL))); |
| 1506 if (sync_compositor_) | 1506 if (sync_compositor_) |
| 1507 sync_compositor_->DidSendBeginFrame(content_view_core_->GetWindowAndroid()); | 1507 sync_compositor_->DidSendBeginFrame(view_.GetWindowAndroid()); |
| 1508 } | 1508 } |
| 1509 | 1509 |
| 1510 bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) { | 1510 bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) { |
| 1511 bool needs_animate = false; | 1511 bool needs_animate = false; |
| 1512 if (overscroll_controller_) { | 1512 if (overscroll_controller_) { |
| 1513 needs_animate |= overscroll_controller_->Animate( | 1513 needs_animate |= overscroll_controller_->Animate( |
| 1514 frame_time, content_view_core_->GetViewAndroid()->GetLayer()); | 1514 frame_time, content_view_core_->GetViewAndroid()->GetLayer()); |
| 1515 } | 1515 } |
| 1516 if (selection_controller_) | 1516 if (selection_controller_) |
| 1517 needs_animate |= selection_controller_->Animate(frame_time); | 1517 needs_animate |= selection_controller_->Animate(frame_time); |
| (...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1801 if (is_showing_) | 1801 if (is_showing_) |
| 1802 StartObservingRootWindow(); | 1802 StartObservingRootWindow(); |
| 1803 | 1803 |
| 1804 if (resize) | 1804 if (resize) |
| 1805 WasResized(); | 1805 WasResized(); |
| 1806 | 1806 |
| 1807 if (!selection_controller_) | 1807 if (!selection_controller_) |
| 1808 selection_controller_ = CreateSelectionController(this, content_view_core_); | 1808 selection_controller_ = CreateSelectionController(this, content_view_core_); |
| 1809 | 1809 |
| 1810 if (!overscroll_controller_ && | 1810 if (!overscroll_controller_ && |
| 1811 content_view_core_->GetWindowAndroid()->GetCompositor()) { | 1811 view_.GetWindowAndroid()->GetCompositor()) { |
| 1812 overscroll_controller_ = CreateOverscrollController( | 1812 overscroll_controller_ = CreateOverscrollController( |
| 1813 content_view_core_, ui::GetScaleFactorForNativeView(GetNativeView())); | 1813 content_view_core_, ui::GetScaleFactorForNativeView(GetNativeView())); |
| 1814 } | 1814 } |
| 1815 | 1815 |
| 1816 if (!sync_compositor_) { | 1816 if (!sync_compositor_) { |
| 1817 sync_compositor_ = SynchronousCompositorHost::Create( | 1817 sync_compositor_ = SynchronousCompositorHost::Create( |
| 1818 this, content_view_core_->GetWebContents()); | 1818 this, content_view_core_->GetWebContents()); |
| 1819 } | 1819 } |
| 1820 } | 1820 } |
| 1821 | 1821 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1861 | 1861 |
| 1862 if (visible) | 1862 if (visible) |
| 1863 ShowInternal(); | 1863 ShowInternal(); |
| 1864 else | 1864 else |
| 1865 HideInternal(); | 1865 HideInternal(); |
| 1866 } | 1866 } |
| 1867 | 1867 |
| 1868 void RenderWidgetHostViewAndroid::OnAttachedToWindow() { | 1868 void RenderWidgetHostViewAndroid::OnAttachedToWindow() { |
| 1869 if (is_showing_) | 1869 if (is_showing_) |
| 1870 StartObservingRootWindow(); | 1870 StartObservingRootWindow(); |
| 1871 DCHECK(content_view_core_ && content_view_core_->GetWindowAndroid()); | 1871 DCHECK(view_.GetWindowAndroid()); |
| 1872 if (content_view_core_->GetWindowAndroid()->GetCompositor()) | 1872 if (view_.GetWindowAndroid()->GetCompositor()) |
| 1873 OnAttachCompositor(); | 1873 OnAttachCompositor(); |
| 1874 } | 1874 } |
| 1875 | 1875 |
| 1876 void RenderWidgetHostViewAndroid::OnDetachedFromWindow() { | 1876 void RenderWidgetHostViewAndroid::OnDetachedFromWindow() { |
| 1877 StopObservingRootWindow(); | 1877 StopObservingRootWindow(); |
| 1878 OnDetachCompositor(); | 1878 OnDetachCompositor(); |
| 1879 } | 1879 } |
| 1880 | 1880 |
| 1881 void RenderWidgetHostViewAndroid::OnAttachCompositor() { | 1881 void RenderWidgetHostViewAndroid::OnAttachCompositor() { |
| 1882 DCHECK(content_view_core_); | 1882 DCHECK(content_view_core_); |
| 1883 if (!overscroll_controller_) | 1883 if (!overscroll_controller_) |
| 1884 overscroll_controller_ = CreateOverscrollController( | 1884 overscroll_controller_ = CreateOverscrollController( |
| 1885 content_view_core_, ui::GetScaleFactorForNativeView(GetNativeView())); | 1885 content_view_core_, ui::GetScaleFactorForNativeView(GetNativeView())); |
| 1886 ui::WindowAndroidCompositor* compositor = | 1886 ui::WindowAndroidCompositor* compositor = |
| 1887 content_view_core_->GetWindowAndroid()->GetCompositor(); | 1887 view_.GetWindowAndroid()->GetCompositor(); |
| 1888 delegated_frame_host_->RegisterFrameSinkHierarchy( | 1888 delegated_frame_host_->RegisterFrameSinkHierarchy( |
| 1889 compositor->GetFrameSinkId()); | 1889 compositor->GetFrameSinkId()); |
| 1890 } | 1890 } |
| 1891 | 1891 |
| 1892 void RenderWidgetHostViewAndroid::OnDetachCompositor() { | 1892 void RenderWidgetHostViewAndroid::OnDetachCompositor() { |
| 1893 DCHECK(content_view_core_); | 1893 DCHECK(content_view_core_); |
| 1894 DCHECK(using_browser_compositor_); | 1894 DCHECK(using_browser_compositor_); |
| 1895 RunAckCallbacks(); | 1895 RunAckCallbacks(); |
| 1896 overscroll_controller_.reset(); | 1896 overscroll_controller_.reset(); |
| 1897 delegated_frame_host_->UnregisterFrameSinkHierarchy(); | 1897 delegated_frame_host_->UnregisterFrameSinkHierarchy(); |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1990 case ui::MotionEvent::ACTION_UP: | 1990 case ui::MotionEvent::ACTION_UP: |
| 1991 case ui::MotionEvent::ACTION_POINTER_UP: | 1991 case ui::MotionEvent::ACTION_POINTER_UP: |
| 1992 UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_RELEASED", | 1992 UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_RELEASED", |
| 1993 delta.InMicroseconds(), 1, 1000000, 50); | 1993 delta.InMicroseconds(), 1, 1000000, 50); |
| 1994 default: | 1994 default: |
| 1995 return; | 1995 return; |
| 1996 } | 1996 } |
| 1997 } | 1997 } |
| 1998 | 1998 |
| 1999 } // namespace content | 1999 } // namespace content |
| OLD | NEW |