Commit message: None
CVE: CVE-2018-6088
CWE: CWE-20
Vulnerability description: An iterator-invalidation bug in PDFium in Google Chrome prior to 66.0.3359.117 allowed a remote attacker to execute arbitrary code inside a sandbox via a crafted PDF file.

Project: Chrome

void PDFiumEngine::AppendBlankPages(int num_pages) {
  DCHECK_NE(num_pages, 0);

  if (!doc_)
    return;

  selection_.clear();
  pending_pages_.clear();

  while (pages_.size() > 1) {
    pages_.pop_back();
    FPDFPage_Delete(doc_, pages_.size());
  }

  std::vector<pp::Rect> page_rects;
  pp::Size page_size = GetPageSize(0);
  page_size.Enlarge(kPageShadowLeft + kPageShadowRight,
                    kPageShadowTop + kPageShadowBottom);
  pp::Size old_document_size = document_size_;
  document_size_ = pp::Size(page_size.width(), 0);
  for (int i = 0; i < num_pages; ++i) {
    if (i != 0) {
      document_size_.Enlarge(0, kPageSeparatorThickness);
    }

    pp::Rect rect(pp::Point(0, document_size_.height()), page_size);
    page_rects.push_back(rect);

    document_size_.Enlarge(0, page_size.height());
  }

  for (int i = 1; i < num_pages; ++i) {
    pp::Rect page_rect(page_rects[i]);
    page_rect.Inset(kPageShadowLeft, kPageShadowTop, kPageShadowRight,
                    kPageShadowBottom);
    double width_in_points =
        ConvertUnitDouble(page_rect.width(), kPixelsPerInch, kPointsPerInch);
    double height_in_points =
        ConvertUnitDouble(page_rect.height(), kPixelsPerInch, kPointsPerInch);
    FPDF_PAGE temp_page =
        FPDFPage_New(doc_, i, width_in_points, height_in_points);
    FPDF_ClosePage(temp_page);
    pages_.push_back(std::make_unique<PDFiumPage>(this, i, page_rect, true));
  }

  CalculateVisiblePages();
  if (document_size_ != old_document_size)
    client_->DocumentSizeUpdated(document_size_);
}

Project: Chrome

int CalculatePosition(FPDF_PAGE page,
                      const PDFiumEngineExports::RenderingSettings& settings,
                      pp::Rect* dest) {
  int dpi = std::max(settings.dpi_x, settings.dpi_y);
  int page_width = static_cast<int>(
      ConvertUnitDouble(FPDF_GetPageWidth(page), kPointsPerInch, dpi));
  int page_height = static_cast<int>(
      ConvertUnitDouble(FPDF_GetPageHeight(page), kPointsPerInch, dpi));

  *dest = settings.bounds;

  int rotate = 0;  // normal orientation.

  if (settings.autorotate &&
      (dest->width() > dest->height()) != (page_width > page_height)) {
    rotate = 3;  // 90 degrees counter-clockwise.
    std::swap(page_width, page_height);
  }

  bool scale_to_bounds = false;
  if (settings.fit_to_bounds &&
      ((page_width > dest->width()) || (page_height > dest->height()))) {
    scale_to_bounds = true;
  } else if (settings.stretch_to_bounds &&
             ((page_width < dest->width()) || (page_height < dest->height()))) {
    scale_to_bounds = true;
  }

  if (scale_to_bounds) {
    if (settings.keep_aspect_ratio) {
      double scale_factor_x = page_width;
      scale_factor_x /= dest->width();
      double scale_factor_y = page_height;
      scale_factor_y /= dest->height();
      if (scale_factor_x > scale_factor_y) {
        dest->set_height(page_height / scale_factor_x);
      } else {
        dest->set_width(page_width / scale_factor_y);
      }
    }
  } else {
    dest->set_width(page_width);
    dest->set_height(page_height);
  }

  if (settings.dpi_x != settings.dpi_y) {
    dest->set_width(dest->width() * settings.dpi_x / dpi);
    dest->set_height(dest->height() * settings.dpi_y / dpi);
  }

  if (settings.center_in_bounds) {
    pp::Point offset(
        (settings.bounds.width() * settings.dpi_x / dpi - dest->width()) / 2,
        (settings.bounds.height() * settings.dpi_y / dpi - dest->height()) / 2);
    dest->Offset(offset);
  }
  return rotate;
}

Project: Chrome

