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