[Extensions] Keep track of whether a user has set the 'allowed on all urls' bit
[chromium-blink-merge.git] / pdf / out_of_process_instance.cc
blob169a4403498a21b2a2eb9dac16f5c88ff5e022f8
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.
5 #include "pdf/out_of_process_instance.h"
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>
13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h"
15 #include "base/logging.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "base/values.h"
20 #include "chrome/common/content_restriction.h"
21 #include "net/base/escape.h"
22 #include "pdf/pdf.h"
23 #include "ppapi/c/dev/ppb_cursor_control_dev.h"
24 #include "ppapi/c/pp_errors.h"
25 #include "ppapi/c/pp_rect.h"
26 #include "ppapi/c/private/ppb_instance_private.h"
27 #include "ppapi/c/private/ppp_pdf.h"
28 #include "ppapi/c/trusted/ppb_url_loader_trusted.h"
29 #include "ppapi/cpp/core.h"
30 #include "ppapi/cpp/dev/memory_dev.h"
31 #include "ppapi/cpp/dev/text_input_dev.h"
32 #include "ppapi/cpp/dev/url_util_dev.h"
33 #include "ppapi/cpp/module.h"
34 #include "ppapi/cpp/point.h"
35 #include "ppapi/cpp/private/pdf.h"
36 #include "ppapi/cpp/private/var_private.h"
37 #include "ppapi/cpp/rect.h"
38 #include "ppapi/cpp/resource.h"
39 #include "ppapi/cpp/url_request_info.h"
40 #include "ppapi/cpp/var_array.h"
41 #include "ppapi/cpp/var_dictionary.h"
42 #include "ui/events/keycodes/keyboard_codes.h"
44 namespace chrome_pdf {
46 const char kChromePrint[] = "chrome://print/";
47 const char kChromeExtension[] =
48 "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai";
50 // Dictionary Value key names for the document accessibility info
51 const char kAccessibleNumberOfPages[] = "numberOfPages";
52 const char kAccessibleLoaded[] = "loaded";
53 const char kAccessibleCopyable[] = "copyable";
55 // PDF background colors.
56 const uint32 kBackgroundColor = 0xFFCCCCCC;
57 const uint32 kBackgroundColorMaterial = 0xFFEEEEEE;
59 // Constants used in handling postMessage() messages.
60 const char kType[] = "type";
61 // Viewport message arguments. (Page -> Plugin).
62 const char kJSViewportType[] = "viewport";
63 const char kJSXOffset[] = "xOffset";
64 const char kJSYOffset[] = "yOffset";
65 const char kJSZoom[] = "zoom";
66 // Stop scrolling message (Page -> Plugin)
67 const char kJSStopScrollingType[] = "stopScrolling";
68 // Document dimension arguments (Plugin -> Page).
69 const char kJSDocumentDimensionsType[] = "documentDimensions";
70 const char kJSDocumentWidth[] = "width";
71 const char kJSDocumentHeight[] = "height";
72 const char kJSPageDimensions[] = "pageDimensions";
73 const char kJSPageX[] = "x";
74 const char kJSPageY[] = "y";
75 const char kJSPageWidth[] = "width";
76 const char kJSPageHeight[] = "height";
77 // Document load progress arguments (Plugin -> Page)
78 const char kJSLoadProgressType[] = "loadProgress";
79 const char kJSProgressPercentage[] = "progress";
80 // Bookmarks
81 const char kJSBookmarksType[] = "bookmarks";
82 const char kJSBookmarks[] = "bookmarks";
83 // Get password arguments (Plugin -> Page)
84 const char kJSGetPasswordType[] = "getPassword";
85 // Get password complete arguments (Page -> Plugin)
86 const char kJSGetPasswordCompleteType[] = "getPasswordComplete";
87 const char kJSPassword[] = "password";
88 // Print (Page -> Plugin)
89 const char kJSPrintType[] = "print";
90 // Save (Page -> Plugin)
91 const char kJSSaveType[] = "save";
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 // Rotation (Page -> Plugin)
133 const char kJSRotateClockwiseType[] = "rotateClockwise";
134 const char kJSRotateCounterclockwiseType[] = "rotateCounterclockwise";
135 // Select all text in the document (Page -> Plugin)
136 const char kJSSelectAllType[] = "selectAll";
137 // Get the selected text in the document (Page -> Plugin)
138 const char kJSGetSelectedTextType[] = "getSelectedText";
139 // Reply with selected text (Plugin -> Page)
140 const char kJSGetSelectedTextReplyType[] = "getSelectedTextReply";
141 const char kJSSelectedText[] = "selectedText";
143 // List of named destinations (Plugin -> Page)
144 const char kJSSetNamedDestinationsType[] = "setNamedDestinations";
145 const char kJSNamedDestinations[] = "namedDestinations";
147 const int kFindResultCooldownMs = 100;
149 const double kMinZoom = 0.01;
151 namespace {
153 static const char kPPPPdfInterface[] = PPP_PDF_INTERFACE_1;
155 PP_Var GetLinkAtPosition(PP_Instance instance, PP_Point point) {
156 pp::Var var;
157 void* object = pp::Instance::GetPerInstanceObject(instance, kPPPPdfInterface);
158 if (object) {
159 var = static_cast<OutOfProcessInstance*>(object)->GetLinkAtPosition(
160 pp::Point(point));
162 return var.Detach();
165 void Transform(PP_Instance instance, PP_PrivatePageTransformType type) {
166 void* object =
167 pp::Instance::GetPerInstanceObject(instance, kPPPPdfInterface);
168 if (object) {
169 OutOfProcessInstance* obj_instance =
170 static_cast<OutOfProcessInstance*>(object);
171 switch (type) {
172 case PP_PRIVATEPAGETRANSFORMTYPE_ROTATE_90_CW:
173 obj_instance->RotateClockwise();
174 break;
175 case PP_PRIVATEPAGETRANSFORMTYPE_ROTATE_90_CCW:
176 obj_instance->RotateCounterclockwise();
177 break;
182 PP_Bool GetPrintPresetOptionsFromDocument(
183 PP_Instance instance,
184 PP_PdfPrintPresetOptions_Dev* options) {
185 void* object = pp::Instance::GetPerInstanceObject(instance, kPPPPdfInterface);
186 if (object) {
187 OutOfProcessInstance* obj_instance =
188 static_cast<OutOfProcessInstance*>(object);
189 obj_instance->GetPrintPresetOptionsFromDocument(options);
191 return PP_TRUE;
194 const PPP_Pdf ppp_private = {
195 &GetLinkAtPosition,
196 &Transform,
197 &GetPrintPresetOptionsFromDocument
200 int ExtractPrintPreviewPageIndex(const std::string& src_url) {
201 // Sample |src_url| format: chrome://print/id/page_index/print.pdf
202 std::vector<std::string> url_substr;
203 base::SplitString(src_url.substr(strlen(kChromePrint)), '/', &url_substr);
204 if (url_substr.size() != 3)
205 return -1;
207 if (url_substr[2] != "print.pdf")
208 return -1;
210 int page_index = 0;
211 if (!base::StringToInt(url_substr[1], &page_index))
212 return -1;
213 return page_index;
216 bool IsPrintPreviewUrl(const std::string& url) {
217 return url.substr(0, strlen(kChromePrint)) == kChromePrint;
220 void ScalePoint(float scale, pp::Point* point) {
221 point->set_x(static_cast<int>(point->x() * scale));
222 point->set_y(static_cast<int>(point->y() * scale));
225 void ScaleRect(float scale, pp::Rect* rect) {
226 int left = static_cast<int>(floorf(rect->x() * scale));
227 int top = static_cast<int>(floorf(rect->y() * scale));
228 int right = static_cast<int>(ceilf((rect->x() + rect->width()) * scale));
229 int bottom = static_cast<int>(ceilf((rect->y() + rect->height()) * scale));
230 rect->SetRect(left, top, right - left, bottom - top);
233 // TODO(raymes): Remove this dependency on VarPrivate/InstancePrivate. It's
234 // needed right now to do a synchronous call to JavaScript, but we could easily
235 // replace this with a custom PPB_PDF function.
236 pp::Var ModalDialog(const pp::Instance* instance,
237 const std::string& type,
238 const std::string& message,
239 const std::string& default_answer) {
240 const PPB_Instance_Private* interface =
241 reinterpret_cast<const PPB_Instance_Private*>(
242 pp::Module::Get()->GetBrowserInterface(
243 PPB_INSTANCE_PRIVATE_INTERFACE));
244 pp::VarPrivate window(pp::PASS_REF,
245 interface->GetWindowObject(instance->pp_instance()));
246 if (default_answer.empty())
247 return window.Call(type, message);
248 else
249 return window.Call(type, message, default_answer);
252 } // namespace
254 OutOfProcessInstance::OutOfProcessInstance(PP_Instance instance)
255 : pp::Instance(instance),
256 pp::Find_Private(this),
257 pp::Printing_Dev(this),
258 pp::Selection_Dev(this),
259 cursor_(PP_CURSORTYPE_POINTER),
260 zoom_(1.0),
261 device_scale_(1.0),
262 full_(false),
263 paint_manager_(this, this, true),
264 first_paint_(true),
265 document_load_state_(LOAD_STATE_LOADING),
266 preview_document_load_state_(LOAD_STATE_COMPLETE),
267 uma_(this),
268 told_browser_about_unsupported_feature_(false),
269 print_preview_page_count_(0),
270 last_progress_sent_(0),
271 recently_sent_find_update_(false),
272 received_viewport_message_(false),
273 did_call_start_loading_(false),
274 stop_scrolling_(false),
275 background_color_(kBackgroundColor) {
276 loader_factory_.Initialize(this);
277 timer_factory_.Initialize(this);
278 form_factory_.Initialize(this);
279 print_callback_factory_.Initialize(this);
280 engine_.reset(PDFEngine::Create(this));
281 pp::Module::Get()->AddPluginInterface(kPPPPdfInterface, &ppp_private);
282 AddPerInstanceObject(kPPPPdfInterface, this);
284 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
285 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
286 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_TOUCH);
289 OutOfProcessInstance::~OutOfProcessInstance() {
290 RemovePerInstanceObject(kPPPPdfInterface, this);
293 bool OutOfProcessInstance::Init(uint32_t argc,
294 const char* argn[],
295 const char* argv[]) {
296 // Check if the PDF is being loaded in the PDF chrome extension. We only allow
297 // the plugin to be put into "full frame" mode when it is being loaded in the
298 // extension because this enables some features that we don't want pages
299 // abusing outside of the extension.
300 pp::Var document_url_var = pp::URLUtil_Dev::Get()->GetDocumentURL(this);
301 std::string document_url = document_url_var.is_string() ?
302 document_url_var.AsString() : std::string();
303 std::string extension_url = std::string(kChromeExtension);
304 bool in_extension =
305 !document_url.compare(0, extension_url.size(), extension_url);
307 if (in_extension) {
308 // Check if the plugin is full frame. This is passed in from JS.
309 for (uint32_t i = 0; i < argc; ++i) {
310 if (strcmp(argn[i], "full-frame") == 0) {
311 full_ = true;
312 break;
317 // Only allow the plugin to handle find requests if it is full frame.
318 if (full_)
319 SetPluginToHandleFindRequests();
321 // Send translated strings to the extension where they will be displayed.
322 // TODO(raymes): It would be better to get these in the extension directly
323 // through an API but no such API currently exists.
324 pp::VarDictionary translated_strings;
325 translated_strings.Set(kType, kJSSetTranslatedStringsType);
326 translated_strings.Set(kJSGetPasswordString,
327 GetLocalizedString(PP_RESOURCESTRING_PDFGETPASSWORD));
328 translated_strings.Set(kJSLoadingString,
329 GetLocalizedString(PP_RESOURCESTRING_PDFLOADING));
330 translated_strings.Set(kJSLoadFailedString,
331 GetLocalizedString(PP_RESOURCESTRING_PDFLOAD_FAILED));
332 PostMessage(translated_strings);
334 text_input_.reset(new pp::TextInput_Dev(this));
336 const char* stream_url = NULL;
337 const char* original_url = NULL;
338 const char* headers = NULL;
339 bool is_material = false;
340 for (uint32_t i = 0; i < argc; ++i) {
341 if (strcmp(argn[i], "src") == 0)
342 original_url = argv[i];
343 else if (strcmp(argn[i], "stream-url") == 0)
344 stream_url = argv[i];
345 else if (strcmp(argn[i], "headers") == 0)
346 headers = argv[i];
347 else if (strcmp(argn[i], "is-material") == 0)
348 is_material = true;
351 if (is_material)
352 background_color_ = kBackgroundColorMaterial;
353 else
354 background_color_ = kBackgroundColor;
356 // TODO(raymes): This is a hack to ensure that if no headers are passed in
357 // then we get the right MIME type. When the in process plugin is removed we
358 // can fix the document loader properly and remove this hack.
359 if (!headers || strcmp(headers, "") == 0)
360 headers = "content-type: application/pdf";
362 if (!original_url)
363 return false;
365 if (!stream_url)
366 stream_url = original_url;
368 // If we're in print preview mode we don't need to load the document yet.
369 // A |kJSResetPrintPreviewModeType| message will be sent to the plugin letting
370 // it know the url to load. By not loading here we avoid loading the same
371 // document twice.
372 if (IsPrintPreviewUrl(original_url))
373 return true;
375 LoadUrl(stream_url);
376 url_ = original_url;
377 return engine_->New(original_url, headers);
380 void OutOfProcessInstance::HandleMessage(const pp::Var& message) {
381 pp::VarDictionary dict(message);
382 if (!dict.Get(kType).is_string()) {
383 NOTREACHED();
384 return;
387 std::string type = dict.Get(kType).AsString();
389 if (type == kJSViewportType &&
390 dict.Get(pp::Var(kJSXOffset)).is_number() &&
391 dict.Get(pp::Var(kJSYOffset)).is_number() &&
392 dict.Get(pp::Var(kJSZoom)).is_number()) {
393 received_viewport_message_ = true;
394 stop_scrolling_ = false;
395 double zoom = dict.Get(pp::Var(kJSZoom)).AsDouble();
396 pp::FloatPoint scroll_offset(dict.Get(pp::Var(kJSXOffset)).AsDouble(),
397 dict.Get(pp::Var(kJSYOffset)).AsDouble());
399 // Bound the input parameters.
400 zoom = std::max(kMinZoom, zoom);
401 SetZoom(zoom);
402 scroll_offset = BoundScrollOffsetToDocument(scroll_offset);
403 engine_->ScrolledToXPosition(scroll_offset.x() * device_scale_);
404 engine_->ScrolledToYPosition(scroll_offset.y() * device_scale_);
405 } else if (type == kJSGetPasswordCompleteType &&
406 dict.Get(pp::Var(kJSPassword)).is_string()) {
407 if (password_callback_) {
408 pp::CompletionCallbackWithOutput<pp::Var> callback = *password_callback_;
409 password_callback_.reset();
410 *callback.output() = dict.Get(pp::Var(kJSPassword)).pp_var();
411 callback.Run(PP_OK);
412 } else {
413 NOTREACHED();
415 } else if (type == kJSPrintType) {
416 Print();
417 } else if (type == kJSSaveType) {
418 pp::PDF::SaveAs(this);
419 } else if (type == kJSRotateClockwiseType) {
420 RotateClockwise();
421 } else if (type == kJSRotateCounterclockwiseType) {
422 RotateCounterclockwise();
423 } else if (type == kJSSelectAllType) {
424 engine_->SelectAll();
425 } else if (type == kJSResetPrintPreviewModeType &&
426 dict.Get(pp::Var(kJSPrintPreviewUrl)).is_string() &&
427 dict.Get(pp::Var(kJSPrintPreviewGrayscale)).is_bool() &&
428 dict.Get(pp::Var(kJSPrintPreviewPageCount)).is_int()) {
429 url_ = dict.Get(pp::Var(kJSPrintPreviewUrl)).AsString();
430 preview_pages_info_ = std::queue<PreviewPageInfo>();
431 preview_document_load_state_ = LOAD_STATE_COMPLETE;
432 document_load_state_ = LOAD_STATE_LOADING;
433 LoadUrl(url_);
434 preview_engine_.reset();
435 engine_.reset(PDFEngine::Create(this));
436 engine_->SetGrayscale(dict.Get(pp::Var(kJSPrintPreviewGrayscale)).AsBool());
437 engine_->New(url_.c_str());
439 print_preview_page_count_ =
440 std::max(dict.Get(pp::Var(kJSPrintPreviewPageCount)).AsInt(), 0);
442 paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_));
443 } else if (type == kJSLoadPreviewPageType &&
444 dict.Get(pp::Var(kJSPreviewPageUrl)).is_string() &&
445 dict.Get(pp::Var(kJSPreviewPageIndex)).is_int()) {
446 ProcessPreviewPageInfo(dict.Get(pp::Var(kJSPreviewPageUrl)).AsString(),
447 dict.Get(pp::Var(kJSPreviewPageIndex)).AsInt());
448 } else if (type == kJSGetAccessibilityJSONType) {
449 pp::VarDictionary reply;
450 reply.Set(pp::Var(kType), pp::Var(kJSGetAccessibilityJSONReplyType));
451 if (dict.Get(pp::Var(kJSAccessibilityPageNumber)).is_int()) {
452 int page = dict.Get(pp::Var(kJSAccessibilityPageNumber)).AsInt();
453 reply.Set(pp::Var(kJSAccessibilityJSON),
454 pp::Var(engine_->GetPageAsJSON(page)));
455 } else {
456 base::DictionaryValue node;
457 node.SetInteger(kAccessibleNumberOfPages, engine_->GetNumberOfPages());
458 node.SetBoolean(kAccessibleLoaded,
459 document_load_state_ != LOAD_STATE_LOADING);
460 bool has_permissions =
461 engine_->HasPermission(PDFEngine::PERMISSION_COPY) ||
462 engine_->HasPermission(PDFEngine::PERMISSION_COPY_ACCESSIBLE);
463 node.SetBoolean(kAccessibleCopyable, has_permissions);
464 std::string json;
465 base::JSONWriter::Write(&node, &json);
466 reply.Set(pp::Var(kJSAccessibilityJSON), pp::Var(json));
468 PostMessage(reply);
469 } else if (type == kJSStopScrollingType) {
470 stop_scrolling_ = true;
471 } else if (type == kJSGetSelectedTextType) {
472 std::string selected_text = engine_->GetSelectedText();
473 // Always return unix newlines to JS.
474 base::ReplaceChars(selected_text, "\r", std::string(), &selected_text);
475 pp::VarDictionary reply;
476 reply.Set(pp::Var(kType), pp::Var(kJSGetSelectedTextReplyType));
477 reply.Set(pp::Var(kJSSelectedText), selected_text);
478 PostMessage(reply);
479 } else {
480 NOTREACHED();
484 bool OutOfProcessInstance::HandleInputEvent(
485 const pp::InputEvent& event) {
486 // To simplify things, convert the event into device coordinates if it is
487 // a mouse event.
488 pp::InputEvent event_device_res(event);
490 pp::MouseInputEvent mouse_event(event);
491 if (!mouse_event.is_null()) {
492 pp::Point point = mouse_event.GetPosition();
493 pp::Point movement = mouse_event.GetMovement();
494 ScalePoint(device_scale_, &point);
495 ScalePoint(device_scale_, &movement);
496 mouse_event = pp::MouseInputEvent(
497 this,
498 event.GetType(),
499 event.GetTimeStamp(),
500 event.GetModifiers(),
501 mouse_event.GetButton(),
502 point,
503 mouse_event.GetClickCount(),
504 movement);
505 event_device_res = mouse_event;
509 pp::InputEvent offset_event(event_device_res);
510 switch (offset_event.GetType()) {
511 case PP_INPUTEVENT_TYPE_MOUSEDOWN:
512 case PP_INPUTEVENT_TYPE_MOUSEUP:
513 case PP_INPUTEVENT_TYPE_MOUSEMOVE:
514 case PP_INPUTEVENT_TYPE_MOUSEENTER:
515 case PP_INPUTEVENT_TYPE_MOUSELEAVE: {
516 pp::MouseInputEvent mouse_event(event_device_res);
517 pp::MouseInputEvent mouse_event_dip(event);
518 pp::Point point = mouse_event.GetPosition();
519 point.set_x(point.x() - available_area_.x());
520 offset_event = pp::MouseInputEvent(
521 this,
522 event.GetType(),
523 event.GetTimeStamp(),
524 event.GetModifiers(),
525 mouse_event.GetButton(),
526 point,
527 mouse_event.GetClickCount(),
528 mouse_event.GetMovement());
529 break;
531 default:
532 break;
534 if (engine_->HandleEvent(offset_event))
535 return true;
537 // TODO(raymes): Implement this scroll behavior in JS:
538 // When click+dragging, scroll the document correctly.
540 // Return true for unhandled clicks so the plugin takes focus.
541 return (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN);
544 void OutOfProcessInstance::DidChangeView(const pp::View& view) {
545 pp::Rect view_rect(view.GetRect());
546 float old_device_scale = device_scale_;
547 float device_scale = view.GetDeviceScale();
548 pp::Size view_device_size(view_rect.width() * device_scale,
549 view_rect.height() * device_scale);
551 if (view_device_size != plugin_size_ || device_scale != device_scale_) {
552 device_scale_ = device_scale;
553 plugin_dip_size_ = view_rect.size();
554 plugin_size_ = view_device_size;
556 paint_manager_.SetSize(view_device_size, device_scale_);
558 pp::Size new_image_data_size = PaintManager::GetNewContextSize(
559 image_data_.size(),
560 plugin_size_);
561 if (new_image_data_size != image_data_.size()) {
562 image_data_ = pp::ImageData(this,
563 PP_IMAGEDATAFORMAT_BGRA_PREMUL,
564 new_image_data_size,
565 false);
566 first_paint_ = true;
569 if (image_data_.is_null()) {
570 DCHECK(plugin_size_.IsEmpty());
571 return;
574 OnGeometryChanged(zoom_, old_device_scale);
577 if (!stop_scrolling_) {
578 pp::Point scroll_offset(view.GetScrollOffset());
579 pp::FloatPoint scroll_offset_float(scroll_offset.x(),
580 scroll_offset.y());
581 scroll_offset_float = BoundScrollOffsetToDocument(scroll_offset_float);
582 engine_->ScrolledToXPosition(scroll_offset_float.x() * device_scale_);
583 engine_->ScrolledToYPosition(scroll_offset_float.y() * device_scale_);
587 void OutOfProcessInstance::GetPrintPresetOptionsFromDocument(
588 PP_PdfPrintPresetOptions_Dev* options) {
589 options->is_scaling_disabled = PP_FromBool(IsPrintScalingDisabled());
590 options->copies = engine_->GetCopiesToPrint();
593 pp::Var OutOfProcessInstance::GetLinkAtPosition(
594 const pp::Point& point) {
595 pp::Point offset_point(point);
596 ScalePoint(device_scale_, &offset_point);
597 offset_point.set_x(offset_point.x() - available_area_.x());
598 return engine_->GetLinkAtPosition(offset_point);
601 pp::Var OutOfProcessInstance::GetSelectedText(bool html) {
602 if (html)
603 return pp::Var();
604 return engine_->GetSelectedText();
607 uint32_t OutOfProcessInstance::QuerySupportedPrintOutputFormats() {
608 return engine_->QuerySupportedPrintOutputFormats();
611 int32_t OutOfProcessInstance::PrintBegin(
612 const PP_PrintSettings_Dev& print_settings) {
613 // For us num_pages is always equal to the number of pages in the PDF
614 // document irrespective of the printable area.
615 int32_t ret = engine_->GetNumberOfPages();
616 if (!ret)
617 return 0;
619 uint32_t supported_formats = engine_->QuerySupportedPrintOutputFormats();
620 if ((print_settings.format & supported_formats) == 0)
621 return 0;
623 print_settings_.is_printing = true;
624 print_settings_.pepper_print_settings = print_settings;
625 engine_->PrintBegin();
626 return ret;
629 pp::Resource OutOfProcessInstance::PrintPages(
630 const PP_PrintPageNumberRange_Dev* page_ranges,
631 uint32_t page_range_count) {
632 if (!print_settings_.is_printing)
633 return pp::Resource();
635 print_settings_.print_pages_called_ = true;
636 return engine_->PrintPages(page_ranges, page_range_count,
637 print_settings_.pepper_print_settings);
640 void OutOfProcessInstance::PrintEnd() {
641 if (print_settings_.print_pages_called_)
642 UserMetricsRecordAction("PDF.PrintPage");
643 print_settings_.Clear();
644 engine_->PrintEnd();
647 bool OutOfProcessInstance::IsPrintScalingDisabled() {
648 return !engine_->GetPrintScaling();
651 bool OutOfProcessInstance::StartFind(const std::string& text,
652 bool case_sensitive) {
653 engine_->StartFind(text.c_str(), case_sensitive);
654 return true;
657 void OutOfProcessInstance::SelectFindResult(bool forward) {
658 engine_->SelectFindResult(forward);
661 void OutOfProcessInstance::StopFind() {
662 engine_->StopFind();
663 tickmarks_.clear();
664 SetTickmarks(tickmarks_);
667 void OutOfProcessInstance::OnPaint(
668 const std::vector<pp::Rect>& paint_rects,
669 std::vector<PaintManager::ReadyRect>* ready,
670 std::vector<pp::Rect>* pending) {
671 if (image_data_.is_null()) {
672 DCHECK(plugin_size_.IsEmpty());
673 return;
675 if (first_paint_) {
676 first_paint_ = false;
677 pp::Rect rect = pp::Rect(pp::Point(), image_data_.size());
678 FillRect(rect, background_color_);
679 ready->push_back(PaintManager::ReadyRect(rect, image_data_, true));
682 if (!received_viewport_message_)
683 return;
685 engine_->PrePaint();
687 for (size_t i = 0; i < paint_rects.size(); i++) {
688 // Intersect with plugin area since there could be pending invalidates from
689 // when the plugin area was larger.
690 pp::Rect rect =
691 paint_rects[i].Intersect(pp::Rect(pp::Point(), plugin_size_));
692 if (rect.IsEmpty())
693 continue;
695 pp::Rect pdf_rect = available_area_.Intersect(rect);
696 if (!pdf_rect.IsEmpty()) {
697 pdf_rect.Offset(available_area_.x() * -1, 0);
699 std::vector<pp::Rect> pdf_ready;
700 std::vector<pp::Rect> pdf_pending;
701 engine_->Paint(pdf_rect, &image_data_, &pdf_ready, &pdf_pending);
702 for (size_t j = 0; j < pdf_ready.size(); ++j) {
703 pdf_ready[j].Offset(available_area_.point());
704 ready->push_back(
705 PaintManager::ReadyRect(pdf_ready[j], image_data_, false));
707 for (size_t j = 0; j < pdf_pending.size(); ++j) {
708 pdf_pending[j].Offset(available_area_.point());
709 pending->push_back(pdf_pending[j]);
713 for (size_t j = 0; j < background_parts_.size(); ++j) {
714 pp::Rect intersection = background_parts_[j].location.Intersect(rect);
715 if (!intersection.IsEmpty()) {
716 FillRect(intersection, background_parts_[j].color);
717 ready->push_back(
718 PaintManager::ReadyRect(intersection, image_data_, false));
723 engine_->PostPaint();
726 void OutOfProcessInstance::DidOpen(int32_t result) {
727 if (result == PP_OK) {
728 if (!engine_->HandleDocumentLoad(embed_loader_)) {
729 document_load_state_ = LOAD_STATE_LOADING;
730 DocumentLoadFailed();
732 } else if (result != PP_ERROR_ABORTED) { // Can happen in tests.
733 NOTREACHED();
734 DocumentLoadFailed();
737 // If it's a progressive load, cancel the stream URL request so that requests
738 // can be made on the original URL.
739 // TODO(raymes): Make this clearer once the in-process plugin is deleted.
740 if (engine_->IsProgressiveLoad()) {
741 pp::VarDictionary message;
742 message.Set(kType, kJSCancelStreamUrlType);
743 PostMessage(message);
747 void OutOfProcessInstance::DidOpenPreview(int32_t result) {
748 if (result == PP_OK) {
749 preview_engine_.reset(PDFEngine::Create(new PreviewModeClient(this)));
750 preview_engine_->HandleDocumentLoad(embed_preview_loader_);
751 } else {
752 NOTREACHED();
756 void OutOfProcessInstance::OnClientTimerFired(int32_t id) {
757 engine_->OnCallback(id);
760 void OutOfProcessInstance::CalculateBackgroundParts() {
761 background_parts_.clear();
762 int left_width = available_area_.x();
763 int right_start = available_area_.right();
764 int right_width = abs(plugin_size_.width() - available_area_.right());
765 int bottom = std::min(available_area_.bottom(), plugin_size_.height());
767 // Add the left, right, and bottom rectangles. Note: we assume only
768 // horizontal centering.
769 BackgroundPart part = {
770 pp::Rect(0, 0, left_width, bottom),
771 background_color_
773 if (!part.location.IsEmpty())
774 background_parts_.push_back(part);
775 part.location = pp::Rect(right_start, 0, right_width, bottom);
776 if (!part.location.IsEmpty())
777 background_parts_.push_back(part);
778 part.location = pp::Rect(
779 0, bottom, plugin_size_.width(), plugin_size_.height() - bottom);
780 if (!part.location.IsEmpty())
781 background_parts_.push_back(part);
784 int OutOfProcessInstance::GetDocumentPixelWidth() const {
785 return static_cast<int>(ceil(document_size_.width() * zoom_ * device_scale_));
788 int OutOfProcessInstance::GetDocumentPixelHeight() const {
789 return static_cast<int>(
790 ceil(document_size_.height() * zoom_ * device_scale_));
793 void OutOfProcessInstance::FillRect(const pp::Rect& rect, uint32 color) {
794 DCHECK(!image_data_.is_null() || rect.IsEmpty());
795 uint32* buffer_start = static_cast<uint32*>(image_data_.data());
796 int stride = image_data_.stride();
797 uint32* ptr = buffer_start + rect.y() * stride / 4 + rect.x();
798 int height = rect.height();
799 int width = rect.width();
800 for (int y = 0; y < height; ++y) {
801 for (int x = 0; x < width; ++x)
802 *(ptr + x) = color;
803 ptr += stride /4;
807 void OutOfProcessInstance::DocumentSizeUpdated(const pp::Size& size) {
808 document_size_ = size;
810 pp::VarDictionary dimensions;
811 dimensions.Set(kType, kJSDocumentDimensionsType);
812 dimensions.Set(kJSDocumentWidth, pp::Var(document_size_.width()));
813 dimensions.Set(kJSDocumentHeight, pp::Var(document_size_.height()));
814 pp::VarArray page_dimensions_array;
815 int num_pages = engine_->GetNumberOfPages();
816 for (int i = 0; i < num_pages; ++i) {
817 pp::Rect page_rect = engine_->GetPageRect(i);
818 pp::VarDictionary page_dimensions;
819 page_dimensions.Set(kJSPageX, pp::Var(page_rect.x()));
820 page_dimensions.Set(kJSPageY, pp::Var(page_rect.y()));
821 page_dimensions.Set(kJSPageWidth, pp::Var(page_rect.width()));
822 page_dimensions.Set(kJSPageHeight, pp::Var(page_rect.height()));
823 page_dimensions_array.Set(i, page_dimensions);
825 dimensions.Set(kJSPageDimensions, page_dimensions_array);
826 PostMessage(dimensions);
828 OnGeometryChanged(zoom_, device_scale_);
831 void OutOfProcessInstance::Invalidate(const pp::Rect& rect) {
832 pp::Rect offset_rect(rect);
833 offset_rect.Offset(available_area_.point());
834 paint_manager_.InvalidateRect(offset_rect);
837 void OutOfProcessInstance::Scroll(const pp::Point& point) {
838 if (!image_data_.is_null())
839 paint_manager_.ScrollRect(available_area_, point);
842 void OutOfProcessInstance::ScrollToX(int x) {
843 pp::VarDictionary position;
844 position.Set(kType, kJSSetScrollPositionType);
845 position.Set(kJSPositionX, pp::Var(x / device_scale_));
846 PostMessage(position);
849 void OutOfProcessInstance::ScrollToY(int y) {
850 pp::VarDictionary position;
851 position.Set(kType, kJSSetScrollPositionType);
852 position.Set(kJSPositionY, pp::Var(y / device_scale_));
853 PostMessage(position);
856 void OutOfProcessInstance::ScrollToPage(int page) {
857 if (engine_->GetNumberOfPages() == 0)
858 return;
860 pp::VarDictionary message;
861 message.Set(kType, kJSGoToPageType);
862 message.Set(kJSPageNumber, pp::Var(page));
863 PostMessage(message);
866 void OutOfProcessInstance::NavigateTo(const std::string& url,
867 bool open_in_new_tab) {
868 pp::VarDictionary message;
869 message.Set(kType, kJSNavigateType);
870 message.Set(kJSNavigateUrl, url);
871 message.Set(kJSNavigateNewTab, open_in_new_tab);
872 PostMessage(message);
875 void OutOfProcessInstance::UpdateCursor(PP_CursorType_Dev cursor) {
876 if (cursor == cursor_)
877 return;
878 cursor_ = cursor;
880 const PPB_CursorControl_Dev* cursor_interface =
881 reinterpret_cast<const PPB_CursorControl_Dev*>(
882 pp::Module::Get()->GetBrowserInterface(PPB_CURSOR_CONTROL_DEV_INTERFACE));
883 if (!cursor_interface) {
884 NOTREACHED();
885 return;
888 cursor_interface->SetCursor(
889 pp_instance(), cursor_, pp::ImageData().pp_resource(), NULL);
892 void OutOfProcessInstance::UpdateTickMarks(
893 const std::vector<pp::Rect>& tickmarks) {
894 float inverse_scale = 1.0f / device_scale_;
895 std::vector<pp::Rect> scaled_tickmarks = tickmarks;
896 for (size_t i = 0; i < scaled_tickmarks.size(); i++)
897 ScaleRect(inverse_scale, &scaled_tickmarks[i]);
898 tickmarks_ = scaled_tickmarks;
901 void OutOfProcessInstance::NotifyNumberOfFindResultsChanged(int total,
902 bool final_result) {
903 // We don't want to spam the renderer with too many updates to the number of
904 // find results. Don't send an update if we sent one too recently. If it's the
905 // final update, we always send it though.
906 if (final_result) {
907 NumberOfFindResultsChanged(total, final_result);
908 SetTickmarks(tickmarks_);
909 return;
912 if (recently_sent_find_update_)
913 return;
915 NumberOfFindResultsChanged(total, final_result);
916 SetTickmarks(tickmarks_);
917 recently_sent_find_update_ = true;
918 pp::CompletionCallback callback =
919 timer_factory_.NewCallback(
920 &OutOfProcessInstance::ResetRecentlySentFindUpdate);
921 pp::Module::Get()->core()->CallOnMainThread(kFindResultCooldownMs,
922 callback, 0);
925 void OutOfProcessInstance::NotifySelectedFindResultChanged(
926 int current_find_index) {
927 DCHECK_GE(current_find_index, 0);
928 SelectedFindResultChanged(current_find_index);
931 void OutOfProcessInstance::GetDocumentPassword(
932 pp::CompletionCallbackWithOutput<pp::Var> callback) {
933 if (password_callback_) {
934 NOTREACHED();
935 return;
938 password_callback_.reset(
939 new pp::CompletionCallbackWithOutput<pp::Var>(callback));
940 pp::VarDictionary message;
941 message.Set(pp::Var(kType), pp::Var(kJSGetPasswordType));
942 PostMessage(message);
945 void OutOfProcessInstance::Alert(const std::string& message) {
946 ModalDialog(this, "alert", message, std::string());
949 bool OutOfProcessInstance::Confirm(const std::string& message) {
950 pp::Var result = ModalDialog(this, "confirm", message, std::string());
951 return result.is_bool() ? result.AsBool() : false;
954 std::string OutOfProcessInstance::Prompt(const std::string& question,
955 const std::string& default_answer) {
956 pp::Var result = ModalDialog(this, "prompt", question, default_answer);
957 return result.is_string() ? result.AsString() : std::string();
960 std::string OutOfProcessInstance::GetURL() {
961 return url_;
964 void OutOfProcessInstance::Email(const std::string& to,
965 const std::string& cc,
966 const std::string& bcc,
967 const std::string& subject,
968 const std::string& body) {
969 pp::VarDictionary message;
970 message.Set(pp::Var(kType), pp::Var(kJSEmailType));
971 message.Set(pp::Var(kJSEmailTo),
972 pp::Var(net::EscapeUrlEncodedData(to, false)));
973 message.Set(pp::Var(kJSEmailCc),
974 pp::Var(net::EscapeUrlEncodedData(cc, false)));
975 message.Set(pp::Var(kJSEmailBcc),
976 pp::Var(net::EscapeUrlEncodedData(bcc, false)));
977 message.Set(pp::Var(kJSEmailSubject),
978 pp::Var(net::EscapeUrlEncodedData(subject, false)));
979 message.Set(pp::Var(kJSEmailBody),
980 pp::Var(net::EscapeUrlEncodedData(body, false)));
981 PostMessage(message);
984 void OutOfProcessInstance::Print() {
985 if (!engine_->HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY) &&
986 !engine_->HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY)) {
987 return;
990 pp::CompletionCallback callback =
991 print_callback_factory_.NewCallback(&OutOfProcessInstance::OnPrint);
992 pp::Module::Get()->core()->CallOnMainThread(0, callback);
995 void OutOfProcessInstance::OnPrint(int32_t) {
996 pp::PDF::Print(this);
999 void OutOfProcessInstance::SubmitForm(const std::string& url,
1000 const void* data,
1001 int length) {
1002 pp::URLRequestInfo request(this);
1003 request.SetURL(url);
1004 request.SetMethod("POST");
1005 request.AppendDataToBody(reinterpret_cast<const char*>(data), length);
1007 pp::CompletionCallback callback =
1008 form_factory_.NewCallback(&OutOfProcessInstance::FormDidOpen);
1009 form_loader_ = CreateURLLoaderInternal();
1010 int rv = form_loader_.Open(request, callback);
1011 if (rv != PP_OK_COMPLETIONPENDING)
1012 callback.Run(rv);
1015 void OutOfProcessInstance::FormDidOpen(int32_t result) {
1016 // TODO: inform the user of success/failure.
1017 if (result != PP_OK) {
1018 NOTREACHED();
1022 std::string OutOfProcessInstance::ShowFileSelectionDialog() {
1023 // Seems like very low priority to implement, since the pdf has no way to get
1024 // the file data anyways. Javascript doesn't let you do this synchronously.
1025 NOTREACHED();
1026 return std::string();
1029 pp::URLLoader OutOfProcessInstance::CreateURLLoader() {
1030 if (full_) {
1031 if (!did_call_start_loading_) {
1032 did_call_start_loading_ = true;
1033 pp::PDF::DidStartLoading(this);
1036 // Disable save and print until the document is fully loaded, since they
1037 // would generate an incomplete document. Need to do this each time we
1038 // call DidStartLoading since that resets the content restrictions.
1039 pp::PDF::SetContentRestriction(this, CONTENT_RESTRICTION_SAVE |
1040 CONTENT_RESTRICTION_PRINT);
1043 return CreateURLLoaderInternal();
1046 void OutOfProcessInstance::ScheduleCallback(int id, int delay_in_ms) {
1047 pp::CompletionCallback callback =
1048 timer_factory_.NewCallback(&OutOfProcessInstance::OnClientTimerFired);
1049 pp::Module::Get()->core()->CallOnMainThread(delay_in_ms, callback, id);
1052 void OutOfProcessInstance::SearchString(const base::char16* string,
1053 const base::char16* term,
1054 bool case_sensitive,
1055 std::vector<SearchStringResult>* results) {
1056 PP_PrivateFindResult* pp_results;
1057 int count = 0;
1058 pp::PDF::SearchString(
1059 this,
1060 reinterpret_cast<const unsigned short*>(string),
1061 reinterpret_cast<const unsigned short*>(term),
1062 case_sensitive,
1063 &pp_results,
1064 &count);
1066 results->resize(count);
1067 for (int i = 0; i < count; ++i) {
1068 (*results)[i].start_index = pp_results[i].start_index;
1069 (*results)[i].length = pp_results[i].length;
1072 pp::Memory_Dev memory;
1073 memory.MemFree(pp_results);
1076 void OutOfProcessInstance::DocumentPaintOccurred() {
1079 void OutOfProcessInstance::DocumentLoadComplete(int page_count) {
1080 // Clear focus state for OSK.
1081 FormTextFieldFocusChange(false);
1083 DCHECK(document_load_state_ == LOAD_STATE_LOADING);
1084 document_load_state_ = LOAD_STATE_COMPLETE;
1085 UserMetricsRecordAction("PDF.LoadSuccess");
1087 // Note: If we are in print preview mode the scroll location is retained
1088 // across document loads so we don't want to scroll again and override it.
1089 if (IsPrintPreview()) {
1090 AppendBlankPrintPreviewPages();
1091 OnGeometryChanged(0, 0);
1094 pp::VarDictionary named_destinations_message;
1095 pp::VarDictionary named_destinations = engine_->GetNamedDestinations();
1096 named_destinations_message.Set(pp::Var(kType),
1097 pp::Var(kJSSetNamedDestinationsType));
1098 named_destinations_message.Set(pp::Var(kJSNamedDestinations),
1099 pp::Var(named_destinations));
1100 PostMessage(named_destinations_message);
1102 pp::VarDictionary bookmarks_message;
1103 bookmarks_message.Set(pp::Var(kType), pp::Var(kJSBookmarksType));
1104 bookmarks_message.Set(pp::Var(kJSBookmarks), engine_->GetBookmarks());
1105 PostMessage(bookmarks_message);
1107 pp::VarDictionary progress_message;
1108 progress_message.Set(pp::Var(kType), pp::Var(kJSLoadProgressType));
1109 progress_message.Set(pp::Var(kJSProgressPercentage), pp::Var(100));
1110 PostMessage(progress_message);
1112 if (!full_)
1113 return;
1115 if (did_call_start_loading_) {
1116 pp::PDF::DidStopLoading(this);
1117 did_call_start_loading_ = false;
1120 int content_restrictions =
1121 CONTENT_RESTRICTION_CUT | CONTENT_RESTRICTION_PASTE;
1122 if (!engine_->HasPermission(PDFEngine::PERMISSION_COPY))
1123 content_restrictions |= CONTENT_RESTRICTION_COPY;
1125 pp::PDF::SetContentRestriction(this, content_restrictions);
1127 uma_.HistogramCustomCounts("PDF.PageCount", page_count,
1128 1, 1000000, 50);
1131 void OutOfProcessInstance::RotateClockwise() {
1132 engine_->RotateClockwise();
1135 void OutOfProcessInstance::RotateCounterclockwise() {
1136 engine_->RotateCounterclockwise();
1139 void OutOfProcessInstance::PreviewDocumentLoadComplete() {
1140 if (preview_document_load_state_ != LOAD_STATE_LOADING ||
1141 preview_pages_info_.empty()) {
1142 return;
1145 preview_document_load_state_ = LOAD_STATE_COMPLETE;
1147 int dest_page_index = preview_pages_info_.front().second;
1148 int src_page_index =
1149 ExtractPrintPreviewPageIndex(preview_pages_info_.front().first);
1150 if (src_page_index > 0 && dest_page_index > -1 && preview_engine_.get())
1151 engine_->AppendPage(preview_engine_.get(), dest_page_index);
1153 preview_pages_info_.pop();
1154 // |print_preview_page_count_| is not updated yet. Do not load any
1155 // other preview pages till we get this information.
1156 if (print_preview_page_count_ == 0)
1157 return;
1159 if (preview_pages_info_.size())
1160 LoadAvailablePreviewPage();
1163 void OutOfProcessInstance::DocumentLoadFailed() {
1164 DCHECK(document_load_state_ == LOAD_STATE_LOADING);
1165 UserMetricsRecordAction("PDF.LoadFailure");
1167 if (did_call_start_loading_) {
1168 pp::PDF::DidStopLoading(this);
1169 did_call_start_loading_ = false;
1172 document_load_state_ = LOAD_STATE_FAILED;
1173 paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_));
1175 // Send a progress value of -1 to indicate a failure.
1176 pp::VarDictionary message;
1177 message.Set(pp::Var(kType), pp::Var(kJSLoadProgressType));
1178 message.Set(pp::Var(kJSProgressPercentage), pp::Var(-1)) ;
1179 PostMessage(message);
1182 void OutOfProcessInstance::PreviewDocumentLoadFailed() {
1183 UserMetricsRecordAction("PDF.PreviewDocumentLoadFailure");
1184 if (preview_document_load_state_ != LOAD_STATE_LOADING ||
1185 preview_pages_info_.empty()) {
1186 return;
1189 preview_document_load_state_ = LOAD_STATE_FAILED;
1190 preview_pages_info_.pop();
1192 if (preview_pages_info_.size())
1193 LoadAvailablePreviewPage();
1196 pp::Instance* OutOfProcessInstance::GetPluginInstance() {
1197 return this;
1200 void OutOfProcessInstance::DocumentHasUnsupportedFeature(
1201 const std::string& feature) {
1202 std::string metric("PDF_Unsupported_");
1203 metric += feature;
1204 if (!unsupported_features_reported_.count(metric)) {
1205 unsupported_features_reported_.insert(metric);
1206 UserMetricsRecordAction(metric);
1209 // Since we use an info bar, only do this for full frame plugins..
1210 if (!full_)
1211 return;
1213 if (told_browser_about_unsupported_feature_)
1214 return;
1215 told_browser_about_unsupported_feature_ = true;
1217 pp::PDF::HasUnsupportedFeature(this);
1220 void OutOfProcessInstance::DocumentLoadProgress(uint32 available,
1221 uint32 doc_size) {
1222 double progress = 0.0;
1223 if (doc_size == 0) {
1224 // Document size is unknown. Use heuristics.
1225 // We'll make progress logarithmic from 0 to 100M.
1226 static const double kFactor = log(100000000.0) / 100.0;
1227 if (available > 0) {
1228 progress = log(static_cast<double>(available)) / kFactor;
1229 if (progress > 100.0)
1230 progress = 100.0;
1232 } else {
1233 progress = 100.0 * static_cast<double>(available) / doc_size;
1236 // We send 100% load progress in DocumentLoadComplete.
1237 if (progress >= 100)
1238 return;
1240 // Avoid sending too many progress messages over PostMessage.
1241 if (progress > last_progress_sent_ + 1) {
1242 last_progress_sent_ = progress;
1243 pp::VarDictionary message;
1244 message.Set(pp::Var(kType), pp::Var(kJSLoadProgressType));
1245 message.Set(pp::Var(kJSProgressPercentage), pp::Var(progress)) ;
1246 PostMessage(message);
1250 void OutOfProcessInstance::FormTextFieldFocusChange(bool in_focus) {
1251 if (!text_input_.get())
1252 return;
1253 if (in_focus)
1254 text_input_->SetTextInputType(PP_TEXTINPUT_TYPE_DEV_TEXT);
1255 else
1256 text_input_->SetTextInputType(PP_TEXTINPUT_TYPE_DEV_NONE);
1259 void OutOfProcessInstance::ResetRecentlySentFindUpdate(int32_t /* unused */) {
1260 recently_sent_find_update_ = false;
1263 void OutOfProcessInstance::OnGeometryChanged(double old_zoom,
1264 float old_device_scale) {
1265 if (zoom_ != old_zoom || device_scale_ != old_device_scale)
1266 engine_->ZoomUpdated(zoom_ * device_scale_);
1268 available_area_ = pp::Rect(plugin_size_);
1269 int doc_width = GetDocumentPixelWidth();
1270 if (doc_width < available_area_.width()) {
1271 available_area_.Offset((available_area_.width() - doc_width) / 2, 0);
1272 available_area_.set_width(doc_width);
1274 int doc_height = GetDocumentPixelHeight();
1275 if (doc_height < available_area_.height()) {
1276 available_area_.set_height(doc_height);
1279 CalculateBackgroundParts();
1280 engine_->PageOffsetUpdated(available_area_.point());
1281 engine_->PluginSizeUpdated(available_area_.size());
1283 if (!document_size_.GetArea())
1284 return;
1285 paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_));
1288 void OutOfProcessInstance::LoadUrl(const std::string& url) {
1289 LoadUrlInternal(url, &embed_loader_, &OutOfProcessInstance::DidOpen);
1292 void OutOfProcessInstance::LoadPreviewUrl(const std::string& url) {
1293 LoadUrlInternal(url, &embed_preview_loader_,
1294 &OutOfProcessInstance::DidOpenPreview);
1297 void OutOfProcessInstance::LoadUrlInternal(
1298 const std::string& url,
1299 pp::URLLoader* loader,
1300 void (OutOfProcessInstance::* method)(int32_t)) {
1301 pp::URLRequestInfo request(this);
1302 request.SetURL(url);
1303 request.SetMethod("GET");
1305 *loader = CreateURLLoaderInternal();
1306 pp::CompletionCallback callback = loader_factory_.NewCallback(method);
1307 int rv = loader->Open(request, callback);
1308 if (rv != PP_OK_COMPLETIONPENDING)
1309 callback.Run(rv);
1312 pp::URLLoader OutOfProcessInstance::CreateURLLoaderInternal() {
1313 pp::URLLoader loader(this);
1315 const PPB_URLLoaderTrusted* trusted_interface =
1316 reinterpret_cast<const PPB_URLLoaderTrusted*>(
1317 pp::Module::Get()->GetBrowserInterface(
1318 PPB_URLLOADERTRUSTED_INTERFACE));
1319 if (trusted_interface)
1320 trusted_interface->GrantUniversalAccess(loader.pp_resource());
1321 return loader;
1324 void OutOfProcessInstance::SetZoom(double scale) {
1325 double old_zoom = zoom_;
1326 zoom_ = scale;
1327 OnGeometryChanged(old_zoom, device_scale_);
1330 std::string OutOfProcessInstance::GetLocalizedString(PP_ResourceString id) {
1331 pp::Var rv(pp::PDF::GetLocalizedString(this, id));
1332 if (!rv.is_string())
1333 return std::string();
1335 return rv.AsString();
1338 void OutOfProcessInstance::AppendBlankPrintPreviewPages() {
1339 if (print_preview_page_count_ == 0)
1340 return;
1341 engine_->AppendBlankPages(print_preview_page_count_);
1342 if (preview_pages_info_.size() > 0)
1343 LoadAvailablePreviewPage();
1346 bool OutOfProcessInstance::IsPrintPreview() {
1347 return IsPrintPreviewUrl(url_);
1350 uint32 OutOfProcessInstance::GetBackgroundColor() {
1351 return background_color_;
1354 void OutOfProcessInstance::ProcessPreviewPageInfo(const std::string& url,
1355 int dst_page_index) {
1356 if (!IsPrintPreview())
1357 return;
1359 int src_page_index = ExtractPrintPreviewPageIndex(url);
1360 if (src_page_index < 1)
1361 return;
1363 preview_pages_info_.push(std::make_pair(url, dst_page_index));
1364 LoadAvailablePreviewPage();
1367 void OutOfProcessInstance::LoadAvailablePreviewPage() {
1368 if (preview_pages_info_.size() <= 0 ||
1369 document_load_state_ != LOAD_STATE_COMPLETE) {
1370 return;
1373 std::string url = preview_pages_info_.front().first;
1374 int dst_page_index = preview_pages_info_.front().second;
1375 int src_page_index = ExtractPrintPreviewPageIndex(url);
1376 if (src_page_index < 1 ||
1377 dst_page_index >= print_preview_page_count_ ||
1378 preview_document_load_state_ == LOAD_STATE_LOADING) {
1379 return;
1382 preview_document_load_state_ = LOAD_STATE_LOADING;
1383 LoadPreviewUrl(url);
1386 void OutOfProcessInstance::UserMetricsRecordAction(
1387 const std::string& action) {
1388 // TODO(raymes): Move this function to PPB_UMA_Private.
1389 pp::PDF::UserMetricsRecordAction(this, pp::Var(action));
1392 pp::FloatPoint OutOfProcessInstance::BoundScrollOffsetToDocument(
1393 const pp::FloatPoint& scroll_offset) {
1394 float max_x = document_size_.width() * zoom_ - plugin_dip_size_.width();
1395 float x = std::max(std::min(scroll_offset.x(), max_x), 0.0f);
1396 float max_y = document_size_.height() * zoom_ - plugin_dip_size_.height();
1397 float y = std::max(std::min(scroll_offset.y(), max_y), 0.0f);
1398 return pp::FloatPoint(x, y);
1401 } // namespace chrome_pdf