root/chrome/renderer/pepper/ppb_pdf_impl.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fd_
  2. GetFontTable
  3. GetWebElement
  4. GetPrintWebViewHelper
  5. IsPrintingEnabled
  6. IsPrintingEnabled
  7. GetLocalizedString
  8. GetFontFileWithFallback
  9. GetFontTableForPrivateFontFile
  10. SearchString
  11. DidStartLoading
  12. DidStopLoading
  13. SetContentRestriction
  14. HistogramPDFPageCount
  15. UserMetricsRecordAction
  16. HasUnsupportedFeature
  17. SaveAs
  18. IsFeatureEnabled
  19. GetResourceImageForScale
  20. GetResourceImage
  21. ModalPromptForPassword
  22. IsOutOfProcess
  23. SetSelectedText
  24. SetLinkUnderCursor
  25. GetInterface
  26. InvokePrintingForInstance

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/pepper/ppb_pdf_impl.h"

#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/render_messages.h"
#include "chrome/renderer/printing/print_web_view_helper.h"
#include "content/public/common/child_process_sandbox_support_linux.h"
#include "content/public/common/referrer.h"
#include "content/public/renderer/pepper_plugin_instance.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
#include "grit/webkit_resources.h"
#include "grit/webkit_strings.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/private/ppb_pdf.h"
#include "ppapi/c/trusted/ppb_browser_font_trusted.h"
#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/shared_impl/resource.h"
#include "ppapi/shared_impl/resource_tracker.h"
#include "ppapi/shared_impl/var.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebElement.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebPluginContainer.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "third_party/icu/source/i18n/unicode/usearch.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/layout.h"
#include "ui/base/resource/resource_bundle.h"

using ppapi::PpapiGlobals;
using blink::WebElement;
using blink::WebView;
using content::RenderThread;

namespace {

#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
class PrivateFontFile : public ppapi::Resource {
 public:
  PrivateFontFile(PP_Instance instance, int fd)
      : Resource(ppapi::OBJECT_IS_IMPL, instance),
        fd_(fd) {
  }

  bool GetFontTable(uint32_t table,
                    void* output,
                    uint32_t* output_length) {
    size_t temp_size = static_cast<size_t>(*output_length);
    bool rv = content::GetFontTable(
        fd_, table, 0 /* offset */, static_cast<uint8_t*>(output), &temp_size);
    *output_length = base::checked_cast<uint32_t>(temp_size);
    return rv;
  }

 protected:
  virtual ~PrivateFontFile() {}

 private:
  int fd_;
};
#endif

struct ResourceImageInfo {
  PP_ResourceImage pp_id;
  int res_id;
};

static const ResourceImageInfo kResourceImageMap[] = {
  { PP_RESOURCEIMAGE_PDF_BUTTON_FTP, IDR_PDF_BUTTON_FTP },
  { PP_RESOURCEIMAGE_PDF_BUTTON_FTP_HOVER, IDR_PDF_BUTTON_FTP_HOVER },
  { PP_RESOURCEIMAGE_PDF_BUTTON_FTP_PRESSED, IDR_PDF_BUTTON_FTP_PRESSED },
  { PP_RESOURCEIMAGE_PDF_BUTTON_FTW, IDR_PDF_BUTTON_FTW },
  { PP_RESOURCEIMAGE_PDF_BUTTON_FTW_HOVER, IDR_PDF_BUTTON_FTW_HOVER },
  { PP_RESOURCEIMAGE_PDF_BUTTON_FTW_PRESSED, IDR_PDF_BUTTON_FTW_PRESSED },
  { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END, IDR_PDF_BUTTON_ZOOMIN_END },
  { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_HOVER,
      IDR_PDF_BUTTON_ZOOMIN_END_HOVER },
  { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_PRESSED,
      IDR_PDF_BUTTON_ZOOMIN_END_PRESSED },
  { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN, IDR_PDF_BUTTON_ZOOMIN },
  { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_HOVER, IDR_PDF_BUTTON_ZOOMIN_HOVER },
  { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_PRESSED, IDR_PDF_BUTTON_ZOOMIN_PRESSED },
  { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT, IDR_PDF_BUTTON_ZOOMOUT },
  { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_HOVER, IDR_PDF_BUTTON_ZOOMOUT_HOVER },
  { PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_PRESSED,
      IDR_PDF_BUTTON_ZOOMOUT_PRESSED },
  { PP_RESOURCEIMAGE_PDF_BUTTON_SAVE, IDR_PDF_BUTTON_SAVE },
  { PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_HOVER, IDR_PDF_BUTTON_SAVE_HOVER },
  { PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_PRESSED, IDR_PDF_BUTTON_SAVE_PRESSED },
  { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT, IDR_PDF_BUTTON_PRINT },
  { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_HOVER, IDR_PDF_BUTTON_PRINT_HOVER },
  { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_PRESSED, IDR_PDF_BUTTON_PRINT_PRESSED },
  { PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_DISABLED, IDR_PDF_BUTTON_PRINT_DISABLED },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_0, IDR_PDF_THUMBNAIL_0 },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_1, IDR_PDF_THUMBNAIL_1 },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_2, IDR_PDF_THUMBNAIL_2 },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_3, IDR_PDF_THUMBNAIL_3 },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_4, IDR_PDF_THUMBNAIL_4 },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_5, IDR_PDF_THUMBNAIL_5 },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_6, IDR_PDF_THUMBNAIL_6 },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_7, IDR_PDF_THUMBNAIL_7 },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_8, IDR_PDF_THUMBNAIL_8 },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_9, IDR_PDF_THUMBNAIL_9 },
  { PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_NUM_BACKGROUND,
      IDR_PDF_THUMBNAIL_NUM_BACKGROUND },
  { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_0, IDR_PDF_PROGRESS_BAR_0 },
  { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_1, IDR_PDF_PROGRESS_BAR_1 },
  { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_2, IDR_PDF_PROGRESS_BAR_2 },
  { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_3, IDR_PDF_PROGRESS_BAR_3 },
  { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_4, IDR_PDF_PROGRESS_BAR_4 },
  { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_5, IDR_PDF_PROGRESS_BAR_5 },
  { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_6, IDR_PDF_PROGRESS_BAR_6 },
  { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_7, IDR_PDF_PROGRESS_BAR_7 },
  { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_8, IDR_PDF_PROGRESS_BAR_8 },
  { PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_BACKGROUND,
      IDR_PDF_PROGRESS_BAR_BACKGROUND },
  { PP_RESOURCEIMAGE_PDF_PAGE_INDICATOR_BACKGROUND,
      IDR_PDF_PAGE_INDICATOR_BACKGROUND },
  { PP_RESOURCEIMAGE_PDF_PAGE_DROPSHADOW, IDR_PDF_PAGE_DROPSHADOW },
  { PP_RESOURCEIMAGE_PDF_PAN_SCROLL_ICON, IDR_PAN_SCROLL_ICON },
};

