Commit message: None
CVE: CVE-2018-18344
CWE: CWE-20
Vulnerability description: Inappropriate allowance of the setDownloadBehavior devtools protocol feature in Extensions in Google Chrome prior to 71.0.3578.80 allowed a remote attacker with control of an installed extension to access files on the local file system via a crafted Chrome Extension.

Project: Chrome

void ExtensionDevToolsClientHost::AgentHostClosed(
    DevToolsAgentHost* agent_host) {
  DCHECK(agent_host == agent_host_.get());
  RespondDetachedToPendingRequests();
  SendDetachedEvent();
  delete this;
}

Project: Chrome

bool ExtensionDevToolsClientHost::Attach() {
  if (!agent_host_->AttachClient(this))
    return false;

  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          ::switches::kSilentDebuggerExtensionAPI)) {
    return true;
  }

  if (Manifest::IsPolicyLocation(extension_->location()))
    return true;

  infobar_ = ExtensionDevToolsInfoBar::Create(
      extension_id(), extension_->name(), this,
      base::Bind(&ExtensionDevToolsClientHost::InfoBarDismissed,
                 base::Unretained(this)));
  return true;
}

Project: Chrome

void ExtensionDevToolsClientHost::DispatchProtocolMessage(
    DevToolsAgentHost* agent_host, const std::string& message) {
  DCHECK(agent_host == agent_host_.get());
  if (!EventRouter::Get(profile_))
    return;

  std::unique_ptr<base::Value> result = base::JSONReader::Read(message);
  if (!result || !result->is_dict())
    return;
  base::DictionaryValue* dictionary =
      static_cast<base::DictionaryValue*>(result.get());

  int id;
  if (!dictionary->GetInteger("id", &id)) {
    std::string method_name;
    if (!dictionary->GetString("method", &method_name))
      return;

    OnEvent::Params params;
    base::DictionaryValue* params_value;
    if (dictionary->GetDictionary("params", &params_value))
      params.additional_properties.Swap(params_value);

    std::unique_ptr<base::ListValue> args(
        OnEvent::Create(debuggee_, method_name, params));
    auto event =
        std::make_unique<Event>(events::DEBUGGER_ON_EVENT, OnEvent::kEventName,
                                std::move(args), profile_);
    EventRouter::Get(profile_)->DispatchEventToExtension(extension_id(),
                                                         std::move(event));
  } else {
    DebuggerSendCommandFunction* function = pending_requests_[id].get();
    if (!function)
      return;

    function->SendResponseBody(dictionary);
    pending_requests_.erase(id);
  }
}

Project: Chrome

bool ExtensionCanAttachToURL(const Extension& extension,
                             const GURL& url,
                             Profile* profile,
                             std::string* error) {
  if (url == content::kUnreachableWebDataURL)
    return true;

  if (extension.permissions_data()->IsRestrictedUrl(url, error))
    return false;

  if (url.SchemeIsFile() && !util::AllowFileAccess(extension.id(), profile)) {
    *error = debugger_api_constants::kRestrictedError;
    return false;
  }

  return true;
}

Project: Chrome

ExtensionDevToolsClientHost* DebuggerFunction::FindClientHost() {
  if (!agent_host_.get())
    return nullptr;

  const std::string& extension_id = extension()->id();
  DevToolsAgentHost* agent_host = agent_host_.get();
  AttachedClientHosts& hosts = g_attached_client_hosts.Get();
  auto it = std::find_if(
      hosts.begin(), hosts.end(),
      [&agent_host, &extension_id](ExtensionDevToolsClientHost* client_host) {
        return client_host->agent_host() == agent_host &&
               client_host->extension_id() == extension_id;
      });

  return it == hosts.end() ? nullptr : *it;
}

Project: Chrome

void ExtensionDevToolsClientHost::InfoBarDismissed() {
  detach_reason_ = api::debugger::DETACH_REASON_CANCELED_BY_USER;
  RespondDetachedToPendingRequests();
  SendDetachedEvent();
  Close();
}

Project: Chrome

bool DebuggerFunction::InitClientHost() {
  if (!InitAgentHost())
    return false;

  client_host_ = FindClientHost();
  if (!client_host_) {
    FormatErrorMessage(debugger_api_constants::kNotAttachedError);
    return false;
  }

  return true;
}

Project: Chrome

bool ExtensionDevToolsClientHost::MayAttachToBrowser() {
  return false;
}

Project: Chrome

bool ExtensionDevToolsClientHost::MayAttachToRenderer(
    content::RenderFrameHost* render_frame_host,
    bool is_webui) {
  if (is_webui)
    return false;

  if (!render_frame_host)
    return true;

  std::string error;
  const GURL& site_instance_url =
      render_frame_host->GetSiteInstance()->GetSiteURL();

  if (site_instance_url.is_empty() || site_instance_url == "about:") {
    return true;
  }

  return ExtensionCanAttachToURL(*extension_, site_instance_url, profile_,
                                 &error);
}

Project: Chrome

bool ExtensionDevToolsClientHost::MayDiscoverTargets() {
   return false;
 }

Project: Chrome

void ExtensionDevToolsClientHost::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
  Close();
}

Project: Chrome

void ExtensionDevToolsClientHost::OnExtensionUnloaded(
    content::BrowserContext* browser_context,
    const Extension* extension,
    UnloadedExtensionReason reason) {
  if (extension->id() == extension_id())
    Close();
}

Project: Chrome

void ExtensionDevToolsClientHost::RespondDetachedToPendingRequests() {
  for (const auto& it : pending_requests_)
    it.second->SendDetachedError();
  pending_requests_.clear();
}

Project: Chrome

