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 "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | |
| 10 #include "base/logging.h" | 11 #include "base/logging.h" |
| 11 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
| 12 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
| 13 #include "cc/layers/layer.h" | 14 #include "cc/layers/layer.h" |
| 14 #include "cc/layers/texture_layer.h" | 15 #include "cc/layers/texture_layer.h" |
| 15 #include "cc/output/compositor_frame.h" | 16 #include "cc/output/compositor_frame.h" |
| 16 #include "cc/output/compositor_frame_ack.h" | 17 #include "cc/output/compositor_frame_ack.h" |
| 17 #include "content/browser/android/content_view_core_impl.h" | 18 #include "content/browser/android/content_view_core_impl.h" |
| 19 #include "content/browser/android/overscroll_glow.h" | |
| 18 #include "content/browser/gpu/gpu_surface_tracker.h" | 20 #include "content/browser/gpu/gpu_surface_tracker.h" |
| 19 #include "content/browser/renderer_host/compositor_impl_android.h" | 21 #include "content/browser/renderer_host/compositor_impl_android.h" |
| 20 #include "content/browser/renderer_host/image_transport_factory_android.h" | 22 #include "content/browser/renderer_host/image_transport_factory_android.h" |
| 21 #include "content/browser/renderer_host/render_widget_host_impl.h" | 23 #include "content/browser/renderer_host/render_widget_host_impl.h" |
| 22 #include "content/browser/renderer_host/surface_texture_transport_client_android .h" | 24 #include "content/browser/renderer_host/surface_texture_transport_client_android .h" |
| 23 #include "content/common/gpu/client/gl_helper.h" | 25 #include "content/common/gpu/client/gl_helper.h" |
| 24 #include "content/common/gpu/gpu_messages.h" | 26 #include "content/common/gpu/gpu_messages.h" |
| 25 #include "content/common/view_messages.h" | 27 #include "content/common/view_messages.h" |
| 28 #include "content/public/common/content_switches.h" | |
| 26 #include "third_party/WebKit/Source/Platform/chromium/public/Platform.h" | 29 #include "third_party/WebKit/Source/Platform/chromium/public/Platform.h" |
| 27 #include "third_party/WebKit/Source/Platform/chromium/public/WebExternalTextureL ayer.h" | 30 #include "third_party/WebKit/Source/Platform/chromium/public/WebExternalTextureL ayer.h" |
| 28 #include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h" | 31 #include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h" |
| 29 #include "third_party/khronos/GLES2/gl2.h" | 32 #include "third_party/khronos/GLES2/gl2.h" |
| 30 #include "third_party/khronos/GLES2/gl2ext.h" | 33 #include "third_party/khronos/GLES2/gl2ext.h" |
| 31 #include "ui/gfx/android/device_display_info.h" | 34 #include "ui/gfx/android/device_display_info.h" |
| 32 #include "ui/gfx/android/java_bitmap.h" | 35 #include "ui/gfx/android/java_bitmap.h" |
| 33 #include "ui/gfx/display.h" | 36 #include "ui/gfx/display.h" |
| 34 #include "ui/gfx/screen.h" | 37 #include "ui/gfx/screen.h" |
| 35 #include "ui/gfx/size_conversions.h" | 38 #include "ui/gfx/size_conversions.h" |
| 36 #include "webkit/compositor_bindings/web_compositor_support_impl.h" | 39 #include "webkit/compositor_bindings/web_compositor_support_impl.h" |
| 37 | 40 |
| 38 namespace content { | 41 namespace content { |
| 39 | 42 |
| 40 namespace { | 43 namespace { |
| 41 | 44 |
| 45 static const int kDesiredAnimationIntervalInMs = 16; | |
| 46 | |
| 42 void InsertSyncPointAndAckForGpu( | 47 void InsertSyncPointAndAckForGpu( |
| 43 int gpu_host_id, int route_id, const std::string& return_mailbox) { | 48 int gpu_host_id, int route_id, const std::string& return_mailbox) { |
| 44 uint32 sync_point = | 49 uint32 sync_point = |
| 45 ImageTransportFactoryAndroid::GetInstance()->InsertSyncPoint(); | 50 ImageTransportFactoryAndroid::GetInstance()->InsertSyncPoint(); |
| 46 AcceleratedSurfaceMsg_BufferPresented_Params ack_params; | 51 AcceleratedSurfaceMsg_BufferPresented_Params ack_params; |
| 47 ack_params.mailbox_name = return_mailbox; | 52 ack_params.mailbox_name = return_mailbox; |
| 48 ack_params.sync_point = sync_point; | 53 ack_params.sync_point = sync_point; |
| 49 RenderWidgetHostImpl::AcknowledgeBufferPresent( | 54 RenderWidgetHostImpl::AcknowledgeBufferPresent( |
| 50 route_id, gpu_host_id, ack_params); | 55 route_id, gpu_host_id, ack_params); |
| 51 } | 56 } |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 66 RenderWidgetHostImpl::SendSwapCompositorFrameAck( | 71 RenderWidgetHostImpl::SendSwapCompositorFrameAck( |
| 67 route_id, renderer_host_id, ack); | 72 route_id, renderer_host_id, ack); |
| 68 } | 73 } |
| 69 | 74 |
| 70 } // anonymous namespace | 75 } // anonymous namespace |
| 71 | 76 |
| 72 RenderWidgetHostViewAndroid::RenderWidgetHostViewAndroid( | 77 RenderWidgetHostViewAndroid::RenderWidgetHostViewAndroid( |
| 73 RenderWidgetHostImpl* widget_host, | 78 RenderWidgetHostImpl* widget_host, |
| 74 ContentViewCoreImpl* content_view_core) | 79 ContentViewCoreImpl* content_view_core) |
| 75 : host_(widget_host), | 80 : host_(widget_host), |
| 76 is_layer_attached_(true), | 81 are_layers_attached_(true), |
| 77 content_view_core_(NULL), | 82 content_view_core_(NULL), |
| 78 ime_adapter_android_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), | 83 ime_adapter_android_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), |
| 79 cached_background_color_(SK_ColorWHITE), | 84 cached_background_color_(SK_ColorWHITE), |
| 80 texture_id_in_layer_(0) { | 85 texture_id_in_layer_(0) { |
| 81 if (CompositorImpl::UsesDirectGL()) { | 86 if (CompositorImpl::UsesDirectGL()) { |
| 82 surface_texture_transport_.reset(new SurfaceTextureTransportClient()); | 87 surface_texture_transport_.reset(new SurfaceTextureTransportClient()); |
| 83 layer_ = surface_texture_transport_->Initialize(); | 88 layer_ = surface_texture_transport_->Initialize(); |
| 84 } else { | 89 } else { |
| 85 texture_layer_ = cc::TextureLayer::Create(NULL); | 90 texture_layer_ = cc::TextureLayer::Create(NULL); |
| 86 layer_ = texture_layer_; | 91 layer_ = texture_layer_; |
| 87 } | 92 } |
| 88 | 93 |
| 89 layer_->SetContentsOpaque(true); | 94 layer_->SetContentsOpaque(true); |
| 90 layer_->SetIsDrawable(true); | 95 layer_->SetIsDrawable(true); |
| 91 | 96 |
| 97 if (CommandLine::ForCurrentProcess()-> | |
| 98 HasSwitch(switches::kEnableOverscrollEdgeEffect)) { | |
| 99 overscroll_effect_ = OverscrollGlow::Create(); | |
| 100 } | |
| 101 | |
| 92 host_->SetView(this); | 102 host_->SetView(this); |
| 93 SetContentViewCore(content_view_core); | 103 SetContentViewCore(content_view_core); |
| 94 } | 104 } |
| 95 | 105 |
| 96 RenderWidgetHostViewAndroid::~RenderWidgetHostViewAndroid() { | 106 RenderWidgetHostViewAndroid::~RenderWidgetHostViewAndroid() { |
| 97 SetContentViewCore(NULL); | 107 SetContentViewCore(NULL); |
| 98 if (texture_id_in_layer_ || !last_mailbox_.IsZero()) { | 108 if (texture_id_in_layer_ || !last_mailbox_.IsZero()) { |
| 99 ImageTransportFactoryAndroid* factory = | 109 ImageTransportFactoryAndroid* factory = |
| 100 ImageTransportFactoryAndroid::GetInstance(); | 110 ImageTransportFactoryAndroid::GetInstance(); |
| 101 // TODO: crbug.com/230137 - make workaround obsolete with refcounting. | 111 // TODO: crbug.com/230137 - make workaround obsolete with refcounting. |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 282 | 292 |
| 283 return content_view_core_->HasFocus(); | 293 return content_view_core_->HasFocus(); |
| 284 } | 294 } |
| 285 | 295 |
| 286 bool RenderWidgetHostViewAndroid::IsSurfaceAvailableForCopy() const { | 296 bool RenderWidgetHostViewAndroid::IsSurfaceAvailableForCopy() const { |
| 287 NOTIMPLEMENTED(); | 297 NOTIMPLEMENTED(); |
| 288 return false; | 298 return false; |
| 289 } | 299 } |
| 290 | 300 |
| 291 void RenderWidgetHostViewAndroid::Show() { | 301 void RenderWidgetHostViewAndroid::Show() { |
| 292 if (is_layer_attached_) | 302 if (are_layers_attached_) |
| 293 return; | 303 return; |
| 294 | 304 |
| 295 is_layer_attached_ = true; | 305 are_layers_attached_ = true; |
| 296 if (content_view_core_) | 306 AttachLayers(); |
| 297 content_view_core_->AttachLayer(layer_); | |
| 298 } | 307 } |
| 299 | 308 |
| 300 void RenderWidgetHostViewAndroid::Hide() { | 309 void RenderWidgetHostViewAndroid::Hide() { |
| 301 if (!is_layer_attached_) | 310 if (!are_layers_attached_) |
| 302 return; | 311 return; |
| 303 | 312 |
| 304 is_layer_attached_ = false; | 313 are_layers_attached_ = false; |
| 305 if (content_view_core_) | 314 RemoveLayers(); |
| 306 content_view_core_->RemoveLayer(layer_); | |
| 307 } | 315 } |
| 308 | 316 |
| 309 bool RenderWidgetHostViewAndroid::IsShowing() { | 317 bool RenderWidgetHostViewAndroid::IsShowing() { |
| 310 // ContentViewCoreImpl represents the native side of the Java | 318 // ContentViewCoreImpl represents the native side of the Java |
| 311 // ContentViewCore. It being NULL means that it is not attached | 319 // ContentViewCore. It being NULL means that it is not attached |
| 312 // to the View system yet, so we treat this RWHVA as hidden. | 320 // to the View system yet, so we treat this RWHVA as hidden. |
| 313 return is_layer_attached_ && content_view_core_; | 321 return are_layers_attached_ && content_view_core_; |
| 314 } | 322 } |
| 315 | 323 |
| 316 gfx::Rect RenderWidgetHostViewAndroid::GetViewBounds() const { | 324 gfx::Rect RenderWidgetHostViewAndroid::GetViewBounds() const { |
| 317 if (!content_view_core_) | 325 if (!content_view_core_) |
| 318 return gfx::Rect(); | 326 return gfx::Rect(); |
| 319 | 327 |
| 320 // If the backing hasn't been initialized yet, report empty view bounds | 328 // If the backing hasn't been initialized yet, report empty view bounds |
| 321 // as well. Otherwise, we may end up stuck in a white-screen state because | 329 // as well. Otherwise, we may end up stuck in a white-screen state because |
| 322 // the resize ack is sent after swapbuffers. | 330 // the resize ack is sent after swapbuffers. |
| 323 if (GetPhysicalBackingSize().IsEmpty()) | 331 if (GetPhysicalBackingSize().IsEmpty()) |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 406 const std::vector<gfx::Rect>& copy_rects) { | 414 const std::vector<gfx::Rect>& copy_rects) { |
| 407 NOTIMPLEMENTED(); | 415 NOTIMPLEMENTED(); |
| 408 } | 416 } |
| 409 | 417 |
| 410 void RenderWidgetHostViewAndroid::RenderViewGone( | 418 void RenderWidgetHostViewAndroid::RenderViewGone( |
| 411 base::TerminationStatus status, int error_code) { | 419 base::TerminationStatus status, int error_code) { |
| 412 Destroy(); | 420 Destroy(); |
| 413 } | 421 } |
| 414 | 422 |
| 415 void RenderWidgetHostViewAndroid::Destroy() { | 423 void RenderWidgetHostViewAndroid::Destroy() { |
| 416 if (content_view_core_) { | 424 RemoveLayers(); |
| 417 content_view_core_->RemoveLayer(layer_); | 425 content_view_core_ = NULL; |
| 418 content_view_core_ = NULL; | 426 |
| 419 } | 427 overscroll_effect_.reset(); |
| 420 | 428 |
| 421 // The RenderWidgetHost's destruction led here, so don't call it. | 429 // The RenderWidgetHost's destruction led here, so don't call it. |
| 422 host_ = NULL; | 430 host_ = NULL; |
| 423 | 431 |
| 424 delete this; | 432 delete this; |
| 425 } | 433 } |
| 426 | 434 |
| 427 void RenderWidgetHostViewAndroid::SetTooltipText( | 435 void RenderWidgetHostViewAndroid::SetTooltipText( |
| 428 const string16& tooltip_text) { | 436 const string16& tooltip_text) { |
| 429 // Tooltips don't makes sense on Android. | 437 // Tooltips don't makes sense on Android. |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 540 if (texture_size.GetArea() > 0) | 548 if (texture_size.GetArea() > 0) |
| 541 offset = frame->metadata.location_bar_content_translation; | 549 offset = frame->metadata.location_bar_content_translation; |
| 542 offset.set_y(offset.y() + frame->metadata.overdraw_bottom_height); | 550 offset.set_y(offset.y() + frame->metadata.overdraw_bottom_height); |
| 543 gfx::SizeF content_size(texture_size.width() - offset.x() * dp2px, | 551 gfx::SizeF content_size(texture_size.width() - offset.x() * dp2px, |
| 544 texture_size.height() - offset.y() * dp2px); | 552 texture_size.height() - offset.y() * dp2px); |
| 545 BuffersSwapped(frame->gl_frame_data->mailbox, | 553 BuffersSwapped(frame->gl_frame_data->mailbox, |
| 546 texture_size, | 554 texture_size, |
| 547 content_size, | 555 content_size, |
| 548 callback); | 556 callback); |
| 549 | 557 |
| 558 if (overscroll_effect_) { | |
| 559 // Disable left/right edge effects when fully zoomed out. | |
| 560 const cc::CompositorFrameMetadata& metadata = frame->metadata; | |
| 561 bool horizontal_scrolling_possible = | |
| 562 metadata.page_scale_factor != metadata.min_page_scale_factor && | |
| 563 metadata.root_layer_size.width() != metadata.viewport_size.width(); | |
| 564 | |
| 565 overscroll_effect_->set_horizontal_overscroll_enabled( | |
| 566 horizontal_scrolling_possible); | |
| 567 overscroll_effect_->set_size(content_size); | |
| 568 } | |
| 550 } | 569 } |
| 551 | 570 |
| 552 void RenderWidgetHostViewAndroid::AcceleratedSurfaceBuffersSwapped( | 571 void RenderWidgetHostViewAndroid::AcceleratedSurfaceBuffersSwapped( |
| 553 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, | 572 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, |
| 554 int gpu_host_id) { | 573 int gpu_host_id) { |
| 555 NOTREACHED() << "Deprecated. Use --composite-to-mailbox."; | 574 NOTREACHED() << "Deprecated. Use --composite-to-mailbox."; |
| 556 | 575 |
| 557 if (params.mailbox_name.empty()) | 576 if (params.mailbox_name.empty()) |
| 558 return; | 577 return; |
| 559 | 578 |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 611 if (texture_size.GetArea() > 0) { | 630 if (texture_size.GetArea() > 0) { |
| 612 uv_max.SetPoint(content_size.width() / texture_size.width(), | 631 uv_max.SetPoint(content_size.width() / texture_size.width(), |
| 613 content_size.height() / texture_size.height()); | 632 content_size.height() / texture_size.height()); |
| 614 } | 633 } |
| 615 texture_layer_->SetUV(gfx::PointF(0, 0), uv_max); | 634 texture_layer_->SetUV(gfx::PointF(0, 0), uv_max); |
| 616 texture_size_in_layer_ = texture_size; | 635 texture_size_in_layer_ = texture_size; |
| 617 current_mailbox_ = mailbox; | 636 current_mailbox_ = mailbox; |
| 618 ack_callback.Run(); | 637 ack_callback.Run(); |
| 619 } | 638 } |
| 620 | 639 |
| 640 void RenderWidgetHostViewAndroid::AttachLayers() { | |
| 641 if (!content_view_core_) | |
| 642 return; | |
| 643 | |
| 644 content_view_core_->AttachLayer(layer_); | |
| 645 | |
| 646 if (overscroll_effect_) | |
| 647 overscroll_effect_->set_parent_layer(content_view_core_->GetLayer()); | |
| 648 } | |
| 649 | |
| 650 void RenderWidgetHostViewAndroid::RemoveLayers() { | |
| 651 if (!content_view_core_) | |
| 652 return; | |
| 653 | |
| 654 if (overscroll_effect_) | |
| 655 overscroll_effect_->set_parent_layer(NULL); | |
| 656 | |
| 657 content_view_core_->RemoveLayer(layer_); | |
| 658 } | |
| 659 | |
| 660 void RenderWidgetHostViewAndroid::AnimationCallback() { | |
| 661 if (overscroll_effect_ && overscroll_effect_->Animate(base::TimeTicks::Now())) | |
| 662 ScheduleAnimation(); | |
| 663 } | |
| 664 | |
| 665 void RenderWidgetHostViewAndroid::ScheduleAnimation() { | |
| 666 if (animation_timer_.IsRunning() || !overscroll_effect_) | |
| 667 return; | |
| 668 | |
| 669 const base::TimeDelta animationInterval | |
| 670 = base::TimeDelta::FromMilliseconds(kDesiredAnimationIntervalInMs); | |
| 671 | |
| 672 animation_timer_.Start(FROM_HERE, animationInterval, this, | |
|
mkosiba (inactive)
2013/04/22 16:24:53
I'm not familiar with how animations work, so I ap
jdduke (slow)
2013/04/22 17:18:16
It's not synchronized :) The timer hack was really
| |
| 673 &RenderWidgetHostViewAndroid::AnimationCallback); | |
| 674 } | |
| 675 | |
| 621 void RenderWidgetHostViewAndroid::AcceleratedSurfacePostSubBuffer( | 676 void RenderWidgetHostViewAndroid::AcceleratedSurfacePostSubBuffer( |
| 622 const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, | 677 const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, |
| 623 int gpu_host_id) { | 678 int gpu_host_id) { |
| 624 NOTREACHED(); | 679 NOTREACHED(); |
| 625 } | 680 } |
| 626 | 681 |
| 627 void RenderWidgetHostViewAndroid::AcceleratedSurfaceSuspend() { | 682 void RenderWidgetHostViewAndroid::AcceleratedSurfaceSuspend() { |
| 628 NOTREACHED(); | 683 NOTREACHED(); |
| 629 } | 684 } |
| 630 | 685 |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 748 | 803 |
| 749 void RenderWidgetHostViewAndroid::MoveCaret(const gfx::Point& point) { | 804 void RenderWidgetHostViewAndroid::MoveCaret(const gfx::Point& point) { |
| 750 if (host_) | 805 if (host_) |
| 751 host_->MoveCaret(point); | 806 host_->MoveCaret(point); |
| 752 } | 807 } |
| 753 | 808 |
| 754 SkColor RenderWidgetHostViewAndroid::GetCachedBackgroundColor() const { | 809 SkColor RenderWidgetHostViewAndroid::GetCachedBackgroundColor() const { |
| 755 return cached_background_color_; | 810 return cached_background_color_; |
| 756 } | 811 } |
| 757 | 812 |
| 813 void RenderWidgetHostViewAndroid::OnOverscrolled( | |
| 814 const gfx::Vector2dF& accumulated_overscroll, | |
| 815 const gfx::Vector2dF& current_fling_velocity) { | |
| 816 if (overscroll_effect_) { | |
| 817 overscroll_effect_->OnOverscrolled(base::TimeTicks::Now(), | |
| 818 accumulated_overscroll, | |
| 819 current_fling_velocity); | |
| 820 ScheduleAnimation(); | |
| 821 } | |
| 822 } | |
| 823 | |
| 758 void RenderWidgetHostViewAndroid::SetContentViewCore( | 824 void RenderWidgetHostViewAndroid::SetContentViewCore( |
| 759 ContentViewCoreImpl* content_view_core) { | 825 ContentViewCoreImpl* content_view_core) { |
| 760 if (content_view_core_ && is_layer_attached_) | 826 if (are_layers_attached_) |
| 761 content_view_core_->RemoveLayer(layer_); | 827 RemoveLayers(); |
| 762 | 828 |
| 763 content_view_core_ = content_view_core; | 829 content_view_core_ = content_view_core; |
| 764 if (content_view_core_ && is_layer_attached_) | 830 if (are_layers_attached_) |
| 765 content_view_core_->AttachLayer(layer_); | 831 AttachLayers(); |
| 766 } | 832 } |
| 767 | 833 |
| 768 void RenderWidgetHostViewAndroid::HasTouchEventHandlers( | 834 void RenderWidgetHostViewAndroid::HasTouchEventHandlers( |
| 769 bool need_touch_events) { | 835 bool need_touch_events) { |
| 770 if (content_view_core_) | 836 if (content_view_core_) |
| 771 content_view_core_->HasTouchEventHandlers(need_touch_events); | 837 content_view_core_->HasTouchEventHandlers(need_touch_events); |
| 772 } | 838 } |
| 773 | 839 |
| 774 // static | 840 // static |
| 775 void RenderWidgetHostViewPort::GetDefaultScreenInfo( | 841 void RenderWidgetHostViewPort::GetDefaultScreenInfo( |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 790 // RenderWidgetHostView, public: | 856 // RenderWidgetHostView, public: |
| 791 | 857 |
| 792 // static | 858 // static |
| 793 RenderWidgetHostView* | 859 RenderWidgetHostView* |
| 794 RenderWidgetHostView::CreateViewForWidget(RenderWidgetHost* widget) { | 860 RenderWidgetHostView::CreateViewForWidget(RenderWidgetHost* widget) { |
| 795 RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(widget); | 861 RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(widget); |
| 796 return new RenderWidgetHostViewAndroid(rwhi, NULL); | 862 return new RenderWidgetHostViewAndroid(rwhi, NULL); |
| 797 } | 863 } |
| 798 | 864 |
| 799 } // namespace content | 865 } // namespace content |
| OLD | NEW |