#if defined(ENABLE_FULL_PRINTING)

blink::WebElement GetWebElement(PP_Instance instance_id) {
  content::PepperPluginInstance* instance =
      content::PepperPluginInstance::Get(instance_id);
  if (!instance)
    return blink::WebElement();
  return instance->GetContainer()->element();
}

printing::PrintWebViewHelper* GetPrintWebViewHelper(
    const blink::WebElement& element) {
  if (element.isNull())
    return NULL;
  blink::WebView* view = element.document().frame()->view();
  content::RenderView* render_view = content::RenderView::FromWebView(view);
  return printing::PrintWebViewHelper::Get(render_view);
}

bool IsPrintingEnabled(PP_Instance instance_id) {
  blink::WebElement element = GetWebElement(instance_id);
  printing::PrintWebViewHelper* helper = GetPrintWebViewHelper(element);
  return helper && helper->IsPrintingEnabled();
}

#else  // ENABLE_FULL_PRINTING

bool IsPrintingEnabled(PP_Instance instance_id) {
  return false;
}

#endif  // ENABLE_FULL_PRINTING



PP_Var GetLocalizedString(PP_Instance instance_id,
                          PP_ResourceString string_id) {
  content::PepperPluginInstance* instance =
      content::PepperPluginInstance::Get(instance_id);
  if (!instance)
    return PP_MakeUndefined();

  std::string rv;
  if (string_id == PP_RESOURCESTRING_PDFGETPASSWORD) {
    rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_NEED_PASSWORD));
  } else if (string_id == PP_RESOURCESTRING_PDFLOADING) {
    rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOADING));
  } else if (string_id == PP_RESOURCESTRING_PDFLOAD_FAILED) {
    rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PAGE_LOAD_FAILED));
  } else if (string_id == PP_RESOURCESTRING_PDFPROGRESSLOADING) {
    rv = base::UTF16ToUTF8(l10n_util::GetStringUTF16(IDS_PDF_PROGRESS_LOADING));
  } else {
    NOTREACHED();
  }

  return ppapi::StringVar::StringToPPVar(rv);
}

PP_Resource GetFontFileWithFallback(
    PP_Instance instance_id,
    const PP_BrowserFont_Trusted_Description* description,
    PP_PrivateFontCharset charset) {
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
  // Validate the instance before using it below.
  if (!content::PepperPluginInstance::Get(instance_id))
    return 0;

  scoped_refptr<ppapi::StringVar> face_name(ppapi::StringVar::FromPPVar(
      description->face));
  if (!face_name.get())
    return 0;

  int fd = content::MatchFontWithFallback(
      face_name->value().c_str(),
      description->weight >= PP_BROWSERFONT_TRUSTED_WEIGHT_BOLD,
      description->italic,
      charset,
      description->family);
  if (fd == -1)
    return 0;

  scoped_refptr<PrivateFontFile> font(new PrivateFontFile(instance_id, fd));

  return font->GetReference();
#else
  // For trusted PPAPI plugins, this is only needed in Linux since font loading
  // on Windows and Mac works through the renderer sandbox.
  return 0;
#endif
}