bool DebuggerAttachFunction::RunAsync() {
  std::unique_ptr<Attach::Params> params(Attach::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  CopyDebuggee(&debuggee_, params->target);
  if (!InitAgentHost())
    return false;

  if (!DevToolsAgentHost::IsSupportedProtocolVersion(
          params->required_version)) {
    error_ = ErrorUtils::FormatErrorMessage(
        debugger_api_constants::kProtocolVersionNotSupportedError,
        params->required_version);
    return false;
  }

  if (FindClientHost()) {
    FormatErrorMessage(debugger_api_constants::kAlreadyAttachedError);
    return false;
  }

  auto host = std::make_unique<ExtensionDevToolsClientHost>(
      GetProfile(), agent_host_.get(), extension(), debuggee_);

  if (!host->Attach()) {
    FormatErrorMessage(debugger_api_constants::kRestrictedError);
    return false;
  }

  host.release();  // An attached client host manages its own lifetime.
  SendResponse(true);
  return true;
}

Project: Chrome

bool DebuggerDetachFunction::RunAsync() {
  std::unique_ptr<Detach::Params> params(Detach::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  CopyDebuggee(&debuggee_, params->target);
  if (!InitClientHost())
    return false;

  client_host_->RespondDetachedToPendingRequests();
  client_host_->Close();
  SendResponse(true);
  return true;
}

Project: Chrome

bool DebuggerSendCommandFunction::RunAsync() {
  std::unique_ptr<SendCommand::Params> params(
      SendCommand::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  CopyDebuggee(&debuggee_, params->target);
  if (!InitClientHost())
    return false;

  client_host_->SendMessageToBackend(this, params->method,
      params->command_params.get());
  return true;
}

Project: Chrome

bool DebuggerGetTargetsFunction::RunAsync() {
  content::DevToolsAgentHost::List list = DevToolsAgentHost::GetOrCreateAll();
  base::PostTaskWithTraits(
      FROM_HERE, {content::BrowserThread::UI},
      base::BindOnce(&DebuggerGetTargetsFunction::SendTargetList, this, list));
  return true;
}

Project: Chrome

void DebuggerSendCommandFunction::SendDetachedError() {
  error_ = debugger_api_constants::kDetachedWhileHandlingError;
  SendResponse(false);
}

Project: Chrome

void ExtensionDevToolsClientHost::SendDetachedEvent() {
  if (!EventRouter::Get(profile_))
    return;

  std::unique_ptr<base::ListValue> args(
      OnDetach::Create(debuggee_, detach_reason_));
  auto event =
      std::make_unique<Event>(events::DEBUGGER_ON_DETACH, OnDetach::kEventName,
                              std::move(args), profile_);
  EventRouter::Get(profile_)->DispatchEventToExtension(extension_id(),
                                                       std::move(event));
}

Project: Chrome

void ExtensionDevToolsClientHost::SendMessageToBackend(
    DebuggerSendCommandFunction* function,
    const std::string& method,
    SendCommand::Params::CommandParams* command_params) {
  base::DictionaryValue protocol_request;
  int request_id = ++last_request_id_;
  pending_requests_[request_id] = function;
  protocol_request.SetInteger("id", request_id);
  protocol_request.SetString("method", method);
  if (command_params) {
    protocol_request.Set(
        "params", command_params->additional_properties.CreateDeepCopy());
  }

  std::string json_args;
  base::JSONWriter::Write(protocol_request, &json_args);
  agent_host_->DispatchProtocolMessage(this, json_args);
}

Project: Chrome

void DebuggerSendCommandFunction::SendResponseBody(
    base::DictionaryValue* response) {
  base::Value* error_body;
  if (response->Get("error", &error_body)) {
    base::JSONWriter::Write(*error_body, &error_);
    SendResponse(false);
    return;
  }

  base::DictionaryValue* result_body;
  SendCommand::Results::Result result;
  if (response->GetDictionary("result", &result_body))
    result.additional_properties.Swap(result_body);

  results_ = SendCommand::Results::Create(result);
  SendResponse(true);
}

Project: Chrome

void DebuggerGetTargetsFunction::SendTargetList(
    const content::DevToolsAgentHost::List& target_list) {
  std::unique_ptr<base::ListValue> result(new base::ListValue());
  for (size_t i = 0; i < target_list.size(); ++i)
    result->Append(SerializeTarget(target_list[i]));
  SetResult(std::move(result));
  SendResponse(true);
}

Project: Chrome

std::unique_ptr<base::DictionaryValue> SerializeTarget(
    scoped_refptr<DevToolsAgentHost> host) {
  std::unique_ptr<base::DictionaryValue> dictionary(
      new base::DictionaryValue());
  dictionary->SetString(kTargetIdField, host->GetId());
  dictionary->SetString(kTargetTitleField, host->GetTitle());
  dictionary->SetBoolean(kTargetAttachedField, host->IsAttached());
  dictionary->SetString(kTargetUrlField, host->GetURL().spec());

  std::string type = host->GetType();
  std::string target_type = kTargetTypeOther;
  if (type == DevToolsAgentHost::kTypePage) {
    int tab_id =
        extensions::ExtensionTabUtil::GetTabId(host->GetWebContents());
    dictionary->SetInteger(kTargetTabIdField, tab_id);
    target_type = kTargetTypePage;
  } else if (type == ChromeDevToolsManagerDelegate::kTypeBackgroundPage) {
    dictionary->SetString(kTargetExtensionIdField, host->GetURL().host());
    target_type = kTargetTypeBackgroundPage;
  } else if (type == DevToolsAgentHost::kTypeServiceWorker ||
             type == DevToolsAgentHost::kTypeSharedWorker) {
    target_type = kTargetTypeWorker;
  }

  dictionary->SetString(kTargetTypeField, target_type);

  GURL favicon_url = host->GetFaviconURL();
  if (favicon_url.is_valid())
    dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());

  return dictionary;
}

Project: Chrome

  DevToolsAgentHost* agent_host() { return agent_host_.get(); }

Project: Chrome

  const std::string& extension_id() { return extension_->id(); }

Project: Chrome

std::unique_ptr<Page::ScreencastFrameMetadata> BuildScreencastFrameMetadata(
    const gfx::Size& surface_size,
    float device_scale_factor,
    float page_scale_factor,
    const gfx::Vector2dF& root_scroll_offset,
    float top_controls_height,
    float top_controls_shown_ratio) {
  if (surface_size.IsEmpty() || device_scale_factor == 0)
    return nullptr;

  const gfx::SizeF content_size_dip =
      gfx::ScaleSize(gfx::SizeF(surface_size), 1 / device_scale_factor);
  float top_offset_dip = top_controls_height * top_controls_shown_ratio;
  gfx::Vector2dF root_scroll_offset_dip = root_scroll_offset;
  if (IsUseZoomForDSFEnabled()) {
    top_offset_dip /= device_scale_factor;
    root_scroll_offset_dip.Scale(1 / device_scale_factor);
  }
  std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata =
      Page::ScreencastFrameMetadata::Create()
          .SetPageScaleFactor(page_scale_factor)
          .SetOffsetTop(top_offset_dip)
          .SetDeviceWidth(content_size_dip.width())
          .SetDeviceHeight(content_size_dip.height())
          .SetScrollOffsetX(root_scroll_offset_dip.x())
          .SetScrollOffsetY(root_scroll_offset_dip.y())
          .SetTimestamp(base::Time::Now().ToDoubleT())
          .Build();
  return page_metadata;
}

Project: Chrome

void PageHandler::CaptureScreenshot(
    Maybe<std::string> format,
    Maybe<int> quality,
    Maybe<Page::Viewport> clip,
    Maybe<bool> from_surface,
    std::unique_ptr<CaptureScreenshotCallback> callback) {
  if (!host_ || !host_->GetRenderWidgetHost() ||
      !host_->GetRenderWidgetHost()->GetView()) {
    callback->sendFailure(Response::InternalError());
    return;
  }

  RenderWidgetHostImpl* widget_host = host_->GetRenderWidgetHost();
  std::string screenshot_format = format.fromMaybe(kPng);
  int screenshot_quality = quality.fromMaybe(kDefaultScreenshotQuality);

  if (!from_surface.fromMaybe(true)) {
    widget_host->GetSnapshotFromBrowser(
        base::Bind(&PageHandler::ScreenshotCaptured, weak_factory_.GetWeakPtr(),
                   base::Passed(std::move(callback)), screenshot_format,
                   screenshot_quality, gfx::Size(), gfx::Size(),
                   blink::WebDeviceEmulationParams()),
        false);
    return;
  }

  bool emulation_enabled = emulation_handler_->device_emulation_enabled();
  blink::WebDeviceEmulationParams original_params =
      emulation_handler_->GetDeviceEmulationParams();
  blink::WebDeviceEmulationParams modified_params = original_params;

  gfx::Size original_view_size =
      emulation_enabled || clip.isJust()
          ? widget_host->GetView()->GetViewBounds().size()
          : gfx::Size();
  gfx::Size emulated_view_size = modified_params.view_size;

  double dpfactor = 1;
  ScreenInfo screen_info;
  widget_host->GetScreenInfo(&screen_info);
  if (emulation_enabled) {
    float original_scale =
        original_params.scale > 0 ? original_params.scale : 1;
    if (!modified_params.view_size.width) {
      emulated_view_size.set_width(
          ceil(original_view_size.width() / original_scale));
    }
    if (!modified_params.view_size.height) {
      emulated_view_size.set_height(
          ceil(original_view_size.height() / original_scale));
    }

    dpfactor = modified_params.device_scale_factor
                   ? modified_params.device_scale_factor /
                         screen_info.device_scale_factor
                   : 1;
    modified_params.scale = clip.isJust() ? 1 : dpfactor;
    modified_params.view_size.width = emulated_view_size.width();
    modified_params.view_size.height = emulated_view_size.height();
  } else if (clip.isJust()) {
    modified_params.view_size.width = original_view_size.width();
    modified_params.view_size.height = original_view_size.height();
    modified_params.screen_size.width = 0;
    modified_params.screen_size.height = 0;
    modified_params.device_scale_factor = 0;
    modified_params.scale = 1;
  }

  if (clip.isJust()) {
    modified_params.viewport_offset.x = clip.fromJust()->GetX();
    modified_params.viewport_offset.y = clip.fromJust()->GetY();
    modified_params.viewport_scale = clip.fromJust()->GetScale() * dpfactor;
  }

  emulation_handler_->SetDeviceEmulationParams(modified_params);

  if (clip.isJust()) {
    double scale = dpfactor * clip.fromJust()->GetScale();
    widget_host->GetView()->SetSize(
        gfx::Size(gfx::ToRoundedInt(clip.fromJust()->GetWidth() * scale),
                  gfx::ToRoundedInt(clip.fromJust()->GetHeight() * scale)));
  } else if (emulation_enabled) {
    widget_host->GetView()->SetSize(
        gfx::ScaleToFlooredSize(emulated_view_size, dpfactor));
  }
  gfx::Size requested_image_size = gfx::Size();
  if (emulation_enabled || clip.isJust()) {
    if (clip.isJust()) {
      requested_image_size =
          gfx::Size(clip.fromJust()->GetWidth(), clip.fromJust()->GetHeight());
    } else {
      requested_image_size = emulated_view_size;
    }
    double scale = emulation_enabled ? original_params.device_scale_factor
                                     : screen_info.device_scale_factor;
    if (clip.isJust())
      scale *= clip.fromJust()->GetScale();
    requested_image_size = gfx::ScaleToRoundedSize(requested_image_size, scale);
  }

  widget_host->GetSnapshotFromBrowser(
      base::Bind(&PageHandler::ScreenshotCaptured, weak_factory_.GetWeakPtr(),
                 base::Passed(std::move(callback)), screenshot_format,
                 screenshot_quality, original_view_size, requested_image_size,
                 original_params),
      true);
}

Project: Chrome

Response PageHandler::Close() {
  WebContentsImpl* web_contents = GetWebContents();
  if (!web_contents)
    return Response::Error("Not attached to a page");
  web_contents->DispatchBeforeUnload(false /* auto_cancel */);
  return Response::OK();
}

Project: Chrome

Response PageHandler::Crash() {
  WebContentsImpl* web_contents = GetWebContents();
  if (!web_contents)
    return Response::Error("Not attached to a page");
  if (web_contents->IsCrashed())
    return Response::Error("The target has already crashed");
  if (web_contents->GetMainFrame()->frame_tree_node()->navigation_request())
    return Response::Error("Page has pending navigations, not killing");
  return Response::FallThrough();
}

Project: Chrome

gfx::Size DetermineSnapshotSize(const gfx::Size& surface_size,
                                int screencast_max_width,
                                int screencast_max_height) {
  if (surface_size.IsEmpty())
    return gfx::Size();  // Nothing to copy (and avoid divide-by-zero below).

  double scale = 1;
  if (screencast_max_width > 0) {
    scale = std::min(scale, static_cast<double>(screencast_max_width) /
                                surface_size.width());
  }
  if (screencast_max_height > 0) {
    scale = std::min(scale, static_cast<double>(screencast_max_height) /
                                surface_size.height());
  }
  return gfx::ToRoundedSize(gfx::ScaleSize(gfx::SizeF(surface_size), scale));
}

Project: Chrome

void PageHandler::DidAttachInterstitialPage() {
  if (!enabled_)
    return;
  frontend_->InterstitialShown();
}

Project: Chrome

void PageHandler::DidCloseJavaScriptDialog(bool success,
                                           const base::string16& user_input) {
  if (!enabled_)
    return;
  pending_dialog_.Reset();
  frontend_->JavascriptDialogClosed(success, base::UTF16ToUTF8(user_input));
}

Project: Chrome

void PageHandler::DidDetachInterstitialPage() {
  if (!enabled_)
    return;
  frontend_->InterstitialHidden();
}

Project: Chrome

Response PageHandler::Disable() {
  enabled_ = false;
  screencast_enabled_ = false;

  if (video_consumer_)
    video_consumer_->StopCapture();

  if (!pending_dialog_.is_null()) {
    WebContentsImpl* web_contents = GetWebContents();
    bool has_dialog_manager =
        web_contents && web_contents->GetDelegate() &&
        web_contents->GetDelegate()->GetJavaScriptDialogManager(web_contents);
    if (!has_dialog_manager)
      std::move(pending_dialog_).Run(false, base::string16());
    pending_dialog_.Reset();
  }

  download_manager_delegate_ = nullptr;
  navigate_callbacks_.clear();
  return Response::FallThrough();
}

Project: Chrome

Response PageHandler::Enable() {
  enabled_ = true;
  if (GetWebContents() && GetWebContents()->ShowingInterstitialPage())
    frontend_->InterstitialShown();
  return Response::FallThrough();
}

Project: Chrome

std::vector<PageHandler*> PageHandler::EnabledForWebContents(
    WebContentsImpl* contents) {
  if (!DevToolsAgentHost::HasFor(contents))
    return std::vector<PageHandler*>();
  std::vector<PageHandler*> result;
  for (auto* handler :
       PageHandler::ForAgentHost(static_cast<DevToolsAgentHostImpl*>(
           DevToolsAgentHost::GetOrCreateFor(contents).get()))) {
    if (handler->enabled_)
      result.push_back(handler);
  }
  return result;
}

Project: Chrome

std::string EncodeImage(const gfx::Image& image,
                        const std::string& format,
                        int quality) {
  DCHECK(!image.IsEmpty());

  scoped_refptr<base::RefCountedMemory> data;
  if (format == kPng) {
    data = image.As1xPNGBytes();
  } else if (format == kJpeg) {
    scoped_refptr<base::RefCountedBytes> bytes(new base::RefCountedBytes());
    if (gfx::JPEG1xEncodedDataFromImage(image, quality, &bytes->data()))
      data = bytes;
  }

  if (!data || !data->front())
    return std::string();

  std::string base_64_data;
  base::Base64Encode(
      base::StringPiece(reinterpret_cast<const char*>(data->front()),
                        data->size()),
      &base_64_data);

  return base_64_data;
}

Project: Chrome

std::string EncodeSkBitmap(const SkBitmap& image,
                           const std::string& format,
                           int quality) {
  return EncodeImage(gfx::Image::CreateFrom1xBitmap(image), format, quality);
}

Project: Chrome

std::vector<PageHandler*> PageHandler::ForAgentHost(
    DevToolsAgentHostImpl* host) {
  return host->HandlersByName<PageHandler>(Page::Metainfo::domainName);
}

Project: Chrome

Response PageHandler::GetNavigationHistory(
    int* current_index,
    std::unique_ptr<NavigationEntries>* entries) {
  WebContentsImpl* web_contents = GetWebContents();
  if (!web_contents)
    return Response::InternalError();

  NavigationController& controller = web_contents->GetController();
  *current_index = controller.GetCurrentEntryIndex();
  *entries = NavigationEntries::create();
  for (int i = 0; i != controller.GetEntryCount(); ++i) {
    auto* entry = controller.GetEntryAtIndex(i);
    (*entries)->addItem(
        Page::NavigationEntry::Create()
            .SetId(entry->GetUniqueID())
            .SetUrl(entry->GetURL().spec())
            .SetUserTypedURL(entry->GetUserTypedURL().spec())
            .SetTitle(base::UTF16ToUTF8(entry->GetTitle()))
            .SetTransitionType(TransitionTypeName(entry->GetTransitionType()))
            .Build());
  }
  return Response::OK();
}

Project: Chrome

WebContentsImpl* PageHandler::GetWebContents() {
  return host_ && !host_->frame_tree_node()->parent()
             ? static_cast<WebContentsImpl*>(
                   WebContents::FromRenderFrameHost(host_))
             : nullptr;
}

Project: Chrome

void PageHandler::GotManifest(std::unique_ptr<GetAppManifestCallback> callback,
                              const GURL& manifest_url,
                              blink::mojom::ManifestDebugInfoPtr debug_info) {
  std::unique_ptr<Array<Page::AppManifestError>> errors =
      Array<Page::AppManifestError>::create();
  bool failed = true;
  if (debug_info) {
    failed = false;
    for (const auto& error : debug_info->errors) {
      errors->addItem(Page::AppManifestError::Create()
                          .SetMessage(error->message)
                          .SetCritical(error->critical)
                          .SetLine(error->line)
                          .SetColumn(error->column)
                          .Build());
      if (error->critical)
        failed = true;
    }
  }
  callback->sendSuccess(
      manifest_url.possibly_invalid_spec(), std::move(errors),
      failed ? Maybe<std::string>() : debug_info->raw_manifest);
}

Project: Chrome

Response PageHandler::HandleJavaScriptDialog(bool accept,
                                             Maybe<std::string> prompt_text) {
  WebContentsImpl* web_contents = GetWebContents();
  if (!web_contents)
    return Response::InternalError();

  if (pending_dialog_.is_null())
    return Response::InvalidParams("No dialog is showing");

  base::string16 prompt_override;
  if (prompt_text.isJust())
    prompt_override = base::UTF8ToUTF16(prompt_text.fromJust());
  std::move(pending_dialog_).Run(accept, prompt_override);

  if (web_contents->GetDelegate()) {
    JavaScriptDialogManager* manager =
        web_contents->GetDelegate()->GetJavaScriptDialogManager(web_contents);
    if (manager) {
      manager->HandleJavaScriptDialog(
          web_contents, accept,
          prompt_text.isJust() ? &prompt_override : nullptr);
    }
  }

  return Response::OK();
}

Project: Chrome

void PageHandler::InnerSwapCompositorFrame() {
  if (!host_)
    return;

  if (frames_in_flight_ > kMaxScreencastFramesInFlight)
    return;

  if (++frame_counter_ % capture_every_nth_frame_)
    return;

  RenderWidgetHostViewBase* const view =
      static_cast<RenderWidgetHostViewBase*>(host_->GetView());
  if (!view || !view->IsSurfaceAvailableForCopy())
    return;

  const gfx::Size surface_size = view->GetCompositorViewportPixelSize();
  if (surface_size.IsEmpty())
    return;

  const gfx::Size snapshot_size = DetermineSnapshotSize(
      surface_size, screencast_max_width_, screencast_max_height_);
  if (snapshot_size.IsEmpty())
    return;

  double top_controls_height =
      last_compositor_frame_metadata_.top_controls_height;
  double top_controls_shown_ratio =
      last_compositor_frame_metadata_.top_controls_shown_ratio;

  std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata =
      BuildScreencastFrameMetadata(
          surface_size, last_compositor_frame_metadata_.device_scale_factor,
          last_compositor_frame_metadata_.page_scale_factor,
          last_compositor_frame_metadata_.root_scroll_offset,
          top_controls_height, top_controls_shown_ratio);
  if (!page_metadata)
    return;

  view->CopyFromSurface(
      gfx::Rect(), snapshot_size,
      base::BindOnce(&PageHandler::ScreencastFrameCaptured,
                     weak_factory_.GetWeakPtr(), std::move(page_metadata)));
  frames_in_flight_++;
}

Project: Chrome

void PageHandler::Navigate(const std::string& url,
                           Maybe<std::string> referrer,
                           Maybe<std::string> maybe_transition_type,
                           Maybe<std::string> frame_id,
                           std::unique_ptr<NavigateCallback> callback) {
  GURL gurl(url);
  if (!gurl.is_valid()) {
    callback->sendFailure(Response::Error("Cannot navigate to invalid URL"));
    return;
  }

  if (!host_) {
    callback->sendFailure(Response::InternalError());
    return;
  }

  ui::PageTransition type;
  std::string transition_type =
      maybe_transition_type.fromMaybe(Page::TransitionTypeEnum::Typed);
  if (transition_type == Page::TransitionTypeEnum::Link)
    type = ui::PAGE_TRANSITION_LINK;
  else if (transition_type == Page::TransitionTypeEnum::Typed)
    type = ui::PAGE_TRANSITION_TYPED;
  else if (transition_type == Page::TransitionTypeEnum::Address_bar)
    type = ui::PAGE_TRANSITION_FROM_ADDRESS_BAR;
  else if (transition_type == Page::TransitionTypeEnum::Auto_bookmark)
    type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
  else if (transition_type == Page::TransitionTypeEnum::Auto_subframe)
    type = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
  else if (transition_type == Page::TransitionTypeEnum::Manual_subframe)
    type = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
  else if (transition_type == Page::TransitionTypeEnum::Generated)
    type = ui::PAGE_TRANSITION_GENERATED;
  else if (transition_type == Page::TransitionTypeEnum::Auto_toplevel)
    type = ui::PAGE_TRANSITION_AUTO_TOPLEVEL;
  else if (transition_type == Page::TransitionTypeEnum::Form_submit)
    type = ui::PAGE_TRANSITION_FORM_SUBMIT;
  else if (transition_type == Page::TransitionTypeEnum::Reload)
    type = ui::PAGE_TRANSITION_RELOAD;
  else if (transition_type == Page::TransitionTypeEnum::Keyword)
    type = ui::PAGE_TRANSITION_KEYWORD;
  else if (transition_type == Page::TransitionTypeEnum::Keyword_generated)
    type = ui::PAGE_TRANSITION_KEYWORD_GENERATED;
  else
    type = ui::PAGE_TRANSITION_TYPED;

  FrameTreeNode* frame_tree_node = nullptr;
  std::string out_frame_id = frame_id.fromMaybe(
      host_->frame_tree_node()->devtools_frame_token().ToString());
  FrameTreeNode* root = host_->frame_tree_node();
  if (root->devtools_frame_token().ToString() == out_frame_id) {
    frame_tree_node = root;
  } else {
    for (FrameTreeNode* node : root->frame_tree()->SubtreeNodes(root)) {
      if (node->devtools_frame_token().ToString() == out_frame_id) {
        frame_tree_node = node;
        break;
      }
    }
  }

  if (!frame_tree_node) {
    callback->sendFailure(Response::Error("No frame with given id found"));
    return;
  }

  NavigationController::LoadURLParams params(gurl);
  params.referrer =
      Referrer(GURL(referrer.fromMaybe("")), blink::kWebReferrerPolicyDefault);
  params.transition_type = type;
  params.frame_tree_node_id = frame_tree_node->frame_tree_node_id();
  frame_tree_node->navigator()->GetController()->LoadURLWithParams(params);

  base::UnguessableToken frame_token = frame_tree_node->devtools_frame_token();
  auto navigate_callback = navigate_callbacks_.find(frame_token);
  if (navigate_callback != navigate_callbacks_.end()) {
    std::string error_string = net::ErrorToString(net::ERR_ABORTED);
    navigate_callback->second->sendSuccess(out_frame_id, Maybe<std::string>(),
                                           Maybe<std::string>(error_string));
  }
  if (frame_tree_node->navigation_request()) {
    navigate_callbacks_[frame_token] = std::move(callback);
  } else {
    callback->sendSuccess(out_frame_id, Maybe<std::string>(),
                          Maybe<std::string>());
  }
}

Project: Chrome

Response PageHandler::NavigateToHistoryEntry(int entry_id) {
  WebContentsImpl* web_contents = GetWebContents();
  if (!web_contents)
    return Response::InternalError();

  NavigationController& controller = web_contents->GetController();
  for (int i = 0; i != controller.GetEntryCount(); ++i) {
    if (controller.GetEntryAtIndex(i)->GetUniqueID() == entry_id) {
      controller.GoToIndex(i);
      return Response::OK();
    }
  }

  return Response::InvalidParams("No entry with passed id");
}

Project: Chrome

void PageHandler::NotifyScreencastVisibility(bool visible) {
  if (visible)
    capture_retry_count_ = kCaptureRetryLimit;
  frontend_->ScreencastVisibilityChanged(visible);
}

Project: Chrome

void PageHandler::OnFrameFromVideoConsumer(
    scoped_refptr<media::VideoFrame> frame) {
  if (!host_)
    return;

  RenderWidgetHostViewBase* const view =
      static_cast<RenderWidgetHostViewBase*>(host_->GetView());
  if (!view)
    return;

  const gfx::Size surface_size = view->GetCompositorViewportPixelSize();
  if (surface_size.IsEmpty())
    return;

  if (surface_size != last_surface_size_) {
    last_surface_size_ = surface_size;
    gfx::Size snapshot_size = DetermineSnapshotSize(
        surface_size, screencast_max_width_, screencast_max_height_);
    if (!snapshot_size.IsEmpty())
      video_consumer_->SetMinAndMaxFrameSize(snapshot_size, snapshot_size);
    return;
  }

  double device_scale_factor, page_scale_factor;
  double top_controls_height, top_controls_shown_ratio;
  gfx::Vector2dF root_scroll_offset;
  GetMetadataFromFrame(*frame, &device_scale_factor, &page_scale_factor,
                       &root_scroll_offset, &top_controls_height,
                       &top_controls_shown_ratio);
  std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata =
      BuildScreencastFrameMetadata(
          surface_size, device_scale_factor, page_scale_factor,
          root_scroll_offset, top_controls_height, top_controls_shown_ratio);
  if (!page_metadata)
    return;

  ScreencastFrameCaptured(std::move(page_metadata),
                          DevToolsVideoConsumer::GetSkBitmapFromFrame(frame));
}

Project: Chrome

void PageHandler::OnSynchronousSwapCompositorFrame(
    viz::CompositorFrameMetadata frame_metadata) {
  if (has_compositor_frame_metadata_) {
    last_compositor_frame_metadata_ =
        std::move(next_compositor_frame_metadata_);
  } else {
    last_compositor_frame_metadata_ = frame_metadata.Clone();
  }
  next_compositor_frame_metadata_ = std::move(frame_metadata);

  has_compositor_frame_metadata_ = true;

  if (screencast_enabled_)
    InnerSwapCompositorFrame();
}

Project: Chrome

void PageHandler::Reload(Maybe<bool> bypassCache,
                         Maybe<std::string> script_to_evaluate_on_load,
                         std::unique_ptr<ReloadCallback> callback) {
  WebContentsImpl* web_contents = GetWebContents();
  if (!web_contents) {
    callback->sendFailure(Response::InternalError());
    return;
  }
  callback->fallThrough();
  web_contents->GetController().Reload(bypassCache.fromMaybe(false)
                                           ? ReloadType::BYPASSING_CACHE
                                           : ReloadType::NORMAL,
                                       false);
}

Project: Chrome

void PageHandler::RenderWidgetHostDestroyed(RenderWidgetHost* widget_host) {
  observer_.Remove(widget_host);
}

Project: Chrome

void PageHandler::RenderWidgetHostVisibilityChanged(
    RenderWidgetHost* widget_host,
    bool became_visible) {
  if (!screencast_enabled_)
    return;
  NotifyScreencastVisibility(became_visible);
}

Project: Chrome

Response PageHandler::RequestAppBanner() {
  WebContentsImpl* web_contents = GetWebContents();
  if (!web_contents || !web_contents->GetDelegate())
    return Response::InternalError();
  web_contents->GetDelegate()->RequestAppBannerFromDevTools(web_contents);
  return Response::OK();
}

Project: Chrome

Response PageHandler::ScreencastFrameAck(int session_id) {
  if (session_id == session_id_)
    --frames_in_flight_;
  return Response::OK();
}

Project: Chrome

void PageHandler::ScreencastFrameCaptured(
    std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata,
    const SkBitmap& bitmap) {
  if (bitmap.drawsNothing()) {
    if (capture_retry_count_) {
      --capture_retry_count_;
      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
          FROM_HERE,
          base::BindOnce(&PageHandler::InnerSwapCompositorFrame,
                         weak_factory_.GetWeakPtr()),
          base::TimeDelta::FromMilliseconds(kFrameRetryDelayMs));
    }
    --frames_in_flight_;
    return;
  }
  base::PostTaskWithTraitsAndReplyWithResult(
      FROM_HERE, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
      base::BindOnce(&EncodeSkBitmap, bitmap, screencast_format_,
                     screencast_quality_),
      base::BindOnce(&PageHandler::ScreencastFrameEncoded,
                     weak_factory_.GetWeakPtr(), std::move(page_metadata)));
}

Project: Chrome

void PageHandler::ScreencastFrameEncoded(
    std::unique_ptr<Page::ScreencastFrameMetadata> page_metadata,
    const std::string& data) {
  if (data.empty()) {
    --frames_in_flight_;
    return;  // Encode failed.
  }

  frontend_->ScreencastFrame(data, std::move(page_metadata), session_id_);
}

Project: Chrome

void PageHandler::ScreenshotCaptured(
    std::unique_ptr<CaptureScreenshotCallback> callback,
    const std::string& format,
    int quality,
    const gfx::Size& original_view_size,
    const gfx::Size& requested_image_size,
    const blink::WebDeviceEmulationParams& original_emulation_params,
    const gfx::Image& image) {
  if (original_view_size.width()) {
    RenderWidgetHostImpl* widget_host = host_->GetRenderWidgetHost();
    widget_host->GetView()->SetSize(original_view_size);
    emulation_handler_->SetDeviceEmulationParams(original_emulation_params);
  }

  if (image.IsEmpty()) {
    callback->sendFailure(Response::Error("Unable to capture screenshot"));
    return;
  }

  if (!requested_image_size.IsEmpty() &&
      (image.Width() != requested_image_size.width() ||
       image.Height() != requested_image_size.height())) {
    const SkBitmap* bitmap = image.ToSkBitmap();
    SkBitmap cropped = SkBitmapOperations::CreateTiledBitmap(
        *bitmap, 0, 0, requested_image_size.width(),
        requested_image_size.height());
    gfx::Image croppedImage = gfx::Image::CreateFrom1xBitmap(cropped);
    callback->sendSuccess(EncodeImage(croppedImage, format, quality));
  } else {
    callback->sendSuccess(EncodeImage(image, format, quality));
  }
}

Project: Chrome

void PageHandler::SetRenderer(int process_host_id,
                              RenderFrameHostImpl* frame_host) {
  if (host_ == frame_host)
    return;

  RenderWidgetHostImpl* widget_host =
      host_ ? host_->GetRenderWidgetHost() : nullptr;
  if (widget_host && observer_.IsObserving(widget_host))
    observer_.Remove(widget_host);

  host_ = frame_host;
  widget_host = host_ ? host_->GetRenderWidgetHost() : nullptr;

  if (widget_host)
    observer_.Add(widget_host);

  if (video_consumer_ && frame_host) {
    video_consumer_->SetFrameSinkId(
        frame_host->GetRenderWidgetHost()->GetFrameSinkId());
  }
}

Project: Chrome

Response PageHandler::StartScreencast(Maybe<std::string> format,
                                      Maybe<int> quality,
                                      Maybe<int> max_width,
                                      Maybe<int> max_height,
                                      Maybe<int> every_nth_frame) {
  WebContentsImpl* web_contents = GetWebContents();
  if (!web_contents)
    return Response::InternalError();
  RenderWidgetHostImpl* widget_host =
      host_ ? host_->GetRenderWidgetHost() : nullptr;
  if (!widget_host)
    return Response::InternalError();

  screencast_enabled_ = true;
  screencast_format_ = format.fromMaybe(kPng);
  screencast_quality_ = quality.fromMaybe(kDefaultScreenshotQuality);
  if (screencast_quality_ < 0 || screencast_quality_ > 100)
    screencast_quality_ = kDefaultScreenshotQuality;
  screencast_max_width_ = max_width.fromMaybe(-1);
  screencast_max_height_ = max_height.fromMaybe(-1);
  ++session_id_;
  frame_counter_ = 0;
  frames_in_flight_ = 0;
  capture_every_nth_frame_ = every_nth_frame.fromMaybe(1);
  bool visible = !widget_host->is_hidden();
  NotifyScreencastVisibility(visible);

  if (video_consumer_) {
    gfx::Size surface_size = gfx::Size();
    RenderWidgetHostViewBase* const view =
        static_cast<RenderWidgetHostViewBase*>(host_->GetView());
    if (view) {
      surface_size = view->GetCompositorViewportPixelSize();
      last_surface_size_ = surface_size;
    }

    gfx::Size snapshot_size = DetermineSnapshotSize(
        surface_size, screencast_max_width_, screencast_max_height_);
    if (!snapshot_size.IsEmpty())
      video_consumer_->SetMinAndMaxFrameSize(snapshot_size, snapshot_size);

    video_consumer_->StartCapture();
    return Response::FallThrough();
  }

  if (!visible)
    return Response::FallThrough();

  if (has_compositor_frame_metadata_) {
    InnerSwapCompositorFrame();
  } else {
    widget_host->Send(
        new WidgetMsg_ForceRedraw(widget_host->GetRoutingID(), 0));
  }
  return Response::FallThrough();
}

Project: Chrome

Response PageHandler::StopLoading() {
  WebContentsImpl* web_contents = GetWebContents();
  if (!web_contents)
    return Response::InternalError();
  web_contents->Stop();
  return Response::OK();
}

Project: Chrome

Response PageHandler::StopScreencast() {
  screencast_enabled_ = false;
  if (video_consumer_)
    video_consumer_->StopCapture();
  return Response::FallThrough();
}

Project: Chrome

static const char* TransitionTypeName(ui::PageTransition type) {
  int32_t t = type & ~ui::PAGE_TRANSITION_QUALIFIER_MASK;
  switch (t) {
    case ui::PAGE_TRANSITION_LINK:
      return Page::TransitionTypeEnum::Link;
    case ui::PAGE_TRANSITION_TYPED:
      return Page::TransitionTypeEnum::Typed;
    case ui::PAGE_TRANSITION_AUTO_BOOKMARK:
      return Page::TransitionTypeEnum::Auto_bookmark;
    case ui::PAGE_TRANSITION_AUTO_SUBFRAME:
      return Page::TransitionTypeEnum::Auto_subframe;
    case ui::PAGE_TRANSITION_MANUAL_SUBFRAME:
      return Page::TransitionTypeEnum::Manual_subframe;
    case ui::PAGE_TRANSITION_GENERATED:
      return Page::TransitionTypeEnum::Generated;
    case ui::PAGE_TRANSITION_AUTO_TOPLEVEL:
      return Page::TransitionTypeEnum::Auto_toplevel;
    case ui::PAGE_TRANSITION_FORM_SUBMIT:
      return Page::TransitionTypeEnum::Form_submit;
    case ui::PAGE_TRANSITION_RELOAD:
      return Page::TransitionTypeEnum::Reload;
    case ui::PAGE_TRANSITION_KEYWORD:
      return Page::TransitionTypeEnum::Keyword;
    case ui::PAGE_TRANSITION_KEYWORD_GENERATED:
      return Page::TransitionTypeEnum::Keyword_generated;
    default:
      return Page::TransitionTypeEnum::Other;
  }
}

Project: Chrome

void PageHandler::Wire(UberDispatcher* dispatcher) {
  frontend_.reset(new Page::Frontend(dispatcher->channel()));
  Page::Dispatcher::wire(dispatcher, this);
}

Project: Chrome

PageHandler::~PageHandler() {
}

Project: Chrome

bool RenderFrameDevToolsAgentHost::Activate() {
  WebContentsImpl* wc = static_cast<WebContentsImpl*>(web_contents());
  if (wc) {
    wc->Activate();
    return true;
  }
  return false;
}

Project: Chrome

void RenderFrameDevToolsAgentHost::AddAllAgentHosts(
    DevToolsAgentHost::List* result) {
  for (WebContentsImpl* wc : WebContentsImpl::GetAllWebContents()) {
    for (FrameTreeNode* node : wc->GetFrameTree()->Nodes()) {
      if (!node->current_frame_host() || !ShouldCreateDevToolsForNode(node))
        continue;
      if (!node->current_frame_host()->IsRenderFrameLive())
        continue;
      result->push_back(RenderFrameDevToolsAgentHost::GetOrCreateFor(node));
    }
  }
}

Project: Chrome

void RenderFrameDevToolsAgentHost::ApplyOverrides(
    FrameTreeNode* frame_tree_node,
    mojom::BeginNavigationParams* begin_params,
    bool* report_raw_headers) {
  bool disable_cache = false;
  frame_tree_node = GetFrameTreeNodeAncestor(frame_tree_node);
  RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(frame_tree_node);
  if (!agent_host)
    return;
  net::HttpRequestHeaders headers;
  headers.AddHeadersFromString(begin_params->headers);
  for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) {
    if (!network->enabled())
      continue;
    *report_raw_headers = true;
    network->ApplyOverrides(&headers, &begin_params->skip_service_worker,
                            &disable_cache);
  }

  for (auto* emulation : protocol::EmulationHandler::ForAgentHost(agent_host))
    emulation->ApplyOverrides(&headers);

  if (disable_cache) {
    begin_params->load_flags &=
        ~(net::LOAD_VALIDATE_CACHE | net::LOAD_SKIP_CACHE_VALIDATION |
          net::LOAD_ONLY_FROM_CACHE | net::LOAD_DISABLE_CACHE);
    begin_params->load_flags |= net::LOAD_BYPASS_CACHE;
  }

  begin_params->headers = headers.ToString();
}

