Home | History | Annotate | Download | only in renderer_host
      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
      3 // found in the LICENSE file.
      4 
      5 #include "base/bind_helpers.h"
      6 #include "base/command_line.h"
      7 #include "base/logging.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "content/browser/browser_plugin/browser_plugin_guest.h"
     10 #include "content/browser/renderer_host/render_view_host_impl.h"
     11 #include "content/browser/renderer_host/render_widget_host_view_guest.h"
     12 #include "content/common/browser_plugin/browser_plugin_messages.h"
     13 #include "content/common/gpu/gpu_messages.h"
     14 #include "content/common/view_messages.h"
     15 #include "content/common/webplugin_geometry.h"
     16 #include "content/public/common/content_switches.h"
     17 #include "skia/ext/platform_canvas.h"
     18 #include "third_party/WebKit/public/web/WebScreenInfo.h"
     19 
     20 #if defined(OS_MACOSX)
     21 #import "content/browser/renderer_host/render_widget_host_view_mac_dictionary_helper.h"
     22 #endif
     23 
     24 #if defined(OS_WIN) || defined(USE_AURA)
     25 #include "content/browser/renderer_host/ui_events_helper.h"
     26 #endif
     27 
     28 namespace content {
     29 
     30 namespace {
     31 
     32 bool ShouldSendPinchGesture() {
     33   static bool pinch_allowed =
     34       CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePinch);
     35   return pinch_allowed;
     36 }
     37 
     38 WebKit::WebGestureEvent CreateFlingCancelEvent(double time_stamp) {
     39   WebKit::WebGestureEvent gesture_event;
     40   gesture_event.timeStampSeconds = time_stamp;
     41   gesture_event.type = WebKit::WebGestureEvent::GestureFlingCancel;
     42   gesture_event.sourceDevice = WebKit::WebGestureEvent::Touchscreen;
     43   return gesture_event;
     44 }
     45 
     46 }  // namespace
     47 
     48 RenderWidgetHostViewGuest::RenderWidgetHostViewGuest(
     49     RenderWidgetHost* widget_host,
     50     BrowserPluginGuest* guest,
     51     RenderWidgetHostView* platform_view)
     52     : host_(RenderWidgetHostImpl::From(widget_host)),
     53       guest_(guest),
     54       is_hidden_(false),
     55       platform_view_(static_cast<RenderWidgetHostViewPort*>(platform_view)) {
     56 #if defined(OS_WIN) || defined(USE_AURA)
     57   gesture_recognizer_.reset(ui::GestureRecognizer::Create(this));
     58 #endif  // defined(OS_WIN) || defined(USE_AURA)
     59   host_->SetView(this);
     60 }
     61 
     62 RenderWidgetHostViewGuest::~RenderWidgetHostViewGuest() {
     63 }
     64 
     65 RenderWidgetHost* RenderWidgetHostViewGuest::GetRenderWidgetHost() const {
     66   return host_;
     67 }
     68 
     69 void RenderWidgetHostViewGuest::WasShown() {
     70   // If the WebContents associated with us showed an interstitial page in the
     71   // beginning, the teardown path might call WasShown() while |host_| is in
     72   // the process of destruction. Avoid calling WasShown below in this case.
     73   // TODO(lazyboy): We shouldn't be showing interstitial pages in guests in the
     74   // first place: http://crbug.com/273089.
     75   //
     76   // |guest_| is NULL during test.
     77   if (!is_hidden_ || (guest_ && guest_->is_in_destruction()))
     78     return;
     79   is_hidden_ = false;
     80   host_->WasShown();
     81 }
     82 
     83 void RenderWidgetHostViewGuest::WasHidden() {
     84   // |guest_| is NULL during test.
     85   if (is_hidden_ || (guest_ && guest_->is_in_destruction()))
     86     return;
     87   is_hidden_ = true;
     88   host_->WasHidden();
     89 }
     90 
     91 void RenderWidgetHostViewGuest::SetSize(const gfx::Size& size) {
     92   size_ = size;
     93   host_->WasResized();
     94 }
     95 
     96 gfx::Rect RenderWidgetHostViewGuest::GetBoundsInRootWindow() {
     97   // We do not have any root window specific parts in this view.
     98   return GetViewBounds();
     99 }
    100 
    101 gfx::GLSurfaceHandle RenderWidgetHostViewGuest::GetCompositingSurface() {
    102   return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::TEXTURE_TRANSPORT);
    103 }
    104 
    105 #if defined(OS_WIN) || defined(USE_AURA)
    106 void RenderWidgetHostViewGuest::ProcessAckedTouchEvent(
    107     const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
    108   // TODO(fsamuel): Currently we will only take this codepath if the guest has
    109   // requested touch events. A better solution is to always forward touchpresses
    110   // to the embedder process to target a BrowserPlugin, and then route all
    111   // subsequent touch points of that touchdown to the appropriate guest until
    112   // that touch point is released.
    113   ScopedVector<ui::TouchEvent> events;
    114   if (!MakeUITouchEventsFromWebTouchEvents(touch, &events, LOCAL_COORDINATES))
    115     return;
    116 
    117   ui::EventResult result = (ack_result ==
    118       INPUT_EVENT_ACK_STATE_CONSUMED) ? ui::ER_HANDLED : ui::ER_UNHANDLED;
    119   for (ScopedVector<ui::TouchEvent>::iterator iter = events.begin(),
    120       end = events.end(); iter != end; ++iter)  {
    121     scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
    122     gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture(
    123         *(*iter), result, this));
    124     ProcessGestures(gestures.get());
    125   }
    126 }
    127 #endif
    128 
    129 void RenderWidgetHostViewGuest::Show() {
    130   WasShown();
    131 }
    132 
    133 void RenderWidgetHostViewGuest::Hide() {
    134   WasHidden();
    135 }
    136 
    137 bool RenderWidgetHostViewGuest::IsShowing() {
    138   return !is_hidden_;
    139 }
    140 
    141 gfx::Rect RenderWidgetHostViewGuest::GetViewBounds() const {
    142   RenderWidgetHostViewPort* rwhv = static_cast<RenderWidgetHostViewPort*>(
    143       guest_->GetEmbedderRenderWidgetHostView());
    144   gfx::Rect embedder_bounds;
    145   if (rwhv)
    146     embedder_bounds = rwhv->GetViewBounds();
    147   gfx::Rect shifted_rect = guest_->ToGuestRect(embedder_bounds);
    148   shifted_rect.set_width(size_.width());
    149   shifted_rect.set_height(size_.height());
    150   return shifted_rect;
    151 }
    152 
    153 void RenderWidgetHostViewGuest::RenderProcessGone(
    154     base::TerminationStatus status,
    155     int error_code) {
    156   platform_view_->RenderProcessGone(status, error_code);
    157   // Destroy the guest view instance only, so we don't end up calling
    158   // platform_view_->Destroy().
    159   DestroyGuestView();
    160 }
    161 
    162 void RenderWidgetHostViewGuest::Destroy() {
    163   // The RenderWidgetHost's destruction led here, so don't call it.
    164   DestroyGuestView();
    165 
    166   platform_view_->Destroy();
    167 }
    168 
    169 void RenderWidgetHostViewGuest::SetTooltipText(const string16& tooltip_text) {
    170   platform_view_->SetTooltipText(tooltip_text);
    171 }
    172 
    173 void RenderWidgetHostViewGuest::AcceleratedSurfaceBuffersSwapped(
    174     const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
    175     int gpu_host_id) {
    176   // If accelerated surface buffers are getting swapped then we're not using
    177   // the software path.
    178   guest_->clear_damage_buffer();
    179   BrowserPluginMsg_BuffersSwapped_Params guest_params;
    180   guest_params.size = params.size;
    181   guest_params.mailbox_name = params.mailbox_name;
    182   guest_params.route_id = params.route_id;
    183   guest_params.host_id = gpu_host_id;
    184   guest_->SendMessageToEmbedder(
    185       new BrowserPluginMsg_BuffersSwapped(guest_->instance_id(), guest_params));
    186 }
    187 
    188 void RenderWidgetHostViewGuest::AcceleratedSurfacePostSubBuffer(
    189     const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
    190     int gpu_host_id) {
    191   NOTREACHED();
    192 }
    193 
    194 void RenderWidgetHostViewGuest::OnSwapCompositorFrame(
    195     uint32 output_surface_id,
    196     scoped_ptr<cc::CompositorFrame> frame) {
    197   if (frame->software_frame_data) {
    198     cc::SoftwareFrameData* frame_data = frame->software_frame_data.get();
    199 #ifdef OS_WIN
    200     base::SharedMemory shared_memory(frame_data->handle, true,
    201                                      host_->GetProcess()->GetHandle());
    202 #else
    203     base::SharedMemory shared_memory(frame_data->handle, true);
    204 #endif
    205 
    206     RenderWidgetHostView* embedder_view =
    207         guest_->GetEmbedderRenderWidgetHostView();
    208     base::ProcessHandle embedder_pid =
    209         embedder_view->GetRenderWidgetHost()->GetProcess()->GetHandle();
    210 
    211     shared_memory.GiveToProcess(embedder_pid, &frame_data->handle);
    212   }
    213 
    214   guest_->clear_damage_buffer();
    215   guest_->SendMessageToEmbedder(
    216       new BrowserPluginMsg_CompositorFrameSwapped(
    217           guest_->instance_id(),
    218           *frame,
    219           host_->GetRoutingID(),
    220           output_surface_id,
    221           host_->GetProcess()->GetID()));
    222 }
    223 
    224 void RenderWidgetHostViewGuest::SetBounds(const gfx::Rect& rect) {
    225   SetSize(rect.size());
    226 }
    227 
    228 bool RenderWidgetHostViewGuest::OnMessageReceived(const IPC::Message& msg) {
    229   return platform_view_->OnMessageReceived(msg);
    230 }
    231 
    232 void RenderWidgetHostViewGuest::InitAsChild(
    233     gfx::NativeView parent_view) {
    234   platform_view_->InitAsChild(parent_view);
    235 }
    236 
    237 void RenderWidgetHostViewGuest::InitAsPopup(
    238     RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
    239   // This should never get called.
    240   NOTREACHED();
    241 }
    242 
    243 void RenderWidgetHostViewGuest::InitAsFullscreen(
    244     RenderWidgetHostView* reference_host_view) {
    245   // This should never get called.
    246   NOTREACHED();
    247 }
    248 
    249 gfx::NativeView RenderWidgetHostViewGuest::GetNativeView() const {
    250   return guest_->GetEmbedderRenderWidgetHostView()->GetNativeView();
    251 }
    252 
    253 gfx::NativeViewId RenderWidgetHostViewGuest::GetNativeViewId() const {
    254   return guest_->GetEmbedderRenderWidgetHostView()->GetNativeViewId();
    255 }
    256 
    257 gfx::NativeViewAccessible RenderWidgetHostViewGuest::GetNativeViewAccessible() {
    258   return guest_->GetEmbedderRenderWidgetHostView()->GetNativeViewAccessible();
    259 }
    260 
    261 void RenderWidgetHostViewGuest::MovePluginWindows(
    262     const gfx::Vector2d& scroll_offset,
    263     const std::vector<WebPluginGeometry>& moves) {
    264   platform_view_->MovePluginWindows(scroll_offset, moves);
    265 }
    266 
    267 void RenderWidgetHostViewGuest::Focus() {
    268 }
    269 
    270 void RenderWidgetHostViewGuest::Blur() {
    271 }
    272 
    273 bool RenderWidgetHostViewGuest::HasFocus() const {
    274   return false;
    275 }
    276 
    277 bool RenderWidgetHostViewGuest::IsSurfaceAvailableForCopy() const {
    278   NOTIMPLEMENTED();
    279   return false;
    280 }
    281 
    282 void RenderWidgetHostViewGuest::UpdateCursor(const WebCursor& cursor) {
    283   platform_view_->UpdateCursor(cursor);
    284 }
    285 
    286 void RenderWidgetHostViewGuest::SetIsLoading(bool is_loading) {
    287   platform_view_->SetIsLoading(is_loading);
    288 }
    289 
    290 void RenderWidgetHostViewGuest::TextInputTypeChanged(
    291     ui::TextInputType type,
    292     bool can_compose_inline,
    293     ui::TextInputMode input_mode) {
    294   RenderWidgetHostViewPort::FromRWHV(
    295       guest_->GetEmbedderRenderWidgetHostView())->
    296           TextInputTypeChanged(type, can_compose_inline, input_mode);
    297 }
    298 
    299 void RenderWidgetHostViewGuest::ImeCancelComposition() {
    300   platform_view_->ImeCancelComposition();
    301 }
    302 
    303 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
    304 void RenderWidgetHostViewGuest::ImeCompositionRangeChanged(
    305     const ui::Range& range,
    306     const std::vector<gfx::Rect>& character_bounds) {
    307 }
    308 #endif
    309 
    310 void RenderWidgetHostViewGuest::DidUpdateBackingStore(
    311     const gfx::Rect& scroll_rect,
    312     const gfx::Vector2d& scroll_delta,
    313     const std::vector<gfx::Rect>& copy_rects,
    314     const ui::LatencyInfo& latency_info) {
    315   NOTREACHED();
    316 }
    317 
    318 void RenderWidgetHostViewGuest::SelectionChanged(const string16& text,
    319                                                  size_t offset,
    320                                                  const ui::Range& range) {
    321   platform_view_->SelectionChanged(text, offset, range);
    322 }
    323 
    324 void RenderWidgetHostViewGuest::SelectionBoundsChanged(
    325     const ViewHostMsg_SelectionBounds_Params& params) {
    326   platform_view_->SelectionBoundsChanged(params);
    327 }
    328 
    329 void RenderWidgetHostViewGuest::ScrollOffsetChanged() {
    330 }
    331 
    332 BackingStore* RenderWidgetHostViewGuest::AllocBackingStore(
    333     const gfx::Size& size) {
    334   NOTREACHED();
    335   return NULL;
    336 }
    337 
    338 void RenderWidgetHostViewGuest::CopyFromCompositingSurface(
    339     const gfx::Rect& src_subrect,
    340     const gfx::Size& /* dst_size */,
    341     const base::Callback<void(bool, const SkBitmap&)>& callback) {
    342   callback.Run(false, SkBitmap());
    343 }
    344 
    345 void RenderWidgetHostViewGuest::CopyFromCompositingSurfaceToVideoFrame(
    346       const gfx::Rect& src_subrect,
    347       const scoped_refptr<media::VideoFrame>& target,
    348       const base::Callback<void(bool)>& callback) {
    349   NOTIMPLEMENTED();
    350   callback.Run(false);
    351 }
    352 
    353 bool RenderWidgetHostViewGuest::CanCopyToVideoFrame() const {
    354   return false;
    355 }
    356 
    357 void RenderWidgetHostViewGuest::AcceleratedSurfaceSuspend() {
    358   NOTREACHED();
    359 }
    360 
    361 void RenderWidgetHostViewGuest::AcceleratedSurfaceRelease() {
    362 }
    363 
    364 bool RenderWidgetHostViewGuest::HasAcceleratedSurface(
    365       const gfx::Size& desired_size) {
    366   return false;
    367 }
    368 
    369 void RenderWidgetHostViewGuest::SetBackground(const SkBitmap& background) {
    370   platform_view_->SetBackground(background);
    371 }
    372 
    373 #if defined(OS_WIN) && !defined(USE_AURA)
    374 void RenderWidgetHostViewGuest::SetClickthroughRegion(SkRegion* region) {
    375 }
    376 #endif
    377 
    378 #if defined(OS_WIN) && defined(USE_AURA)
    379 gfx::NativeViewAccessible
    380 RenderWidgetHostViewGuest::AccessibleObjectFromChildId(long child_id) {
    381   NOTIMPLEMENTED();
    382   return NULL;
    383 }
    384 #endif
    385 
    386 void RenderWidgetHostViewGuest::SetHasHorizontalScrollbar(
    387     bool has_horizontal_scrollbar) {
    388   platform_view_->SetHasHorizontalScrollbar(has_horizontal_scrollbar);
    389 }
    390 
    391 void RenderWidgetHostViewGuest::SetScrollOffsetPinning(
    392     bool is_pinned_to_left, bool is_pinned_to_right) {
    393   platform_view_->SetScrollOffsetPinning(
    394       is_pinned_to_left, is_pinned_to_right);
    395 }
    396 
    397 void RenderWidgetHostViewGuest::OnAcceleratedCompositingStateChange() {
    398 }
    399 
    400 bool RenderWidgetHostViewGuest::LockMouse() {
    401   return platform_view_->LockMouse();
    402 }
    403 
    404 void RenderWidgetHostViewGuest::UnlockMouse() {
    405   return platform_view_->UnlockMouse();
    406 }
    407 
    408 void RenderWidgetHostViewGuest::GetScreenInfo(WebKit::WebScreenInfo* results) {
    409   RenderWidgetHostViewPort* embedder_view =
    410       RenderWidgetHostViewPort::FromRWHV(
    411           guest_->GetEmbedderRenderWidgetHostView());
    412   embedder_view->GetScreenInfo(results);
    413 }
    414 
    415 void RenderWidgetHostViewGuest::OnAccessibilityNotifications(
    416     const std::vector<AccessibilityHostMsg_NotificationParams>& params) {
    417 }
    418 
    419 #if defined(OS_MACOSX)
    420 void RenderWidgetHostViewGuest::SetActive(bool active) {
    421   platform_view_->SetActive(active);
    422 }
    423 
    424 void RenderWidgetHostViewGuest::SetTakesFocusOnlyOnMouseDown(bool flag) {
    425   platform_view_->SetTakesFocusOnlyOnMouseDown(flag);
    426 }
    427 
    428 void RenderWidgetHostViewGuest::SetWindowVisibility(bool visible) {
    429   platform_view_->SetWindowVisibility(visible);
    430 }
    431 
    432 void RenderWidgetHostViewGuest::WindowFrameChanged() {
    433   platform_view_->WindowFrameChanged();
    434 }
    435 
    436 void RenderWidgetHostViewGuest::ShowDefinitionForSelection() {
    437   gfx::Point origin;
    438   gfx::Rect guest_bounds = GetViewBounds();
    439   gfx::Rect embedder_bounds =
    440       guest_->GetEmbedderRenderWidgetHostView()->GetViewBounds();
    441 
    442   gfx::Vector2d guest_offset = gfx::Vector2d(
    443       // Horizontal offset of guest from embedder.
    444       guest_bounds.x() - embedder_bounds.x(),
    445       // Vertical offset from guest's top to embedder's bottom edge.
    446       embedder_bounds.bottom() - guest_bounds.y());
    447 
    448   RenderWidgetHostViewMacDictionaryHelper helper(platform_view_);
    449   helper.SetTargetView(guest_->GetEmbedderRenderWidgetHostView());
    450   helper.set_offset(guest_offset);
    451   helper.ShowDefinitionForSelection();
    452 }
    453 
    454 bool RenderWidgetHostViewGuest::SupportsSpeech() const {
    455   return platform_view_->SupportsSpeech();
    456 }
    457 
    458 void RenderWidgetHostViewGuest::SpeakSelection() {
    459   platform_view_->SpeakSelection();
    460 }
    461 
    462 bool RenderWidgetHostViewGuest::IsSpeaking() const {
    463   return platform_view_->IsSpeaking();
    464 }
    465 
    466 void RenderWidgetHostViewGuest::StopSpeaking() {
    467   platform_view_->StopSpeaking();
    468 }
    469 
    470 void RenderWidgetHostViewGuest::AboutToWaitForBackingStoreMsg() {
    471   NOTREACHED();
    472 }
    473 
    474 bool RenderWidgetHostViewGuest::PostProcessEventForPluginIme(
    475     const NativeWebKeyboardEvent& event) {
    476   return false;
    477 }
    478 
    479 #endif  // defined(OS_MACOSX)
    480 
    481 #if defined(OS_ANDROID)
    482 void RenderWidgetHostViewGuest::ShowDisambiguationPopup(
    483     const gfx::Rect& target_rect,
    484     const SkBitmap& zoomed_bitmap) {
    485 }
    486 
    487 void RenderWidgetHostViewGuest::HasTouchEventHandlers(bool need_touch_events) {
    488 }
    489 #endif  // defined(OS_ANDROID)
    490 
    491 #if defined(TOOLKIT_GTK)
    492 GdkEventButton* RenderWidgetHostViewGuest::GetLastMouseDown() {
    493   return NULL;
    494 }
    495 
    496 gfx::NativeView RenderWidgetHostViewGuest::BuildInputMethodsGtkMenu() {
    497   return platform_view_->BuildInputMethodsGtkMenu();
    498 }
    499 #endif  // defined(TOOLKIT_GTK)
    500 
    501 #if defined(OS_WIN) && !defined(USE_AURA)
    502 void RenderWidgetHostViewGuest::WillWmDestroy() {
    503 }
    504 #endif
    505 
    506 #if defined(OS_WIN) && defined(USE_AURA)
    507 void RenderWidgetHostViewGuest::SetParentNativeViewAccessible(
    508     gfx::NativeViewAccessible accessible_parent) {
    509 }
    510 #endif
    511 
    512 void RenderWidgetHostViewGuest::DestroyGuestView() {
    513   host_->SetView(NULL);
    514   host_ = NULL;
    515   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
    516 }
    517 
    518 bool RenderWidgetHostViewGuest::DispatchLongPressGestureEvent(
    519     ui::GestureEvent* event) {
    520   return ForwardGestureEventToRenderer(event);
    521 }
    522 
    523 bool RenderWidgetHostViewGuest::DispatchCancelTouchEvent(
    524     ui::TouchEvent* event) {
    525   if (!host_)
    526     return false;
    527 
    528   WebKit::WebTouchEvent cancel_event;
    529   cancel_event.type = WebKit::WebInputEvent::TouchCancel;
    530   cancel_event.timeStampSeconds = event->time_stamp().InSecondsF();
    531   host_->ForwardTouchEventWithLatencyInfo(cancel_event, *event->latency());
    532   return true;
    533 }
    534 
    535 bool RenderWidgetHostViewGuest::ForwardGestureEventToRenderer(
    536     ui::GestureEvent* gesture) {
    537 #if defined(OS_WIN) || defined(USE_AURA)
    538   if (!host_)
    539     return false;
    540 
    541   // Pinch gestures are disabled by default on windows desktop. See
    542   // crbug.com/128477 and crbug.com/148816
    543   if ((gesture->type() == ui::ET_GESTURE_PINCH_BEGIN ||
    544       gesture->type() == ui::ET_GESTURE_PINCH_UPDATE ||
    545       gesture->type() == ui::ET_GESTURE_PINCH_END) &&
    546       !ShouldSendPinchGesture()) {
    547     return true;
    548   }
    549 
    550   WebKit::WebGestureEvent web_gesture =
    551       MakeWebGestureEventFromUIEvent(*gesture);
    552   const gfx::Point& client_point = gesture->location();
    553   const gfx::Point& screen_point = gesture->location();
    554 
    555   web_gesture.x = client_point.x();
    556   web_gesture.y = client_point.y();
    557   web_gesture.globalX = screen_point.x();
    558   web_gesture.globalY = screen_point.y();
    559 
    560   if (web_gesture.type == WebKit::WebGestureEvent::Undefined)
    561     return false;
    562   if (web_gesture.type == WebKit::WebGestureEvent::GestureTapDown) {
    563     host_->ForwardGestureEvent(
    564         CreateFlingCancelEvent(gesture->time_stamp().InSecondsF()));
    565   }
    566   host_->ForwardGestureEvent(web_gesture);
    567   return true;
    568 #else
    569   return false;
    570 #endif
    571 }
    572 
    573 void RenderWidgetHostViewGuest::ProcessGestures(
    574     ui::GestureRecognizer::Gestures* gestures) {
    575   if ((gestures == NULL) || gestures->empty())
    576     return;
    577   for (ui::GestureRecognizer::Gestures::iterator g_it = gestures->begin();
    578       g_it != gestures->end();
    579       ++g_it) {
    580     ForwardGestureEventToRenderer(*g_it);
    581   }
    582 }
    583 
    584 
    585 }  // namespace content
    586