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