Project: Chrome

bool RenderFrameDevToolsAgentHost::Close() {
  if (web_contents()) {
    web_contents()->ClosePage();
    return true;
  }
  return false;
}

Project: Chrome

void RenderFrameDevToolsAgentHost::ConnectWebContents(WebContents* wc) {
  RenderFrameHostImpl* host =
      static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
  DCHECK(host);
  SetFrameTreeNode(host->frame_tree_node());
  UpdateFrameHost(host);
}

Project: Chrome

RenderFrameDevToolsAgentHost::CreateNavigationThrottles(
    NavigationHandleImpl* navigation_handle) {
  std::vector<std::unique_ptr<NavigationThrottle>> result;
  FrameTreeNode* frame_tree_node = navigation_handle->frame_tree_node();

  RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(frame_tree_node);
  if (agent_host) {
    for (auto* network_handler :
         protocol::NetworkHandler::ForAgentHost(agent_host)) {
      std::unique_ptr<NavigationThrottle> throttle =
          network_handler->CreateThrottleForNavigation(navigation_handle);
      if (throttle)
        result.push_back(std::move(throttle));
    }
  }

  agent_host = nullptr;
  if (frame_tree_node->parent()) {
    agent_host =
        FindAgentHost(GetFrameTreeNodeAncestor(frame_tree_node->parent()));
  }
  if (agent_host) {
    for (auto* target_handler :
         protocol::TargetHandler::ForAgentHost(agent_host)) {
      std::unique_ptr<NavigationThrottle> throttle =
          target_handler->CreateThrottleForNavigation(navigation_handle);
      if (throttle)
        result.push_back(std::move(throttle));
    }
  }

  return result;
}

