Home | History | Annotate | Download | only in pdf
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "pdf/out_of_process_instance.h"
      6 
      7 #include <algorithm>  // for min/max()
      8 #define _USE_MATH_DEFINES  // for M_PI
      9 #include <cmath>      // for log() and pow()
     10 #include <math.h>
     11 #include <list>
     12 
     13 #include "base/json/json_reader.h"
     14 #include "base/json/json_writer.h"
     15 #include "base/logging.h"
     16 #include "base/strings/string_number_conversions.h"
     17 #include "base/strings/string_split.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/values.h"
     20 #include "chrome/common/content_restriction.h"
     21 #include "net/base/escape.h"
     22 #include "pdf/draw_utils.h"
     23 #include "pdf/pdf.h"
     24 #include "ppapi/c/dev/ppb_cursor_control_dev.h"
     25 #include "ppapi/c/pp_errors.h"
     26 #include "ppapi/c/pp_rect.h"
     27 #include "ppapi/c/private/ppb_instance_private.h"
     28 #include "ppapi/c/private/ppp_pdf.h"
     29 #include "ppapi/c/trusted/ppb_url_loader_trusted.h"
     30 #include "ppapi/cpp/core.h"
     31 #include "ppapi/cpp/dev/memory_dev.h"
     32 #include "ppapi/cpp/dev/text_input_dev.h"
     33 #include "ppapi/cpp/dev/url_util_dev.h"
     34 #include "ppapi/cpp/module.h"
     35 #include "ppapi/cpp/point.h"
     36 #include "ppapi/cpp/private/pdf.h"
     37 #include "ppapi/cpp/rect.h"
     38 #include "ppapi/cpp/resource.h"
     39 #include "ppapi/cpp/url_request_info.h"
     40 #include "ppapi/cpp/var_array.h"
     41 #include "ppapi/cpp/var_dictionary.h"
     42 #include "ui/events/keycodes/keyboard_codes.h"
     43 
     44 #if defined(OS_MACOSX)
     45 #include "base/mac/mac_util.h"
     46 #endif
     47 
     48 namespace chrome_pdf {
     49 
     50 // URL reference parameters.
     51 // For more possible parameters, see RFC 3778 and the "PDF Open Parameters"
     52 // document from Adobe.
     53 const char kDelimiters[] = "#&";
     54 const char kNamedDest[] = "nameddest";
     55 const char kPage[] = "page";
     56 
     57 const char kChromePrint[] = "chrome://print/";
     58 const char kChromeExtension[] =
     59     "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai";
     60 
     61 // Dictionary Value key names for the document accessibility info
     62 const char kAccessibleNumberOfPages[] = "numberOfPages";
     63 const char kAccessibleLoaded[] = "loaded";
     64 const char kAccessibleCopyable[] = "copyable";
     65 
     66 // Constants used in handling postMessage() messages.
     67 const char* kType = "type";
     68 // Viewport message arguments. (Page -> Plugin).
     69 const char* kJSViewportType = "viewport";
     70 const char* kJSXOffset = "xOffset";
     71 const char* kJSYOffset = "yOffset";
     72 const char* kJSZoom = "zoom";
     73 // Document dimension arguments (Plugin -> Page).
     74 const char* kJSDocumentDimensionsType = "documentDimensions";
     75 const char* kJSDocumentWidth = "width";
     76 const char* kJSDocumentHeight = "height";
     77 const char* kJSPageDimensions = "pageDimensions";
     78 const char* kJSPageX = "x";
     79 const char* kJSPageY = "y";
     80 const char* kJSPageWidth = "width";
     81 const char* kJSPageHeight = "height";
     82 // Document load progress arguments (Plugin -> Page)
     83 const char* kJSLoadProgressType = "loadProgress";
     84 const char* kJSProgressPercentage = "progress";
     85 // Get password arguments (Plugin -> Page)
     86 const char* kJSGetPasswordType = "getPassword";
     87 // Get password complete arguments (Page -> Plugin)
     88 const char* kJSGetPasswordCompleteType = "getPasswordComplete";
     89 const char* kJSPassword = "password";
     90 // Print (Page -> Plugin)
     91 const char* kJSPrintType = "print";
     92 // Go to page (Plugin -> Page)
     93 const char* kJSGoToPageType = "goToPage";
     94 const char* kJSPageNumber = "page";
     95 // Reset print preview mode (Page -> Plugin)
     96 const char* kJSResetPrintPreviewModeType = "resetPrintPreviewMode";
     97 const char* kJSPrintPreviewUrl = "url";
     98 const char* kJSPrintPreviewGrayscale = "grayscale";
     99 const char* kJSPrintPreviewPageCount = "pageCount";
    100 // Load preview page (Page -> Plugin)
    101 const char* kJSLoadPreviewPageType = "loadPreviewPage";
    102 const char* kJSPreviewPageUrl = "url";
    103 const char* kJSPreviewPageIndex = "index";
    104 // Set scroll position (Plugin -> Page)
    105 const char* kJSSetScrollPositionType = "setScrollPosition";
    106 const char* kJSPositionX = "x";
    107 const char* kJSPositionY = "y";
    108 // Set translated strings (Plugin -> Page)
    109 const char* kJSSetTranslatedStringsType = "setTranslatedStrings";
    110 const char* kJSGetPasswordString = "getPasswordString";
    111 const char* kJSLoadingString = "loadingString";
    112 const char* kJSLoadFailedString = "loadFailedString";
    113 // Request accessibility JSON data (Page -> Plugin)
    114 const char* kJSGetAccessibilityJSONType = "getAccessibilityJSON";
    115 const char* kJSAccessibilityPageNumber = "page";
    116 // Reply with accessibility JSON data (Plugin -> Page)
    117 const char* kJSGetAccessibilityJSONReplyType = "getAccessibilityJSONReply";
    118 const char* kJSAccessibilityJSON = "json";
    119 // Cancel the stream URL request (Plugin -> Page)
    120 const char* kJSCancelStreamUrlType = "cancelStreamUrl";
    121 // Navigate to the given URL (Plugin -> Page)
    122 const char* kJSNavigateType = "navigate";
    123 const char* kJSNavigateUrl = "url";
    124 const char* kJSNavigateNewTab = "newTab";
    125 // Open the email editor with the given parameters (Plugin -> Page)
    126 const char* kJSEmailType = "email";
    127 const char* kJSEmailTo = "to";
    128 const char* kJSEmailCc = "cc";
    129 const char* kJSEmailBcc = "bcc";
    130 const char* kJSEmailSubject = "subject";
    131 const char* kJSEmailBody = "body";
    132 
    133 const int kFindResultCooldownMs = 100;
    134 
    135 const double kMinZoom = 0.01;
    136 
    137 namespace {
    138 
    139 static const char kPPPPdfInterface[] = PPP_PDF_INTERFACE_1;
    140 
    141 PP_Var GetLinkAtPosition(PP_Instance instance, PP_Point point) {
    142   pp::Var var;
    143   void* object = pp::Instance::GetPerInstanceObject(instance, kPPPPdfInterface);
    144   if (object) {
    145     var = static_cast<OutOfProcessInstance*>(object)->GetLinkAtPosition(
    146         pp::Point(point));
    147   }
    148   return var.Detach();
    149 }
    150 
    151 void Transform(PP_Instance instance, PP_PrivatePageTransformType type) {
    152   void* object =
    153       pp::Instance::GetPerInstanceObject(instance, kPPPPdfInterface);
    154   if (object) {
    155     OutOfProcessInstance* obj_instance =
    156         static_cast<OutOfProcessInstance*>(object);
    157     switch (type) {
    158       case PP_PRIVATEPAGETRANSFORMTYPE_ROTATE_90_CW:
    159         obj_instance->RotateClockwise();
    160         break;
    161       case PP_PRIVATEPAGETRANSFORMTYPE_ROTATE_90_CCW:
    162         obj_instance->RotateCounterclockwise();
    163         break;
    164     }
    165   }
    166 }
    167 
    168 const PPP_Pdf ppp_private = {
    169   &GetLinkAtPosition,
    170   &Transform
    171 };
    172 
    173 int ExtractPrintPreviewPageIndex(const std::string& src_url) {
    174   // Sample |src_url| format: chrome://print/id/page_index/print.pdf
    175   std::vector<std::string> url_substr;
    176   base::SplitString(src_url.substr(strlen(kChromePrint)), '/', &url_substr);
    177   if (url_substr.size() != 3)
    178     return -1;
    179 
    180   if (url_substr[2] != "print.pdf")
    181     return -1;
    182 
    183   int page_index = 0;
    184   if (!base::StringToInt(url_substr[1], &page_index))
    185     return -1;
    186   return page_index;
    187 }
    188 
    189 bool IsPrintPreviewUrl(const std::string& url) {
    190   return url.substr(0, strlen(kChromePrint)) == kChromePrint;
    191 }
    192 
    193 void ScalePoint(float scale, pp::Point* point) {
    194   point->set_x(static_cast<int>(point->x() * scale));
    195   point->set_y(static_cast<int>(point->y() * scale));
    196 }
    197 
    198 void ScaleRect(float scale, pp::Rect* rect) {
    199   int left = static_cast<int>(floorf(rect->x() * scale));
    200   int top = static_cast<int>(floorf(rect->y() * scale));
    201   int right = static_cast<int>(ceilf((rect->x() + rect->width()) * scale));
    202   int bottom = static_cast<int>(ceilf((rect->y() + rect->height()) * scale));
    203   rect->SetRect(left, top, right - left, bottom - top);
    204 }
    205 
    206 // TODO(raymes): Remove this dependency on VarPrivate/InstancePrivate. It's
    207 // needed right now to do a synchronous call to JavaScript, but we could easily
    208 // replace this with a custom PPB_PDF function.
    209 pp::Var ModalDialog(const pp::Instance* instance,
    210                     const std::string& type,
    211                     const std::string& message,
    212                     const std::string& default_answer) {
    213   const PPB_Instance_Private* interface =
    214       reinterpret_cast<const PPB_Instance_Private*>(
    215           pp::Module::Get()->GetBrowserInterface(
    216               PPB_INSTANCE_PRIVATE_INTERFACE));
    217   pp::VarPrivate window(pp::PASS_REF,
    218       interface->GetWindowObject(instance->pp_instance()));
    219   if (default_answer.empty())
    220     return window.Call(type, message);
    221   else
    222     return window.Call(type, message, default_answer);
    223 }
    224 
    225 }  // namespace
    226 
    227 OutOfProcessInstance::OutOfProcessInstance(PP_Instance instance)
    228     : pp::Instance(instance),
    229       pp::Find_Private(this),
    230       pp::Printing_Dev(this),
    231       pp::Selection_Dev(this),
    232       cursor_(PP_CURSORTYPE_POINTER),
    233       zoom_(1.0),
    234       device_scale_(1.0),
    235       printing_enabled_(true),
    236       full_(false),
    237       paint_manager_(this, this, true),
    238       first_paint_(true),
    239       document_load_state_(LOAD_STATE_LOADING),
    240       preview_document_load_state_(LOAD_STATE_COMPLETE),
    241       uma_(this),
    242       told_browser_about_unsupported_feature_(false),
    243       print_preview_page_count_(0),
    244       last_progress_sent_(0),
    245       recently_sent_find_update_(false),
    246       received_viewport_message_(false),
    247       did_call_start_loading_(false) {
    248   loader_factory_.Initialize(this);
    249   timer_factory_.Initialize(this);
    250   form_factory_.Initialize(this);
    251   print_callback_factory_.Initialize(this);
    252   engine_.reset(PDFEngine::Create(this));
    253   pp::Module::Get()->AddPluginInterface(kPPPPdfInterface, &ppp_private);
    254   AddPerInstanceObject(kPPPPdfInterface, this);
    255 
    256   RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
    257   RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
    258   RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_TOUCH);
    259 }
    260 
    261 OutOfProcessInstance::~OutOfProcessInstance() {
    262   RemovePerInstanceObject(kPPPPdfInterface, this);
    263 }
    264 
    265 bool OutOfProcessInstance::Init(uint32_t argc,
    266                                 const char* argn[],
    267                                 const char* argv[]) {
    268   // Check if the PDF is being loaded in the PDF chrome extension. We only allow
    269   // the plugin to be put into "full frame" mode when it is being loaded in the
    270   // extension because this enables some features that we don't want pages
    271   // abusing outside of the extension.
    272   pp::Var document_url_var = pp::URLUtil_Dev::Get()->GetDocumentURL(this);
    273   std::string document_url = document_url_var.is_string() ?
    274       document_url_var.AsString() : std::string();
    275   std::string extension_url = std::string(kChromeExtension);
    276   bool in_extension =
    277       !document_url.compare(0, extension_url.size(), extension_url);
    278 
    279   if (in_extension) {
    280     // Check if the plugin is full frame. This is passed in from JS.
    281     for (uint32_t i = 0; i < argc; ++i) {
    282       if (strcmp(argn[i], "full-frame") == 0) {
    283         full_ = true;
    284         break;
    285       }
    286     }
    287   }
    288 
    289   // Only allow the plugin to handle find requests if it is full frame.
    290   if (full_)
    291     SetPluginToHandleFindRequests();
    292 
    293   // Send translated strings to the extension where they will be displayed.
    294   // TODO(raymes): It would be better to get these in the extension directly
    295   // through an API but no such API currently exists.
    296   pp::VarDictionary translated_strings;
    297   translated_strings.Set(kType, kJSSetTranslatedStringsType);
    298   translated_strings.Set(kJSGetPasswordString,
    299       GetLocalizedString(PP_RESOURCESTRING_PDFGETPASSWORD));
    300   translated_strings.Set(kJSLoadingString,
    301       GetLocalizedString(PP_RESOURCESTRING_PDFLOADING));
    302   translated_strings.Set(kJSLoadFailedString,
    303       GetLocalizedString(PP_RESOURCESTRING_PDFLOAD_FAILED));
    304   PostMessage(translated_strings);
    305 
    306   text_input_.reset(new pp::TextInput_Dev(this));
    307 
    308   const char* stream_url = NULL;
    309   const char* original_url = NULL;
    310   const char* headers = NULL;
    311   for (uint32_t i = 0; i < argc; ++i) {
    312     if (strcmp(argn[i], "src") == 0)
    313       original_url = argv[i];
    314     else if (strcmp(argn[i], "stream-url") == 0)
    315       stream_url = argv[i];
    316     else if (strcmp(argn[i], "headers") == 0)
    317       headers = argv[i];
    318   }
    319 
    320   // TODO(raymes): This is a hack to ensure that if no headers are passed in
    321   // then we get the right MIME type. When the in process plugin is removed we
    322   // can fix the document loader properly and remove this hack.
    323   if (!headers || strcmp(headers, "") == 0)
    324     headers = "content-type: application/pdf";
    325 
    326   if (!original_url)
    327     return false;
    328 
    329   if (!stream_url)
    330     stream_url = original_url;
    331 
    332   // If we're in print preview mode we don't need to load the document yet.
    333   // A |kJSResetPrintPreviewModeType| message will be sent to the plugin letting
    334   // it know the url to load. By not loading here we avoid loading the same
    335   // document twice.
    336   if (IsPrintPreviewUrl(original_url))
    337     return true;
    338 
    339   LoadUrl(stream_url);
    340   url_ = original_url;
    341   return engine_->New(original_url, headers);
    342 }
    343 
    344 void OutOfProcessInstance::HandleMessage(const pp::Var& message) {
    345   pp::VarDictionary dict(message);
    346   if (!dict.Get(kType).is_string()) {
    347     NOTREACHED();
    348     return;
    349   }
    350 
    351   std::string type = dict.Get(kType).AsString();
    352 
    353   if (type == kJSViewportType &&
    354       dict.Get(pp::Var(kJSXOffset)).is_int() &&
    355       dict.Get(pp::Var(kJSYOffset)).is_int() &&
    356       dict.Get(pp::Var(kJSZoom)).is_number()) {
    357     received_viewport_message_ = true;
    358     double zoom = dict.Get(pp::Var(kJSZoom)).AsDouble();
    359     int x = dict.Get(pp::Var(kJSXOffset)).AsInt();
    360     int y = dict.Get(pp::Var(kJSYOffset)).AsInt();
    361 
    362     // Bound the input parameters.
    363     zoom = std::max(kMinZoom, zoom);
    364     int max_x = document_size_.width() * zoom - plugin_dip_size_.width();
    365     x = std::max(std::min(x, max_x), 0);
    366     int max_y = document_size_.height() * zoom - plugin_dip_size_.height();
    367     y = std::max(std::min(y, max_y), 0);
    368 
    369     SetZoom(zoom);
    370     engine_->ScrolledToXPosition(x * device_scale_);
    371     engine_->ScrolledToYPosition(y * device_scale_);
    372   } else if (type == kJSGetPasswordCompleteType &&
    373              dict.Get(pp::Var(kJSPassword)).is_string()) {
    374     if (password_callback_) {
    375       pp::CompletionCallbackWithOutput<pp::Var> callback = *password_callback_;
    376       password_callback_.reset();
    377       *callback.output() = dict.Get(pp::Var(kJSPassword)).pp_var();
    378       callback.Run(PP_OK);
    379     } else {
    380       NOTREACHED();
    381     }
    382   } else if (type == kJSPrintType) {
    383     Print();
    384   } else if (type == kJSResetPrintPreviewModeType &&
    385              dict.Get(pp::Var(kJSPrintPreviewUrl)).is_string() &&
    386              dict.Get(pp::Var(kJSPrintPreviewGrayscale)).is_bool() &&
    387              dict.Get(pp::Var(kJSPrintPreviewPageCount)).is_int()) {
    388     url_ = dict.Get(pp::Var(kJSPrintPreviewUrl)).AsString();
    389     preview_pages_info_ = std::queue<PreviewPageInfo>();
    390     preview_document_load_state_ = LOAD_STATE_COMPLETE;
    391     document_load_state_ = LOAD_STATE_LOADING;
    392     LoadUrl(url_);
    393     preview_engine_.reset();
    394     engine_.reset(PDFEngine::Create(this));
    395     engine_->SetGrayscale(dict.Get(pp::Var(kJSPrintPreviewGrayscale)).AsBool());
    396     engine_->New(url_.c_str());
    397 
    398     print_preview_page_count_ =
    399         std::max(dict.Get(pp::Var(kJSPrintPreviewPageCount)).AsInt(), 0);
    400 
    401     paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_));
    402   } else if (type == kJSLoadPreviewPageType &&
    403              dict.Get(pp::Var(kJSPreviewPageUrl)).is_string() &&
    404              dict.Get(pp::Var(kJSPreviewPageIndex)).is_int()) {
    405     ProcessPreviewPageInfo(dict.Get(pp::Var(kJSPreviewPageUrl)).AsString(),
    406                            dict.Get(pp::Var(kJSPreviewPageIndex)).AsInt());
    407   } else if (type == kJSGetAccessibilityJSONType) {
    408     pp::VarDictionary reply;
    409     reply.Set(pp::Var(kType), pp::Var(kJSGetAccessibilityJSONReplyType));
    410     if (dict.Get(pp::Var(kJSAccessibilityPageNumber)).is_int()) {
    411       int page = dict.Get(pp::Var(kJSAccessibilityPageNumber)).AsInt();
    412       reply.Set(pp::Var(kJSAccessibilityJSON),
    413                         pp::Var(engine_->GetPageAsJSON(page)));
    414     } else {
    415       base::DictionaryValue node;
    416       node.SetInteger(kAccessibleNumberOfPages, engine_->GetNumberOfPages());
    417       node.SetBoolean(kAccessibleLoaded,
    418                       document_load_state_ != LOAD_STATE_LOADING);
    419       bool has_permissions =
    420           engine_->HasPermission(PDFEngine::PERMISSION_COPY) ||
    421           engine_->HasPermission(PDFEngine::PERMISSION_COPY_ACCESSIBLE);
    422       node.SetBoolean(kAccessibleCopyable, has_permissions);
    423       std::string json;
    424       base::JSONWriter::Write(&node, &json);
    425       reply.Set(pp::Var(kJSAccessibilityJSON), pp::Var(json));
    426     }
    427     PostMessage(reply);
    428   } else {
    429     NOTREACHED();
    430   }
    431 }
    432 
    433 bool OutOfProcessInstance::HandleInputEvent(
    434     const pp::InputEvent& event) {
    435   // To simplify things, convert the event into device coordinates if it is
    436   // a mouse event.
    437   pp::InputEvent event_device_res(event);
    438   {
    439     pp::MouseInputEvent mouse_event(event);
    440     if (!mouse_event.is_null()) {
    441       pp::Point point = mouse_event.GetPosition();
    442       pp::Point movement = mouse_event.GetMovement();
    443       ScalePoint(device_scale_, &point);
    444       ScalePoint(device_scale_, &movement);
    445       mouse_event = pp::MouseInputEvent(
    446           this,
    447           event.GetType(),
    448           event.GetTimeStamp(),
    449           event.GetModifiers(),
    450           mouse_event.GetButton(),
    451           point,
    452           mouse_event.GetClickCount(),
    453           movement);
    454       event_device_res = mouse_event;
    455     }
    456   }
    457 
    458   pp::InputEvent offset_event(event_device_res);
    459   switch (offset_event.GetType()) {
    460     case PP_INPUTEVENT_TYPE_MOUSEDOWN:
    461     case PP_INPUTEVENT_TYPE_MOUSEUP:
    462     case PP_INPUTEVENT_TYPE_MOUSEMOVE:
    463     case PP_INPUTEVENT_TYPE_MOUSEENTER:
    464     case PP_INPUTEVENT_TYPE_MOUSELEAVE: {
    465       pp::MouseInputEvent mouse_event(event_device_res);
    466       pp::MouseInputEvent mouse_event_dip(event);
    467       pp::Point point = mouse_event.GetPosition();
    468       point.set_x(point.x() - available_area_.x());
    469       offset_event = pp::MouseInputEvent(
    470           this,
    471           event.GetType(),
    472           event.GetTimeStamp(),
    473           event.GetModifiers(),
    474           mouse_event.GetButton(),
    475           point,
    476           mouse_event.GetClickCount(),
    477           mouse_event.GetMovement());
    478       break;
    479     }
    480     default:
    481       break;
    482   }
    483   if (engine_->HandleEvent(offset_event))
    484     return true;
    485 
    486   // TODO(raymes): Implement this scroll behavior in JS:
    487   // When click+dragging, scroll the document correctly.
    488 
    489   if (event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN &&
    490       event.GetModifiers() & kDefaultKeyModifier) {
    491     pp::KeyboardInputEvent keyboard_event(event);
    492     switch (keyboard_event.GetKeyCode()) {
    493       case 'A':
    494         engine_->SelectAll();
    495         return true;
    496     }
    497   }
    498 
    499   // Return true for unhandled clicks so the plugin takes focus.
    500   return (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN);
    501 }
    502 
    503 void OutOfProcessInstance::DidChangeView(const pp::View& view) {
    504   pp::Rect view_rect(view.GetRect());
    505   float old_device_scale = device_scale_;
    506   float device_scale = view.GetDeviceScale();
    507   pp::Size view_device_size(view_rect.width() * device_scale,
    508                             view_rect.height() * device_scale);
    509 
    510   if (view_device_size == plugin_size_ && device_scale == device_scale_)
    511     return; // We don't care about the position, only the size.
    512 
    513   device_scale_ = device_scale;
    514   plugin_dip_size_ = view_rect.size();
    515   plugin_size_ = view_device_size;
    516 
    517   paint_manager_.SetSize(view_device_size, device_scale_);
    518 
    519   pp::Size new_image_data_size = PaintManager::GetNewContextSize(
    520       image_data_.size(),
    521       plugin_size_);
    522   if (new_image_data_size != image_data_.size()) {
    523     image_data_ = pp::ImageData(this,
    524                                 PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    525                                 new_image_data_size,
    526                                 false);
    527     first_paint_ = true;
    528   }
    529 
    530   if (image_data_.is_null()) {
    531     DCHECK(plugin_size_.IsEmpty());
    532     return;
    533   }
    534 
    535   OnGeometryChanged(zoom_, old_device_scale);
    536 }
    537 
    538 pp::Var OutOfProcessInstance::GetLinkAtPosition(
    539     const pp::Point& point) {
    540   pp::Point offset_point(point);
    541   ScalePoint(device_scale_, &offset_point);
    542   offset_point.set_x(offset_point.x() - available_area_.x());
    543   return engine_->GetLinkAtPosition(offset_point);
    544 }
    545 
    546 pp::Var OutOfProcessInstance::GetSelectedText(bool html) {
    547   if (html || !engine_->HasPermission(PDFEngine::PERMISSION_COPY))
    548     return pp::Var();
    549   return engine_->GetSelectedText();
    550 }
    551 
    552 uint32_t OutOfProcessInstance::QuerySupportedPrintOutputFormats() {
    553   return engine_->QuerySupportedPrintOutputFormats();
    554 }
    555 
    556 int32_t OutOfProcessInstance::PrintBegin(
    557     const PP_PrintSettings_Dev& print_settings) {
    558   // For us num_pages is always equal to the number of pages in the PDF
    559   // document irrespective of the printable area.
    560   int32_t ret = engine_->GetNumberOfPages();
    561   if (!ret)
    562     return 0;
    563 
    564   uint32_t supported_formats = engine_->QuerySupportedPrintOutputFormats();
    565   if ((print_settings.format & supported_formats) == 0)
    566     return 0;
    567 
    568   print_settings_.is_printing = true;
    569   print_settings_.pepper_print_settings = print_settings;
    570   engine_->PrintBegin();
    571   return ret;
    572 }
    573 
    574 pp::Resource OutOfProcessInstance::PrintPages(
    575     const PP_PrintPageNumberRange_Dev* page_ranges,
    576     uint32_t page_range_count) {
    577   if (!print_settings_.is_printing)
    578     return pp::Resource();
    579 
    580   print_settings_.print_pages_called_ = true;
    581   return engine_->PrintPages(page_ranges, page_range_count,
    582                              print_settings_.pepper_print_settings);
    583 }
    584 
    585 void OutOfProcessInstance::PrintEnd() {
    586   if (print_settings_.print_pages_called_)
    587     UserMetricsRecordAction("PDF.PrintPage");
    588   print_settings_.Clear();
    589   engine_->PrintEnd();
    590 }
    591 
    592 bool OutOfProcessInstance::IsPrintScalingDisabled() {
    593   return !engine_->GetPrintScaling();
    594 }
    595 
    596 bool OutOfProcessInstance::StartFind(const std::string& text,
    597                                                  bool case_sensitive) {
    598   engine_->StartFind(text.c_str(), case_sensitive);
    599   return true;
    600 }
    601 
    602 void OutOfProcessInstance::SelectFindResult(bool forward) {
    603   engine_->SelectFindResult(forward);
    604 }
    605 
    606 void OutOfProcessInstance::StopFind() {
    607   engine_->StopFind();
    608   tickmarks_.clear();
    609   SetTickmarks(tickmarks_);
    610 }
    611 
    612 void OutOfProcessInstance::OnPaint(
    613     const std::vector<pp::Rect>& paint_rects,
    614     std::vector<PaintManager::ReadyRect>* ready,
    615     std::vector<pp::Rect>* pending) {
    616   if (image_data_.is_null()) {
    617     DCHECK(plugin_size_.IsEmpty());
    618     return;
    619   }
    620   if (first_paint_) {
    621     first_paint_ = false;
    622     pp::Rect rect = pp::Rect(pp::Point(), image_data_.size());
    623     unsigned int color = kBackgroundColorA << 24 |
    624                          kBackgroundColorR << 16 |
    625                          kBackgroundColorG << 8 |
    626                          kBackgroundColorB;
    627     FillRect(rect, color);
    628     ready->push_back(PaintManager::ReadyRect(rect, image_data_, true));
    629   }
    630 
    631   if (!received_viewport_message_)
    632     return;
    633 
    634   engine_->PrePaint();
    635 
    636   for (size_t i = 0; i < paint_rects.size(); i++) {
    637     // Intersect with plugin area since there could be pending invalidates from
    638     // when the plugin area was larger.
    639     pp::Rect rect =
    640         paint_rects[i].Intersect(pp::Rect(pp::Point(), plugin_size_));
    641     if (rect.IsEmpty())
    642       continue;
    643 
    644     pp::Rect pdf_rect = available_area_.Intersect(rect);
    645     if (!pdf_rect.IsEmpty()) {
    646       pdf_rect.Offset(available_area_.x() * -1, 0);
    647 
    648       std::vector<pp::Rect> pdf_ready;
    649       std::vector<pp::Rect> pdf_pending;
    650       engine_->Paint(pdf_rect, &image_data_, &pdf_ready, &pdf_pending);
    651       for (size_t j = 0; j < pdf_ready.size(); ++j) {
    652         pdf_ready[j].Offset(available_area_.point());
    653         ready->push_back(
    654             PaintManager::ReadyRect(pdf_ready[j], image_data_, false));
    655       }
    656       for (size_t j = 0; j < pdf_pending.size(); ++j) {
    657         pdf_pending[j].Offset(available_area_.point());
    658         pending->push_back(pdf_pending[j]);
    659       }
    660     }
    661 
    662     for (size_t j = 0; j < background_parts_.size(); ++j) {
    663       pp::Rect intersection = background_parts_[j].location.Intersect(rect);
    664       if (!intersection.IsEmpty()) {
    665         FillRect(intersection, background_parts_[j].color);
    666         ready->push_back(
    667             PaintManager::ReadyRect(intersection, image_data_, false));
    668       }
    669     }
    670   }
    671 
    672   engine_->PostPaint();
    673 }
    674 
    675 void OutOfProcessInstance::DidOpen(int32_t result) {
    676   if (result == PP_OK) {
    677     if (!engine_->HandleDocumentLoad(embed_loader_)) {
    678       document_load_state_ = LOAD_STATE_LOADING;
    679       DocumentLoadFailed();
    680     }
    681   } else if (result != PP_ERROR_ABORTED) {  // Can happen in tests.
    682     NOTREACHED();
    683     DocumentLoadFailed();
    684   }
    685 
    686   // If it's a progressive load, cancel the stream URL request so that requests
    687   // can be made on the original URL.
    688   // TODO(raymes): Make this clearer once the in-process plugin is deleted.
    689   if (engine_->IsProgressiveLoad()) {
    690     pp::VarDictionary message;
    691     message.Set(kType, kJSCancelStreamUrlType);
    692     PostMessage(message);
    693   }
    694 }
    695 
    696 void OutOfProcessInstance::DidOpenPreview(int32_t result) {
    697   if (result == PP_OK) {
    698     preview_engine_.reset(PDFEngine::Create(new PreviewModeClient(this)));
    699     preview_engine_->HandleDocumentLoad(embed_preview_loader_);
    700   } else {
    701     NOTREACHED();
    702   }
    703 }
    704 
    705 void OutOfProcessInstance::OnClientTimerFired(int32_t id) {
    706   engine_->OnCallback(id);
    707 }
    708 
    709 void OutOfProcessInstance::CalculateBackgroundParts() {
    710   background_parts_.clear();
    711   int left_width = available_area_.x();
    712   int right_start = available_area_.right();
    713   int right_width = abs(plugin_size_.width() - available_area_.right());
    714   int bottom = std::min(available_area_.bottom(), plugin_size_.height());
    715 
    716   // Add the left, right, and bottom rectangles.  Note: we assume only
    717   // horizontal centering.
    718   BackgroundPart part;
    719   part.color = kBackgroundColorA << 24 |
    720                kBackgroundColorR << 16 |
    721                kBackgroundColorG << 8 |
    722                kBackgroundColorB;
    723   part.location = pp::Rect(0, 0, left_width, bottom);
    724   if (!part.location.IsEmpty())
    725     background_parts_.push_back(part);
    726   part.location = pp::Rect(right_start, 0, right_width, bottom);
    727   if (!part.location.IsEmpty())
    728     background_parts_.push_back(part);
    729   part.location = pp::Rect(
    730       0, bottom, plugin_size_.width(), plugin_size_.height() - bottom);
    731   if (!part.location.IsEmpty())
    732     background_parts_.push_back(part);
    733 }
    734 
    735 int OutOfProcessInstance::GetDocumentPixelWidth() const {
    736   return static_cast<int>(ceil(document_size_.width() * zoom_ * device_scale_));
    737 }
    738 
    739 int OutOfProcessInstance::GetDocumentPixelHeight() const {
    740   return static_cast<int>(
    741       ceil(document_size_.height() * zoom_ * device_scale_));
    742 }
    743 
    744 void OutOfProcessInstance::FillRect(const pp::Rect& rect, unsigned int color) {
    745   DCHECK(!image_data_.is_null() || rect.IsEmpty());
    746   unsigned int* buffer_start = static_cast<unsigned int*>(image_data_.data());
    747   int stride = image_data_.stride();
    748   unsigned int* ptr = buffer_start + rect.y() * stride / 4 + rect.x();
    749   int height = rect.height();
    750   int width = rect.width();
    751   for (int y = 0; y < height; ++y) {
    752     for (int x = 0; x < width; ++x)
    753       *(ptr + x) = color;
    754     ptr += stride /4;
    755   }
    756 }
    757 
    758 void OutOfProcessInstance::DocumentSizeUpdated(const pp::Size& size) {
    759   document_size_ = size;
    760 
    761   pp::VarDictionary dimensions;
    762   dimensions.Set(kType, kJSDocumentDimensionsType);
    763   dimensions.Set(kJSDocumentWidth, pp::Var(document_size_.width()));
    764   dimensions.Set(kJSDocumentHeight, pp::Var(document_size_.height()));
    765   pp::VarArray page_dimensions_array;
    766   int num_pages = engine_->GetNumberOfPages();
    767   for (int i = 0; i < num_pages; ++i) {
    768     pp::Rect page_rect = engine_->GetPageRect(i);
    769     pp::VarDictionary page_dimensions;
    770     page_dimensions.Set(kJSPageX, pp::Var(page_rect.x()));
    771     page_dimensions.Set(kJSPageY, pp::Var(page_rect.y()));
    772     page_dimensions.Set(kJSPageWidth, pp::Var(page_rect.width()));
    773     page_dimensions.Set(kJSPageHeight, pp::Var(page_rect.height()));
    774     page_dimensions_array.Set(i, page_dimensions);
    775   }
    776   dimensions.Set(kJSPageDimensions, page_dimensions_array);
    777   PostMessage(dimensions);
    778 
    779   OnGeometryChanged(zoom_, device_scale_);
    780 }
    781 
    782 void OutOfProcessInstance::Invalidate(const pp::Rect& rect) {
    783   pp::Rect offset_rect(rect);
    784   offset_rect.Offset(available_area_.point());
    785   paint_manager_.InvalidateRect(offset_rect);
    786 }
    787 
    788 void OutOfProcessInstance::Scroll(const pp::Point& point) {
    789   paint_manager_.ScrollRect(available_area_, point);
    790 }
    791 
    792 void OutOfProcessInstance::ScrollToX(int x) {
    793   pp::VarDictionary position;
    794   position.Set(kType, kJSSetScrollPositionType);
    795   position.Set(kJSPositionX, pp::Var(x / device_scale_));
    796   PostMessage(position);
    797 }
    798 
    799 void OutOfProcessInstance::ScrollToY(int y) {
    800   pp::VarDictionary position;
    801   position.Set(kType, kJSSetScrollPositionType);
    802   position.Set(kJSPositionY, pp::Var(y / device_scale_));
    803   PostMessage(position);
    804 }
    805 
    806 void OutOfProcessInstance::ScrollToPage(int page) {
    807   if (engine_->GetNumberOfPages() == 0)
    808     return;
    809 
    810   pp::VarDictionary message;
    811   message.Set(kType, kJSGoToPageType);
    812   message.Set(kJSPageNumber, pp::Var(page));
    813   PostMessage(message);
    814 }
    815 
    816 void OutOfProcessInstance::NavigateTo(const std::string& url,
    817                                       bool open_in_new_tab) {
    818   std::string url_copy(url);
    819 
    820   // Empty |url_copy| is ok, and will effectively be a reload.
    821   // Skip the code below so an empty URL does not turn into "http://", which
    822   // will cause GURL to fail a DCHECK.
    823   if (!url_copy.empty()) {
    824     // If there's no scheme, add http.
    825     if (url_copy.find("://") == std::string::npos &&
    826         url_copy.find("mailto:") == std::string::npos) {
    827       url_copy = std::string("http://") + url_copy;
    828     }
    829     // Make sure |url_copy| starts with a valid scheme.
    830     if (url_copy.find("http://") != 0 &&
    831         url_copy.find("https://") != 0 &&
    832         url_copy.find("ftp://") != 0 &&
    833         url_copy.find("mailto:") != 0) {
    834       return;
    835     }
    836     // Make sure |url_copy| is not only a scheme.
    837     if (url_copy == "http://" ||
    838         url_copy == "https://" ||
    839         url_copy == "ftp://" ||
    840         url_copy == "mailto:") {
    841       return;
    842     }
    843   }
    844   pp::VarDictionary message;
    845   message.Set(kType, kJSNavigateType);
    846   message.Set(kJSNavigateUrl, url_copy);
    847   message.Set(kJSNavigateNewTab, open_in_new_tab);
    848   PostMessage(message);
    849 }
    850 
    851 void OutOfProcessInstance::UpdateCursor(PP_CursorType_Dev cursor) {
    852   if (cursor == cursor_)
    853     return;
    854   cursor_ = cursor;
    855 
    856   const PPB_CursorControl_Dev* cursor_interface =
    857       reinterpret_cast<const PPB_CursorControl_Dev*>(
    858       pp::Module::Get()->GetBrowserInterface(PPB_CURSOR_CONTROL_DEV_INTERFACE));
    859   if (!cursor_interface) {
    860     NOTREACHED();
    861     return;
    862   }
    863 
    864   cursor_interface->SetCursor(
    865       pp_instance(), cursor_, pp::ImageData().pp_resource(), NULL);
    866 }
    867 
    868 void OutOfProcessInstance::UpdateTickMarks(
    869     const std::vector<pp::Rect>& tickmarks) {
    870   float inverse_scale = 1.0f / device_scale_;
    871   std::vector<pp::Rect> scaled_tickmarks = tickmarks;
    872   for (size_t i = 0; i < scaled_tickmarks.size(); i++)
    873     ScaleRect(inverse_scale, &scaled_tickmarks[i]);
    874   tickmarks_ = scaled_tickmarks;
    875 }
    876 
    877 void OutOfProcessInstance::NotifyNumberOfFindResultsChanged(int total,
    878                                                             bool final_result) {
    879   // We don't want to spam the renderer with too many updates to the number of
    880   // find results. Don't send an update if we sent one too recently. If it's the
    881   // final update, we always send it though.
    882   if (final_result) {
    883     NumberOfFindResultsChanged(total, final_result);
    884     SetTickmarks(tickmarks_);
    885     return;
    886   }
    887 
    888   if (recently_sent_find_update_)
    889     return;
    890 
    891   NumberOfFindResultsChanged(total, final_result);
    892   SetTickmarks(tickmarks_);
    893   recently_sent_find_update_ = true;
    894   pp::CompletionCallback callback =
    895       timer_factory_.NewCallback(
    896           &OutOfProcessInstance::ResetRecentlySentFindUpdate);
    897   pp::Module::Get()->core()->CallOnMainThread(kFindResultCooldownMs,
    898                                               callback, 0);
    899 }
    900 
    901 void OutOfProcessInstance::NotifySelectedFindResultChanged(
    902     int current_find_index) {
    903   SelectedFindResultChanged(current_find_index);
    904 }
    905 
    906 void OutOfProcessInstance::GetDocumentPassword(
    907     pp::CompletionCallbackWithOutput<pp::Var> callback) {
    908   if (password_callback_) {
    909     NOTREACHED();
    910     return;
    911   }
    912 
    913   password_callback_.reset(
    914       new pp::CompletionCallbackWithOutput<pp::Var>(callback));
    915   pp::VarDictionary message;
    916   message.Set(pp::Var(kType), pp::Var(kJSGetPasswordType));
    917   PostMessage(message);
    918 }
    919 
    920 void OutOfProcessInstance::Alert(const std::string& message) {
    921   ModalDialog(this, "alert", message, std::string());
    922 }
    923 
    924 bool OutOfProcessInstance::Confirm(const std::string& message) {
    925   pp::Var result = ModalDialog(this, "confirm", message, std::string());
    926   return result.is_bool() ? result.AsBool() : false;
    927 }
    928 
    929 std::string OutOfProcessInstance::Prompt(const std::string& question,
    930                                          const std::string& default_answer) {
    931   pp::Var result = ModalDialog(this, "prompt", question, default_answer);
    932   return result.is_string() ? result.AsString() : std::string();
    933 }
    934 
    935 std::string OutOfProcessInstance::GetURL() {
    936   return url_;
    937 }
    938 
    939 void OutOfProcessInstance::Email(const std::string& to,
    940                                  const std::string& cc,
    941                                  const std::string& bcc,
    942                                  const std::string& subject,
    943                                  const std::string& body) {
    944   pp::VarDictionary message;
    945   message.Set(pp::Var(kType), pp::Var(kJSEmailType));
    946   message.Set(pp::Var(kJSEmailTo),
    947               pp::Var(net::EscapeUrlEncodedData(to, false)));
    948   message.Set(pp::Var(kJSEmailCc),
    949               pp::Var(net::EscapeUrlEncodedData(cc, false)));
    950   message.Set(pp::Var(kJSEmailBcc),
    951               pp::Var(net::EscapeUrlEncodedData(bcc, false)));
    952   message.Set(pp::Var(kJSEmailSubject),
    953               pp::Var(net::EscapeUrlEncodedData(subject, false)));
    954   message.Set(pp::Var(kJSEmailBody),
    955               pp::Var(net::EscapeUrlEncodedData(body, false)));
    956   PostMessage(message);
    957 }
    958 
    959 void OutOfProcessInstance::Print() {
    960   if (!printing_enabled_ ||
    961       (!engine_->HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY) &&
    962        !engine_->HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY))) {
    963     return;
    964   }
    965 
    966   pp::CompletionCallback callback =
    967       print_callback_factory_.NewCallback(&OutOfProcessInstance::OnPrint);
    968   pp::Module::Get()->core()->CallOnMainThread(0, callback);
    969 }
    970 
    971 void OutOfProcessInstance::OnPrint(int32_t) {
    972   pp::PDF::Print(this);
    973 }
    974 
    975 void OutOfProcessInstance::SubmitForm(const std::string& url,
    976                                       const void* data,
    977                                       int length) {
    978   pp::URLRequestInfo request(this);
    979   request.SetURL(url);
    980   request.SetMethod("POST");
    981   request.AppendDataToBody(reinterpret_cast<const char*>(data), length);
    982 
    983   pp::CompletionCallback callback =
    984       form_factory_.NewCallback(&OutOfProcessInstance::FormDidOpen);
    985   form_loader_ = CreateURLLoaderInternal();
    986   int rv = form_loader_.Open(request, callback);
    987   if (rv != PP_OK_COMPLETIONPENDING)
    988     callback.Run(rv);
    989 }
    990 
    991 void OutOfProcessInstance::FormDidOpen(int32_t result) {
    992   // TODO: inform the user of success/failure.
    993   if (result != PP_OK) {
    994     NOTREACHED();
    995   }
    996 }
    997 
    998 std::string OutOfProcessInstance::ShowFileSelectionDialog() {
    999   // Seems like very low priority to implement, since the pdf has no way to get
   1000   // the file data anyways.  Javascript doesn't let you do this synchronously.
   1001   NOTREACHED();
   1002   return std::string();
   1003 }
   1004 
   1005 pp::URLLoader OutOfProcessInstance::CreateURLLoader() {
   1006   if (full_) {
   1007     if (!did_call_start_loading_) {
   1008       did_call_start_loading_ = true;
   1009       pp::PDF::DidStartLoading(this);
   1010     }
   1011 
   1012     // Disable save and print until the document is fully loaded, since they
   1013     // would generate an incomplete document.  Need to do this each time we
   1014     // call DidStartLoading since that resets the content restrictions.
   1015     pp::PDF::SetContentRestriction(this, CONTENT_RESTRICTION_SAVE |
   1016                                    CONTENT_RESTRICTION_PRINT);
   1017   }
   1018 
   1019   return CreateURLLoaderInternal();
   1020 }
   1021 
   1022 void OutOfProcessInstance::ScheduleCallback(int id, int delay_in_ms) {
   1023   pp::CompletionCallback callback =
   1024       timer_factory_.NewCallback(&OutOfProcessInstance::OnClientTimerFired);
   1025   pp::Module::Get()->core()->CallOnMainThread(delay_in_ms, callback, id);
   1026 }
   1027 
   1028 void OutOfProcessInstance::SearchString(const base::char16* string,
   1029                             const base::char16* term,
   1030                             bool case_sensitive,
   1031                             std::vector<SearchStringResult>* results) {
   1032   PP_PrivateFindResult* pp_results;
   1033   int count = 0;
   1034   pp::PDF::SearchString(
   1035       this,
   1036       reinterpret_cast<const unsigned short*>(string),
   1037       reinterpret_cast<const unsigned short*>(term),
   1038       case_sensitive,
   1039       &pp_results,
   1040       &count);
   1041 
   1042   results->resize(count);
   1043   for (int i = 0; i < count; ++i) {
   1044     (*results)[i].start_index = pp_results[i].start_index;
   1045     (*results)[i].length = pp_results[i].length;
   1046   }
   1047 
   1048   pp::Memory_Dev memory;
   1049   memory.MemFree(pp_results);
   1050 }
   1051 
   1052 void OutOfProcessInstance::DocumentPaintOccurred() {
   1053 }
   1054 
   1055 void OutOfProcessInstance::DocumentLoadComplete(int page_count) {
   1056   // Clear focus state for OSK.
   1057   FormTextFieldFocusChange(false);
   1058 
   1059   DCHECK(document_load_state_ == LOAD_STATE_LOADING);
   1060   document_load_state_ = LOAD_STATE_COMPLETE;
   1061   UserMetricsRecordAction("PDF.LoadSuccess");
   1062 
   1063   // Note: If we are in print preview mode the scroll location is retained
   1064   // across document loads so we don't want to scroll again and override it.
   1065   if (!IsPrintPreview()) {
   1066     int initial_page = GetInitialPage(url_);
   1067     if (initial_page >= 0)
   1068       ScrollToPage(initial_page);
   1069   } else {
   1070     AppendBlankPrintPreviewPages();
   1071     OnGeometryChanged(0, 0);
   1072   }
   1073 
   1074   pp::VarDictionary message;
   1075   message.Set(pp::Var(kType), pp::Var(kJSLoadProgressType));
   1076   message.Set(pp::Var(kJSProgressPercentage), pp::Var(100)) ;
   1077   PostMessage(message);
   1078 
   1079   if (!full_)
   1080     return;
   1081 
   1082   if (did_call_start_loading_) {
   1083     pp::PDF::DidStopLoading(this);
   1084     did_call_start_loading_ = false;
   1085   }
   1086 
   1087   int content_restrictions =
   1088       CONTENT_RESTRICTION_CUT | CONTENT_RESTRICTION_PASTE;
   1089   if (!engine_->HasPermission(PDFEngine::PERMISSION_COPY))
   1090     content_restrictions |= CONTENT_RESTRICTION_COPY;
   1091 
   1092   if (!engine_->HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY) &&
   1093       !engine_->HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY)) {
   1094     printing_enabled_ = false;
   1095   }
   1096 
   1097   pp::PDF::SetContentRestriction(this, content_restrictions);
   1098 
   1099   uma_.HistogramCustomCounts("PDF.PageCount", page_count,
   1100                              1, 1000000, 50);
   1101 }
   1102 
   1103 void OutOfProcessInstance::RotateClockwise() {
   1104   engine_->RotateClockwise();
   1105 }
   1106 
   1107 void OutOfProcessInstance::RotateCounterclockwise() {
   1108   engine_->RotateCounterclockwise();
   1109 }
   1110 
   1111 void OutOfProcessInstance::PreviewDocumentLoadComplete() {
   1112   if (preview_document_load_state_ != LOAD_STATE_LOADING ||
   1113       preview_pages_info_.empty()) {
   1114     return;
   1115   }
   1116 
   1117   preview_document_load_state_ = LOAD_STATE_COMPLETE;
   1118 
   1119   int dest_page_index = preview_pages_info_.front().second;
   1120   int src_page_index =
   1121       ExtractPrintPreviewPageIndex(preview_pages_info_.front().first);
   1122   if (src_page_index > 0 &&  dest_page_index > -1 && preview_engine_.get())
   1123     engine_->AppendPage(preview_engine_.get(), dest_page_index);
   1124 
   1125   preview_pages_info_.pop();
   1126   // |print_preview_page_count_| is not updated yet. Do not load any
   1127   // other preview pages till we get this information.
   1128   if (print_preview_page_count_ == 0)
   1129     return;
   1130 
   1131   if (preview_pages_info_.size())
   1132     LoadAvailablePreviewPage();
   1133 }
   1134 
   1135 void OutOfProcessInstance::DocumentLoadFailed() {
   1136   DCHECK(document_load_state_ == LOAD_STATE_LOADING);
   1137   UserMetricsRecordAction("PDF.LoadFailure");
   1138 
   1139   if (did_call_start_loading_) {
   1140     pp::PDF::DidStopLoading(this);
   1141     did_call_start_loading_ = false;
   1142   }
   1143 
   1144   document_load_state_ = LOAD_STATE_FAILED;
   1145   paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_));
   1146 
   1147   // Send a progress value of -1 to indicate a failure.
   1148   pp::VarDictionary message;
   1149   message.Set(pp::Var(kType), pp::Var(kJSLoadProgressType));
   1150   message.Set(pp::Var(kJSProgressPercentage), pp::Var(-1)) ;
   1151   PostMessage(message);
   1152 }
   1153 
   1154 void OutOfProcessInstance::PreviewDocumentLoadFailed() {
   1155   UserMetricsRecordAction("PDF.PreviewDocumentLoadFailure");
   1156   if (preview_document_load_state_ != LOAD_STATE_LOADING ||
   1157       preview_pages_info_.empty()) {
   1158     return;
   1159   }
   1160 
   1161   preview_document_load_state_ = LOAD_STATE_FAILED;
   1162   preview_pages_info_.pop();
   1163 
   1164   if (preview_pages_info_.size())
   1165     LoadAvailablePreviewPage();
   1166 }
   1167 
   1168 pp::Instance* OutOfProcessInstance::GetPluginInstance() {
   1169   return this;
   1170 }
   1171 
   1172 void OutOfProcessInstance::DocumentHasUnsupportedFeature(
   1173     const std::string& feature) {
   1174   std::string metric("PDF_Unsupported_");
   1175   metric += feature;
   1176   if (!unsupported_features_reported_.count(metric)) {
   1177     unsupported_features_reported_.insert(metric);
   1178     UserMetricsRecordAction(metric);
   1179   }
   1180 
   1181   // Since we use an info bar, only do this for full frame plugins..
   1182   if (!full_)
   1183     return;
   1184 
   1185   if (told_browser_about_unsupported_feature_)
   1186     return;
   1187   told_browser_about_unsupported_feature_ = true;
   1188 
   1189   pp::PDF::HasUnsupportedFeature(this);
   1190 }
   1191 
   1192 void OutOfProcessInstance::DocumentLoadProgress(uint32 available,
   1193                                                 uint32 doc_size) {
   1194   double progress = 0.0;
   1195   if (doc_size == 0) {
   1196     // Document size is unknown. Use heuristics.
   1197     // We'll make progress logarithmic from 0 to 100M.
   1198     static const double kFactor = log(100000000.0) / 100.0;
   1199     if (available > 0) {
   1200       progress = log(static_cast<double>(available)) / kFactor;
   1201       if (progress > 100.0)
   1202         progress = 100.0;
   1203     }
   1204   } else {
   1205     progress = 100.0 * static_cast<double>(available) / doc_size;
   1206   }
   1207 
   1208   // We send 100% load progress in DocumentLoadComplete.
   1209   if (progress >= 100)
   1210     return;
   1211 
   1212   // Avoid sending too many progress messages over PostMessage.
   1213   if (progress > last_progress_sent_ + 1) {
   1214     last_progress_sent_ = progress;
   1215     pp::VarDictionary message;
   1216     message.Set(pp::Var(kType), pp::Var(kJSLoadProgressType));
   1217     message.Set(pp::Var(kJSProgressPercentage), pp::Var(progress)) ;
   1218     PostMessage(message);
   1219   }
   1220 }
   1221 
   1222 void OutOfProcessInstance::FormTextFieldFocusChange(bool in_focus) {
   1223   if (!text_input_.get())
   1224     return;
   1225   if (in_focus)
   1226     text_input_->SetTextInputType(PP_TEXTINPUT_TYPE_DEV_TEXT);
   1227   else
   1228     text_input_->SetTextInputType(PP_TEXTINPUT_TYPE_DEV_NONE);
   1229 }
   1230 
   1231 void OutOfProcessInstance::ResetRecentlySentFindUpdate(int32_t /* unused */) {
   1232   recently_sent_find_update_ = false;
   1233 }
   1234 
   1235 void OutOfProcessInstance::OnGeometryChanged(double old_zoom,
   1236                                              float old_device_scale) {
   1237   if (zoom_ != old_zoom || device_scale_ != old_device_scale)
   1238     engine_->ZoomUpdated(zoom_ * device_scale_);
   1239 
   1240   available_area_ = pp::Rect(plugin_size_);
   1241   int doc_width = GetDocumentPixelWidth();
   1242   if (doc_width < available_area_.width()) {
   1243     available_area_.Offset((available_area_.width() - doc_width) / 2, 0);
   1244     available_area_.set_width(doc_width);
   1245   }
   1246   int doc_height = GetDocumentPixelHeight();
   1247   if (doc_height < available_area_.height()) {
   1248     available_area_.set_height(doc_height);
   1249   }
   1250 
   1251   CalculateBackgroundParts();
   1252   engine_->PageOffsetUpdated(available_area_.point());
   1253   engine_->PluginSizeUpdated(available_area_.size());
   1254 
   1255   if (!document_size_.GetArea())
   1256     return;
   1257   paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_));
   1258 }
   1259 
   1260 void OutOfProcessInstance::LoadUrl(const std::string& url) {
   1261   LoadUrlInternal(url, &embed_loader_, &OutOfProcessInstance::DidOpen);
   1262 }
   1263 
   1264 void OutOfProcessInstance::LoadPreviewUrl(const std::string& url) {
   1265   LoadUrlInternal(url, &embed_preview_loader_,
   1266                   &OutOfProcessInstance::DidOpenPreview);
   1267 }
   1268 
   1269 void OutOfProcessInstance::LoadUrlInternal(
   1270     const std::string& url,
   1271     pp::URLLoader* loader,
   1272     void (OutOfProcessInstance::* method)(int32_t)) {
   1273   pp::URLRequestInfo request(this);
   1274   request.SetURL(url);
   1275   request.SetMethod("GET");
   1276 
   1277   *loader = CreateURLLoaderInternal();
   1278   pp::CompletionCallback callback = loader_factory_.NewCallback(method);
   1279   int rv = loader->Open(request, callback);
   1280   if (rv != PP_OK_COMPLETIONPENDING)
   1281     callback.Run(rv);
   1282 }
   1283 
   1284 pp::URLLoader OutOfProcessInstance::CreateURLLoaderInternal() {
   1285   pp::URLLoader loader(this);
   1286 
   1287   const PPB_URLLoaderTrusted* trusted_interface =
   1288       reinterpret_cast<const PPB_URLLoaderTrusted*>(
   1289           pp::Module::Get()->GetBrowserInterface(
   1290               PPB_URLLOADERTRUSTED_INTERFACE));
   1291   if (trusted_interface)
   1292     trusted_interface->GrantUniversalAccess(loader.pp_resource());
   1293   return loader;
   1294 }
   1295 
   1296 int OutOfProcessInstance::GetInitialPage(const std::string& url) {
   1297 #if defined(OS_NACL)
   1298   return -1;
   1299 #else
   1300   size_t found_idx = url.find('#');
   1301   if (found_idx == std::string::npos)
   1302     return -1;
   1303 
   1304   const std::string& ref = url.substr(found_idx + 1);
   1305   std::vector<std::string> fragments;
   1306   Tokenize(ref, kDelimiters, &fragments);
   1307 
   1308   // Page number to return, zero-based.
   1309   int page = -1;
   1310 
   1311   // Handle the case of http://foo.com/bar#NAMEDDEST. This is not explicitly
   1312   // mentioned except by example in the Adobe "PDF Open Parameters" document.
   1313   if ((fragments.size() == 1) && (fragments[0].find('=') == std::string::npos))
   1314     return engine_->GetNamedDestinationPage(fragments[0]);
   1315 
   1316   for (size_t i = 0; i < fragments.size(); ++i) {
   1317     std::vector<std::string> key_value;
   1318     base::SplitString(fragments[i], '=', &key_value);
   1319     if (key_value.size() != 2)
   1320       continue;
   1321     const std::string& key = key_value[0];
   1322     const std::string& value = key_value[1];
   1323 
   1324     if (base::strcasecmp(kPage, key.c_str()) == 0) {
   1325       // |page_value| is 1-based.
   1326       int page_value = -1;
   1327       if (base::StringToInt(value, &page_value) && page_value > 0)
   1328         page = page_value - 1;
   1329       continue;
   1330     }
   1331     if (base::strcasecmp(kNamedDest, key.c_str()) == 0) {
   1332       // |page_value| is 0-based.
   1333       int page_value = engine_->GetNamedDestinationPage(value);
   1334       if (page_value >= 0)
   1335         page = page_value;
   1336       continue;
   1337     }
   1338   }
   1339   return page;
   1340 #endif
   1341 }
   1342 
   1343 void OutOfProcessInstance::SetZoom(double scale) {
   1344   double old_zoom = zoom_;
   1345   zoom_ = scale;
   1346   OnGeometryChanged(old_zoom, device_scale_);
   1347 }
   1348 
   1349 std::string OutOfProcessInstance::GetLocalizedString(PP_ResourceString id) {
   1350   pp::Var rv(pp::PDF::GetLocalizedString(this, id));
   1351   if (!rv.is_string())
   1352     return std::string();
   1353 
   1354   return rv.AsString();
   1355 }
   1356 
   1357 void OutOfProcessInstance::AppendBlankPrintPreviewPages() {
   1358   if (print_preview_page_count_ == 0)
   1359     return;
   1360   engine_->AppendBlankPages(print_preview_page_count_);
   1361   if (preview_pages_info_.size() > 0)
   1362     LoadAvailablePreviewPage();
   1363 }
   1364 
   1365 bool OutOfProcessInstance::IsPrintPreview() {
   1366   return IsPrintPreviewUrl(url_);
   1367 }
   1368 
   1369 void OutOfProcessInstance::ProcessPreviewPageInfo(const std::string& url,
   1370                                                   int dst_page_index) {
   1371   if (!IsPrintPreview())
   1372     return;
   1373 
   1374   int src_page_index = ExtractPrintPreviewPageIndex(url);
   1375   if (src_page_index < 1)
   1376     return;
   1377 
   1378   preview_pages_info_.push(std::make_pair(url, dst_page_index));
   1379   LoadAvailablePreviewPage();
   1380 }
   1381 
   1382 void OutOfProcessInstance::LoadAvailablePreviewPage() {
   1383   if (preview_pages_info_.size() <= 0 ||
   1384       document_load_state_ != LOAD_STATE_COMPLETE) {
   1385     return;
   1386   }
   1387 
   1388   std::string url = preview_pages_info_.front().first;
   1389   int dst_page_index = preview_pages_info_.front().second;
   1390   int src_page_index = ExtractPrintPreviewPageIndex(url);
   1391   if (src_page_index < 1 ||
   1392       dst_page_index >= print_preview_page_count_ ||
   1393       preview_document_load_state_ == LOAD_STATE_LOADING) {
   1394     return;
   1395   }
   1396 
   1397   preview_document_load_state_ = LOAD_STATE_LOADING;
   1398   LoadPreviewUrl(url);
   1399 }
   1400 
   1401 void OutOfProcessInstance::UserMetricsRecordAction(
   1402     const std::string& action) {
   1403   // TODO(raymes): Move this function to PPB_UMA_Private.
   1404   pp::PDF::UserMetricsRecordAction(this, pp::Var(action));
   1405 }
   1406 
   1407 }  // namespace chrome_pdf
   1408