void PDFiumEngine::CalculateVisiblePages() {
  if (!doc_loader_)
    return;
  pending_pages_.clear();
  doc_loader_->ClearPendingRequests();

  std::vector<int> formerly_visible_pages;
  std::swap(visible_pages_, formerly_visible_pages);

  pp::Rect visible_rect(plugin_size_);
  for (int i = 0; i < static_cast<int>(pages_.size()); ++i) {
    if (visible_rect.Intersects(GetPageScreenRect(i))) {
      visible_pages_.push_back(i);
      if (CheckPageAvailable(i, &pending_pages_)) {
        auto it = std::find(formerly_visible_pages.begin(),
                            formerly_visible_pages.end(), i);
        if (it == formerly_visible_pages.end())
          client_->NotifyPageBecameVisible(pages_[i]->GetPageFeatures());
      }
    } else {
      if (defer_page_unload_) {
        deferred_page_unloads_.push_back(i);
      } else {
        pages_[i]->Unload();
      }

      if (static_cast<int>(i) == last_page_mouse_down_)
        last_page_mouse_down_ = -1;
    }
  }

  form_highlights_.clear();

  int most_visible_page = visible_pages_.empty() ? -1 : visible_pages_.front();
  if (most_visible_page != -1 && !pages_.empty() &&
      most_visible_page < static_cast<int>(pages_.size()) - 1) {
    pp::Rect rc_first =
        visible_rect.Intersect(GetPageScreenRect(most_visible_page));
    pp::Rect rc_next =
        visible_rect.Intersect(GetPageScreenRect(most_visible_page + 1));
    if (rc_next.height() > rc_first.height())
      most_visible_page++;
  }

  SetCurrentPage(most_visible_page);
}

Project: Chrome

std::string ConvertViewIntToViewString(unsigned long view_int) {
  switch (view_int) {
    case PDFDEST_VIEW_XYZ:
      return "XYZ";
    case PDFDEST_VIEW_FIT:
      return "Fit";
    case PDFDEST_VIEW_FITH:
      return "FitH";
    case PDFDEST_VIEW_FITV:
      return "FitV";
    case PDFDEST_VIEW_FITR:
      return "FitR";
    case PDFDEST_VIEW_FITB:
      return "FitB";
    case PDFDEST_VIEW_FITBH:
      return "FitBH";
    case PDFDEST_VIEW_FITBV:
      return "FitBV";
    case PDFDEST_VIEW_UNKNOWN_MODE:
      return "";
    default:
      NOTREACHED();
      return "";
  }
}

Project: Chrome

FPDF_DOCUMENT PDFiumEngine::CreateSinglePageRasterPdf(
    double source_page_width,
    double source_page_height,
    const PP_PrintSettings_Dev& print_settings,
    PDFiumPage* page_to_print) {
  FPDF_DOCUMENT temp_doc = FPDF_CreateNewDocument();
  if (!temp_doc)
    return temp_doc;

  const pp::Size& bitmap_size(page_to_print->rect().size());

  pp::ImageData image =
      pp::ImageData(client_->GetPluginInstance(),
                    PP_IMAGEDATAFORMAT_BGRA_PREMUL, bitmap_size, false);

  FPDF_BITMAP bitmap =
      FPDFBitmap_CreateEx(bitmap_size.width(), bitmap_size.height(),
                          FPDFBitmap_BGRx, image.data(), image.stride());

  FPDFBitmap_FillRect(bitmap, 0, 0, bitmap_size.width(), bitmap_size.height(),
                      0xFFFFFFFF);

  pp::Rect page_rect = page_to_print->rect();
  FPDF_RenderPageBitmap(bitmap, page_to_print->GetPrintPage(), page_rect.x(),
                        page_rect.y(), page_rect.width(), page_rect.height(),
                        print_settings.orientation,
                        FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);

  unsigned char* bitmap_data =
      static_cast<unsigned char*>(FPDFBitmap_GetBuffer(bitmap));
  double ratio_x = ConvertUnitDouble(bitmap_size.width(), print_settings.dpi,
                                     kPointsPerInch);
  double ratio_y = ConvertUnitDouble(bitmap_size.height(), print_settings.dpi,
                                     kPointsPerInch);

  FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImageObj(temp_doc);

  bool encoded = false;
  std::vector<uint8_t> compressed_bitmap_data;
  if (!(print_settings.format & PP_PRINTOUTPUTFORMAT_PDF)) {
    const int kQuality = 40;
    SkImageInfo info = SkImageInfo::Make(
        FPDFBitmap_GetWidth(bitmap), FPDFBitmap_GetHeight(bitmap),
        kBGRA_8888_SkColorType, kOpaque_SkAlphaType);
    SkPixmap src(info, bitmap_data, FPDFBitmap_GetStride(bitmap));
    encoded = gfx::JPEGCodec::Encode(src, kQuality, &compressed_bitmap_data);
  }

  {
    std::unique_ptr<void, FPDFPageDeleter> temp_page_holder(
        FPDFPage_New(temp_doc, 0, source_page_width, source_page_height));
    FPDF_PAGE temp_page = temp_page_holder.get();
    if (encoded) {
      FPDF_FILEACCESS file_access = {};
      file_access.m_FileLen =
          static_cast<unsigned long>(compressed_bitmap_data.size());
      file_access.m_GetBlock = &GetBlockForJpeg;
      file_access.m_Param = &compressed_bitmap_data;

      FPDFImageObj_LoadJpegFileInline(&temp_page, 1, temp_img, &file_access);
    } else {
      FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, bitmap);
    }

    FPDFImageObj_SetMatrix(temp_img, ratio_x, 0, 0, ratio_y, 0, 0);
    FPDFPage_InsertObject(temp_page, temp_img);
    FPDFPage_GenerateContent(temp_page);
  }

  page_to_print->ClosePrintPage();
  FPDFBitmap_Destroy(bitmap);

  return temp_doc;
}