Project: Chrome

void RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone() {
  scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
  if (IsAttached())
    RevokePolicy();
  ForceDetachAllSessions();
  frame_host_ = nullptr;
  UpdateRendererChannel(IsAttached());
  SetFrameTreeNode(nullptr);
  Release();
}

Project: Chrome

void RenderFrameDevToolsAgentHost::DetachSession(DevToolsSession* session) {
  if (sessions().empty()) {
    frame_trace_recorder_.reset();
    RevokePolicy();
#if defined(OS_ANDROID)
    GetWakeLock()->CancelWakeLock();
#endif
  }
}

Project: Chrome

void RenderFrameDevToolsAgentHost::DidDetachInterstitialPage() {
  for (auto* page : protocol::PageHandler::ForAgentHost(this))
    page->DidDetachInterstitialPage();
}

Project: Chrome

void RenderFrameDevToolsAgentHost::DidFinishNavigation(
    NavigationHandle* navigation_handle) {
  NavigationHandleImpl* handle =
      static_cast<NavigationHandleImpl*>(navigation_handle);
  if (handle->frame_tree_node() != frame_tree_node_)
    return;
  navigation_handles_.erase(handle);
  NotifyNavigated();

  scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
  UpdateFrameHost(frame_tree_node_->current_frame_host());

  if (navigation_handles_.empty()) {
    for (DevToolsSession* session : sessions())
      session->ResumeSendingMessagesToAgent();
  }
  if (handle->HasCommitted()) {
    for (auto* target : protocol::TargetHandler::ForAgentHost(this))
      target->DidCommitNavigation();
  }
}