bool GetFontTableForPrivateFontFile(PP_Resource font_file,
                                    uint32_t table,
                                    void* output,
                                    uint32_t* output_length) {
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
  ppapi::Resource* resource =
      PpapiGlobals::Get()->GetResourceTracker()->GetResource(font_file);
  if (!resource)
    return false;

  PrivateFontFile* font = static_cast<PrivateFontFile*>(resource);
  return font->GetFontTable(table, output, output_length);
#else
  return false;
#endif
}

void SearchString(PP_Instance instance,
                  const unsigned short* input_string,
                  const unsigned short* input_term,
                  bool case_sensitive,
                  PP_PrivateFindResult** results,
                  int* count) {
  const base::char16* string =
      reinterpret_cast<const base::char16*>(input_string);
  const base::char16* term =
      reinterpret_cast<const base::char16*>(input_term);

  UErrorCode status = U_ZERO_ERROR;
  UStringSearch* searcher = usearch_open(
      term, -1, string, -1, RenderThread::Get()->GetLocale().c_str(), 0,
      &status);
  DCHECK(status == U_ZERO_ERROR || status == U_USING_FALLBACK_WARNING ||
         status == U_USING_DEFAULT_WARNING);
  UCollationStrength strength = case_sensitive ? UCOL_TERTIARY : UCOL_PRIMARY;

  UCollator* collator = usearch_getCollator(searcher);
  if (ucol_getStrength(collator) != strength) {
    ucol_setStrength(collator, strength);
    usearch_reset(searcher);
  }

  status = U_ZERO_ERROR;
  int match_start = usearch_first(searcher, &status);
  DCHECK(status == U_ZERO_ERROR);

  std::vector<PP_PrivateFindResult> pp_results;
  while (match_start != USEARCH_DONE) {
    size_t matched_length = usearch_getMatchedLength(searcher);
    PP_PrivateFindResult result;
    result.start_index = match_start;
    result.length = matched_length;
    pp_results.push_back(result);
    match_start = usearch_next(searcher, &status);
    DCHECK(status == U_ZERO_ERROR);
  }

  *count = pp_results.size();
  if (*count) {
    *results = reinterpret_cast<PP_PrivateFindResult*>(
        malloc(*count * sizeof(PP_PrivateFindResult)));
    memcpy(*results, &pp_results[0], *count * sizeof(PP_PrivateFindResult));
  } else {
    *results = NULL;
  }

  usearch_close(searcher);
}

void DidStartLoading(PP_Instance instance_id) {
  content::PepperPluginInstance* instance =
      content::PepperPluginInstance::Get(instance_id);
  if (!instance)
    return;
  instance->GetRenderView()->DidStartLoading();
}

void DidStopLoading(PP_Instance instance_id) {
  content::PepperPluginInstance* instance =
      content::PepperPluginInstance::Get(instance_id);
  if (!instance)
    return;
  instance->GetRenderView()->DidStopLoading();
}

void SetContentRestriction(PP_Instance instance_id, int restrictions) {
  content::PepperPluginInstance* instance =
      content::PepperPluginInstance::Get(instance_id);
  if (!instance)
    return;
  instance->GetRenderView()->Send(
      new ChromeViewHostMsg_PDFUpdateContentRestrictions(
          instance->GetRenderView()->GetRoutingID(), restrictions));
}

void HistogramPDFPageCount(PP_Instance instance, int count) {
  UMA_HISTOGRAM_COUNTS_10000("PDF.PageCount", count);
}

void UserMetricsRecordAction(PP_Instance instance, PP_Var action) {
  scoped_refptr<ppapi::StringVar> action_str(
      ppapi::StringVar::FromPPVar(action));
  if (action_str.get())
    RenderThread::Get()->RecordComputedAction(action_str->value());
}

void HasUnsupportedFeature(PP_Instance instance_id) {
  content::PepperPluginInstance* instance =
      content::PepperPluginInstance::Get(instance_id);
  if (!instance)
    return;

  // Only want to show an info bar if the pdf is the whole tab.
  if (!instance->IsFullPagePlugin())
    return;

  WebView* view =
      instance->GetContainer()->element().document().frame()->view();
  content::RenderView* render_view = content::RenderView::FromWebView(view);
  render_view->Send(new ChromeViewHostMsg_PDFHasUnsupportedFeature(
      render_view->GetRoutingID()));
}