Project: Chrome

void PDFiumEngine::Form_DisplayCaret(FPDF_FORMFILLINFO* param,
                                     FPDF_PAGE page,
                                     FPDF_BOOL visible,
                                     double left,
                                     double top,
                                     double right,
                                     double bottom) {}

Project: Chrome

int PDFiumEngine::Form_GetPlatform(FPDF_FORMFILLINFO* param,
                                   void* platform,
                                   int length) {
  int platform_flag = -1;

#if defined(WIN32)
  platform_flag = 0;
#elif defined(__linux__)
  platform_flag = 1;
#else
  platform_flag = 2;
#endif

  std::string javascript =
      "alert(\"Platform:" + base::NumberToString(platform_flag) + "\")";

  return platform_flag;
}

Project: Chrome

void PDFiumEngine::Form_OnChange(FPDF_FORMFILLINFO* param) {
  PDFiumEngine* engine = static_cast<PDFiumEngine*>(param);
  engine->SetEditMode(true);
}

Project: Chrome

void PDFiumEngine::Form_OutputSelectedRect(FPDF_FORMFILLINFO* param,
                                           FPDF_PAGE page,
                                           double left,
                                           double top,
                                           double right,
                                           double bottom) {
  PDFiumEngine* engine = static_cast<PDFiumEngine*>(param);
  int page_index = engine->GetVisiblePageIndex(page);
  if (page_index == -1) {
    NOTREACHED();
    return;
  }
  pp::Rect rect = engine->pages_[page_index]->PageToScreen(
      engine->GetVisibleRect().point(), engine->current_zoom_, left, top, right,
      bottom, engine->current_rotation_);
  if (rect.IsEmpty())
    return;

  engine->form_highlights_.push_back(rect);
}

Project: Chrome

base::Optional<PDFEngine::NamedDestination> PDFiumEngine::GetNamedDestination(
    const std::string& destination) {
  FPDF_DEST dest = FPDF_GetNamedDestByName(doc_, destination.c_str());
  if (!dest) {
    base::string16 destination_wide = base::UTF8ToUTF16(destination);
    FPDF_WIDESTRING destination_pdf_wide =
        reinterpret_cast<FPDF_WIDESTRING>(destination_wide.c_str());
    FPDF_BOOKMARK bookmark = FPDFBookmark_Find(doc_, destination_pdf_wide);
    if (bookmark)
      dest = FPDFBookmark_GetDest(doc_, bookmark);
  }

  if (!dest)
    return {};

  int page = FPDFDest_GetDestPageIndex(doc_, dest);
  if (page < 0)
    return {};

  PDFEngine::NamedDestination result;
  result.page = page;
  unsigned long view_int =
      FPDFDest_GetView(dest, &result.num_params, result.params);
  result.view = ConvertViewIntToViewString(view_int);
  return result;
}

Project: Chrome

