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