text: // Copyright (c) 2017 GitHub, Inc.
  // Use of this source code is governed by the MIT license that can be
  // found in the LICENSE file.
  
  #include "atom/browser/ui/webui/pdf_viewer_handler.h"
  
  #include "atom/common/atom_constants.h"
  #include "base/bind.h"
  #include "base/memory/ptr_util.h"
  #include "base/values.h"
  #include "content/public/browser/stream_handle.h"
  #include "content/public/browser/stream_info.h"
  #include "content/public/browser/web_contents.h"
  #include "content/public/browser/web_ui.h"
  #include "content/public/common/page_zoom.h"
  #include "content/public/common/url_constants.h"
  #include "net/http/http_response_headers.h"
  #include "ui/base/l10n/l10n_util.h"
  #include "ui/base/webui/web_ui_util.h"
  
  namespace atom {
  
  namespace {
  
  void CreateResponseHeadersDictionary(const net::HttpResponseHeaders* headers,
                                       base::DictionaryValue* result) {
    if (!headers)
      return;
  
    size_t iter = 0;
    std::string header_name;
    std::string header_value;
    while (headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) {
      base::Value* existing_value = nullptr;
      if (result->Get(header_name, &existing_value)) {
        base::StringValue* existing_string_value =
            static_cast<base::StringValue*>(existing_value);
        existing_string_value->GetString()->append(", ").append(header_value);
      } else {
        result->SetString(header_name, header_value);
      }
    }
  }
  
  void PopulateStreamInfo(base::DictionaryValue* stream_info,
                          content::StreamInfo* stream,
                          const std::string& original_url) {
    auto headers_dict = base::MakeUnique<base::DictionaryValue>();
    auto stream_url = stream->handle->GetURL().spec();
    CreateResponseHeadersDictionary(stream->response_headers.get(),
                                    headers_dict.get());
    stream_info->SetString("streamURL", stream_url);
    stream_info->SetString("originalURL", original_url);
    stream_info->Set("responseHeaders", std::move(headers_dict));
  }
  
  }  // namespace
  
  PdfViewerHandler::PdfViewerHandler(const std::string& src)
      : stream_(nullptr), original_url_(src) {}
  
  PdfViewerHandler::~PdfViewerHandler() {}
  
  void PdfViewerHandler::SetPdfResourceStream(content::StreamInfo* stream) {
    stream_ = stream;
    if (!!initialize_callback_id_.get()) {
      auto list = base::MakeUnique<base::ListValue>();
      list->Set(0, std::move(initialize_callback_id_));
      Initialize(list.get());
    }
  }
  
  void PdfViewerHandler::RegisterMessages() {
    web_ui()->RegisterMessageCallback(
        "initialize",
        base::Bind(&PdfViewerHandler::Initialize, base::Unretained(this)));
    web_ui()->RegisterMessageCallback(
        "getDefaultZoom",
        base::Bind(&PdfViewerHandler::GetInitialZoom, base::Unretained(this)));
    web_ui()->RegisterMessageCallback(
        "getInitialZoom",
        base::Bind(&PdfViewerHandler::GetInitialZoom, base::Unretained(this)));
    web_ui()->RegisterMessageCallback(
        "setZoom",
        base::Bind(&PdfViewerHandler::SetZoom, base::Unretained(this)));
    web_ui()->RegisterMessageCallback(
        "getStrings",
        base::Bind(&PdfViewerHandler::GetStrings, base::Unretained(this)));
    web_ui()->RegisterMessageCallback(
        "reload", base::Bind(&PdfViewerHandler::Reload, base::Unretained(this)));
  }
  
  void PdfViewerHandler::OnJavascriptAllowed() {
    auto host_zoom_map =
        content::HostZoomMap::GetForWebContents(web_ui()->GetWebContents());
    host_zoom_map_subscription_ =
        host_zoom_map->AddZoomLevelChangedCallback(base::Bind(
            &PdfViewerHandler::OnZoomLevelChanged, base::Unretained(this)));
  }
  
  void PdfViewerHandler::OnJavascriptDisallowed() {
    host_zoom_map_subscription_.reset();
  }
  
  void PdfViewerHandler::Initialize(const base::ListValue* args) {
    CHECK_EQ(1U, args->GetSize());
    const base::Value* callback_id;
    CHECK(args->Get(0, &callback_id));
  
    if (stream_) {
      CHECK(!initialize_callback_id_.get());
      AllowJavascript();
  
      auto stream_info = base::MakeUnique<base::DictionaryValue>();
      PopulateStreamInfo(stream_info.get(), stream_, original_url_);
      ResolveJavascriptCallback(*callback_id, *stream_info);
    } else {
      initialize_callback_id_ = callback_id->CreateDeepCopy();
    }
  }
  
  void PdfViewerHandler::GetDefaultZoom(const base::ListValue* args) {
    if (!IsJavascriptAllowed())
      return;
    CHECK_EQ(1U, args->GetSize());
    const base::Value* callback_id;
    CHECK(args->Get(0, &callback_id));
  
    auto host_zoom_map =
        content::HostZoomMap::GetForWebContents(web_ui()->GetWebContents());
    double zoom_level = host_zoom_map->GetDefaultZoomLevel();
    ResolveJavascriptCallback(
        *callback_id,
        base::FundamentalValue(content::ZoomLevelToZoomFactor(zoom_level)));
  }
  
  void PdfViewerHandler::GetInitialZoom(const base::ListValue* args) {
    if (!IsJavascriptAllowed())
      return;
    CHECK_EQ(1U, args->GetSize());
    const base::Value* callback_id;
    CHECK(args->Get(0, &callback_id));
  
    double zoom_level =
        content::HostZoomMap::GetZoomLevel(web_ui()->GetWebContents());
    ResolveJavascriptCallback(
        *callback_id,
        base::FundamentalValue(content::ZoomLevelToZoomFactor(zoom_level)));
  }
  
  void PdfViewerHandler::SetZoom(const base::ListValue* args) {
    if (!IsJavascriptAllowed())
      return;
    CHECK_EQ(2U, args->GetSize());
    const base::Value* callback_id;
    CHECK(args->Get(0, &callback_id));
    double zoom_level = 0.0;
    CHECK(args->GetDouble(1, &zoom_level));
  
    content::HostZoomMap::SetZoomLevel(web_ui()->GetWebContents(),
                                       zoom_level);
    ResolveJavascriptCallback(*callback_id, base::FundamentalValue(zoom_level));
  }
  
  void PdfViewerHandler::GetStrings(const base::ListValue* args) {
    if (!IsJavascriptAllowed())
      return;
    CHECK_EQ(1U, args->GetSize());
    const base::Value* callback_id;
    CHECK(args->Get(0, &callback_id));
  
    auto result = base::MakeUnique<base::DictionaryValue>();
  // TODO(deepak1556): Generate strings from components/pdf_strings.grdp.
  #define SET_STRING(id, resource) result->SetString(id, resource)
    SET_STRING("passwordPrompt",
               "This document is password protected.  Please enter a password.");
    SET_STRING("passwordSubmit", "Submit");
    SET_STRING("passwordInvalid", "Incorrect password");
    SET_STRING("pageLoading", "Loading...");
    SET_STRING("pageLoadFailed", "Failed to load PDF document");
    SET_STRING("pageReload", "Reload");
    SET_STRING("bookmarks", "Bookmarks");
    SET_STRING("labelPageNumber", "Page number");
    SET_STRING("tooltipRotateCW", "Rotate clockwise");
    SET_STRING("tooltipDownload", "Download");
    SET_STRING("tooltipFitToPage", "Fit to page");
    SET_STRING("tooltipFitToWidth", "Fit to width");
    SET_STRING("tooltipZoomIn", "Zoom in");
    SET_STRING("tooltipZoomOut", "Zoom out");
  #undef SET_STRING
  
    webui::SetLoadTimeDataDefaults(l10n_util::GetApplicationLocale(""),
                                   result.get());
    ResolveJavascriptCallback(*callback_id, *result);
  }
  
  void PdfViewerHandler::Reload(const base::ListValue* args) {
    CHECK_EQ(0U, args->GetSize());
    web_ui()->GetWebContents()->ReloadFocusedFrame(false);
  }
  
  void PdfViewerHandler::OnZoomLevelChanged(
      const content::HostZoomMap::ZoomLevelChange& change) {
    if (change.host == kPdfViewerUIHost) {
      CallJavascriptFunction(
          "cr.webUIListenerCallback", base::StringValue("onZoomLevelChanged"),
          base::FundamentalValue(
              content::ZoomLevelToZoomFactor(change.zoom_level)));
    }
  }
  
  }  // namespace atom
  