bool PDFiumEngine::HandleEvent(const pp::InputEvent& event) {
  DCHECK(!defer_page_unload_);
  defer_page_unload_ = true;
  bool rv = false;
  switch (event.GetType()) {
    case PP_INPUTEVENT_TYPE_MOUSEDOWN:
      rv = OnMouseDown(pp::MouseInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_MOUSEUP:
      rv = OnMouseUp(pp::MouseInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_MOUSEMOVE:
      rv = OnMouseMove(pp::MouseInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_KEYDOWN:
      rv = OnKeyDown(pp::KeyboardInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_KEYUP:
      rv = OnKeyUp(pp::KeyboardInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_CHAR:
      rv = OnChar(pp::KeyboardInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_TOUCHSTART: {
      KillTouchTimer(next_touch_timer_id_);

      pp::TouchInputEvent touch_event(event);
      if (touch_event.GetTouchCount(PP_TOUCHLIST_TYPE_TARGETTOUCHES) == 1)
        ScheduleTouchTimer(touch_event);
      break;
    }
    case PP_INPUTEVENT_TYPE_TOUCHEND:
      KillTouchTimer(next_touch_timer_id_);
      break;
    case PP_INPUTEVENT_TYPE_TOUCHMOVE:
      KillTouchTimer(next_touch_timer_id_);
      break;
    default:
      break;
  }

  DCHECK(defer_page_unload_);
  defer_page_unload_ = false;

  std::vector<int> pages_to_unload;
  std::swap(pages_to_unload, deferred_page_unloads_);
  for (int page_index : pages_to_unload)
    pages_[page_index]->Unload();

  return rv;
}

Project: Chrome

bool PDFiumEngine::IsPointInEditableFormTextArea(FPDF_PAGE page,
                                                 double page_x,
                                                 double page_y,
                                                 int form_type) {
#if defined(PDF_ENABLE_XFA)
  if (IS_XFA_FORMFIELD(form_type))
    return form_type == FPDF_FORMFIELD_XFA_TEXTFIELD ||
           form_type == FPDF_FORMFIELD_XFA_COMBOBOX;
#endif  // defined(PDF_ENABLE_XFA)

  FPDF_ANNOTATION annot =
      FPDFAnnot_GetFormFieldAtPoint(form_, page, page_x, page_y);
  if (!annot)
    return false;

  int flags = FPDFAnnot_GetFormFieldFlags(page, annot);
  bool is_editable_form_text_area =
      CheckIfEditableFormTextArea(flags, form_type);
  FPDFPage_CloseAnnot(annot);
  return is_editable_form_text_area;
}

Project: Chrome

void PDFiumEngine::KillFormFocus() {
  FORM_ForceToKillFocus(form_);
  SetInFormTextArea(false);
}

Project: Chrome

void PDFiumEngine::LoadForm() {
  if (form_)
    return;
  DCHECK(doc_);
  form_status_ = FPDFAvail_IsFormAvail(fpdf_availability_, &download_hints_);
  if (form_status_ != PDF_FORM_NOTAVAIL || doc_loader_->IsDocumentComplete()) {
    form_ = FPDFDOC_InitFormFillEnvironment(
        doc_, static_cast<FPDF_FORMFILLINFO*>(this));
#if defined(PDF_ENABLE_XFA)
    FPDF_LoadXFA(doc_);
#endif

    FPDF_SetFormFieldHighlightColor(form_, FPDF_FORMFIELD_UNKNOWN,
                                    kFormHighlightColor);
    FPDF_SetFormFieldHighlightAlpha(form_, kFormHighlightAlpha);
  }
}  // namespace chrome_pdf

Project: Chrome

bool PDFiumEngine::OnMouseUp(const pp::MouseInputEvent& event) {
  if (event.GetButton() != PP_INPUTEVENT_MOUSEBUTTON_LEFT &&
      event.GetButton() != PP_INPUTEVENT_MOUSEBUTTON_MIDDLE) {
    return false;
  }

  if (event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT)
    SetMouseLeftButtonDown(false);

  int page_index = -1;
  int char_index = -1;
  int form_type = FPDF_FORMFIELD_UNKNOWN;
  PDFiumPage::LinkTarget target;
  pp::Point point = event.GetPosition();
  PDFiumPage::Area area =
      GetCharIndex(point, &page_index, &char_index, &form_type, &target);

  if (mouse_down_state_.Matches(area, target)) {
    if (area == PDFiumPage::WEBLINK_AREA) {
      uint32_t modifiers = event.GetModifiers();
      bool middle_button =
          !!(modifiers & PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN);
      bool alt_key = !!(modifiers & PP_INPUTEVENT_MODIFIER_ALTKEY);
      bool ctrl_key = !!(modifiers & PP_INPUTEVENT_MODIFIER_CONTROLKEY);
      bool meta_key = !!(modifiers & PP_INPUTEVENT_MODIFIER_METAKEY);
      bool shift_key = !!(modifiers & PP_INPUTEVENT_MODIFIER_SHIFTKEY);

      WindowOpenDisposition disposition = ui::DispositionFromClick(
          middle_button, alt_key, ctrl_key, meta_key, shift_key);

      client_->NavigateTo(target.url, disposition);
      SetInFormTextArea(false);
      return true;
    }
    if (area == PDFiumPage::DOCLINK_AREA) {
      if (!PageIndexInBounds(target.page))
        return true;

      pp::Rect page_rect(GetPageScreenRect(target.page));
      int y = position_.y() + page_rect.y();
      if (target.y_in_pixels)
        y += target.y_in_pixels.value() * current_zoom_;
      client_->ScrollToY(y, /*compensate_for_toolbar=*/true);
      SetInFormTextArea(false);
      return true;
    }
  }

  if (event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_MIDDLE)
    return false;

  if (page_index != -1) {
    double page_x;
    double page_y;
    DeviceToPage(page_index, point.x(), point.y(), &page_x, &page_y);
    FORM_OnLButtonUp(form_, pages_[page_index]->GetPage(), 0, page_x, page_y);
  }

  if (!selecting_)
    return false;

  SetSelecting(false);
  return true;
}

Project: Chrome

PDFiumEngine::PDFiumEngine(PDFEngine::Client* client)
    : client_(client),
      current_zoom_(1.0),
      current_rotation_(0),
      doc_(nullptr),
      form_(nullptr),
      defer_page_unload_(false),
      selecting_(false),
      mouse_down_state_(PDFiumPage::NONSELECTABLE_AREA,
                        PDFiumPage::LinkTarget()),
      in_form_text_area_(false),
      editable_form_text_area_(false),
      mouse_left_button_down_(false),
      permissions_(0),
      permissions_handler_revision_(-1),
      fpdf_availability_(nullptr),
      last_page_mouse_down_(-1),
      most_visible_page_(-1),
      called_do_document_action_(false),
      render_grayscale_(false),
      render_annots_(true),
      edit_mode_(false) {
  find_factory_.Initialize(this);
  password_factory_.Initialize(this);

  file_access_.m_FileLen = 0;
  file_access_.m_GetBlock = &GetBlock;
  file_access_.m_Param = this;

  file_availability_.version = 1;
  file_availability_.IsDataAvail = &IsDataAvail;
  file_availability_.engine = this;

  download_hints_.version = 1;
  download_hints_.AddSegment = &AddSegment;
  download_hints_.engine = this;

  FPDF_FORMFILLINFO::version = 1;
  FPDF_FORMFILLINFO::m_pJsPlatform = this;
  FPDF_FORMFILLINFO::Release = nullptr;
  FPDF_FORMFILLINFO::FFI_Invalidate = Form_Invalidate;
  FPDF_FORMFILLINFO::FFI_OutputSelectedRect = Form_OutputSelectedRect;
  FPDF_FORMFILLINFO::FFI_SetCursor = Form_SetCursor;
  FPDF_FORMFILLINFO::FFI_SetTimer = Form_SetTimer;
  FPDF_FORMFILLINFO::FFI_KillTimer = Form_KillTimer;
  FPDF_FORMFILLINFO::FFI_GetLocalTime = Form_GetLocalTime;
  FPDF_FORMFILLINFO::FFI_OnChange = Form_OnChange;
  FPDF_FORMFILLINFO::FFI_GetPage = Form_GetPage;
  FPDF_FORMFILLINFO::FFI_GetCurrentPage = Form_GetCurrentPage;
  FPDF_FORMFILLINFO::FFI_GetRotation = Form_GetRotation;
  FPDF_FORMFILLINFO::FFI_ExecuteNamedAction = Form_ExecuteNamedAction;
  FPDF_FORMFILLINFO::FFI_SetTextFieldFocus = Form_SetTextFieldFocus;
  FPDF_FORMFILLINFO::FFI_DoURIAction = Form_DoURIAction;
  FPDF_FORMFILLINFO::FFI_DoGoToAction = Form_DoGoToAction;
#if defined(PDF_ENABLE_XFA)
  FPDF_FORMFILLINFO::version = 2;
  FPDF_FORMFILLINFO::FFI_EmailTo = Form_EmailTo;
  FPDF_FORMFILLINFO::FFI_DisplayCaret = Form_DisplayCaret;
  FPDF_FORMFILLINFO::FFI_SetCurrentPage = Form_SetCurrentPage;
  FPDF_FORMFILLINFO::FFI_GetCurrentPageIndex = Form_GetCurrentPageIndex;
  FPDF_FORMFILLINFO::FFI_GetPageViewRect = Form_GetPageViewRect;
  FPDF_FORMFILLINFO::FFI_GetPlatform = Form_GetPlatform;
  FPDF_FORMFILLINFO::FFI_PageEvent = nullptr;
  FPDF_FORMFILLINFO::FFI_PopupMenu = Form_PopupMenu;
  FPDF_FORMFILLINFO::FFI_PostRequestURL = Form_PostRequestURL;
  FPDF_FORMFILLINFO::FFI_PutRequestURL = Form_PutRequestURL;
  FPDF_FORMFILLINFO::FFI_UploadTo = Form_UploadTo;
  FPDF_FORMFILLINFO::FFI_DownloadFromURL = Form_DownloadFromURL;
  FPDF_FORMFILLINFO::FFI_OpenFile = Form_OpenFile;
  FPDF_FORMFILLINFO::FFI_GotoURL = Form_GotoURL;
  FPDF_FORMFILLINFO::FFI_GetLanguage = Form_GetLanguage;
#endif  // defined(PDF_ENABLE_XFA)
  IPDF_JSPLATFORM::version = 3;
  IPDF_JSPLATFORM::app_alert = Form_Alert;
  IPDF_JSPLATFORM::app_beep = Form_Beep;
  IPDF_JSPLATFORM::app_response = Form_Response;
  IPDF_JSPLATFORM::Doc_getFilePath = Form_GetFilePath;
  IPDF_JSPLATFORM::Doc_mail = Form_Mail;
  IPDF_JSPLATFORM::Doc_print = Form_Print;
  IPDF_JSPLATFORM::Doc_submitForm = Form_SubmitForm;
  IPDF_JSPLATFORM::Doc_gotoPage = Form_GotoPage;
  IPDF_JSPLATFORM::Field_browse = nullptr;

  IFSDK_PAUSE::version = 1;
  IFSDK_PAUSE::user = nullptr;
  IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow;

#if defined(OS_LINUX)
  pp::Instance* instance = client_->GetPluginInstance();
  if (instance)
    g_last_instance_id = instance->pp_instance();
#endif
}

Project: Chrome

pp::Buffer_Dev PDFiumEngine::PrintPagesAsPDF(
    const PP_PrintPageNumberRange_Dev* page_ranges,
    uint32_t page_range_count,
    const PP_PrintSettings_Dev& print_settings) {
  if (!page_range_count)
    return pp::Buffer_Dev();

  DCHECK(doc_);
  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
  if (!output_doc)
    return pp::Buffer_Dev();

  KillFormFocus();

  std::string page_number_str;
  for (uint32_t index = 0; index < page_range_count; ++index) {
    if (!page_number_str.empty())
      page_number_str.append(",");
    const PP_PrintPageNumberRange_Dev& range = page_ranges[index];
    page_number_str.append(base::UintToString(range.first_page_number + 1));
    if (range.first_page_number != range.last_page_number) {
      page_number_str.append("-");
      page_number_str.append(base::UintToString(range.last_page_number + 1));
    }
  }

  std::vector<uint32_t> page_numbers =
      GetPageNumbersFromPrintPageNumberRange(page_ranges, page_range_count);
  for (uint32_t page_number : page_numbers) {
    pages_[page_number]->GetPage();
    if (!IsPageVisible(page_number))
      pages_[page_number]->Unload();
  }

  FPDF_CopyViewerPreferences(output_doc, doc_);
  if (!FPDF_ImportPages(output_doc, doc_, page_number_str.c_str(), 0)) {
    FPDF_CloseDocument(output_doc);
    return pp::Buffer_Dev();
  }

  FitContentsToPrintableAreaIfRequired(output_doc, print_settings);

  pp::Buffer_Dev buffer = GetFlattenedPrintData(output_doc);
  FPDF_CloseDocument(output_doc);
  return buffer;
}

Project: Chrome

pp::Buffer_Dev PDFiumEngine::PrintPagesAsRasterPDF(
    const PP_PrintPageNumberRange_Dev* page_ranges,
    uint32_t page_range_count,
    const PP_PrintSettings_Dev& print_settings) {
  if (!page_range_count)
    return pp::Buffer_Dev();

  if (doc_ && !doc_loader_->IsDocumentComplete())
    return pp::Buffer_Dev();

  FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument();
  if (!output_doc)
    return pp::Buffer_Dev();

  KillFormFocus();

  std::vector<PDFiumPage> pages_to_print;
  std::vector<std::pair<double, double>> source_page_sizes;
  std::vector<uint32_t> page_numbers =
      GetPageNumbersFromPrintPageNumberRange(page_ranges, page_range_count);
  for (uint32_t page_number : page_numbers) {
    FPDF_PAGE pdf_page = FPDF_LoadPage(doc_, page_number);
    double source_page_width = FPDF_GetPageWidth(pdf_page);
    double source_page_height = FPDF_GetPageHeight(pdf_page);
    source_page_sizes.push_back(
        std::make_pair(source_page_width, source_page_height));
    int width_in_pixels =
        ConvertUnit(source_page_width, kPointsPerInch, print_settings.dpi);
    int height_in_pixels =
        ConvertUnit(source_page_height, kPointsPerInch, print_settings.dpi);

    pp::Rect rect(width_in_pixels, height_in_pixels);
    pages_to_print.push_back(PDFiumPage(this, page_number, rect, true));
    FPDF_ClosePage(pdf_page);
  }

#if defined(OS_LINUX)
  g_last_instance_id = client_->GetPluginInstance()->pp_instance();
#endif

  size_t i = 0;
  for (; i < pages_to_print.size(); ++i) {
    double source_page_width = source_page_sizes[i].first;
    double source_page_height = source_page_sizes[i].second;

    FPDF_DOCUMENT temp_doc =
        CreateSinglePageRasterPdf(source_page_width, source_page_height,
                                  print_settings, &pages_to_print[i]);

    if (!temp_doc)
      break;

    pp::Buffer_Dev buffer = GetFlattenedPrintData(temp_doc);
    FPDF_CloseDocument(temp_doc);

    PDFiumMemBufferFileRead file_read(buffer.data(), buffer.size());
    temp_doc = FPDF_LoadCustomDocument(&file_read, nullptr);

    FPDF_BOOL imported = FPDF_ImportPages(output_doc, temp_doc, "1", i);
    FPDF_CloseDocument(temp_doc);
    if (!imported)
      break;
  }

  pp::Buffer_Dev buffer;
  if (i == pages_to_print.size()) {
    FPDF_CopyViewerPreferences(output_doc, doc_);
    FitContentsToPrintableAreaIfRequired(output_doc, print_settings);
    buffer = GetFlattenedPrintData(output_doc);
  }
  FPDF_CloseDocument(output_doc);
  return buffer;
}

Project: Chrome

void PDFiumEngine::RotateInternal() {
  std::string current_find_text = current_find_text_;
  resume_find_index_ = current_find_index_;

  int most_visible_page = most_visible_page_;

  InvalidateAllPages();

  if (!current_find_text.empty()) {
    client_->NotifyNumberOfFindResultsChanged(0, false);
    StartFind(current_find_text, false);
  }

  client_->ScrollToPage(most_visible_page);
}

Project: Chrome

void PDFiumEngine::SearchUsingICU(const base::string16& term,
                                  bool case_sensitive,
                                  bool first_search,
                                  int character_to_start_searching_from,
                                  int current_page) {
  DCHECK(!term.empty());

  const int original_text_length = pages_[current_page]->GetCharCount();
  int text_length = original_text_length;
  if (character_to_start_searching_from) {
    text_length -= character_to_start_searching_from;
  } else if (!first_search && last_character_index_to_search_ != -1 &&
             current_page == last_page_to_search_) {
    text_length = last_character_index_to_search_;
  }
  if (text_length <= 0)
    return;

  base::string16 page_text;
  PDFiumAPIStringBufferAdapter<base::string16> api_string_adapter(
      &page_text, text_length, false);
  unsigned short* data =
      reinterpret_cast<unsigned short*>(api_string_adapter.GetData());
  int written =
      FPDFText_GetText(pages_[current_page]->GetTextPage(),
                       character_to_start_searching_from, text_length, data);
  api_string_adapter.Close(written);

  std::vector<PDFEngine::Client::SearchStringResult> results =
      client_->SearchString(page_text.c_str(), term.c_str(), case_sensitive);
  for (const auto& result : results) {
    int text_to_start_searching_from = FPDFText_GetTextIndexFromCharIndex(
        pages_[current_page]->GetTextPage(), character_to_start_searching_from);
    int temp_start = result.start_index + text_to_start_searching_from;
    int start = FPDFText_GetCharIndexFromTextIndex(
        pages_[current_page]->GetTextPage(), temp_start);
    int end = FPDFText_GetCharIndexFromTextIndex(
        pages_[current_page]->GetTextPage(), temp_start + result.length);

    if (temp_start + result.length == original_text_length) {
      DCHECK_EQ(-1, end);
      end = original_text_length;
    }
    DCHECK_LT(start, end);
    DCHECK_EQ(term.size(), static_cast<size_t>(end - start));
    AddFindResult(PDFiumRange(pages_[current_page].get(), start, end - start));
  }
}

Project: Chrome

bool PDFiumEngine::SelectFindResult(bool forward) {
  if (find_results_.empty()) {
    NOTREACHED();
    return false;
  }

  SelectionChangeInvalidator selection_invalidator(this);

  size_t new_index;
  const size_t last_index = find_results_.size() - 1;

  if (resume_find_index_) {
    new_index = resume_find_index_.value();
    resume_find_index_.reset();
  } else if (current_find_index_) {
    size_t current_index = current_find_index_.value();
    if ((forward && current_index >= last_index) ||
        (!forward && current_index == 0)) {
      current_find_index_.reset();
      client_->NotifySelectedFindResultChanged(-1);
      client_->NotifyNumberOfFindResultsChanged(find_results_.size(), true);
      return true;
    }
    int increment = forward ? 1 : -1;
    new_index = current_index + increment;
  } else {
    new_index = forward ? 0 : last_index;
  }
  current_find_index_ = new_index;

  selection_.clear();
  selection_.push_back(find_results_[current_find_index_.value()]);

  pp::Rect bounding_rect;
  pp::Rect visible_rect = GetVisibleRect();
  const std::vector<pp::Rect>& rects =
      find_results_[current_find_index_.value()].GetScreenRects(
          pp::Point(), 1.0, current_rotation_);
  for (const auto& rect : rects)
    bounding_rect = bounding_rect.Union(rect);
  if (!visible_rect.Contains(bounding_rect)) {
    pp::Point center = bounding_rect.CenterPoint();
    int new_y = CalculateCenterForZoom(center.y(), visible_rect.height(),
                                       current_zoom_);
    client_->ScrollToY(new_y, /*compensate_for_toolbar=*/false);

    if (center.x() < visible_rect.x() || center.x() > visible_rect.right()) {
      int new_x = CalculateCenterForZoom(center.x(), visible_rect.width(),
                                         current_zoom_);
      client_->ScrollToX(new_x);
    }
  }

  client_->NotifySelectedFindResultChanged(current_find_index_.value());
  client_->NotifyNumberOfFindResultsChanged(find_results_.size(), true);
  return true;
}

Project: Chrome

void PDFiumEngine::SetEditMode(bool edit_mode) {
  if (!kIsEditModeTracked || edit_mode_ == edit_mode)
    return;

  edit_mode_ = edit_mode;
  client_->IsEditModeChanged(edit_mode_);
}

Project: Chrome

void PDFiumEngine::StopFind() {
  SelectionChangeInvalidator selection_invalidator(this);
  selection_.clear();
  selecting_ = false;

  find_results_.clear();
  next_page_to_search_ = -1;
  last_page_to_search_ = -1;
  last_character_index_to_search_ = -1;
  current_find_index_.reset();
  current_find_text_.clear();

  UpdateTickMarks();
  find_factory_.CancelAll();
}

Project: Chrome

void PDFiumEngine::TransformPDFPageForPrinting(
    FPDF_PAGE page,
    const PP_PrintSettings_Dev& print_settings) {
  const double src_page_width = FPDF_GetPageWidth(page);
  const double src_page_height = FPDF_GetPageHeight(page);

  const int src_page_rotation = FPDFPage_GetRotation(page);
  const bool fit_to_page = print_settings.print_scaling_option ==
                           PP_PRINTSCALINGOPTION_FIT_TO_PRINTABLE_AREA;

  pp::Size page_size(print_settings.paper_size);
  pp::Rect content_rect(print_settings.printable_area);
  const bool rotated = (src_page_rotation % 2 == 1);
  SetPageSizeAndContentRect(rotated, src_page_width > src_page_height,
                            &page_size, &content_rect);

  const int actual_page_width =
      rotated ? page_size.height() : page_size.width();
  const int actual_page_height =
      rotated ? page_size.width() : page_size.height();

  const gfx::Rect gfx_content_rect(content_rect.x(), content_rect.y(),
                                   content_rect.width(), content_rect.height());
  const double scale_factor =
      fit_to_page ? CalculateScaleFactor(gfx_content_rect, src_page_width,
                                         src_page_height, rotated)
                  : 1.0;

  PdfRectangle media_box;
  PdfRectangle crop_box;
  bool has_media_box =
      !!FPDFPage_GetMediaBox(page, &media_box.left, &media_box.bottom,
                             &media_box.right, &media_box.top);
  bool has_crop_box = !!FPDFPage_GetCropBox(
      page, &crop_box.left, &crop_box.bottom, &crop_box.right, &crop_box.top);
  CalculateMediaBoxAndCropBox(rotated, has_media_box, has_crop_box, &media_box,
                              &crop_box);
  PdfRectangle source_clip_box = CalculateClipBoxBoundary(media_box, crop_box);
  ScalePdfRectangle(scale_factor, &source_clip_box);

  double offset_x = 0;
  double offset_y = 0;
  if (fit_to_page) {
    CalculateScaledClipBoxOffset(gfx_content_rect, source_clip_box, &offset_x,
                                 &offset_y);
  } else {
    CalculateNonScaledClipBoxOffset(gfx_content_rect, src_page_rotation,
                                    actual_page_width, actual_page_height,
                                    source_clip_box, &offset_x, &offset_y);
  }

  FPDFPage_SetMediaBox(page, 0, 0, page_size.width(), page_size.height());
  FPDFPage_SetCropBox(page, 0, 0, page_size.width(), page_size.height());

  if (scale_factor == 1.0 && offset_x == 0 && offset_y == 0)
    return;

  FS_MATRIX matrix = {static_cast<float>(scale_factor),
                      0,
                      0,
                      static_cast<float>(scale_factor),
                      static_cast<float>(offset_x),
                      static_cast<float>(offset_y)};
  FS_RECTF cliprect = {static_cast<float>(source_clip_box.left + offset_x),
                       static_cast<float>(source_clip_box.top + offset_y),
                       static_cast<float>(source_clip_box.right + offset_x),
                       static_cast<float>(source_clip_box.bottom + offset_y)};
  FPDFPage_TransFormWithClip(page, &matrix, &cliprect);
  FPDFPage_TransformAnnots(page, scale_factor, 0, 0, scale_factor, offset_x,
                           offset_y);
}

Project: Chrome

gfx::PointF PDFiumEngine::TransformPagePoint(int page_index,
                                             const gfx::PointF& page_xy) {
  DCHECK(PageIndexInBounds(page_index));
  return pages_[page_index]->TransformPageToScreenXY(page_xy);
}

Project: Chrome

pp::VarDictionary PDFiumEngine::TraverseBookmarks(FPDF_BOOKMARK bookmark,
                                                  unsigned int depth) {
  pp::VarDictionary dict;
  base::string16 title;
  unsigned long buffer_size = FPDFBookmark_GetTitle(bookmark, nullptr, 0);
  if (buffer_size > 0) {
    PDFiumAPIStringBufferSizeInBytesAdapter<base::string16> api_string_adapter(
        &title, buffer_size, true);
    api_string_adapter.Close(FPDFBookmark_GetTitle(
        bookmark, api_string_adapter.GetData(), buffer_size));
  }
  dict.Set(pp::Var("title"), pp::Var(base::UTF16ToUTF8(title)));

  FPDF_DEST dest = FPDFBookmark_GetDest(doc_, bookmark);
  if (dest) {
    int page_index = FPDFDest_GetDestPageIndex(doc_, dest);
    if (PageIndexInBounds(page_index)) {
      dict.Set(pp::Var("page"), pp::Var(page_index));

      base::Optional<gfx::PointF> xy =
          pages_[page_index]->GetPageXYTarget(dest);
      if (xy)
        dict.Set(pp::Var("y"), pp::Var(static_cast<int>(xy.value().y())));
    }
  } else {
    FPDF_ACTION action = FPDFBookmark_GetAction(bookmark);
    buffer_size = FPDFAction_GetURIPath(doc_, action, nullptr, 0);
    if (buffer_size > 0) {
      std::string uri;
      PDFiumAPIStringBufferAdapter<std::string> api_string_adapter(
          &uri, buffer_size, true);
      api_string_adapter.Close(FPDFAction_GetURIPath(
          doc_, action, api_string_adapter.GetData(), buffer_size));
      dict.Set(pp::Var("uri"), pp::Var(uri));
    }
  }

  pp::VarArray children;

  const unsigned int kMaxDepth = 128;
  if (depth < kMaxDepth) {
    int child_index = 0;
    std::set<FPDF_BOOKMARK> seen_bookmarks;
    for (FPDF_BOOKMARK child_bookmark =
             FPDFBookmark_GetFirstChild(doc_, bookmark);
         child_bookmark;
         child_bookmark = FPDFBookmark_GetNextSibling(doc_, child_bookmark)) {
      if (base::ContainsKey(seen_bookmarks, child_bookmark))
        break;

      seen_bookmarks.insert(child_bookmark);
      children.Set(child_index, TraverseBookmarks(child_bookmark, depth + 1));
      child_index++;
    }
  }
  dict.Set(pp::Var("children"), children);
  return dict;
}