• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetLinkAtPosition(PP_Instance instance,PP_Point point)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 
Transform(PP_Instance instance,PP_PrivatePageTransformType type)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 
ExtractPrintPreviewPageIndex(const std::string & src_url)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 
IsPrintPreviewUrl(const std::string & url)189 bool IsPrintPreviewUrl(const std::string& url) {
190   return url.substr(0, strlen(kChromePrint)) == kChromePrint;
191 }
192 
ScalePoint(float scale,pp::Point * point)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 
ScaleRect(float scale,pp::Rect * rect)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.
ModalDialog(const pp::Instance * instance,const std::string & type,const std::string & message,const std::string & default_answer)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 
OutOfProcessInstance(PP_Instance instance)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 
~OutOfProcessInstance()261 OutOfProcessInstance::~OutOfProcessInstance() {
262   RemovePerInstanceObject(kPPPPdfInterface, this);
263 }
264 
Init(uint32_t argc,const char * argn[],const char * argv[])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 
HandleMessage(const pp::Var & message)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 
HandleInputEvent(const pp::InputEvent & event)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 
DidChangeView(const pp::View & view)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 
GetLinkAtPosition(const pp::Point & point)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 
GetSelectedText(bool html)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 
QuerySupportedPrintOutputFormats()552 uint32_t OutOfProcessInstance::QuerySupportedPrintOutputFormats() {
553   return engine_->QuerySupportedPrintOutputFormats();
554 }
555 
PrintBegin(const PP_PrintSettings_Dev & print_settings)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 
PrintPages(const PP_PrintPageNumberRange_Dev * page_ranges,uint32_t page_range_count)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 
PrintEnd()585 void OutOfProcessInstance::PrintEnd() {
586   if (print_settings_.print_pages_called_)
587     UserMetricsRecordAction("PDF.PrintPage");
588   print_settings_.Clear();
589   engine_->PrintEnd();
590 }
591 
IsPrintScalingDisabled()592 bool OutOfProcessInstance::IsPrintScalingDisabled() {
593   return !engine_->GetPrintScaling();
594 }
595 
StartFind(const std::string & text,bool case_sensitive)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 
SelectFindResult(bool forward)602 void OutOfProcessInstance::SelectFindResult(bool forward) {
603   engine_->SelectFindResult(forward);
604 }
605 
StopFind()606 void OutOfProcessInstance::StopFind() {
607   engine_->StopFind();
608   tickmarks_.clear();
609   SetTickmarks(tickmarks_);
610 }
611 
OnPaint(const std::vector<pp::Rect> & paint_rects,std::vector<PaintManager::ReadyRect> * ready,std::vector<pp::Rect> * pending)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 
DidOpen(int32_t result)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 
DidOpenPreview(int32_t result)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 
OnClientTimerFired(int32_t id)705 void OutOfProcessInstance::OnClientTimerFired(int32_t id) {
706   engine_->OnCallback(id);
707 }
708 
CalculateBackgroundParts()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 
GetDocumentPixelWidth() const735 int OutOfProcessInstance::GetDocumentPixelWidth() const {
736   return static_cast<int>(ceil(document_size_.width() * zoom_ * device_scale_));
737 }
738 
GetDocumentPixelHeight() const739 int OutOfProcessInstance::GetDocumentPixelHeight() const {
740   return static_cast<int>(
741       ceil(document_size_.height() * zoom_ * device_scale_));
742 }
743 
FillRect(const pp::Rect & rect,unsigned int color)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 
DocumentSizeUpdated(const pp::Size & size)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 
Invalidate(const pp::Rect & rect)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 
Scroll(const pp::Point & point)788 void OutOfProcessInstance::Scroll(const pp::Point& point) {
789   paint_manager_.ScrollRect(available_area_, point);
790 }
791 
ScrollToX(int x)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 
ScrollToY(int y)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 
ScrollToPage(int page)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 
NavigateTo(const std::string & url,bool open_in_new_tab)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 
UpdateCursor(PP_CursorType_Dev cursor)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 
UpdateTickMarks(const std::vector<pp::Rect> & tickmarks)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 
NotifyNumberOfFindResultsChanged(int total,bool final_result)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 
NotifySelectedFindResultChanged(int current_find_index)901 void OutOfProcessInstance::NotifySelectedFindResultChanged(
902     int current_find_index) {
903   SelectedFindResultChanged(current_find_index);
904 }
905 
GetDocumentPassword(pp::CompletionCallbackWithOutput<pp::Var> callback)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 
Alert(const std::string & message)920 void OutOfProcessInstance::Alert(const std::string& message) {
921   ModalDialog(this, "alert", message, std::string());
922 }
923 
Confirm(const std::string & message)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 
Prompt(const std::string & question,const std::string & default_answer)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 
GetURL()935 std::string OutOfProcessInstance::GetURL() {
936   return url_;
937 }
938 
Email(const std::string & to,const std::string & cc,const std::string & bcc,const std::string & subject,const std::string & body)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 
Print()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 
OnPrint(int32_t)971 void OutOfProcessInstance::OnPrint(int32_t) {
972   pp::PDF::Print(this);
973 }
974 
SubmitForm(const std::string & url,const void * data,int length)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 
FormDidOpen(int32_t result)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 
ShowFileSelectionDialog()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 
CreateURLLoader()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 
ScheduleCallback(int id,int delay_in_ms)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 
SearchString(const base::char16 * string,const base::char16 * term,bool case_sensitive,std::vector<SearchStringResult> * results)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 
DocumentPaintOccurred()1052 void OutOfProcessInstance::DocumentPaintOccurred() {
1053 }
1054 
DocumentLoadComplete(int page_count)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 
RotateClockwise()1103 void OutOfProcessInstance::RotateClockwise() {
1104   engine_->RotateClockwise();
1105 }
1106 
RotateCounterclockwise()1107 void OutOfProcessInstance::RotateCounterclockwise() {
1108   engine_->RotateCounterclockwise();
1109 }
1110 
PreviewDocumentLoadComplete()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 
DocumentLoadFailed()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 
PreviewDocumentLoadFailed()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 
GetPluginInstance()1168 pp::Instance* OutOfProcessInstance::GetPluginInstance() {
1169   return this;
1170 }
1171 
DocumentHasUnsupportedFeature(const std::string & feature)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 
DocumentLoadProgress(uint32 available,uint32 doc_size)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 
FormTextFieldFocusChange(bool in_focus)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 
ResetRecentlySentFindUpdate(int32_t)1231 void OutOfProcessInstance::ResetRecentlySentFindUpdate(int32_t /* unused */) {
1232   recently_sent_find_update_ = false;
1233 }
1234 
OnGeometryChanged(double old_zoom,float old_device_scale)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 
LoadUrl(const std::string & url)1260 void OutOfProcessInstance::LoadUrl(const std::string& url) {
1261   LoadUrlInternal(url, &embed_loader_, &OutOfProcessInstance::DidOpen);
1262 }
1263 
LoadPreviewUrl(const std::string & url)1264 void OutOfProcessInstance::LoadPreviewUrl(const std::string& url) {
1265   LoadUrlInternal(url, &embed_preview_loader_,
1266                   &OutOfProcessInstance::DidOpenPreview);
1267 }
1268 
LoadUrlInternal(const std::string & url,pp::URLLoader * loader,void (OutOfProcessInstance::* method)(int32_t))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 
CreateURLLoaderInternal()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 
GetInitialPage(const std::string & url)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 
SetZoom(double scale)1343 void OutOfProcessInstance::SetZoom(double scale) {
1344   double old_zoom = zoom_;
1345   zoom_ = scale;
1346   OnGeometryChanged(old_zoom, device_scale_);
1347 }
1348 
GetLocalizedString(PP_ResourceString id)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 
AppendBlankPrintPreviewPages()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 
IsPrintPreview()1365 bool OutOfProcessInstance::IsPrintPreview() {
1366   return IsPrintPreviewUrl(url_);
1367 }
1368 
ProcessPreviewPageInfo(const std::string & url,int dst_page_index)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 
LoadAvailablePreviewPage()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 
UserMetricsRecordAction(const std::string & action)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