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/renderer/render_widget.h" | 5 #include "content/renderer/render_widget.h" |
| 6 | 6 |
| 7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
| 8 #include "base/bind.h" | 8 #include "base/bind.h" |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 30 #include "content/common/input_messages.h" | 30 #include "content/common/input_messages.h" |
| 31 #include "content/common/swapped_out_messages.h" | 31 #include "content/common/swapped_out_messages.h" |
| 32 #include "content/common/view_messages.h" | 32 #include "content/common/view_messages.h" |
| 33 #include "content/public/common/content_switches.h" | 33 #include "content/public/common/content_switches.h" |
| 34 #include "content/public/common/context_menu_params.h" | 34 #include "content/public/common/context_menu_params.h" |
| 35 #include "content/renderer/cursor_utils.h" | 35 #include "content/renderer/cursor_utils.h" |
| 36 #include "content/renderer/external_popup_menu.h" | 36 #include "content/renderer/external_popup_menu.h" |
| 37 #include "content/renderer/gpu/compositor_output_surface.h" | 37 #include "content/renderer/gpu/compositor_output_surface.h" |
| 38 #include "content/renderer/gpu/compositor_software_output_device.h" | 38 #include "content/renderer/gpu/compositor_software_output_device.h" |
| 39 #include "content/renderer/gpu/delegated_compositor_output_surface.h" | 39 #include "content/renderer/gpu/delegated_compositor_output_surface.h" |
| 40 #include "content/renderer/gpu/frame_swap_message_queue.h" | |
| 40 #include "content/renderer/gpu/mailbox_output_surface.h" | 41 #include "content/renderer/gpu/mailbox_output_surface.h" |
| 41 #include "content/renderer/gpu/render_widget_compositor.h" | 42 #include "content/renderer/gpu/render_widget_compositor.h" |
| 42 #include "content/renderer/ime_event_guard.h" | 43 #include "content/renderer/ime_event_guard.h" |
| 43 #include "content/renderer/input/input_handler_manager.h" | 44 #include "content/renderer/input/input_handler_manager.h" |
| 44 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" | 45 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" |
| 45 #include "content/renderer/render_frame_impl.h" | 46 #include "content/renderer/render_frame_impl.h" |
| 46 #include "content/renderer/render_process.h" | 47 #include "content/renderer/render_process.h" |
| 47 #include "content/renderer/render_thread_impl.h" | 48 #include "content/renderer/render_thread_impl.h" |
| 48 #include "content/renderer/renderer_webkitplatformsupport_impl.h" | 49 #include "content/renderer/renderer_webkitplatformsupport_impl.h" |
| 49 #include "content/renderer/resizing_mode_selector.h" | 50 #include "content/renderer/resizing_mode_selector.h" |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 147 return ui::TEXT_INPUT_MODE_DEFAULT; | 148 return ui::TEXT_INPUT_MODE_DEFAULT; |
| 148 return it->second; | 149 return it->second; |
| 149 } | 150 } |
| 150 | 151 |
| 151 // TODO(brianderson): Replace the hard-coded threshold with a fraction of | 152 // TODO(brianderson): Replace the hard-coded threshold with a fraction of |
| 152 // the BeginMainFrame interval. | 153 // the BeginMainFrame interval. |
| 153 // 4166us will allow 1/4 of a 60Hz interval or 1/2 of a 120Hz interval to | 154 // 4166us will allow 1/4 of a 60Hz interval or 1/2 of a 120Hz interval to |
| 154 // be spent in input hanlders before input starts getting throttled. | 155 // be spent in input hanlders before input starts getting throttled. |
| 155 const int kInputHandlingTimeThrottlingThresholdMicroseconds = 4166; | 156 const int kInputHandlingTimeThrottlingThresholdMicroseconds = 4166; |
| 156 | 157 |
| 158 class QueueMessageSwapPromise : public cc::SwapPromise { | |
| 159 public: | |
| 160 QueueMessageSwapPromise( | |
| 161 scoped_refptr<IPC::SyncMessageFilter> message_sender, | |
| 162 scoped_refptr<content::FrameSwapMessageQueue> message_queue, | |
| 163 int source_frame_number) | |
| 164 : message_sender_(message_sender), | |
| 165 message_queue_(message_queue), | |
| 166 source_frame_number_(source_frame_number), | |
| 167 completed_(false) { | |
| 168 DCHECK(message_sender_.get()); | |
| 169 DCHECK(message_queue_.get()); | |
| 170 } | |
| 171 | |
| 172 virtual ~QueueMessageSwapPromise() { | |
| 173 // The promise should have either been kept or broken before it's deleted. | |
| 174 DCHECK(completed_); | |
| 175 } | |
| 176 | |
| 177 virtual void DidSwap(cc::CompositorFrameMetadata* metadata) OVERRIDE { | |
| 178 DCHECK(!completed_); | |
| 179 message_queue_->AdvanceToFrame(source_frame_number_); | |
| 180 // The OutputSurface will take care of the Drain+Send. | |
| 181 completed_ = true; | |
| 182 } | |
| 183 | |
| 184 virtual void DidNotSwap(DidNotSwapReason reason) OVERRIDE { | |
| 185 DCHECK(!completed_); | |
| 186 // This will also send messages explicitly queued with the WITH_NEXT_SWAP | |
| 187 // policy. It makes sense to send them if reason == SWAP_FAILS, but they may | |
| 188 // end up being sent prematurely if reason == COMMIT_FAILS. | |
|
mkosiba (inactive)
2014/06/18 14:58:02
Locally I have a CL which adds policy-specific sub
piman
2014/06/19 23:02:29
SWAP_FAILS happens when we went all the way to dra
mkosiba (inactive)
2014/06/24 18:29:54
If I'm reading that right it means there are cases
piman
2014/06/24 19:35:53
I don't think that would happen. The visibility is
| |
| 189 std::vector<IPC::Message> messages; | |
| 190 message_queue_->AdvanceToFrame(source_frame_number_); | |
| 191 scoped_ptr<content::FrameSwapMessageQueue::SendMessageScope> | |
| 192 send_message_scope = message_queue_->AcquireSendMessageScope(); | |
| 193 message_queue_->DrainMessages(&messages); | |
| 194 for (std::vector<IPC::Message>::iterator i = messages.begin(); | |
| 195 i != messages.end(); | |
| 196 ++i) { | |
| 197 message_sender_->Send(&*i); | |
|
piman
2014/06/19 23:02:29
Send is supposed to take ownership of the IPC::Mes
mkosiba (inactive)
2014/06/24 18:29:54
argh, right right
| |
| 198 } | |
| 199 completed_ = true; | |
| 200 } | |
| 201 | |
| 202 private: | |
| 203 scoped_refptr<IPC::SyncMessageFilter> message_sender_; | |
| 204 scoped_refptr<content::FrameSwapMessageQueue> message_queue_; | |
| 205 int source_frame_number_; | |
| 206 bool completed_; | |
|
piman
2014/06/19 23:02:29
Is this used only for DCHECKs? Should it be in #if
mkosiba (inactive)
2014/06/24 18:29:54
Done.
| |
| 207 }; | |
| 208 | |
| 157 } // namespace | 209 } // namespace |
| 158 | 210 |
| 159 namespace content { | 211 namespace content { |
| 160 | 212 |
| 161 // RenderWidget::ScreenMetricsEmulator ---------------------------------------- | 213 // RenderWidget::ScreenMetricsEmulator ---------------------------------------- |
| 162 | 214 |
| 163 class RenderWidget::ScreenMetricsEmulator { | 215 class RenderWidget::ScreenMetricsEmulator { |
| 164 public: | 216 public: |
| 165 ScreenMetricsEmulator( | 217 ScreenMetricsEmulator( |
| 166 RenderWidget* widget, | 218 RenderWidget* widget, |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 390 device_scale_factor_(screen_info_.deviceScaleFactor), | 442 device_scale_factor_(screen_info_.deviceScaleFactor), |
| 391 is_threaded_compositing_enabled_(false), | 443 is_threaded_compositing_enabled_(false), |
| 392 current_event_latency_info_(NULL), | 444 current_event_latency_info_(NULL), |
| 393 next_output_surface_id_(0), | 445 next_output_surface_id_(0), |
| 394 #if defined(OS_ANDROID) | 446 #if defined(OS_ANDROID) |
| 395 text_field_is_dirty_(false), | 447 text_field_is_dirty_(false), |
| 396 outstanding_ime_acks_(0), | 448 outstanding_ime_acks_(0), |
| 397 body_background_color_(SK_ColorWHITE), | 449 body_background_color_(SK_ColorWHITE), |
| 398 #endif | 450 #endif |
| 399 popup_origin_scale_for_emulation_(0.f), | 451 popup_origin_scale_for_emulation_(0.f), |
| 452 frame_swap_message_queue_(new FrameSwapMessageQueue()), | |
| 400 resizing_mode_selector_(new ResizingModeSelector()), | 453 resizing_mode_selector_(new ResizingModeSelector()), |
| 401 context_menu_source_type_(ui::MENU_SOURCE_MOUSE) { | 454 context_menu_source_type_(ui::MENU_SOURCE_MOUSE) { |
| 402 if (!swapped_out) | 455 if (!swapped_out) |
| 403 RenderProcess::current()->AddRefProcess(); | 456 RenderProcess::current()->AddRefProcess(); |
| 404 DCHECK(RenderThread::Get()); | 457 DCHECK(RenderThread::Get()); |
| 405 is_threaded_compositing_enabled_ = | 458 is_threaded_compositing_enabled_ = |
| 406 CommandLine::ForCurrentProcess()->HasSwitch( | 459 CommandLine::ForCurrentProcess()->HasSwitch( |
| 407 switches::kEnableThreadedCompositing); | 460 switches::kEnableThreadedCompositing); |
| 408 } | 461 } |
| 409 | 462 |
| (...skipping 395 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 805 } | 858 } |
| 806 | 859 |
| 807 scoped_ptr<cc::OutputSurface> RenderWidget::CreateOutputSurface(bool fallback) { | 860 scoped_ptr<cc::OutputSurface> RenderWidget::CreateOutputSurface(bool fallback) { |
| 808 // For widgets that are never visible, we don't start the compositor, so we | 861 // For widgets that are never visible, we don't start the compositor, so we |
| 809 // never get a request for a cc::OutputSurface. | 862 // never get a request for a cc::OutputSurface. |
| 810 DCHECK(!never_visible_); | 863 DCHECK(!never_visible_); |
| 811 | 864 |
| 812 #if defined(OS_ANDROID) | 865 #if defined(OS_ANDROID) |
| 813 if (SynchronousCompositorFactory* factory = | 866 if (SynchronousCompositorFactory* factory = |
| 814 SynchronousCompositorFactory::GetInstance()) { | 867 SynchronousCompositorFactory::GetInstance()) { |
| 815 return factory->CreateOutputSurface(routing_id()); | 868 return factory->CreateOutputSurface(routing_id(), |
| 869 frame_swap_message_queue_); | |
| 816 } | 870 } |
| 817 #endif | 871 #endif |
| 818 | 872 |
| 819 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | 873 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| 820 bool use_software = fallback; | 874 bool use_software = fallback; |
| 821 if (command_line.HasSwitch(switches::kDisableGpuCompositing)) | 875 if (command_line.HasSwitch(switches::kDisableGpuCompositing)) |
| 822 use_software = true; | 876 use_software = true; |
| 823 | 877 |
| 824 scoped_refptr<ContextProviderCommandBuffer> context_provider; | 878 scoped_refptr<ContextProviderCommandBuffer> context_provider; |
| 825 if (!use_software) { | 879 if (!use_software) { |
| 826 context_provider = ContextProviderCommandBuffer::Create( | 880 context_provider = ContextProviderCommandBuffer::Create( |
| 827 CreateGraphicsContext3D(), "RenderCompositor"); | 881 CreateGraphicsContext3D(), "RenderCompositor"); |
| 828 if (!context_provider.get()) { | 882 if (!context_provider.get()) { |
| 829 // Cause the compositor to wait and try again. | 883 // Cause the compositor to wait and try again. |
| 830 return scoped_ptr<cc::OutputSurface>(); | 884 return scoped_ptr<cc::OutputSurface>(); |
| 831 } | 885 } |
| 832 } | 886 } |
| 833 | 887 |
| 834 uint32 output_surface_id = next_output_surface_id_++; | 888 uint32 output_surface_id = next_output_surface_id_++; |
| 835 if (command_line.HasSwitch(switches::kEnableDelegatedRenderer)) { | 889 if (command_line.HasSwitch(switches::kEnableDelegatedRenderer)) { |
| 836 DCHECK(is_threaded_compositing_enabled_); | 890 DCHECK(is_threaded_compositing_enabled_); |
| 837 return scoped_ptr<cc::OutputSurface>( | 891 return scoped_ptr<cc::OutputSurface>( |
| 838 new DelegatedCompositorOutputSurface( | 892 new DelegatedCompositorOutputSurface(routing_id(), |
| 839 routing_id(), | 893 output_surface_id, |
| 840 output_surface_id, | 894 context_provider, |
| 841 context_provider)); | 895 frame_swap_message_queue_)); |
| 842 } | 896 } |
| 843 if (!context_provider.get()) { | 897 if (!context_provider.get()) { |
| 844 scoped_ptr<cc::SoftwareOutputDevice> software_device( | 898 scoped_ptr<cc::SoftwareOutputDevice> software_device( |
| 845 new CompositorSoftwareOutputDevice()); | 899 new CompositorSoftwareOutputDevice()); |
| 846 | 900 |
| 847 return scoped_ptr<cc::OutputSurface>(new CompositorOutputSurface( | 901 return scoped_ptr<cc::OutputSurface>( |
| 848 routing_id(), | 902 new CompositorOutputSurface(routing_id(), |
| 849 output_surface_id, | 903 output_surface_id, |
| 850 NULL, | 904 NULL, |
| 851 software_device.Pass(), | 905 software_device.Pass(), |
| 852 true)); | 906 frame_swap_message_queue_, |
| 907 true)); | |
| 853 } | 908 } |
| 854 | 909 |
| 855 if (command_line.HasSwitch(cc::switches::kCompositeToMailbox)) { | 910 if (command_line.HasSwitch(cc::switches::kCompositeToMailbox)) { |
| 856 // Composite-to-mailbox is currently used for layout tests in order to cause | 911 // Composite-to-mailbox is currently used for layout tests in order to cause |
| 857 // them to draw inside in the renderer to do the readback there. This should | 912 // them to draw inside in the renderer to do the readback there. This should |
| 858 // no longer be the case when crbug.com/311404 is fixed. | 913 // no longer be the case when crbug.com/311404 is fixed. |
| 859 DCHECK(is_threaded_compositing_enabled_ || | 914 DCHECK(is_threaded_compositing_enabled_ || |
| 860 RenderThreadImpl::current()->layout_test_mode()); | 915 RenderThreadImpl::current()->layout_test_mode()); |
| 861 cc::ResourceFormat format = cc::RGBA_8888; | 916 cc::ResourceFormat format = cc::RGBA_8888; |
| 862 #if defined(OS_ANDROID) | 917 #if defined(OS_ANDROID) |
| 863 if (base::android::SysUtils::IsLowEndDevice()) | 918 if (base::android::SysUtils::IsLowEndDevice()) |
| 864 format = cc::RGB_565; | 919 format = cc::RGB_565; |
| 865 #endif | 920 #endif |
| 866 return scoped_ptr<cc::OutputSurface>( | 921 return scoped_ptr<cc::OutputSurface>( |
| 867 new MailboxOutputSurface( | 922 new MailboxOutputSurface(routing_id(), |
| 868 routing_id(), | 923 output_surface_id, |
| 869 output_surface_id, | 924 context_provider, |
| 870 context_provider, | 925 scoped_ptr<cc::SoftwareOutputDevice>(), |
| 871 scoped_ptr<cc::SoftwareOutputDevice>(), | 926 frame_swap_message_queue_, |
| 872 format)); | 927 format)); |
| 873 } | 928 } |
| 874 bool use_swap_compositor_frame_message = false; | 929 bool use_swap_compositor_frame_message = false; |
| 875 return scoped_ptr<cc::OutputSurface>( | 930 return scoped_ptr<cc::OutputSurface>( |
| 876 new CompositorOutputSurface( | 931 new CompositorOutputSurface(routing_id(), |
| 877 routing_id(), | 932 output_surface_id, |
| 878 output_surface_id, | 933 context_provider, |
| 879 context_provider, | 934 scoped_ptr<cc::SoftwareOutputDevice>(), |
| 880 scoped_ptr<cc::SoftwareOutputDevice>(), | 935 frame_swap_message_queue_, |
| 881 use_swap_compositor_frame_message)); | 936 use_swap_compositor_frame_message)); |
| 882 } | 937 } |
| 883 | 938 |
| 884 void RenderWidget::OnSwapBuffersAborted() { | 939 void RenderWidget::OnSwapBuffersAborted() { |
| 885 TRACE_EVENT0("renderer", "RenderWidget::OnSwapBuffersAborted"); | 940 TRACE_EVENT0("renderer", "RenderWidget::OnSwapBuffersAborted"); |
| 886 // Schedule another frame so the compositor learns about it. | 941 // Schedule another frame so the compositor learns about it. |
| 887 scheduleComposite(); | 942 scheduleComposite(); |
| 888 } | 943 } |
| 889 | 944 |
| 890 void RenderWidget::OnSwapBuffersPosted() { | 945 void RenderWidget::OnSwapBuffersPosted() { |
| 891 TRACE_EVENT0("renderer", "RenderWidget::OnSwapBuffersPosted"); | 946 TRACE_EVENT0("renderer", "RenderWidget::OnSwapBuffersPosted"); |
| (...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1211 ObserverListBase<RenderFrameImpl>::Iterator iter(video_hole_frames_); | 1266 ObserverListBase<RenderFrameImpl>::Iterator iter(video_hole_frames_); |
| 1212 RenderFrameImpl* frame; | 1267 RenderFrameImpl* frame; |
| 1213 while ((frame = iter.GetNext()) != NULL) { | 1268 while ((frame = iter.GetNext()) != NULL) { |
| 1214 // Prevent duplicate notification of DidCommitCompositorFrame(). | 1269 // Prevent duplicate notification of DidCommitCompositorFrame(). |
| 1215 if (!swapped_out_frames_.HasObserver(frame)) | 1270 if (!swapped_out_frames_.HasObserver(frame)) |
| 1216 frame->DidCommitCompositorFrame(); | 1271 frame->DidCommitCompositorFrame(); |
| 1217 } | 1272 } |
| 1218 #endif // defined(VIDEO_HOLE) | 1273 #endif // defined(VIDEO_HOLE) |
| 1219 } | 1274 } |
| 1220 | 1275 |
| 1276 namespace {} // namespace | |
| 1277 | |
| 1278 void RenderWidget::QueueMessage(IPC::Message* msg, | |
| 1279 MessageDeliveryPolicy policy) { | |
| 1280 if (!compositor_) { | |
| 1281 Send(msg); | |
| 1282 return; | |
| 1283 } | |
| 1284 | |
| 1285 if (policy == MESSAGE_DELIVERY_POLICY_WITH_NEXT_SWAP) { | |
| 1286 frame_swap_message_queue_->QueueMessage(msg); | |
| 1287 return; | |
| 1288 } | |
| 1289 | |
| 1290 if (policy == MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE && | |
| 1291 // No need for lock: this gets changed only on this thread. | |
| 1292 !compositor_->commitRequested() && | |
| 1293 // No need for lock: Messages are only enqueued from this thread, if we | |
| 1294 // don't have any now, no other thread will add any. | |
| 1295 !frame_swap_message_queue_->Empty()) { | |
|
mkosiba (inactive)
2014/06/18 14:58:03
so this should handle FIFO ordering correctly now.
piman
2014/06/19 23:02:29
Did you mean && frame_swap_message_queue_->Empty()
piman
2014/06/19 23:02:29
For that case, you may want to force a commit if !
mkosiba (inactive)
2014/06/24 18:29:54
yes, right, I had this method called HasMessages b
mkosiba (inactive)
2014/06/24 18:29:54
Done.
| |
| 1296 Send(msg); | |
| 1297 return; | |
| 1298 } | |
| 1299 | |
| 1300 DCHECK(policy == MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE || | |
| 1301 policy == MESSAGE_DELIVERY_POLICY_WITH_NEXT_COMMIT); | |
| 1302 const int source_frame_number = compositor_->GetSourceFrameNumber(); | |
| 1303 bool first_message_for_frame = false; | |
| 1304 frame_swap_message_queue_->QueueMessage( | |
| 1305 source_frame_number, msg, &first_message_for_frame); | |
| 1306 if (first_message_for_frame) { | |
| 1307 scoped_ptr<cc::SwapPromise> promise(new QueueMessageSwapPromise( | |
| 1308 RenderThreadImpl::current()->sync_message_filter(), | |
| 1309 frame_swap_message_queue_, | |
| 1310 source_frame_number)); | |
|
mkosiba (inactive)
2014/06/18 14:58:03
This way uses less memory since we'll use at most
| |
| 1311 compositor_->QueueSwapPromise(promise.Pass()); | |
| 1312 } | |
| 1313 } | |
| 1314 | |
| 1221 void RenderWidget::didCommitAndDrawCompositorFrame() { | 1315 void RenderWidget::didCommitAndDrawCompositorFrame() { |
| 1222 // NOTE: Tests may break if this event is renamed or moved. See | 1316 // NOTE: Tests may break if this event is renamed or moved. See |
| 1223 // tab_capture_performancetest.cc. | 1317 // tab_capture_performancetest.cc. |
| 1224 TRACE_EVENT0("gpu", "RenderWidget::didCommitAndDrawCompositorFrame"); | 1318 TRACE_EVENT0("gpu", "RenderWidget::didCommitAndDrawCompositorFrame"); |
| 1225 // Notify subclasses that we initiated the paint operation. | 1319 // Notify subclasses that we initiated the paint operation. |
| 1226 DidInitiatePaint(); | 1320 DidInitiatePaint(); |
| 1227 } | 1321 } |
| 1228 | 1322 |
| 1229 void RenderWidget::didCompleteSwapBuffers() { | 1323 void RenderWidget::didCompleteSwapBuffers() { |
| 1230 TRACE_EVENT0("renderer", "RenderWidget::didCompleteSwapBuffers"); | 1324 TRACE_EVENT0("renderer", "RenderWidget::didCompleteSwapBuffers"); |
| (...skipping 839 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2070 void RenderWidget::RegisterVideoHoleFrame(RenderFrameImpl* frame) { | 2164 void RenderWidget::RegisterVideoHoleFrame(RenderFrameImpl* frame) { |
| 2071 video_hole_frames_.AddObserver(frame); | 2165 video_hole_frames_.AddObserver(frame); |
| 2072 } | 2166 } |
| 2073 | 2167 |
| 2074 void RenderWidget::UnregisterVideoHoleFrame(RenderFrameImpl* frame) { | 2168 void RenderWidget::UnregisterVideoHoleFrame(RenderFrameImpl* frame) { |
| 2075 video_hole_frames_.RemoveObserver(frame); | 2169 video_hole_frames_.RemoveObserver(frame); |
| 2076 } | 2170 } |
| 2077 #endif // defined(VIDEO_HOLE) | 2171 #endif // defined(VIDEO_HOLE) |
| 2078 | 2172 |
| 2079 } // namespace content | 2173 } // namespace content |
| OLD | NEW |