Project: Chrome

void RenderFrameDevToolsAgentHost::DidStartNavigation(
    NavigationHandle* navigation_handle) {
  NavigationHandleImpl* handle =
      static_cast<NavigationHandleImpl*>(navigation_handle);
  if (handle->frame_tree_node() != frame_tree_node_)
    return;
  if (navigation_handles_.empty()) {
    for (DevToolsSession* session : sessions())
      session->SuspendSendingMessagesToAgent();
  }
  navigation_handles_.insert(handle);
}

Project: Chrome

void RenderFrameDevToolsAgentHost::DisconnectWebContents() {
  navigation_handles_.clear();
  SetFrameTreeNode(nullptr);
  scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
  UpdateFrameHost(nullptr);
  for (DevToolsSession* session : sessions())
    session->ResumeSendingMessagesToAgent();
}

Project: Chrome

void DispatchToAgents(int frame_tree_node_id,
                      void (Handler::*method)(MethodArgs...),
                      Args&&... args) {
  FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
  if (ftn)
    DispatchToAgents(ftn, method, std::forward<Args>(args)...);
}

Project: Chrome

RenderFrameDevToolsAgentHost* FindAgentHost(FrameTreeNode* frame_tree_node) {
  if (!g_agent_host_instances.IsCreated())
    return nullptr;
  auto it = g_agent_host_instances.Get().find(frame_tree_node);
  return it == g_agent_host_instances.Get().end() ? nullptr : it->second;
}

