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