blob: 5c32b77c16edbff78fe4ba90137b0574757fb187 [file] [log] [blame]
// Copyright 2018 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 "pdf/pdfium/pdfium_form_filler.h"
#include <algorithm>
#include <string>
#include <utility>
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "pdf/pdfium/pdfium_engine.h"
namespace chrome_pdf {
namespace {
std::string WideStringToString(FPDF_WIDESTRING wide_string) {
return base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(wide_string));
}
} // namespace
PDFiumFormFiller::PDFiumFormFiller(PDFiumEngine* engine, bool enable_javascript)
: engine_(engine) {
// Initialize FPDF_FORMFILLINFO member variables. Deriving from this struct
// allows the static callbacks to be able to cast the FPDF_FORMFILLINFO in
// callbacks to ourself instead of maintaining a map of them to
// PDFiumEngine.
FPDF_FORMFILLINFO::version = 1;
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 = Form_PageEvent;
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)
if (enable_javascript) {
FPDF_FORMFILLINFO::m_pJsPlatform = this;
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;
} else {
FPDF_FORMFILLINFO::m_pJsPlatform = nullptr;
}
}
PDFiumFormFiller::~PDFiumFormFiller() = default;
// static
void PDFiumFormFiller::Form_Invalidate(FPDF_FORMFILLINFO* param,
FPDF_PAGE page,
double left,
double top,
double right,
double bottom) {
PDFiumEngine* engine = GetEngine(param);
int page_index = engine->GetVisiblePageIndex(page);
if (page_index == -1) {
// This can sometime happen when the page is closed because it went off
// screen, and PDFium invalidates the control as it's being deleted.
return;
}
pp::Rect rect = engine->pages_[page_index]->PageToScreen(
engine->GetVisibleRect().point(), engine->current_zoom_, left, top, right,
bottom, engine->current_rotation_);
engine->client_->Invalidate(rect);
}
// static
void PDFiumFormFiller::Form_OutputSelectedRect(FPDF_FORMFILLINFO* param,
FPDF_PAGE page,
double left,
double top,
double right,
double bottom) {
PDFiumEngine* engine = GetEngine(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);
}
// static
void PDFiumFormFiller::Form_SetCursor(FPDF_FORMFILLINFO* param,
int cursor_type) {
// We don't need this since it's not enough to change the cursor in all
// scenarios. Instead, we check which form field we're under in OnMouseMove.
}
// static
int PDFiumFormFiller::Form_SetTimer(FPDF_FORMFILLINFO* param,
int elapse,
TimerCallback timer_func) {
auto* form_filler = static_cast<PDFiumFormFiller*>(param);
return form_filler->SetTimer(base::TimeDelta::FromMilliseconds(elapse),
timer_func);
}
// static
void PDFiumFormFiller::Form_KillTimer(FPDF_FORMFILLINFO* param, int timer_id) {
auto* form_filler = static_cast<PDFiumFormFiller*>(param);
form_filler->KillTimer(timer_id);
}
// static
FPDF_SYSTEMTIME PDFiumFormFiller::Form_GetLocalTime(FPDF_FORMFILLINFO* param) {
base::Time time = base::Time::Now();
base::Time::Exploded exploded;
time.LocalExplode(&exploded);
FPDF_SYSTEMTIME rv;
rv.wYear = exploded.year;
rv.wMonth = exploded.month;
rv.wDayOfWeek = exploded.day_of_week;
rv.wDay = exploded.day_of_month;
rv.wHour = exploded.hour;
rv.wMinute = exploded.minute;
rv.wSecond = exploded.second;
rv.wMilliseconds = exploded.millisecond;
return rv;
}
// static
void PDFiumFormFiller::Form_OnChange(FPDF_FORMFILLINFO* param) {
PDFiumEngine* engine = GetEngine(param);
engine->SetEditMode(true);
}
// static
FPDF_PAGE PDFiumFormFiller::Form_GetPage(FPDF_FORMFILLINFO* param,
FPDF_DOCUMENT document,
int page_index) {
PDFiumEngine* engine = GetEngine(param);
if (!engine->PageIndexInBounds(page_index))
return nullptr;
return engine->pages_[page_index]->GetPage();
}
// static
FPDF_PAGE PDFiumFormFiller::Form_GetCurrentPage(FPDF_FORMFILLINFO* param,
FPDF_DOCUMENT document) {
PDFiumEngine* engine = GetEngine(param);
int index = engine->last_page_mouse_down_;
if (index == -1) {
index = engine->GetMostVisiblePage();
if (index == -1) {
NOTREACHED();
return nullptr;
}
}
return engine->pages_[index]->GetPage();
}
// static
int PDFiumFormFiller::Form_GetRotation(FPDF_FORMFILLINFO* param,
FPDF_PAGE page) {
return 0;
}
// static
void PDFiumFormFiller::Form_ExecuteNamedAction(FPDF_FORMFILLINFO* param,
FPDF_BYTESTRING named_action) {
PDFiumEngine* engine = GetEngine(param);
std::string action(named_action);
if (action == "Print") {
engine->client_->Print();
return;
}
int index = engine->last_page_mouse_down_;
/* Don't try to calculate the most visible page if we don't have a left click
before this event (this code originally copied Form_GetCurrentPage which of
course needs to do that and which doesn't have recursion). This can end up
causing infinite recursion. See http://crbug.com/240413 for more
information. Either way, it's not necessary for the spec'd list of named
actions.
if (index == -1)
index = engine->GetMostVisiblePage();
*/
if (index == -1)
return;
// This is the only list of named actions per the spec (see 12.6.4.11). Adobe
// Reader supports more, like FitWidth, but since they're not part of the spec
// and we haven't got bugs about them, no need to now.
if (action == "NextPage") {
engine->ScrollToPage(index + 1);
} else if (action == "PrevPage") {
engine->ScrollToPage(index - 1);
} else if (action == "FirstPage") {
engine->ScrollToPage(0);
} else if (action == "LastPage") {
engine->ScrollToPage(engine->pages_.size() - 1);
}
}
// static
void PDFiumFormFiller::Form_SetTextFieldFocus(FPDF_FORMFILLINFO* param,
FPDF_WIDESTRING value,
FPDF_DWORD valueLen,
FPDF_BOOL is_focus) {
// Do nothing for now.
// TODO(gene): use this signal to trigger OSK.
}
// static
void PDFiumFormFiller::Form_DoURIAction(FPDF_FORMFILLINFO* param,
FPDF_BYTESTRING uri) {
PDFiumEngine* engine = GetEngine(param);
engine->client_->NavigateTo(std::string(uri),
WindowOpenDisposition::CURRENT_TAB);
}
// static
void PDFiumFormFiller::Form_DoGoToAction(FPDF_FORMFILLINFO* param,
int page_index,
int zoom_mode,
float* position_array,
int size_of_array) {
PDFiumEngine* engine = GetEngine(param);
engine->ScrollToPage(page_index);
}
#if defined(PDF_ENABLE_XFA)
// static
void PDFiumFormFiller::Form_EmailTo(FPDF_FORMFILLINFO* param,
FPDF_FILEHANDLER* file_handler,
FPDF_WIDESTRING to,
FPDF_WIDESTRING subject,
FPDF_WIDESTRING cc,
FPDF_WIDESTRING bcc,
FPDF_WIDESTRING message) {
std::string to_str = WideStringToString(to);
std::string subject_str = WideStringToString(subject);
std::string cc_str = WideStringToString(cc);
std::string bcc_str = WideStringToString(bcc);
std::string message_str = WideStringToString(message);
PDFiumEngine* engine = GetEngine(param);
engine->client_->Email(to_str, cc_str, bcc_str, subject_str, message_str);
}
// static
void PDFiumFormFiller::Form_DisplayCaret(FPDF_FORMFILLINFO* param,
FPDF_PAGE page,
FPDF_BOOL visible,
double left,
double top,
double right,
double bottom) {}
// static
void PDFiumFormFiller::Form_SetCurrentPage(FPDF_FORMFILLINFO* param,
FPDF_DOCUMENT document,
int page) {
PDFiumEngine* engine = GetEngine(param);
engine->ScrollToPage(page);
}
// static
int PDFiumFormFiller::Form_GetCurrentPageIndex(FPDF_FORMFILLINFO* param,
FPDF_DOCUMENT document) {
PDFiumEngine* engine = GetEngine(param);
return engine->GetMostVisiblePage();
}
// static
void PDFiumFormFiller::Form_GetPageViewRect(FPDF_FORMFILLINFO* param,
FPDF_PAGE page,
double* left,
double* top,
double* right,
double* bottom) {
PDFiumEngine* engine = GetEngine(param);
int page_index = engine->GetVisiblePageIndex(page);
if (!engine->PageIndexInBounds(page_index)) {
*left = 0;
*right = 0;
*top = 0;
*bottom = 0;
return;
}
pp::Rect page_view_rect = engine->GetPageContentsRect(page_index);
float toolbar_height_in_screen_coords =
engine->GetToolbarHeightInScreenCoords();
float page_width = FPDF_GetPageWidth(page);
float page_height = FPDF_GetPageHeight(page);
// To convert from a screen scale to a page scale, we multiply by
// (page_height / page_view_rect.height()) and
// (page_width / page_view_rect.width()),
// The base point of the page in screen coords is (page_view_rect.x(),
// page_view_rect.y()).
// Therefore, to convert an x position from screen to page
// coords, we use (page_width * (x - base_x) / page_view_rect.width()).
// For y positions, (page_height * (y - base_y) / page_view_rect.height()).
// The top-most y position that can be relied to be visible on the screen is
// the bottom of the toolbar, which is y = toolbar_height_in_screen_coords.
float screen_top_in_page_coords =
page_height * (toolbar_height_in_screen_coords - page_view_rect.y()) /
page_view_rect.height();
// The bottom-most y position that is visible on the screen is the bottom of
// the plugin area, which is y = engine->plugin_size_.height().
float screen_bottom_in_page_coords =
page_height * (engine->plugin_size_.height() - page_view_rect.y()) /
page_view_rect.height();
// The left-most x position that is visible on the screen is the left of the
// plugin area, which is x = 0.
float screen_left_in_page_coords =
page_width * (0 - page_view_rect.x()) / page_view_rect.width();
// The right-most x position that is visible on the screen is the right of the
// plugin area, which is x = engine->plugin_size_.width().
float screen_right_in_page_coords =
page_width * (engine->plugin_size_.width() - page_view_rect.x()) /
page_view_rect.width();
// Return the edge of the screen or of the page, since we're restricted to
// both.
*left = std::max(screen_left_in_page_coords, 0.0f);
*right = std::min(screen_right_in_page_coords, page_width);
*top = std::max(screen_top_in_page_coords, 0.0f);
*bottom = std::min(screen_bottom_in_page_coords, page_height);
}
// static
int PDFiumFormFiller::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;
}
// static
void PDFiumFormFiller::Form_PageEvent(FPDF_FORMFILLINFO* param,
int page_count,
unsigned long event_type) {
DCHECK(page_count != 0);
DCHECK(event_type == FXFA_PAGEVIEWEVENT_POSTADDED ||
event_type == FXFA_PAGEVIEWEVENT_POSTREMOVED);
PDFiumEngine* engine = GetEngine(param);
engine->UpdatePageCount();
}
// static
FPDF_BOOL PDFiumFormFiller::Form_PopupMenu(FPDF_FORMFILLINFO* param,
FPDF_PAGE page,
FPDF_WIDGET widget,
int menu_flag,
float x,
float y) {
return false;
}
// static
FPDF_BOOL PDFiumFormFiller::Form_PostRequestURL(FPDF_FORMFILLINFO* param,
FPDF_WIDESTRING url,
FPDF_WIDESTRING data,
FPDF_WIDESTRING content_type,
FPDF_WIDESTRING encode,
FPDF_WIDESTRING header,
FPDF_BSTR* response) {
std::string url_str = WideStringToString(url);
std::string data_str = WideStringToString(data);
std::string content_type_str = WideStringToString(content_type);
std::string encode_str = WideStringToString(encode);
std::string header_str = WideStringToString(header);
std::string javascript = "alert(\"Post:" + url_str + "," + data_str + "," +
content_type_str + "," + encode_str + "," +
header_str + "\")";
return true;
}
// static
FPDF_BOOL PDFiumFormFiller::Form_PutRequestURL(FPDF_FORMFILLINFO* param,
FPDF_WIDESTRING url,
FPDF_WIDESTRING data,
FPDF_WIDESTRING encode) {
std::string url_str = WideStringToString(url);
std::string data_str = WideStringToString(data);
std::string encode_str = WideStringToString(encode);
std::string javascript =
"alert(\"Put:" + url_str + "," + data_str + "," + encode_str + "\")";
return true;
}
// static
void PDFiumFormFiller::Form_UploadTo(FPDF_FORMFILLINFO* param,
FPDF_FILEHANDLER* file_handle,
int file_flag,
FPDF_WIDESTRING to) {
std::string to_str = WideStringToString(to);
// TODO: needs the full implementation of form uploading
}
// static
FPDF_LPFILEHANDLER PDFiumFormFiller::Form_DownloadFromURL(
FPDF_FORMFILLINFO* param,
FPDF_WIDESTRING url) {
// NOTE: Think hard about the security implications before allowing
// a PDF file to perform this action.
return nullptr;
}
// static
FPDF_FILEHANDLER* PDFiumFormFiller::Form_OpenFile(FPDF_FORMFILLINFO* param,
int file_flag,
FPDF_WIDESTRING url,
const char* mode) {
// NOTE: Think hard about the security implications before allowing
// a PDF file to perform this action.
return nullptr;
}
// static
void PDFiumFormFiller::Form_GotoURL(FPDF_FORMFILLINFO* param,
FPDF_DOCUMENT document,
FPDF_WIDESTRING url) {
std::string url_str = WideStringToString(url);
// TODO: needs to implement GOTO URL action
}
// static
int PDFiumFormFiller::Form_GetLanguage(FPDF_FORMFILLINFO* param,
void* language,
int length) {
return 0;
}
#endif // defined(PDF_ENABLE_XFA)
// static
int PDFiumFormFiller::Form_Alert(IPDF_JSPLATFORM* param,
FPDF_WIDESTRING message,
FPDF_WIDESTRING title,
int type,
int icon) {
// See fpdfformfill.h for these values.
enum AlertType {
ALERT_TYPE_OK = 0,
ALERT_TYPE_OK_CANCEL,
ALERT_TYPE_YES_ON,
ALERT_TYPE_YES_NO_CANCEL
};
enum AlertResult {
ALERT_RESULT_OK = 1,
ALERT_RESULT_CANCEL,
ALERT_RESULT_NO,
ALERT_RESULT_YES
};
PDFiumEngine* engine = GetEngine(param);
std::string message_str = WideStringToString(message);
if (type == ALERT_TYPE_OK) {
engine->client_->Alert(message_str);
return ALERT_RESULT_OK;
}
bool rv = engine->client_->Confirm(message_str);
if (type == ALERT_TYPE_OK_CANCEL)
return rv ? ALERT_RESULT_OK : ALERT_RESULT_CANCEL;
return rv ? ALERT_RESULT_YES : ALERT_RESULT_NO;
}
// static
void PDFiumFormFiller::Form_Beep(IPDF_JSPLATFORM* param, int type) {
PDFiumEngine* engine = GetEngine(param);
engine->client_->Beep();
}
// static
int PDFiumFormFiller::Form_Response(IPDF_JSPLATFORM* param,
FPDF_WIDESTRING question,
FPDF_WIDESTRING title,
FPDF_WIDESTRING default_response,
FPDF_WIDESTRING label,
FPDF_BOOL password,
void* response,
int length) {
std::string question_str = WideStringToString(question);
std::string default_str = WideStringToString(default_response);
PDFiumEngine* engine = GetEngine(param);
std::string rv = engine->client_->Prompt(question_str, default_str);
base::string16 rv_16 = base::UTF8ToUTF16(rv);
int rv_bytes = rv_16.size() * sizeof(base::char16);
if (response) {
int bytes_to_copy = rv_bytes < length ? rv_bytes : length;
memcpy(response, rv_16.c_str(), bytes_to_copy);
}
return rv_bytes;
}
// static
int PDFiumFormFiller::Form_GetFilePath(IPDF_JSPLATFORM* param,
void* file_path,
int length) {
PDFiumEngine* engine = GetEngine(param);
std::string rv = engine->client_->GetURL();
if (file_path && rv.size() <= static_cast<size_t>(length))
memcpy(file_path, rv.c_str(), rv.size());
return rv.size();
}
// static
void PDFiumFormFiller::Form_Mail(IPDF_JSPLATFORM* param,
void* mail_data,
int length,
FPDF_BOOL ui,
FPDF_WIDESTRING to,
FPDF_WIDESTRING subject,
FPDF_WIDESTRING cc,
FPDF_WIDESTRING bcc,
FPDF_WIDESTRING message) {
// Note: |mail_data| and |length| are ignored. We don't handle attachments;
// there is no way with mailto.
std::string to_str = WideStringToString(to);
std::string cc_str = WideStringToString(cc);
std::string bcc_str = WideStringToString(bcc);
std::string subject_str = WideStringToString(subject);
std::string message_str = WideStringToString(message);
PDFiumEngine* engine = GetEngine(param);
engine->client_->Email(to_str, cc_str, bcc_str, subject_str, message_str);
}
// static
void PDFiumFormFiller::Form_Print(IPDF_JSPLATFORM* param,
FPDF_BOOL ui,
int start,
int end,
FPDF_BOOL silent,
FPDF_BOOL shrink_to_fit,
FPDF_BOOL print_as_image,
FPDF_BOOL reverse,
FPDF_BOOL annotations) {
// No way to pass the extra information to the print dialog using JavaScript.
// Just opening it is fine for now.
PDFiumEngine* engine = GetEngine(param);
engine->client_->Print();
}
// static
void PDFiumFormFiller::Form_SubmitForm(IPDF_JSPLATFORM* param,
void* form_data,
int length,
FPDF_WIDESTRING url) {
std::string url_str = WideStringToString(url);
PDFiumEngine* engine = GetEngine(param);
engine->client_->SubmitForm(url_str, form_data, length);
}
// static
void PDFiumFormFiller::Form_GotoPage(IPDF_JSPLATFORM* param, int page_number) {
PDFiumEngine* engine = GetEngine(param);
engine->ScrollToPage(page_number);
}
// static
PDFiumEngine* PDFiumFormFiller::GetEngine(FPDF_FORMFILLINFO* info) {
auto* form_filler = static_cast<PDFiumFormFiller*>(info);
return form_filler->engine_;
}
// static
PDFiumEngine* PDFiumFormFiller::GetEngine(IPDF_JSPLATFORM* platform) {
auto* form_filler = static_cast<PDFiumFormFiller*>(platform);
return form_filler->engine_;
}
int PDFiumFormFiller::SetTimer(const base::TimeDelta& delay,
TimerCallback timer_func) {
const int timer_id = ++last_timer_id_;
auto timer = std::make_unique<base::RepeatingTimer>();
timer->Start(FROM_HERE, delay, base::BindRepeating(timer_func, timer_id));
timers_[timer_id] = std::move(timer);
return timer_id;
}
void PDFiumFormFiller::KillTimer(int timer_id) {
timers_.erase(timer_id);
}
} // namespace chrome_pdf