Project: Chrome

scoped_refptr<DevToolsAgentHost> RenderFrameDevToolsAgentHost::FindForDangling(
    FrameTreeNode* frame_tree_node) {
  return FindAgentHost(frame_tree_node);
}

Project: Chrome

BrowserContext* RenderFrameDevToolsAgentHost::GetBrowserContext() {
  WebContents* contents = web_contents();
  return contents ? contents->GetBrowserContext() : nullptr;
}

Project: Chrome

std::string RenderFrameDevToolsAgentHost::GetDescription() {
  DevToolsManager* manager = DevToolsManager::GetInstance();
  if (manager->delegate() && web_contents())
    return manager->delegate()->GetTargetDescription(web_contents());
  return std::string();
}

Project: Chrome

FrameTreeNode* GetFrameTreeNodeAncestor(FrameTreeNode* frame_tree_node) {
  while (frame_tree_node && !ShouldCreateDevToolsForNode(frame_tree_node))
    frame_tree_node = frame_tree_node->parent();
  DCHECK(frame_tree_node);
  return frame_tree_node;
}

Project: Chrome

base::TimeTicks RenderFrameDevToolsAgentHost::GetLastActivityTime() {
  if (WebContents* contents = web_contents())
    return contents->GetLastActiveTime();
  return base::TimeTicks();
}