void SaveAs(PP_Instance instance_id) {
  content::PepperPluginInstance* instance =
      content::PepperPluginInstance::Get(instance_id);
  if (!instance)
    return;
  GURL url = instance->GetPluginURL();

  content::RenderView* render_view = instance->GetRenderView();
  blink::WebFrame* frame = render_view->GetWebView()->mainFrame();
  content::Referrer referrer(frame->document().url(),
                             frame->document().referrerPolicy());
  render_view->Send(new ChromeViewHostMsg_PDFSaveURLAs(
      render_view->GetRoutingID(), url, referrer));
}

PP_Bool IsFeatureEnabled(PP_Instance instance, PP_PDFFeature feature) {
  switch (feature) {
    case PP_PDFFEATURE_HIDPI:
      return PP_TRUE;
    case PP_PDFFEATURE_PRINTING:
      return IsPrintingEnabled(instance) ? PP_TRUE : PP_FALSE;
  }
  return PP_FALSE;
}

PP_Resource GetResourceImageForScale(PP_Instance instance_id,
                                     PP_ResourceImage image_id,
                                     float scale) {
  ui::ScaleFactor supported_scale_factor = ui::GetSupportedScaleFactor(scale);
  DCHECK(supported_scale_factor != ui::SCALE_FACTOR_NONE);
  float supported_scale = ui::GetImageScale(supported_scale_factor);

  int res_id = 0;
  for (size_t i = 0; i < arraysize(kResourceImageMap); ++i) {
    if (kResourceImageMap[i].pp_id == image_id) {
      res_id = kResourceImageMap[i].res_id;
      break;
    }
  }
  if (res_id == 0)
    return 0;

  // Validate the instance.
  content::PepperPluginInstance* instance =
      content::PepperPluginInstance::Get(instance_id);
  if (!instance)
    return 0;

  gfx::ImageSkia* res_image_skia =
      ResourceBundle::GetSharedInstance().GetImageSkiaNamed(res_id);

  if (!res_image_skia)
    return 0;

  return instance->CreateImage(res_image_skia, supported_scale);
}

PP_Resource GetResourceImage(PP_Instance instance_id,
                             PP_ResourceImage image_id) {
  return GetResourceImageForScale(instance_id, image_id, 1.0f);
}

PP_Var ModalPromptForPassword(PP_Instance instance_id,
                              PP_Var message) {
  content::PepperPluginInstance* instance =
      content::PepperPluginInstance::Get(instance_id);
  if (!instance)
    return PP_MakeUndefined();

  std::string actual_value;
  scoped_refptr<ppapi::StringVar> message_string(
      ppapi::StringVar::FromPPVar(message));

  IPC::SyncMessage* msg = new ChromeViewHostMsg_PDFModalPromptForPassword(
      instance->GetRenderView()->GetRoutingID(),
      message_string->value(),
      &actual_value);
  msg->EnableMessagePumping();
  instance->GetRenderView()->Send(msg);

  return ppapi::StringVar::StringToPPVar(actual_value);
}

PP_Bool IsOutOfProcess(PP_Instance instance_id) {
  return PP_FALSE;
}

void SetSelectedText(PP_Instance instance_id, const char* selected_text) {
  // This function is intended for out of process PDF plugin.
  NOTIMPLEMENTED();
}

void SetLinkUnderCursor(PP_Instance instance_id, const char* url) {
  content::PepperPluginInstance* instance =
      content::PepperPluginInstance::Get(instance_id);
  if (!instance)
    return;
  instance->SetLinkUnderCursor(url);
}

const PPB_PDF ppb_pdf = {
  &GetLocalizedString,
  &GetResourceImage,
  &GetFontFileWithFallback,
  &GetFontTableForPrivateFontFile,
  &SearchString,
  &DidStartLoading,
  &DidStopLoading,
  &SetContentRestriction,
  &HistogramPDFPageCount,
  &UserMetricsRecordAction,
  &HasUnsupportedFeature,
  &SaveAs,
  &PPB_PDF_Impl::InvokePrintingForInstance,
  &IsFeatureEnabled,
  &GetResourceImageForScale,
  &ModalPromptForPassword,
  &IsOutOfProcess,
  &SetSelectedText,
  &SetLinkUnderCursor,
};

}  // namespace

// static
const PPB_PDF* PPB_PDF_Impl::GetInterface() {
  return &ppb_pdf;
}

// static
void PPB_PDF_Impl::InvokePrintingForInstance(PP_Instance instance_id) {
#if defined(ENABLE_FULL_PRINTING)
  blink::WebElement element = GetWebElement(instance_id);
  printing::PrintWebViewHelper* helper = GetPrintWebViewHelper(element);
  if (helper)
    helper->PrintNode(element);
#endif  // ENABLE_FULL_PRINTING
}

/* [<][>][^][v][top][bottom][index][help] */