Project: Chrome

DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) {
  FrameTreeNode* node =
      static_cast<WebContentsImpl*>(web_contents)->GetFrameTree()->root();
  if (!node)
    return nullptr;
  return RenderFrameDevToolsAgentHost::GetOrCreateFor(node);
}

Project: Chrome

scoped_refptr<DevToolsAgentHost> RenderFrameDevToolsAgentHost::GetOrCreateFor(
    FrameTreeNode* frame_tree_node) {
  frame_tree_node = GetFrameTreeNodeAncestor(frame_tree_node);
  RenderFrameDevToolsAgentHost* result = FindAgentHost(frame_tree_node);
  if (!result)
    result = new RenderFrameDevToolsAgentHost(frame_tree_node);
  return result;
}

Project: Chrome

RenderFrameDevToolsAgentHost::GetOrCreateForDangling(
    FrameTreeNode* frame_tree_node) {
  RenderFrameDevToolsAgentHost* result = FindAgentHost(frame_tree_node);
  if (!result)
    result = new RenderFrameDevToolsAgentHost(frame_tree_node);
  return result;
}

Project: Chrome

std::string RenderFrameDevToolsAgentHost::GetParentId() {
  if (IsChildFrame()) {
    FrameTreeNode* frame_tree_node =
        GetFrameTreeNodeAncestor(frame_tree_node_->parent());
    return RenderFrameDevToolsAgentHost::GetOrCreateFor(frame_tree_node)
        ->GetId();
  }

  WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
  if (!contents)
    return "";
  contents = contents->GetOuterWebContents();
  if (contents)
    return DevToolsAgentHost::GetOrCreateFor(contents)->GetId();
  return "";
}

Project: Chrome

std::string RenderFrameDevToolsAgentHost::GetTitle() {
  DevToolsManager* manager = DevToolsManager::GetInstance();
  if (manager->delegate() && web_contents()) {
    std::string title = manager->delegate()->GetTargetTitle(web_contents());
    if (!title.empty())
      return title;
  }
  if (IsChildFrame() && frame_host_)
    return frame_host_->GetLastCommittedURL().spec();
  if (web_contents())
    return base::UTF16ToUTF8(web_contents()->GetTitle());
  return GetURL().spec();
}

Project: Chrome

GURL RenderFrameDevToolsAgentHost::GetURL() {
  WebContents* web_contents = GetWebContents();
  if (web_contents && !IsChildFrame())
    return web_contents->GetVisibleURL();
  if (frame_host_)
    return frame_host_->GetLastCommittedURL();
  return GURL();
}

Project: Chrome

device::mojom::WakeLock* RenderFrameDevToolsAgentHost::GetWakeLock() {
  if (!wake_lock_) {
    device::mojom::WakeLockRequest request = mojo::MakeRequest(&wake_lock_);
    device::mojom::WakeLockContext* wake_lock_context =
        web_contents()->GetWakeLockContext();
    if (wake_lock_context) {
      wake_lock_context->GetWakeLock(
          device::mojom::WakeLockType::kPreventDisplaySleep,
          device::mojom::WakeLockReason::kOther, "DevTools",
          std::move(request));
    }
  }
  return wake_lock_.get();
}

Project: Chrome

WebContents* RenderFrameDevToolsAgentHost::GetWebContents() {
  return web_contents();
}

Project: Chrome

bool DevToolsAgentHost::HasFor(WebContents* web_contents) {
  FrameTreeNode* node =
      static_cast<WebContentsImpl*>(web_contents)->GetFrameTree()->root();
  return node ? FindAgentHost(node) != nullptr : false;
}

Project: Chrome

void RenderFrameDevToolsAgentHost::InspectElement(RenderFrameHost* frame_host,
                                                  int x,
                                                  int y) {
  FrameTreeNode* ftn =
      static_cast<RenderFrameHostImpl*>(frame_host)->frame_tree_node();
  RenderFrameDevToolsAgentHost* host =
      static_cast<RenderFrameDevToolsAgentHost*>(GetOrCreateFor(ftn).get());

  gfx::Point point(x, y);
  if (host->frame_host_) {
    if (RenderWidgetHostView* view = host->frame_host_->GetView()) {
      point = gfx::ToRoundedPoint(
          view->TransformRootPointToViewCoordSpace(gfx::PointF(point)));
    }
  }
  host->GetRendererChannel()->InspectElement(point);
}

Project: Chrome

bool RenderFrameDevToolsAgentHost::IsChildFrame() {
  return frame_tree_node_ && frame_tree_node_->parent();
}

Project: Chrome

bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) {
  FrameTreeNode* node =
      static_cast<WebContentsImpl*>(web_contents)->GetFrameTree()->root();
  RenderFrameDevToolsAgentHost* host = node ? FindAgentHost(node) : nullptr;
  return host && host->IsAttached();
}

Project: Chrome

void RenderFrameDevToolsAgentHost::OnNavigationRequestWillBeSent(
    const NavigationRequest& navigation_request) {
  DispatchToAgents(navigation_request.frame_tree_node(),
                   &protocol::NetworkHandler::NavigationRequestWillBeSent,
                   navigation_request);
}

Project: Chrome

void RenderFrameDevToolsAgentHost::OnNavigationResponseReceived(
    const NavigationRequest& nav_request,
    const network::ResourceResponse& response) {
  FrameTreeNode* ftn = nav_request.frame_tree_node();
  std::string id = nav_request.devtools_navigation_token().ToString();
  std::string frame_id = ftn->devtools_frame_token().ToString();
  GURL url = nav_request.common_params().url;
  DispatchToAgents(ftn, &protocol::NetworkHandler::ResponseReceived, id, id,
                   url, protocol::Network::ResourceTypeEnum::Document,
                   response.head, frame_id);
}

Project: Chrome

void RenderFrameDevToolsAgentHost::OnPageScaleFactorChanged(
    float page_scale_factor) {
  for (auto* input : protocol::InputHandler::ForAgentHost(this))
    input->OnPageScaleFactorChanged(page_scale_factor);
}

Project: Chrome

void RenderFrameDevToolsAgentHost::OnResetNavigationRequest(
    NavigationRequest* navigation_request) {
  for (FrameTreeNode* node = navigation_request->frame_tree_node(); node;
       node = node->parent()) {
    DispatchToAgents(node, &protocol::PageHandler::NavigationReset,
                     navigation_request);
  }
}

Project: Chrome

void RenderFrameDevToolsAgentHost::OnSignedExchangeCertificateRequestSent(
    FrameTreeNode* frame_tree_node,
    const base::UnguessableToken& request_id,
    const base::UnguessableToken& loader_id,
    const network::ResourceRequest& request,
    const GURL& signed_exchange_url) {
  DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::RequestSent,
                   request_id.ToString(), loader_id.ToString(), request,
                   protocol::Network::Initiator::TypeEnum::SignedExchange,
                   signed_exchange_url);
}

Project: Chrome

void RenderFrameDevToolsAgentHost::OnSignedExchangeCertificateResponseReceived(
    FrameTreeNode* frame_tree_node,
    const base::UnguessableToken& request_id,
    const base::UnguessableToken& loader_id,
    const GURL& url,
    const network::ResourceResponseHead& head) {
  DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::ResponseReceived,
                   request_id.ToString(), loader_id.ToString(), url,
                   protocol::Network::ResourceTypeEnum::Other, head,
                   protocol::Maybe<std::string>());
}

Project: Chrome

void RenderFrameDevToolsAgentHost::OnVisibilityChanged(
    content::Visibility visibility) {
#if defined(OS_ANDROID)
  if (visibility == content::Visibility::HIDDEN)
    GetWakeLock()->CancelWakeLock();
  else
    GetWakeLock()->RequestWakeLock();
#endif
}

Project: Chrome

void RenderFrameDevToolsAgentHost::ReadyToCommitNavigation(
    NavigationHandle* navigation_handle) {
  NavigationHandleImpl* handle =
      static_cast<NavigationHandleImpl*>(navigation_handle);
  for (auto* tracing : protocol::TracingHandler::ForAgentHost(this))
    tracing->ReadyToCommitNavigation(handle);

  if (handle->frame_tree_node() != frame_tree_node_) {
    if (ShouldForceCreation() && handle->GetRenderFrameHost() &&
        handle->GetRenderFrameHost()->IsCrossProcessSubframe()) {
      RenderFrameDevToolsAgentHost::GetOrCreateForDangling(
          handle->frame_tree_node());
    }
    return;
  }

  UpdateFrameHost(handle->GetRenderFrameHost());
}

Project: Chrome

void RenderFrameDevToolsAgentHost::RenderFrameDeleted(RenderFrameHost* rfh) {
  if (rfh == frame_host_) {
    render_frame_alive_ = false;
    UpdateRendererChannel(IsAttached());
  }
}

Project: Chrome

RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost(
    FrameTreeNode* frame_tree_node)
    : DevToolsAgentHostImpl(frame_tree_node->devtools_frame_token().ToString()),
      frame_tree_node_(nullptr) {
  SetFrameTreeNode(frame_tree_node);
  frame_host_ = frame_tree_node->current_frame_host();
  render_frame_alive_ = frame_host_ && frame_host_->IsRenderFrameLive();
  AddRef();  // Balanced in DestroyOnRenderFrameGone.
  NotifyCreated();
}

Project: Chrome

void RenderFrameDevToolsAgentHost::RenderFrameHostChanged(
    RenderFrameHost* old_host,
    RenderFrameHost* new_host) {
  if (old_host != frame_host_)
    return;

  UpdateFrameHost(nullptr);
}

Project: Chrome

void RenderFrameDevToolsAgentHost::RenderProcessGone(
    base::TerminationStatus status) {
  switch(status) {
    case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
    case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
#if defined(OS_CHROMEOS)
    case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
#endif
    case base::TERMINATION_STATUS_PROCESS_CRASHED:
#if defined(OS_ANDROID)
    case base::TERMINATION_STATUS_OOM_PROTECTED:
#endif
    case base::TERMINATION_STATUS_LAUNCH_FAILED:
      for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
        inspector->TargetCrashed();
      NotifyCrashed(status);
      break;
    default:
      for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
        inspector->TargetDetached("Render process gone.");
      break;
  }
}

Project: Chrome

void RenderFrameDevToolsAgentHost::SetFrameTreeNode(
    FrameTreeNode* frame_tree_node) {
  if (frame_tree_node_)
    g_agent_host_instances.Get().erase(frame_tree_node_);
  frame_tree_node_ = frame_tree_node;
  if (frame_tree_node_) {
    g_agent_host_instances.Get()[frame_tree_node] = this;
  }
  WebContentsObserver::Observe(
      frame_tree_node_ ? WebContentsImpl::FromFrameTreeNode(frame_tree_node_)
                       : nullptr);
}

Project: Chrome

bool RenderFrameDevToolsAgentHost::ShouldAllowSession(
    DevToolsSession* session) {
  DevToolsManager* manager = DevToolsManager::GetInstance();
  if (manager->delegate() && frame_host_) {
    if (!manager->delegate()->AllowInspectingRenderFrameHost(frame_host_))
      return false;
  }
  const bool is_webui =
      frame_host_ && (frame_host_->web_ui() || frame_host_->pending_web_ui());
  if (!session->client()->MayAttachToRenderer(frame_host_, is_webui))
    return false;
  return true;
}

Project: Chrome

bool ShouldCreateDevToolsForNode(FrameTreeNode* ftn) {
  return !ftn->parent() || ftn->current_frame_host()->IsCrossProcessSubframe();
}

Project: Chrome

void RenderFrameDevToolsAgentHost::SynchronousSwapCompositorFrame(
    viz::CompositorFrameMetadata frame_metadata) {
  for (auto* page : protocol::PageHandler::ForAgentHost(this))
    page->OnSynchronousSwapCompositorFrame(frame_metadata.Clone());

  if (!frame_trace_recorder_)
    return;
  bool did_initiate_recording = false;
  for (auto* tracing : protocol::TracingHandler::ForAgentHost(this))
    did_initiate_recording |= tracing->did_initiate_recording();
  if (did_initiate_recording) {
    frame_trace_recorder_->OnSynchronousSwapCompositorFrame(frame_host_,
                                                            frame_metadata);
  }
}

Project: Chrome

void RenderFrameDevToolsAgentHost::UpdateFrameHost(
    RenderFrameHostImpl* frame_host) {
  if (frame_host == frame_host_) {
    if (frame_host && !render_frame_alive_) {
      render_frame_alive_ = true;
      for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
        inspector->TargetReloadedAfterCrash();
      UpdateRendererChannel(IsAttached());
    }
    return;
  }

  if (frame_host && !ShouldCreateDevToolsForHost(frame_host)) {
    DestroyOnRenderFrameGone();
    return;
  }

  if (IsAttached())
    RevokePolicy();

  frame_host_ = frame_host;

  std::vector<DevToolsSession*> restricted_sessions;
  for (DevToolsSession* session : sessions()) {
    if (!ShouldAllowSession(session))
      restricted_sessions.push_back(session);
  }
  if (!restricted_sessions.empty())
    ForceDetachRestrictedSessions(restricted_sessions);

  if (!render_frame_alive_) {
    render_frame_alive_ = true;
    for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
      inspector->TargetReloadedAfterCrash();
  }

  if (IsAttached())
    GrantPolicy();
  UpdateRendererChannel(IsAttached());
}

Project: Chrome

void RenderFrameDevToolsAgentHost::UpdateRendererChannel(bool force) {
  blink::mojom::DevToolsAgentAssociatedPtr agent_ptr;
  if (frame_host_ && render_frame_alive_ && force)
    frame_host_->GetRemoteAssociatedInterfaces()->GetInterface(&agent_ptr);
  int process_id = frame_host_ ? frame_host_->GetProcess()->GetID()
                               : ChildProcessHost::kInvalidUniqueID;
  GetRendererChannel()->SetRenderer(std::move(agent_ptr), process_id,
                                    frame_host_);
}

Project: Chrome

void RenderFrameDevToolsAgentHost::WebContentsCreated(
    WebContents* web_contents) {
  if (ShouldForceCreation()) {
    DevToolsAgentHost::GetOrCreateFor(web_contents);
  }
}

Project: Chrome

bool RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
    RenderFrameHostImpl* rfh,
    bool is_navigation,
    bool is_download,
    network::mojom::URLLoaderFactoryRequest* target_factory_request) {
  FrameTreeNode* frame_tree_node = rfh->frame_tree_node();
  base::UnguessableToken frame_token = frame_tree_node->devtools_frame_token();
  frame_tree_node = GetFrameTreeNodeAncestor(frame_tree_node);
  RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(frame_tree_node);
  if (!agent_host)
    return false;
  int process_id = is_navigation ? 0 : rfh->GetProcess()->GetID();
  DCHECK(!is_download || is_navigation);
  for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) {
    if (network->MaybeCreateProxyForInterception(
            frame_token, process_id, is_download, target_factory_request)) {
      return true;
    }
  }
  return false;
}

Project: Chrome

RenderFrameDevToolsAgentHost::~RenderFrameDevToolsAgentHost() {
  SetFrameTreeNode(nullptr);
}

Project: Chrome

bool DevToolsAgentHostClient::MayAttachToBrowser() {
  return true;
}

Project: Chrome

bool DevToolsAgentHostClient::MayAttachToRenderer(
    content::RenderFrameHost* render_frame_host,
    bool is_webui) {
  return true;
}

Project: Chrome

bool DevToolsAgentHostClient::MayDiscoverTargets() {
   return true;
 }