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/instance.h" 6 7 #include <algorithm> // for min() 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/browser/chrome_page_zoom_constants.h" 21 #include "chrome/common/content_restriction.h" 22 #include "content/public/common/page_zoom.h" 23 #include "net/base/escape.h" 24 #include "pdf/draw_utils.h" 25 #include "pdf/number_image_generator.h" 26 #include "pdf/pdf.h" 27 #include "pdf/resource_consts.h" 28 #include "ppapi/c/dev/ppb_cursor_control_dev.h" 29 #include "ppapi/c/pp_errors.h" 30 #include "ppapi/c/pp_rect.h" 31 #include "ppapi/c/private/ppp_pdf.h" 32 #include "ppapi/c/trusted/ppb_url_loader_trusted.h" 33 #include "ppapi/cpp/core.h" 34 #include "ppapi/cpp/dev/font_dev.h" 35 #include "ppapi/cpp/dev/memory_dev.h" 36 #include "ppapi/cpp/dev/text_input_dev.h" 37 #include "ppapi/cpp/module.h" 38 #include "ppapi/cpp/point.h" 39 #include "ppapi/cpp/private/pdf.h" 40 #include "ppapi/cpp/rect.h" 41 #include "ppapi/cpp/resource.h" 42 #include "ppapi/cpp/url_request_info.h" 43 #include "ui/events/keycodes/keyboard_codes.h" 44 45 #if defined(OS_MACOSX) 46 #include "base/mac/mac_util.h" 47 #endif 48 49 namespace chrome_pdf { 50 51 struct ToolbarButtonInfo { 52 uint32 id; 53 Button::ButtonStyle style; 54 PP_ResourceImage normal; 55 PP_ResourceImage highlighted; 56 PP_ResourceImage pressed; 57 }; 58 59 namespace { 60 61 // Uncomment following #define to enable thumbnails. 62 // #define ENABLE_THUMBNAILS 63 64 const uint32 kToolbarSplashTimeoutMs = 6000; 65 const uint32 kMessageTextColor = 0xFF575757; 66 const uint32 kMessageTextSize = 22; 67 const uint32 kProgressFadeTimeoutMs = 250; 68 const uint32 kProgressDelayTimeoutMs = 1000; 69 const uint32 kAutoScrollTimeoutMs = 50; 70 const double kAutoScrollFactor = 0.2; 71 72 // Javascript methods. 73 const char* kJSAccessibility = "accessibility"; 74 const char* kJSDocumentLoadComplete = "documentLoadComplete"; 75 const char* kJSGetHeight = "getHeight"; 76 const char* kJSGetHorizontalScrollbarThickness = 77 "getHorizontalScrollbarThickness"; 78 const char* kJSGetPageLocationNormalized = "getPageLocationNormalized"; 79 const char* kJSGetVerticalScrollbarThickness = "getVerticalScrollbarThickness"; 80 const char* kJSGetWidth = "getWidth"; 81 const char* kJSGetZoomLevel = "getZoomLevel"; 82 const char* kJSGoToPage = "goToPage"; 83 const char* kJSGrayscale = "grayscale"; 84 const char* kJSLoadPreviewPage = "loadPreviewPage"; 85 const char* kJSOnLoad = "onload"; 86 const char* kJSOnPluginSizeChanged = "onPluginSizeChanged"; 87 const char* kJSOnScroll = "onScroll"; 88 const char* kJSPageXOffset = "pageXOffset"; 89 const char* kJSPageYOffset = "pageYOffset"; 90 const char* kJSPrintPreviewPageCount = "printPreviewPageCount"; 91 const char* kJSReload = "reload"; 92 const char* kJSRemovePrintButton = "removePrintButton"; 93 const char* kJSResetPrintPreviewUrl = "resetPrintPreviewUrl"; 94 const char* kJSSendKeyEvent = "sendKeyEvent"; 95 const char* kJSSetPageNumbers = "setPageNumbers"; 96 const char* kJSSetPageXOffset = "setPageXOffset"; 97 const char* kJSSetPageYOffset = "setPageYOffset"; 98 const char* kJSSetZoomLevel = "setZoomLevel"; 99 const char* kJSZoomFitToHeight = "fitToHeight"; 100 const char* kJSZoomFitToWidth = "fitToWidth"; 101 const char* kJSZoomIn = "zoomIn"; 102 const char* kJSZoomOut = "zoomOut"; 103 104 // URL reference parameters. 105 // For more possible parameters, see RFC 3778 and the "PDF Open Parameters" 106 // document from Adobe. 107 const char kDelimiters[] = "#&"; 108 const char kNamedDest[] = "nameddest"; 109 const char kPage[] = "page"; 110 111 const char kChromePrint[] = "chrome://print/"; 112 113 // Dictionary Value key names for the document accessibility info 114 const char kAccessibleNumberOfPages[] = "numberOfPages"; 115 const char kAccessibleLoaded[] = "loaded"; 116 const char kAccessibleCopyable[] = "copyable"; 117 118 const ToolbarButtonInfo kPDFToolbarButtons[] = { 119 { kFitToPageButtonId, Button::BUTTON_STATE, 120 PP_RESOURCEIMAGE_PDF_BUTTON_FTP, 121 PP_RESOURCEIMAGE_PDF_BUTTON_FTP_HOVER, 122 PP_RESOURCEIMAGE_PDF_BUTTON_FTP_PRESSED }, 123 { kFitToWidthButtonId, Button::BUTTON_STATE, 124 PP_RESOURCEIMAGE_PDF_BUTTON_FTW, 125 PP_RESOURCEIMAGE_PDF_BUTTON_FTW_HOVER, 126 PP_RESOURCEIMAGE_PDF_BUTTON_FTW_PRESSED }, 127 { kZoomOutButtonId, Button::BUTTON_CLICKABLE, 128 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT, 129 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_HOVER, 130 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_PRESSED }, 131 { kZoomInButtonId, Button::BUTTON_CLICKABLE, 132 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN, 133 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_HOVER, 134 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_PRESSED }, 135 { kSaveButtonId, Button::BUTTON_CLICKABLE, 136 PP_RESOURCEIMAGE_PDF_BUTTON_SAVE, 137 PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_HOVER, 138 PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_PRESSED }, 139 { kPrintButtonId, Button::BUTTON_CLICKABLE, 140 PP_RESOURCEIMAGE_PDF_BUTTON_PRINT, 141 PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_HOVER, 142 PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_PRESSED }, 143 }; 144 145 const ToolbarButtonInfo kPDFNoPrintToolbarButtons[] = { 146 { kFitToPageButtonId, Button::BUTTON_STATE, 147 PP_RESOURCEIMAGE_PDF_BUTTON_FTP, 148 PP_RESOURCEIMAGE_PDF_BUTTON_FTP_HOVER, 149 PP_RESOURCEIMAGE_PDF_BUTTON_FTP_PRESSED }, 150 { kFitToWidthButtonId, Button::BUTTON_STATE, 151 PP_RESOURCEIMAGE_PDF_BUTTON_FTW, 152 PP_RESOURCEIMAGE_PDF_BUTTON_FTW_HOVER, 153 PP_RESOURCEIMAGE_PDF_BUTTON_FTW_PRESSED }, 154 { kZoomOutButtonId, Button::BUTTON_CLICKABLE, 155 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT, 156 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_HOVER, 157 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_PRESSED }, 158 { kZoomInButtonId, Button::BUTTON_CLICKABLE, 159 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN, 160 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_HOVER, 161 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_PRESSED }, 162 { kSaveButtonId, Button::BUTTON_CLICKABLE, 163 PP_RESOURCEIMAGE_PDF_BUTTON_SAVE, 164 PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_HOVER, 165 PP_RESOURCEIMAGE_PDF_BUTTON_SAVE_PRESSED }, 166 { kPrintButtonId, Button::BUTTON_CLICKABLE, 167 PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_DISABLED, 168 PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_DISABLED, 169 PP_RESOURCEIMAGE_PDF_BUTTON_PRINT_DISABLED } 170 }; 171 172 const ToolbarButtonInfo kPrintPreviewToolbarButtons[] = { 173 { kFitToPageButtonId, Button::BUTTON_STATE, 174 PP_RESOURCEIMAGE_PDF_BUTTON_FTP, 175 PP_RESOURCEIMAGE_PDF_BUTTON_FTP_HOVER, 176 PP_RESOURCEIMAGE_PDF_BUTTON_FTP_PRESSED }, 177 { kFitToWidthButtonId, Button::BUTTON_STATE, 178 PP_RESOURCEIMAGE_PDF_BUTTON_FTW, 179 PP_RESOURCEIMAGE_PDF_BUTTON_FTW_HOVER, 180 PP_RESOURCEIMAGE_PDF_BUTTON_FTW_PRESSED }, 181 { kZoomOutButtonId, Button::BUTTON_CLICKABLE, 182 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT, 183 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_HOVER, 184 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMOUT_PRESSED }, 185 { kZoomInButtonId, Button::BUTTON_CLICKABLE, 186 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END, 187 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_HOVER, 188 PP_RESOURCEIMAGE_PDF_BUTTON_ZOOMIN_END_PRESSED }, 189 }; 190 191 static const char kPPPPdfInterface[] = PPP_PDF_INTERFACE_1; 192 193 PP_Var GetLinkAtPosition(PP_Instance instance, PP_Point point) { 194 pp::Var var; 195 void* object = 196 pp::Instance::GetPerInstanceObject(instance, kPPPPdfInterface); 197 if (object) 198 var = static_cast<Instance*>(object)->GetLinkAtPosition(pp::Point(point)); 199 return var.Detach(); 200 } 201 202 void Transform(PP_Instance instance, PP_PrivatePageTransformType type) { 203 void* object = 204 pp::Instance::GetPerInstanceObject(instance, kPPPPdfInterface); 205 if (object) { 206 Instance* obj_instance = static_cast<Instance*>(object); 207 switch (type) { 208 case PP_PRIVATEPAGETRANSFORMTYPE_ROTATE_90_CW: 209 obj_instance->RotateClockwise(); 210 break; 211 case PP_PRIVATEPAGETRANSFORMTYPE_ROTATE_90_CCW: 212 obj_instance->RotateCounterclockwise(); 213 break; 214 } 215 } 216 } 217 218 const PPP_Pdf ppp_private = { 219 &GetLinkAtPosition, 220 &Transform 221 }; 222 223 int ExtractPrintPreviewPageIndex(const std::string& src_url) { 224 // Sample |src_url| format: chrome://print/id/page_index/print.pdf 225 std::vector<std::string> url_substr; 226 base::SplitString(src_url.substr(strlen(kChromePrint)), '/', &url_substr); 227 if (url_substr.size() != 3) 228 return -1; 229 230 if (url_substr[2] != "print.pdf") 231 return -1; 232 233 int page_index = 0; 234 if (!base::StringToInt(url_substr[1], &page_index)) 235 return -1; 236 return page_index; 237 } 238 239 bool IsPrintPreviewUrl(const std::string& url) { 240 return url.substr(0, strlen(kChromePrint)) == kChromePrint; 241 } 242 243 void ScalePoint(float scale, pp::Point* point) { 244 point->set_x(static_cast<int>(point->x() * scale)); 245 point->set_y(static_cast<int>(point->y() * scale)); 246 } 247 248 void ScaleRect(float scale, pp::Rect* rect) { 249 int left = static_cast<int>(floorf(rect->x() * scale)); 250 int top = static_cast<int>(floorf(rect->y() * scale)); 251 int right = static_cast<int>(ceilf((rect->x() + rect->width()) * scale)); 252 int bottom = static_cast<int>(ceilf((rect->y() + rect->height()) * scale)); 253 rect->SetRect(left, top, right - left, bottom - top); 254 } 255 256 template<class T> 257 T ClipToRange(T value, T lower_boundary, T upper_boundary) { 258 DCHECK(lower_boundary <= upper_boundary); 259 return std::max<T>(lower_boundary, std::min<T>(value, upper_boundary)); 260 } 261 262 } // namespace 263 264 Instance::Instance(PP_Instance instance) 265 : pp::InstancePrivate(instance), 266 pp::Find_Private(this), 267 pp::Printing_Dev(this), 268 pp::Selection_Dev(this), 269 pp::WidgetClient_Dev(this), 270 pp::Zoom_Dev(this), 271 cursor_(PP_CURSORTYPE_POINTER), 272 timer_pending_(false), 273 current_timer_id_(0), 274 zoom_(1.0), 275 device_scale_(1.0), 276 printing_enabled_(true), 277 hidpi_enabled_(false), 278 full_(IsFullFrame()), 279 zoom_mode_(full_ ? ZOOM_AUTO : ZOOM_SCALE), 280 did_call_start_loading_(false), 281 is_autoscroll_(false), 282 scrollbar_thickness_(-1), 283 scrollbar_reserved_thickness_(-1), 284 current_tb_info_(NULL), 285 current_tb_info_size_(0), 286 paint_manager_(this, this, true), 287 delayed_progress_timer_id_(0), 288 first_paint_(true), 289 painted_first_page_(false), 290 show_page_indicator_(false), 291 document_load_state_(LOAD_STATE_LOADING), 292 preview_document_load_state_(LOAD_STATE_COMPLETE), 293 told_browser_about_unsupported_feature_(false), 294 print_preview_page_count_(0) { 295 loader_factory_.Initialize(this); 296 timer_factory_.Initialize(this); 297 form_factory_.Initialize(this); 298 print_callback_factory_.Initialize(this); 299 engine_.reset(PDFEngine::Create(this)); 300 pp::Module::Get()->AddPluginInterface(kPPPPdfInterface, &ppp_private); 301 AddPerInstanceObject(kPPPPdfInterface, this); 302 303 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_MOUSE); 304 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_WHEEL); 305 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); 306 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_TOUCH); 307 } 308 309 Instance::~Instance() { 310 RemovePerInstanceObject(kPPPPdfInterface, this); 311 } 312 313 bool Instance::Init(uint32_t argc, const char* argn[], const char* argv[]) { 314 // For now, we hide HiDPI support behind a flag. 315 if (pp::PDF::IsFeatureEnabled(this, PP_PDFFEATURE_HIDPI)) 316 hidpi_enabled_ = true; 317 318 printing_enabled_ = pp::PDF::IsFeatureEnabled(this, PP_PDFFEATURE_PRINTING); 319 if (printing_enabled_) { 320 CreateToolbar(kPDFToolbarButtons, arraysize(kPDFToolbarButtons)); 321 } else { 322 CreateToolbar(kPDFNoPrintToolbarButtons, 323 arraysize(kPDFNoPrintToolbarButtons)); 324 } 325 326 CreateProgressBar(); 327 328 // Load autoscroll anchor image. 329 autoscroll_anchor_ = 330 CreateResourceImage(PP_RESOURCEIMAGE_PDF_PAN_SCROLL_ICON); 331 332 #ifdef ENABLE_THUMBNAILS 333 CreateThumbnails(); 334 #endif 335 const char* url = NULL; 336 for (uint32_t i = 0; i < argc; ++i) { 337 if (strcmp(argn[i], "src") == 0) { 338 url = argv[i]; 339 break; 340 } 341 } 342 343 if (!url) 344 return false; 345 346 CreatePageIndicator(IsPrintPreviewUrl(url)); 347 348 if (!full_) { 349 // For PDFs embedded in a frame, we don't get the data automatically like we 350 // do for full-frame loads. Start loading the data manually. 351 LoadUrl(url); 352 } else { 353 DCHECK(!did_call_start_loading_); 354 pp::PDF::DidStartLoading(this); 355 did_call_start_loading_ = true; 356 } 357 358 ZoomLimitsChanged(kMinZoom, kMaxZoom); 359 360 text_input_.reset(new pp::TextInput_Dev(this)); 361 362 url_ = url; 363 return engine_->New(url); 364 } 365 366 bool Instance::HandleDocumentLoad(const pp::URLLoader& loader) { 367 delayed_progress_timer_id_ = ScheduleTimer(kProgressBarId, 368 kProgressDelayTimeoutMs); 369 return engine_->HandleDocumentLoad(loader); 370 } 371 372 bool Instance::HandleInputEvent(const pp::InputEvent& event) { 373 // To simplify things, convert the event into device coordinates if it is 374 // a mouse event. 375 pp::InputEvent event_device_res(event); 376 { 377 pp::MouseInputEvent mouse_event(event); 378 if (!mouse_event.is_null()) { 379 pp::Point point = mouse_event.GetPosition(); 380 pp::Point movement = mouse_event.GetMovement(); 381 ScalePoint(device_scale_, &point); 382 ScalePoint(device_scale_, &movement); 383 mouse_event = pp::MouseInputEvent( 384 this, 385 event.GetType(), 386 event.GetTimeStamp(), 387 event.GetModifiers(), 388 mouse_event.GetButton(), 389 point, 390 mouse_event.GetClickCount(), 391 movement); 392 event_device_res = mouse_event; 393 } 394 } 395 396 // Check if we need to go to autoscroll mode. 397 if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEMOVE && 398 (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN)) { 399 pp::MouseInputEvent mouse_event(event_device_res); 400 pp::Point pos = mouse_event.GetPosition(); 401 EnableAutoscroll(pos); 402 UpdateCursor(CalculateAutoscroll(pos)); 403 return true; 404 } else { 405 // Quit autoscrolling on any other event. 406 DisableAutoscroll(); 407 } 408 409 #ifdef ENABLE_THUMBNAILS 410 if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSELEAVE) 411 thumbnails_.SlideOut(); 412 413 if (thumbnails_.HandleEvent(event_device_res)) 414 return true; 415 #endif 416 417 if (toolbar_->HandleEvent(event_device_res)) 418 return true; 419 420 #ifdef ENABLE_THUMBNAILS 421 if (v_scrollbar_.get() && event.GetType() == PP_INPUTEVENT_TYPE_MOUSEMOVE) { 422 pp::MouseInputEvent mouse_event(event); 423 pp::Point pt = mouse_event.GetPosition(); 424 pp::Rect v_scrollbar_rc; 425 v_scrollbar_->GetLocation(&v_scrollbar_rc); 426 // There is a bug (https://bugs.webkit.org/show_bug.cgi?id=45208) 427 // in the webkit that makes event.u.mouse.button 428 // equal to PP_INPUTEVENT_MOUSEBUTTON_LEFT, even when no button is pressed. 429 // To work around this issue we use modifier for now, and will switch 430 // to button once the bug is fixed and webkit got merged back to our tree. 431 if (v_scrollbar_rc.Contains(pt) && 432 (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN)) { 433 thumbnails_.SlideIn(); 434 } 435 436 // When scrollbar is in the scrolling mode we should display thumbnails 437 // even the mouse is outside the thumbnail and scrollbar areas. 438 // If mouse is outside plugin area, we are still getting mouse move events 439 // while scrolling. See bug description for details: 440 // http://code.google.com/p/chromium/issues/detail?id=56444 441 if (!v_scrollbar_rc.Contains(pt) && thumbnails_.visible() && 442 !(event.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) && 443 !thumbnails_.rect().Contains(pt)) { 444 thumbnails_.SlideOut(); 445 } 446 } 447 #endif 448 449 // Need to pass the event to the engine first, since if we're over an edit 450 // control we want it to get keyboard events (like space) instead of the 451 // scrollbar. 452 // TODO: will have to offset the mouse coordinates once we support bidi and 453 // there could be scrollbars on the left. 454 pp::InputEvent offset_event(event_device_res); 455 bool try_engine_first = true; 456 switch (offset_event.GetType()) { 457 case PP_INPUTEVENT_TYPE_MOUSEDOWN: 458 case PP_INPUTEVENT_TYPE_MOUSEUP: 459 case PP_INPUTEVENT_TYPE_MOUSEMOVE: 460 case PP_INPUTEVENT_TYPE_MOUSEENTER: 461 case PP_INPUTEVENT_TYPE_MOUSELEAVE: { 462 pp::MouseInputEvent mouse_event(event_device_res); 463 pp::MouseInputEvent mouse_event_dip(event); 464 pp::Point point = mouse_event.GetPosition(); 465 point.set_x(point.x() - available_area_.x()); 466 offset_event = pp::MouseInputEvent( 467 this, 468 event.GetType(), 469 event.GetTimeStamp(), 470 event.GetModifiers(), 471 mouse_event.GetButton(), 472 point, 473 mouse_event.GetClickCount(), 474 mouse_event.GetMovement()); 475 if (!engine_->IsSelecting()) { 476 if (!IsOverlayScrollbar() && 477 !available_area_.Contains(mouse_event.GetPosition())) { 478 try_engine_first = false; 479 } else if (IsOverlayScrollbar()) { 480 pp::Rect temp; 481 if ((v_scrollbar_.get() && v_scrollbar_->GetLocation(&temp) && 482 temp.Contains(mouse_event_dip.GetPosition())) || 483 (h_scrollbar_.get() && h_scrollbar_->GetLocation(&temp) && 484 temp.Contains(mouse_event_dip.GetPosition()))) { 485 try_engine_first = false; 486 } 487 } 488 } 489 break; 490 } 491 default: 492 break; 493 } 494 if (try_engine_first && engine_->HandleEvent(offset_event)) 495 return true; 496 497 // Left/Right arrows should scroll to the beginning of the Prev/Next page if 498 // there is no horizontal scroll bar. 499 // If fit-to-height, PgDown/PgUp should scroll to the beginning of the 500 // Prev/Next page. 501 if (v_scrollbar_.get() && event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN) { 502 pp::KeyboardInputEvent keyboard_event(event); 503 bool page_down = 504 (!h_scrollbar_.get() && 505 keyboard_event.GetKeyCode() == ui::VKEY_RIGHT) || 506 (zoom_mode_ == ZOOM_FIT_TO_PAGE && 507 keyboard_event.GetKeyCode() == ui::VKEY_NEXT); 508 bool page_up = 509 (!h_scrollbar_.get() && 510 keyboard_event.GetKeyCode() == ui::VKEY_LEFT) || 511 (zoom_mode_ == ZOOM_FIT_TO_PAGE && 512 keyboard_event.GetKeyCode() == ui::VKEY_PRIOR); 513 514 if (page_down) { 515 int page = engine_->GetFirstVisiblePage(); 516 // Engine calculates visible page including delimiter to the page size. 517 // We need to check here if the page itself is completely out of view and 518 // scroll to the next one in that case. 519 if (engine_->GetPageRect(page).bottom() * zoom_ <= 520 v_scrollbar_->GetValue()) 521 page++; 522 ScrollToPage(page + 1); 523 UpdateCursor(PP_CURSORTYPE_POINTER); 524 return true; 525 } else if (page_up) { 526 int page = engine_->GetFirstVisiblePage(); 527 if (engine_->GetPageRect(page).y() * zoom_ >= v_scrollbar_->GetValue()) 528 page--; 529 ScrollToPage(page); 530 UpdateCursor(PP_CURSORTYPE_POINTER); 531 return true; 532 } 533 } 534 535 if (v_scrollbar_.get() && v_scrollbar_->HandleEvent(event)) { 536 UpdateCursor(PP_CURSORTYPE_POINTER); 537 return true; 538 } 539 540 if (h_scrollbar_.get() && h_scrollbar_->HandleEvent(event)) { 541 UpdateCursor(PP_CURSORTYPE_POINTER); 542 return true; 543 } 544 545 if (timer_pending_ && 546 (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP || 547 event.GetType() == PP_INPUTEVENT_TYPE_MOUSEMOVE)) { 548 timer_factory_.CancelAll(); 549 timer_pending_ = false; 550 } else if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEMOVE && 551 engine_->IsSelecting()) { 552 bool set_timer = false; 553 pp::MouseInputEvent mouse_event(event); 554 if (v_scrollbar_.get() && 555 (mouse_event.GetPosition().y() <= 0 || 556 mouse_event.GetPosition().y() >= (plugin_dip_size_.height() - 1))) { 557 v_scrollbar_->ScrollBy( 558 PP_SCROLLBY_LINE, mouse_event.GetPosition().y() >= 0 ? 1: -1); 559 set_timer = true; 560 } 561 if (h_scrollbar_.get() && 562 (mouse_event.GetPosition().x() <= 0 || 563 mouse_event.GetPosition().x() >= (plugin_dip_size_.width() - 1))) { 564 h_scrollbar_->ScrollBy(PP_SCROLLBY_LINE, 565 mouse_event.GetPosition().x() >= 0 ? 1: -1); 566 set_timer = true; 567 } 568 569 if (set_timer) { 570 last_mouse_event_ = pp::MouseInputEvent(event); 571 572 pp::CompletionCallback callback = 573 timer_factory_.NewCallback(&Instance::OnTimerFired); 574 pp::Module::Get()->core()->CallOnMainThread(kDragTimerMs, callback); 575 timer_pending_ = true; 576 } 577 } 578 579 if (event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN && 580 event.GetModifiers() & kDefaultKeyModifier) { 581 pp::KeyboardInputEvent keyboard_event(event); 582 switch (keyboard_event.GetKeyCode()) { 583 case 'A': 584 engine_->SelectAll(); 585 return true; 586 } 587 } 588 589 // Return true for unhandled clicks so the plugin takes focus. 590 return (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN); 591 } 592 593 void Instance::DidChangeView(const pp::View& view) { 594 pp::Rect view_rect(view.GetRect()); 595 float device_scale = 1.0f; 596 float old_device_scale = device_scale_; 597 if (hidpi_enabled_) 598 device_scale = view.GetDeviceScale(); 599 pp::Size view_device_size(view_rect.width() * device_scale, 600 view_rect.height() * device_scale); 601 if (view_device_size == plugin_size_ && device_scale == device_scale_) 602 return; // We don't care about the position, only the size. 603 604 image_data_ = pp::ImageData(); 605 device_scale_ = device_scale; 606 plugin_dip_size_ = view_rect.size(); 607 plugin_size_ = view_device_size; 608 609 paint_manager_.SetSize(view_device_size, device_scale_); 610 611 image_data_ = pp::ImageData(this, 612 PP_IMAGEDATAFORMAT_BGRA_PREMUL, 613 plugin_size_, 614 false); 615 if (image_data_.is_null()) { 616 DCHECK(plugin_size_.IsEmpty()); 617 return; 618 } 619 620 // View dimensions changed, disable autoscroll (if it was enabled). 621 DisableAutoscroll(); 622 623 OnGeometryChanged(zoom_, old_device_scale); 624 } 625 626 pp::Var Instance::GetInstanceObject() { 627 if (instance_object_.is_undefined()) { 628 PDFScriptableObject* object = new PDFScriptableObject(this); 629 // The pp::Var takes ownership of object here. 630 instance_object_ = pp::VarPrivate(this, object); 631 } 632 633 return instance_object_; 634 } 635 636 pp::Var Instance::GetLinkAtPosition(const pp::Point& point) { 637 pp::Point offset_point(point); 638 ScalePoint(device_scale_, &offset_point); 639 offset_point.set_x(offset_point.x() - available_area_.x()); 640 return engine_->GetLinkAtPosition(offset_point); 641 } 642 643 pp::Var Instance::GetSelectedText(bool html) { 644 if (html || !engine_->HasPermission(PDFEngine::PERMISSION_COPY)) 645 return pp::Var(); 646 return engine_->GetSelectedText(); 647 } 648 649 void Instance::InvalidateWidget(pp::Widget_Dev widget, 650 const pp::Rect& dirty_rect) { 651 if (v_scrollbar_.get() && *v_scrollbar_ == widget) { 652 if (!image_data_.is_null()) 653 v_scrollbar_->Paint(dirty_rect.pp_rect(), &image_data_); 654 } else if (h_scrollbar_.get() && *h_scrollbar_ == widget) { 655 if (!image_data_.is_null()) 656 h_scrollbar_->Paint(dirty_rect.pp_rect(), &image_data_); 657 } else { 658 // Possible to hit this condition since sometimes the scrollbar codes posts 659 // a task to do something later, and we could have deleted our reference in 660 // the meantime. 661 return; 662 } 663 664 pp::Rect dirty_rect_scaled = dirty_rect; 665 ScaleRect(device_scale_, &dirty_rect_scaled); 666 paint_manager_.InvalidateRect(dirty_rect_scaled); 667 } 668 669 void Instance::ScrollbarValueChanged(pp::Scrollbar_Dev scrollbar, 670 uint32_t value) { 671 value = GetScaled(value); 672 if (v_scrollbar_.get() && *v_scrollbar_ == scrollbar) { 673 engine_->ScrolledToYPosition(value); 674 pp::Rect rc; 675 v_scrollbar_->GetLocation(&rc); 676 int32 doc_height = GetDocumentPixelHeight(); 677 doc_height -= GetScaled(rc.height()); 678 #ifdef ENABLE_THUMBNAILS 679 if (thumbnails_.visible()) { 680 thumbnails_.SetPosition(value, doc_height, true); 681 } 682 #endif 683 pp::Point origin( 684 plugin_size_.width() - page_indicator_.rect().width() - 685 GetScaled(GetScrollbarReservedThickness()), 686 page_indicator_.GetYPosition(value, doc_height, plugin_size_.height())); 687 page_indicator_.MoveTo(origin, page_indicator_.visible()); 688 } else if (h_scrollbar_.get() && *h_scrollbar_ == scrollbar) { 689 engine_->ScrolledToXPosition(value); 690 } 691 } 692 693 void Instance::ScrollbarOverlayChanged(pp::Scrollbar_Dev scrollbar, 694 bool overlay) { 695 scrollbar_reserved_thickness_ = overlay ? 0 : scrollbar_thickness_; 696 OnGeometryChanged(zoom_, device_scale_); 697 } 698 699 uint32_t Instance::QuerySupportedPrintOutputFormats() { 700 return engine_->QuerySupportedPrintOutputFormats(); 701 } 702 703 int32_t Instance::PrintBegin(const PP_PrintSettings_Dev& print_settings) { 704 // For us num_pages is always equal to the number of pages in the PDF 705 // document irrespective of the printable area. 706 int32_t ret = engine_->GetNumberOfPages(); 707 if (!ret) 708 return 0; 709 710 uint32_t supported_formats = engine_->QuerySupportedPrintOutputFormats(); 711 if ((print_settings.format & supported_formats) == 0) 712 return 0; 713 714 print_settings_.is_printing = true; 715 print_settings_.pepper_print_settings = print_settings; 716 engine_->PrintBegin(); 717 return ret; 718 } 719 720 pp::Resource Instance::PrintPages( 721 const PP_PrintPageNumberRange_Dev* page_ranges, 722 uint32_t page_range_count) { 723 if (!print_settings_.is_printing) 724 return pp::Resource(); 725 726 print_settings_.print_pages_called_ = true; 727 return engine_->PrintPages(page_ranges, page_range_count, 728 print_settings_.pepper_print_settings); 729 } 730 731 void Instance::PrintEnd() { 732 if (print_settings_.print_pages_called_) 733 UserMetricsRecordAction("PDF.PrintPage"); 734 print_settings_.Clear(); 735 engine_->PrintEnd(); 736 } 737 738 bool Instance::IsPrintScalingDisabled() { 739 return !engine_->GetPrintScaling(); 740 } 741 742 bool Instance::StartFind(const std::string& text, bool case_sensitive) { 743 engine_->StartFind(text.c_str(), case_sensitive); 744 return true; 745 } 746 747 void Instance::SelectFindResult(bool forward) { 748 engine_->SelectFindResult(forward); 749 } 750 751 void Instance::StopFind() { 752 engine_->StopFind(); 753 } 754 755 void Instance::Zoom(double scale, bool text_only) { 756 UserMetricsRecordAction("PDF.ZoomFromBrowser"); 757 SetZoom(ZOOM_SCALE, scale); 758 } 759 760 void Instance::ZoomChanged(double factor) { 761 if (full_) 762 Zoom_Dev::ZoomChanged(factor); 763 } 764 765 void Instance::OnPaint(const std::vector<pp::Rect>& paint_rects, 766 std::vector<PaintManager::ReadyRect>* ready, 767 std::vector<pp::Rect>* pending) { 768 if (image_data_.is_null()) { 769 DCHECK(plugin_size_.IsEmpty()); 770 return; 771 } 772 if (first_paint_) { 773 first_paint_ = false; 774 pp::Rect rect = pp::Rect(pp::Point(), plugin_size_); 775 unsigned int color = kBackgroundColorA << 24 | 776 kBackgroundColorR << 16 | 777 kBackgroundColorG << 8 | 778 kBackgroundColorB; 779 FillRect(rect, color); 780 ready->push_back(PaintManager::ReadyRect(rect, image_data_, true)); 781 *pending = paint_rects; 782 return; 783 } 784 785 engine_->PrePaint(); 786 787 for (size_t i = 0; i < paint_rects.size(); i++) { 788 // Intersect with plugin area since there could be pending invalidates from 789 // when the plugin area was larger. 790 pp::Rect rect = 791 paint_rects[i].Intersect(pp::Rect(pp::Point(), plugin_size_)); 792 if (rect.IsEmpty()) 793 continue; 794 795 pp::Rect pdf_rect = available_area_.Intersect(rect); 796 if (!pdf_rect.IsEmpty()) { 797 pdf_rect.Offset(available_area_.x() * -1, 0); 798 799 std::vector<pp::Rect> pdf_ready; 800 std::vector<pp::Rect> pdf_pending; 801 engine_->Paint(pdf_rect, &image_data_, &pdf_ready, &pdf_pending); 802 for (size_t j = 0; j < pdf_ready.size(); ++j) { 803 pdf_ready[j].Offset(available_area_.point()); 804 ready->push_back( 805 PaintManager::ReadyRect(pdf_ready[j], image_data_, false)); 806 } 807 for (size_t j = 0; j < pdf_pending.size(); ++j) { 808 pdf_pending[j].Offset(available_area_.point()); 809 pending->push_back(pdf_pending[j]); 810 } 811 } 812 813 for (size_t j = 0; j < background_parts_.size(); ++j) { 814 pp::Rect intersection = background_parts_[j].location.Intersect(rect); 815 if (!intersection.IsEmpty()) { 816 FillRect(intersection, background_parts_[j].color); 817 ready->push_back( 818 PaintManager::ReadyRect(intersection, image_data_, false)); 819 } 820 } 821 822 if (document_load_state_ == LOAD_STATE_FAILED) { 823 pp::Point top_center; 824 top_center.set_x(plugin_size_.width() / 2); 825 top_center.set_y(plugin_size_.height() / 2); 826 DrawText(top_center, PP_RESOURCESTRING_PDFLOAD_FAILED); 827 } 828 829 #ifdef ENABLE_THUMBNAILS 830 thumbnails_.Paint(&image_data_, rect); 831 #endif 832 } 833 834 engine_->PostPaint(); 835 836 // Must paint scrollbars after the background parts, in case we have an 837 // overlay scrollbar that's over the background. We also do this in a separate 838 // loop because the scrollbar painting logic uses the signal of whether there 839 // are pending paints or not to figure out if it should draw right away or 840 // not. 841 for (size_t i = 0; i < paint_rects.size(); i++) { 842 PaintIfWidgetIntersects(h_scrollbar_.get(), paint_rects[i], ready, pending); 843 PaintIfWidgetIntersects(v_scrollbar_.get(), paint_rects[i], ready, pending); 844 } 845 846 if (progress_bar_.visible()) 847 PaintOverlayControl(&progress_bar_, &image_data_, ready); 848 849 if (page_indicator_.visible()) 850 PaintOverlayControl(&page_indicator_, &image_data_, ready); 851 852 if (toolbar_->current_transparency() != kTransparentAlpha) 853 PaintOverlayControl(toolbar_.get(), &image_data_, ready); 854 855 // Paint autoscroll anchor if needed. 856 if (is_autoscroll_) { 857 size_t limit = ready->size(); 858 for (size_t i = 0; i < limit; i++) { 859 pp::Rect anchor_rect = autoscroll_rect_.Intersect((*ready)[i].rect); 860 if (!anchor_rect.IsEmpty()) { 861 pp::Rect draw_rc = pp::Rect( 862 pp::Point(anchor_rect.x() - autoscroll_rect_.x(), 863 anchor_rect.y() - autoscroll_rect_.y()), 864 anchor_rect.size()); 865 // Paint autoscroll anchor. 866 AlphaBlend(autoscroll_anchor_, draw_rc, 867 &image_data_, anchor_rect.point(), kOpaqueAlpha); 868 } 869 } 870 } 871 } 872 873 void Instance::PaintOverlayControl( 874 Control* ctrl, 875 pp::ImageData* image_data, 876 std::vector<PaintManager::ReadyRect>* ready) { 877 // Make sure that we only paint overlay controls over an area that's ready, 878 // i.e. not pending. Otherwise we'll mark the control rect as ready and 879 // it'll overwrite the pdf region. 880 std::list<pp::Rect> ctrl_rects; 881 for (size_t i = 0; i < ready->size(); i++) { 882 pp::Rect rc = ctrl->rect().Intersect((*ready)[i].rect); 883 if (!rc.IsEmpty()) 884 ctrl_rects.push_back(rc); 885 } 886 887 if (!ctrl_rects.empty()) { 888 ctrl->PaintMultipleRects(image_data, ctrl_rects); 889 890 std::list<pp::Rect>::iterator iter; 891 for (iter = ctrl_rects.begin(); iter != ctrl_rects.end(); ++iter) { 892 ready->push_back(PaintManager::ReadyRect(*iter, *image_data, false)); 893 } 894 } 895 } 896 897 void Instance::DidOpen(int32_t result) { 898 if (result == PP_OK) { 899 engine_->HandleDocumentLoad(embed_loader_); 900 } else if (result != PP_ERROR_ABORTED) { // Can happen in tests. 901 NOTREACHED(); 902 } 903 } 904 905 void Instance::DidOpenPreview(int32_t result) { 906 if (result == PP_OK) { 907 preview_engine_.reset(PDFEngine::Create(new PreviewModeClient(this))); 908 preview_engine_->HandleDocumentLoad(embed_preview_loader_); 909 } else { 910 NOTREACHED(); 911 } 912 } 913 914 void Instance::PaintIfWidgetIntersects( 915 pp::Widget_Dev* widget, 916 const pp::Rect& rect, 917 std::vector<PaintManager::ReadyRect>* ready, 918 std::vector<pp::Rect>* pending) { 919 if (!widget) 920 return; 921 922 pp::Rect location; 923 if (!widget->GetLocation(&location)) 924 return; 925 926 ScaleRect(device_scale_, &location); 927 location = location.Intersect(rect); 928 if (location.IsEmpty()) 929 return; 930 931 if (IsOverlayScrollbar()) { 932 // If we're using overlay scrollbars, and there are pending paints under the 933 // scrollbar, don't update the scrollbar instantly. While it would be nice, 934 // we would need to double buffer the plugin area in order to make this 935 // work. This is because we'd need to always have a copy of what the pdf 936 // under the scrollbar looks like, and additionally we couldn't paint the 937 // pdf under the scrollbar if it's ready until we got the preceding flush. 938 // So in practice, it would make painting slower and introduce extra buffer 939 // copies for the general case. 940 for (size_t i = 0; i < pending->size(); ++i) { 941 if ((*pending)[i].Intersects(location)) 942 return; 943 } 944 945 // Even if none of the pending paints are under the scrollbar, we never want 946 // to paint it if it's over the pdf if there are other pending paints. 947 // Otherwise different parts of the pdf plugin would display at different 948 // scroll offsets. 949 if (!pending->empty() && available_area_.Intersects(rect)) { 950 pending->push_back(location); 951 return; 952 } 953 } 954 955 pp::Rect location_dip = location; 956 ScaleRect(1.0f / device_scale_, &location_dip); 957 958 DCHECK(!image_data_.is_null()); 959 widget->Paint(location_dip, &image_data_); 960 961 ready->push_back(PaintManager::ReadyRect(location, image_data_, true)); 962 } 963 964 void Instance::OnTimerFired(int32_t) { 965 HandleInputEvent(last_mouse_event_); 966 } 967 968 void Instance::OnClientTimerFired(int32_t id) { 969 engine_->OnCallback(id); 970 } 971 972 void Instance::OnControlTimerFired(int32_t, 973 const uint32& control_id, 974 const uint32& timer_id) { 975 if (control_id == toolbar_->id()) { 976 toolbar_->OnTimerFired(timer_id); 977 } else if (control_id == progress_bar_.id()) { 978 if (timer_id == delayed_progress_timer_id_) { 979 if (document_load_state_ == LOAD_STATE_LOADING && 980 !progress_bar_.visible()) { 981 progress_bar_.Fade(true, kProgressFadeTimeoutMs); 982 } 983 delayed_progress_timer_id_ = 0; 984 } else { 985 progress_bar_.OnTimerFired(timer_id); 986 } 987 } else if (control_id == kAutoScrollId) { 988 if (is_autoscroll_) { 989 if (autoscroll_x_ != 0 && h_scrollbar_.get()) { 990 h_scrollbar_->ScrollBy(PP_SCROLLBY_PIXEL, autoscroll_x_); 991 } 992 if (autoscroll_y_ != 0 && v_scrollbar_.get()) { 993 v_scrollbar_->ScrollBy(PP_SCROLLBY_PIXEL, autoscroll_y_); 994 } 995 996 // Reschedule timer. 997 ScheduleTimer(kAutoScrollId, kAutoScrollTimeoutMs); 998 } 999 } else if (control_id == kPageIndicatorId) { 1000 page_indicator_.OnTimerFired(timer_id); 1001 } 1002 #ifdef ENABLE_THUMBNAILS 1003 else if (control_id == thumbnails_.id()) { 1004 thumbnails_.OnTimerFired(timer_id); 1005 } 1006 #endif 1007 } 1008 1009 void Instance::CalculateBackgroundParts() { 1010 background_parts_.clear(); 1011 int v_scrollbar_thickness = 1012 GetScaled(v_scrollbar_.get() ? GetScrollbarReservedThickness() : 0); 1013 int h_scrollbar_thickness = 1014 GetScaled(h_scrollbar_.get() ? GetScrollbarReservedThickness() : 0); 1015 int width_without_scrollbar = std::max( 1016 plugin_size_.width() - v_scrollbar_thickness, 0); 1017 int height_without_scrollbar = std::max( 1018 plugin_size_.height() - h_scrollbar_thickness, 0); 1019 int left_width = available_area_.x(); 1020 int right_start = available_area_.right(); 1021 int right_width = abs(width_without_scrollbar - available_area_.right()); 1022 int bottom = std::min(available_area_.bottom(), height_without_scrollbar); 1023 1024 // Add the left, right, and bottom rectangles. Note: we assume only 1025 // horizontal centering. 1026 BackgroundPart part; 1027 part.color = kBackgroundColorA << 24 | 1028 kBackgroundColorR << 16 | 1029 kBackgroundColorG << 8 | 1030 kBackgroundColorB; 1031 part.location = pp::Rect(0, 0, left_width, bottom); 1032 if (!part.location.IsEmpty()) 1033 background_parts_.push_back(part); 1034 part.location = pp::Rect(right_start, 0, right_width, bottom); 1035 if (!part.location.IsEmpty()) 1036 background_parts_.push_back(part); 1037 part.location = pp::Rect( 1038 0, bottom, width_without_scrollbar, height_without_scrollbar - bottom); 1039 if (!part.location.IsEmpty()) 1040 background_parts_.push_back(part); 1041 1042 if (h_scrollbar_thickness 1043 #if defined(OS_MACOSX) 1044 || 1045 #else 1046 && 1047 #endif 1048 v_scrollbar_thickness) { 1049 part.color = 0xFFFFFFFF; 1050 part.location = pp::Rect(plugin_size_.width() - v_scrollbar_thickness, 1051 plugin_size_.height() - h_scrollbar_thickness, 1052 h_scrollbar_thickness, 1053 v_scrollbar_thickness); 1054 background_parts_.push_back(part); 1055 } 1056 } 1057 1058 int Instance::GetDocumentPixelWidth() const { 1059 return static_cast<int>(ceil(document_size_.width() * zoom_ * device_scale_)); 1060 } 1061 1062 int Instance::GetDocumentPixelHeight() const { 1063 return static_cast<int>(ceil(document_size_.height() * 1064 zoom_ * 1065 device_scale_)); 1066 } 1067 1068 void Instance::FillRect(const pp::Rect& rect, unsigned int color) { 1069 DCHECK(!image_data_.is_null() || rect.IsEmpty()); 1070 unsigned int* buffer_start = static_cast<unsigned int*>(image_data_.data()); 1071 int stride = image_data_.stride(); 1072 unsigned int* ptr = buffer_start + rect.y() * stride / 4 + rect.x(); 1073 int height = rect.height(); 1074 int width = rect.width(); 1075 for (int y = 0; y < height; ++y) { 1076 for (int x = 0; x < width; ++x) 1077 *(ptr + x) = color; 1078 ptr += stride /4; 1079 } 1080 } 1081 1082 void Instance::DocumentSizeUpdated(const pp::Size& size) { 1083 document_size_ = size; 1084 1085 OnGeometryChanged(zoom_, device_scale_); 1086 } 1087 1088 void Instance::Invalidate(const pp::Rect& rect) { 1089 pp::Rect offset_rect(rect); 1090 offset_rect.Offset(available_area_.point()); 1091 paint_manager_.InvalidateRect(offset_rect); 1092 } 1093 1094 void Instance::Scroll(const pp::Point& point) { 1095 pp::Rect scroll_area = available_area_; 1096 if (IsOverlayScrollbar()) { 1097 pp::Rect rc; 1098 if (h_scrollbar_.get()) { 1099 h_scrollbar_->GetLocation(&rc); 1100 ScaleRect(device_scale_, &rc); 1101 if (scroll_area.bottom() > rc.y()) { 1102 scroll_area.set_height(rc.y() - scroll_area.y()); 1103 paint_manager_.InvalidateRect(rc); 1104 } 1105 } 1106 if (v_scrollbar_.get()) { 1107 v_scrollbar_->GetLocation(&rc); 1108 ScaleRect(device_scale_, &rc); 1109 if (scroll_area.right() > rc.x()) { 1110 scroll_area.set_width(rc.x() - scroll_area.x()); 1111 paint_manager_.InvalidateRect(rc); 1112 } 1113 } 1114 } 1115 paint_manager_.ScrollRect(scroll_area, point); 1116 1117 if (toolbar_->current_transparency() != kTransparentAlpha) 1118 paint_manager_.InvalidateRect(toolbar_->GetControlsRect()); 1119 1120 if (progress_bar_.visible()) 1121 paint_manager_.InvalidateRect(progress_bar_.rect()); 1122 1123 if (is_autoscroll_) 1124 paint_manager_.InvalidateRect(autoscroll_rect_); 1125 1126 if (show_page_indicator_) { 1127 page_indicator_.set_current_page(GetPageNumberToDisplay()); 1128 page_indicator_.Splash(); 1129 } 1130 1131 if (page_indicator_.visible()) 1132 paint_manager_.InvalidateRect(page_indicator_.rect()); 1133 1134 if (on_scroll_callback_.is_string()) 1135 ExecuteScript(on_scroll_callback_); 1136 } 1137 1138 void Instance::ScrollToX(int position) { 1139 if (!h_scrollbar_.get()) { 1140 NOTREACHED(); 1141 return; 1142 } 1143 int position_dip = static_cast<int>(position / device_scale_); 1144 h_scrollbar_->SetValue(position_dip); 1145 } 1146 1147 void Instance::ScrollToY(int position) { 1148 if (!v_scrollbar_.get()) { 1149 NOTREACHED(); 1150 return; 1151 } 1152 int position_dip = static_cast<int>(position / device_scale_); 1153 v_scrollbar_->SetValue(ClipToRange(position_dip, 0, valid_v_range_)); 1154 } 1155 1156 void Instance::ScrollToPage(int page) { 1157 if (!v_scrollbar_.get()) 1158 return; 1159 1160 if (engine_->GetNumberOfPages() == 0) 1161 return; 1162 1163 int index = ClipToRange(page, 0, engine_->GetNumberOfPages() - 1); 1164 pp::Rect rect = engine_->GetPageRect(index); 1165 // If we are trying to scroll pass the last page, 1166 // scroll to the end of the last page. 1167 int position = index < page ? rect.bottom() : rect.y(); 1168 ScrollToY(position * zoom_ * device_scale_); 1169 } 1170 1171 void Instance::NavigateTo(const std::string& url, bool open_in_new_tab) { 1172 std::string url_copy(url); 1173 1174 // Empty |url_copy| is ok, and will effectively be a reload. 1175 // Skip the code below so an empty URL does not turn into "http://", which 1176 // will cause GURL to fail a DCHECK. 1177 if (!url_copy.empty()) { 1178 // If there's no scheme, add http. 1179 if (url_copy.find("://") == std::string::npos && 1180 url_copy.find("mailto:") == std::string::npos) { 1181 url_copy = std::string("http://") + url_copy; 1182 } 1183 // Make sure |url_copy| starts with a valid scheme. 1184 if (url_copy.find("http://") != 0 && 1185 url_copy.find("https://") != 0 && 1186 url_copy.find("ftp://") != 0 && 1187 url_copy.find("mailto:") != 0) { 1188 return; 1189 } 1190 // Make sure |url_copy| is not only a scheme. 1191 if (url_copy == "http://" || 1192 url_copy == "https://" || 1193 url_copy == "ftp://" || 1194 url_copy == "mailto:") { 1195 return; 1196 } 1197 } 1198 if (open_in_new_tab) { 1199 GetWindowObject().Call("open", url_copy); 1200 } else { 1201 GetWindowObject().GetProperty("top").GetProperty("location"). 1202 SetProperty("href", url_copy); 1203 } 1204 } 1205 1206 void Instance::UpdateCursor(PP_CursorType_Dev cursor) { 1207 if (cursor == cursor_) 1208 return; 1209 cursor_ = cursor; 1210 1211 const PPB_CursorControl_Dev* cursor_interface = 1212 reinterpret_cast<const PPB_CursorControl_Dev*>( 1213 pp::Module::Get()->GetBrowserInterface(PPB_CURSOR_CONTROL_DEV_INTERFACE)); 1214 if (!cursor_interface) { 1215 NOTREACHED(); 1216 return; 1217 } 1218 1219 cursor_interface->SetCursor( 1220 pp_instance(), cursor_, pp::ImageData().pp_resource(), NULL); 1221 } 1222 1223 void Instance::UpdateTickMarks(const std::vector<pp::Rect>& tickmarks) { 1224 if (!v_scrollbar_.get()) 1225 return; 1226 1227 float inverse_scale = 1.0f / device_scale_; 1228 std::vector<pp::Rect> scaled_tickmarks = tickmarks; 1229 for (size_t i = 0; i < scaled_tickmarks.size(); i++) { 1230 ScaleRect(inverse_scale, &scaled_tickmarks[i]); 1231 } 1232 1233 v_scrollbar_->SetTickMarks( 1234 scaled_tickmarks.empty() ? NULL : &scaled_tickmarks[0], tickmarks.size()); 1235 } 1236 1237 void Instance::NotifyNumberOfFindResultsChanged(int total, bool final_result) { 1238 NumberOfFindResultsChanged(total, final_result); 1239 } 1240 1241 void Instance::NotifySelectedFindResultChanged(int current_find_index) { 1242 SelectedFindResultChanged(current_find_index); 1243 } 1244 1245 void Instance::OnEvent(uint32 control_id, uint32 event_id, void* data) { 1246 if (event_id == Button::EVENT_ID_BUTTON_CLICKED || 1247 event_id == Button::EVENT_ID_BUTTON_STATE_CHANGED) { 1248 switch (control_id) { 1249 case kFitToPageButtonId: 1250 UserMetricsRecordAction("PDF.FitToPageButton"); 1251 SetZoom(ZOOM_FIT_TO_PAGE, 0); 1252 ZoomChanged(zoom_); 1253 break; 1254 case kFitToWidthButtonId: 1255 UserMetricsRecordAction("PDF.FitToWidthButton"); 1256 SetZoom(ZOOM_FIT_TO_WIDTH, 0); 1257 ZoomChanged(zoom_); 1258 break; 1259 case kZoomOutButtonId: 1260 case kZoomInButtonId: 1261 UserMetricsRecordAction(control_id == kZoomOutButtonId ? 1262 "PDF.ZoomOutButton" : "PDF.ZoomInButton"); 1263 SetZoom(ZOOM_SCALE, CalculateZoom(control_id)); 1264 ZoomChanged(zoom_); 1265 break; 1266 case kSaveButtonId: 1267 UserMetricsRecordAction("PDF.SaveButton"); 1268 SaveAs(); 1269 break; 1270 case kPrintButtonId: 1271 UserMetricsRecordAction("PDF.PrintButton"); 1272 Print(); 1273 break; 1274 } 1275 } 1276 if (control_id == kThumbnailsId && 1277 event_id == ThumbnailControl::EVENT_ID_THUMBNAIL_SELECTED) { 1278 int page = *static_cast<int*>(data); 1279 pp::Rect page_rc(engine_->GetPageRect(page)); 1280 ScrollToY(static_cast<int>(page_rc.y() * zoom_ * device_scale_)); 1281 } 1282 } 1283 1284 void Instance::Invalidate(uint32 control_id, const pp::Rect& rc) { 1285 paint_manager_.InvalidateRect(rc); 1286 } 1287 1288 uint32 Instance::ScheduleTimer(uint32 control_id, uint32 timeout_ms) { 1289 current_timer_id_++; 1290 pp::CompletionCallback callback = 1291 timer_factory_.NewCallback(&Instance::OnControlTimerFired, 1292 control_id, 1293 current_timer_id_); 1294 pp::Module::Get()->core()->CallOnMainThread(timeout_ms, callback); 1295 return current_timer_id_; 1296 } 1297 1298 void Instance::SetEventCapture(uint32 control_id, bool set_capture) { 1299 // TODO(gene): set event capture here. 1300 } 1301 1302 void Instance::SetCursor(uint32 control_id, PP_CursorType_Dev cursor_type) { 1303 UpdateCursor(cursor_type); 1304 } 1305 1306 pp::Instance* Instance::GetInstance() { 1307 return this; 1308 } 1309 1310 void Instance::GetDocumentPassword( 1311 pp::CompletionCallbackWithOutput<pp::Var> callback) { 1312 std::string message(GetLocalizedString(PP_RESOURCESTRING_PDFGETPASSWORD)); 1313 pp::Var result = pp::PDF::ModalPromptForPassword(this, message); 1314 *callback.output() = result.pp_var(); 1315 callback.Run(PP_OK); 1316 } 1317 1318 void Instance::Alert(const std::string& message) { 1319 GetWindowObject().Call("alert", message); 1320 } 1321 1322 bool Instance::Confirm(const std::string& message) { 1323 pp::Var result = GetWindowObject().Call("confirm", message); 1324 return result.is_bool() ? result.AsBool() : false; 1325 } 1326 1327 std::string Instance::Prompt(const std::string& question, 1328 const std::string& default_answer) { 1329 pp::Var result = GetWindowObject().Call("prompt", question, default_answer); 1330 return result.is_string() ? result.AsString() : std::string(); 1331 } 1332 1333 std::string Instance::GetURL() { 1334 return url_; 1335 } 1336 1337 void Instance::Email(const std::string& to, 1338 const std::string& cc, 1339 const std::string& bcc, 1340 const std::string& subject, 1341 const std::string& body) { 1342 std::string javascript = 1343 "var href = 'mailto:" + net::EscapeUrlEncodedData(to, false) + 1344 "?cc=" + net::EscapeUrlEncodedData(cc, false) + 1345 "&bcc=" + net::EscapeUrlEncodedData(bcc, false) + 1346 "&subject=" + net::EscapeUrlEncodedData(subject, false) + 1347 "&body=" + net::EscapeUrlEncodedData(body, false) + 1348 "';var temp = window.open(href, '_blank', " + 1349 "'width=1,height=1');if(temp) temp.close();"; 1350 ExecuteScript(javascript); 1351 } 1352 1353 void Instance::Print() { 1354 if (!printing_enabled_ || 1355 (!engine_->HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY) && 1356 !engine_->HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY))) { 1357 return; 1358 } 1359 1360 pp::CompletionCallback callback = 1361 print_callback_factory_.NewCallback(&Instance::OnPrint); 1362 pp::Module::Get()->core()->CallOnMainThread(0, callback); 1363 } 1364 1365 void Instance::OnPrint(int32_t) { 1366 pp::PDF::Print(this); 1367 } 1368 1369 void Instance::SaveAs() { 1370 pp::PDF::SaveAs(this); 1371 } 1372 1373 void Instance::SubmitForm(const std::string& url, 1374 const void* data, 1375 int length) { 1376 pp::URLRequestInfo request(this); 1377 request.SetURL(url); 1378 request.SetMethod("POST"); 1379 request.AppendDataToBody(reinterpret_cast<const char*>(data), length); 1380 1381 pp::CompletionCallback callback = 1382 form_factory_.NewCallback(&Instance::FormDidOpen); 1383 form_loader_ = CreateURLLoaderInternal(); 1384 int rv = form_loader_.Open(request, callback); 1385 if (rv != PP_OK_COMPLETIONPENDING) 1386 callback.Run(rv); 1387 } 1388 1389 void Instance::FormDidOpen(int32_t result) { 1390 // TODO: inform the user of success/failure. 1391 if (result != PP_OK) { 1392 NOTREACHED(); 1393 } 1394 } 1395 1396 std::string Instance::ShowFileSelectionDialog() { 1397 // Seems like very low priority to implement, since the pdf has no way to get 1398 // the file data anyways. Javascript doesn't let you do this synchronously. 1399 NOTREACHED(); 1400 return std::string(); 1401 } 1402 1403 pp::URLLoader Instance::CreateURLLoader() { 1404 if (full_) { 1405 if (!did_call_start_loading_) { 1406 did_call_start_loading_ = true; 1407 pp::PDF::DidStartLoading(this); 1408 } 1409 1410 // Disable save and print until the document is fully loaded, since they 1411 // would generate an incomplete document. Need to do this each time we 1412 // call DidStartLoading since that resets the content restrictions. 1413 pp::PDF::SetContentRestriction(this, CONTENT_RESTRICTION_SAVE | 1414 CONTENT_RESTRICTION_PRINT); 1415 } 1416 1417 return CreateURLLoaderInternal(); 1418 } 1419 1420 void Instance::ScheduleCallback(int id, int delay_in_ms) { 1421 pp::CompletionCallback callback = 1422 timer_factory_.NewCallback(&Instance::OnClientTimerFired); 1423 pp::Module::Get()->core()->CallOnMainThread(delay_in_ms, callback, id); 1424 } 1425 1426 void Instance::SearchString(const base::char16* string, 1427 const base::char16* term, 1428 bool case_sensitive, 1429 std::vector<SearchStringResult>* results) { 1430 if (!pp::PDF::IsAvailable()) { 1431 NOTREACHED(); 1432 return; 1433 } 1434 1435 PP_PrivateFindResult* pp_results; 1436 int count = 0; 1437 pp::PDF::SearchString( 1438 this, 1439 reinterpret_cast<const unsigned short*>(string), 1440 reinterpret_cast<const unsigned short*>(term), 1441 case_sensitive, 1442 &pp_results, 1443 &count); 1444 1445 results->resize(count); 1446 for (int i = 0; i < count; ++i) { 1447 (*results)[i].start_index = pp_results[i].start_index; 1448 (*results)[i].length = pp_results[i].length; 1449 } 1450 1451 pp::Memory_Dev memory; 1452 memory.MemFree(pp_results); 1453 } 1454 1455 void Instance::DocumentPaintOccurred() { 1456 if (painted_first_page_) 1457 return; 1458 1459 painted_first_page_ = true; 1460 UpdateToolbarPosition(false); 1461 toolbar_->Splash(kToolbarSplashTimeoutMs); 1462 1463 if (engine_->GetNumberOfPages() > 1) 1464 show_page_indicator_ = true; 1465 else 1466 show_page_indicator_ = false; 1467 1468 if (v_scrollbar_.get() && show_page_indicator_) { 1469 page_indicator_.set_current_page(GetPageNumberToDisplay()); 1470 page_indicator_.Splash(kToolbarSplashTimeoutMs, 1471 kPageIndicatorInitialFadeTimeoutMs); 1472 } 1473 } 1474 1475 void Instance::DocumentLoadComplete(int page_count) { 1476 // Clear focus state for OSK. 1477 FormTextFieldFocusChange(false); 1478 1479 // Update progress control. 1480 if (progress_bar_.visible()) 1481 progress_bar_.Fade(false, kProgressFadeTimeoutMs); 1482 1483 DCHECK(document_load_state_ == LOAD_STATE_LOADING); 1484 document_load_state_ = LOAD_STATE_COMPLETE; 1485 UserMetricsRecordAction("PDF.LoadSuccess"); 1486 if (on_load_callback_.is_string()) 1487 ExecuteScript(on_load_callback_); 1488 // Note: If we are in print preview mode on_load_callback_ might call 1489 // ScrollTo{X|Y}() and we don't want to scroll again and override it. 1490 // #page=N is not supported in Print Preview. 1491 if (!IsPrintPreview()) { 1492 int initial_page = GetInitialPage(url_); 1493 if (initial_page >= 0) 1494 ScrollToPage(initial_page); 1495 } 1496 1497 if (!full_) 1498 return; 1499 if (!pp::PDF::IsAvailable()) 1500 return; 1501 1502 if (did_call_start_loading_) { 1503 pp::PDF::DidStopLoading(this); 1504 did_call_start_loading_ = false; 1505 } 1506 1507 int content_restrictions = 1508 CONTENT_RESTRICTION_CUT | CONTENT_RESTRICTION_PASTE; 1509 if (!engine_->HasPermission(PDFEngine::PERMISSION_COPY)) 1510 content_restrictions |= CONTENT_RESTRICTION_COPY; 1511 1512 if (!engine_->HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY) && 1513 !engine_->HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY)) { 1514 printing_enabled_ = false; 1515 if (current_tb_info_ == kPDFToolbarButtons) { 1516 // Remove Print button. 1517 CreateToolbar(kPDFNoPrintToolbarButtons, 1518 arraysize(kPDFNoPrintToolbarButtons)); 1519 UpdateToolbarPosition(false); 1520 Invalidate(pp::Rect(plugin_size_)); 1521 } 1522 } 1523 1524 pp::PDF::SetContentRestriction(this, content_restrictions); 1525 1526 pp::PDF::HistogramPDFPageCount(this, page_count); 1527 } 1528 1529 void Instance::RotateClockwise() { 1530 engine_->RotateClockwise(); 1531 } 1532 1533 void Instance::RotateCounterclockwise() { 1534 engine_->RotateCounterclockwise(); 1535 } 1536 1537 void Instance::PreviewDocumentLoadComplete() { 1538 if (preview_document_load_state_ != LOAD_STATE_LOADING || 1539 preview_pages_info_.empty()) { 1540 return; 1541 } 1542 1543 preview_document_load_state_ = LOAD_STATE_COMPLETE; 1544 1545 int dest_page_index = preview_pages_info_.front().second; 1546 int src_page_index = 1547 ExtractPrintPreviewPageIndex(preview_pages_info_.front().first); 1548 if (src_page_index > 0 && dest_page_index > -1 && preview_engine_.get()) 1549 engine_->AppendPage(preview_engine_.get(), dest_page_index); 1550 1551 preview_pages_info_.pop(); 1552 // |print_preview_page_count_| is not updated yet. Do not load any 1553 // other preview pages till we get this information. 1554 if (print_preview_page_count_ == 0) 1555 return; 1556 1557 if (preview_pages_info_.size()) 1558 LoadAvailablePreviewPage(); 1559 } 1560 1561 void Instance::DocumentLoadFailed() { 1562 DCHECK(document_load_state_ == LOAD_STATE_LOADING); 1563 UserMetricsRecordAction("PDF.LoadFailure"); 1564 1565 // Hide progress control. 1566 progress_bar_.Fade(false, kProgressFadeTimeoutMs); 1567 1568 if (did_call_start_loading_) { 1569 pp::PDF::DidStopLoading(this); 1570 did_call_start_loading_ = false; 1571 } 1572 1573 document_load_state_ = LOAD_STATE_FAILED; 1574 paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_)); 1575 } 1576 1577 void Instance::PreviewDocumentLoadFailed() { 1578 UserMetricsRecordAction("PDF.PreviewDocumentLoadFailure"); 1579 if (preview_document_load_state_ != LOAD_STATE_LOADING || 1580 preview_pages_info_.empty()) { 1581 return; 1582 } 1583 1584 preview_document_load_state_ = LOAD_STATE_FAILED; 1585 preview_pages_info_.pop(); 1586 1587 if (preview_pages_info_.size()) 1588 LoadAvailablePreviewPage(); 1589 } 1590 1591 pp::Instance* Instance::GetPluginInstance() { 1592 return GetInstance(); 1593 } 1594 1595 void Instance::DocumentHasUnsupportedFeature(const std::string& feature) { 1596 std::string metric("PDF_Unsupported_"); 1597 metric += feature; 1598 if (!unsupported_features_reported_.count(metric)) { 1599 unsupported_features_reported_.insert(metric); 1600 UserMetricsRecordAction(metric); 1601 } 1602 1603 // Since we use an info bar, only do this for full frame plugins.. 1604 if (!full_) 1605 return; 1606 1607 if (told_browser_about_unsupported_feature_) 1608 return; 1609 told_browser_about_unsupported_feature_ = true; 1610 1611 pp::PDF::HasUnsupportedFeature(this); 1612 } 1613 1614 void Instance::DocumentLoadProgress(uint32 available, uint32 doc_size) { 1615 double progress = 0.0; 1616 if (doc_size == 0) { 1617 // Document size is unknown. Use heuristics. 1618 // We'll make progress logarithmic from 0 to 100M. 1619 static const double kFactor = log(100000000.0) / 100.0; 1620 if (available > 0) { 1621 progress = log(static_cast<double>(available)) / kFactor; 1622 if (progress > 100.0) 1623 progress = 100.0; 1624 } 1625 } else { 1626 progress = 100.0 * static_cast<double>(available) / doc_size; 1627 } 1628 progress_bar_.SetProgress(progress); 1629 } 1630 1631 void Instance::FormTextFieldFocusChange(bool in_focus) { 1632 if (!text_input_.get()) 1633 return; 1634 if (in_focus) 1635 text_input_->SetTextInputType(PP_TEXTINPUT_TYPE_DEV_TEXT); 1636 else 1637 text_input_->SetTextInputType(PP_TEXTINPUT_TYPE_DEV_NONE); 1638 } 1639 1640 // Called by PDFScriptableObject. 1641 bool Instance::HasScriptableMethod(const pp::Var& method, pp::Var* exception) { 1642 std::string method_str = method.AsString(); 1643 return (method_str == kJSAccessibility || 1644 method_str == kJSDocumentLoadComplete || 1645 method_str == kJSGetHeight || 1646 method_str == kJSGetHorizontalScrollbarThickness || 1647 method_str == kJSGetPageLocationNormalized || 1648 method_str == kJSGetVerticalScrollbarThickness || 1649 method_str == kJSGetWidth || 1650 method_str == kJSGetZoomLevel || 1651 method_str == kJSGoToPage || 1652 method_str == kJSGrayscale || 1653 method_str == kJSLoadPreviewPage || 1654 method_str == kJSOnLoad || 1655 method_str == kJSOnPluginSizeChanged || 1656 method_str == kJSOnScroll || 1657 method_str == kJSPageXOffset || 1658 method_str == kJSPageYOffset || 1659 method_str == kJSPrintPreviewPageCount || 1660 method_str == kJSReload || 1661 method_str == kJSRemovePrintButton || 1662 method_str == kJSResetPrintPreviewUrl || 1663 method_str == kJSSendKeyEvent || 1664 method_str == kJSSetPageNumbers || 1665 method_str == kJSSetPageXOffset || 1666 method_str == kJSSetPageYOffset || 1667 method_str == kJSSetZoomLevel || 1668 method_str == kJSZoomFitToHeight || 1669 method_str == kJSZoomFitToWidth || 1670 method_str == kJSZoomIn || 1671 method_str == kJSZoomOut); 1672 } 1673 1674 pp::Var Instance::CallScriptableMethod(const pp::Var& method, 1675 const std::vector<pp::Var>& args, 1676 pp::Var* exception) { 1677 std::string method_str = method.AsString(); 1678 if (method_str == kJSGrayscale) { 1679 if (args.size() == 1 && args[0].is_bool()) { 1680 engine_->SetGrayscale(args[0].AsBool()); 1681 // Redraw 1682 paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_)); 1683 #ifdef ENABLE_THUMBNAILS 1684 if (thumbnails_.visible()) 1685 thumbnails_.Show(true, true); 1686 #endif 1687 return pp::Var(true); 1688 } 1689 return pp::Var(false); 1690 } 1691 if (method_str == kJSOnLoad) { 1692 if (args.size() == 1 && args[0].is_string()) { 1693 on_load_callback_ = args[0]; 1694 return pp::Var(true); 1695 } 1696 return pp::Var(false); 1697 } 1698 if (method_str == kJSOnScroll) { 1699 if (args.size() == 1 && args[0].is_string()) { 1700 on_scroll_callback_ = args[0]; 1701 return pp::Var(true); 1702 } 1703 return pp::Var(false); 1704 } 1705 if (method_str == kJSOnPluginSizeChanged) { 1706 if (args.size() == 1 && args[0].is_string()) { 1707 on_plugin_size_changed_callback_ = args[0]; 1708 return pp::Var(true); 1709 } 1710 return pp::Var(false); 1711 } 1712 if (method_str == kJSReload) { 1713 document_load_state_ = LOAD_STATE_LOADING; 1714 if (!full_) 1715 LoadUrl(url_); 1716 preview_engine_.reset(); 1717 print_preview_page_count_ = 0; 1718 engine_.reset(PDFEngine::Create(this)); 1719 engine_->New(url_.c_str()); 1720 #ifdef ENABLE_THUMBNAILS 1721 thumbnails_.ResetEngine(engine_.get()); 1722 #endif 1723 return pp::Var(); 1724 } 1725 if (method_str == kJSResetPrintPreviewUrl) { 1726 if (args.size() == 1 && args[0].is_string()) { 1727 url_ = args[0].AsString(); 1728 preview_pages_info_ = std::queue<PreviewPageInfo>(); 1729 preview_document_load_state_ = LOAD_STATE_COMPLETE; 1730 } 1731 return pp::Var(); 1732 } 1733 if (method_str == kJSZoomFitToHeight) { 1734 SetZoom(ZOOM_FIT_TO_PAGE, 0); 1735 return pp::Var(); 1736 } 1737 if (method_str == kJSZoomFitToWidth) { 1738 SetZoom(ZOOM_FIT_TO_WIDTH, 0); 1739 return pp::Var(); 1740 } 1741 if (method_str == kJSZoomIn) { 1742 SetZoom(ZOOM_SCALE, CalculateZoom(kZoomInButtonId)); 1743 return pp::Var(); 1744 } 1745 if (method_str == kJSZoomOut) { 1746 SetZoom(ZOOM_SCALE, CalculateZoom(kZoomOutButtonId)); 1747 return pp::Var(); 1748 } 1749 if (method_str == kJSSetZoomLevel) { 1750 if (args.size() == 1 && args[0].is_double()) 1751 SetZoom(ZOOM_SCALE, args[0].AsDouble()); 1752 return pp::Var(); 1753 } 1754 if (method_str == kJSGetZoomLevel) { 1755 return pp::Var(zoom_); 1756 } 1757 if (method_str == kJSGetHeight) { 1758 return pp::Var(plugin_size_.height()); 1759 } 1760 if (method_str == kJSGetWidth) { 1761 return pp::Var(plugin_size_.width()); 1762 } 1763 if (method_str == kJSGetHorizontalScrollbarThickness) { 1764 return pp::Var( 1765 h_scrollbar_.get() ? GetScrollbarReservedThickness() : 0); 1766 } 1767 if (method_str == kJSGetVerticalScrollbarThickness) { 1768 return pp::Var( 1769 v_scrollbar_.get() ? GetScrollbarReservedThickness() : 0); 1770 } 1771 if (method_str == kJSDocumentLoadComplete) { 1772 return pp::Var((document_load_state_ != LOAD_STATE_LOADING)); 1773 } 1774 if (method_str == kJSPageYOffset) { 1775 return pp::Var(static_cast<int32_t>( 1776 v_scrollbar_.get() ? v_scrollbar_->GetValue() : 0)); 1777 } 1778 if (method_str == kJSSetPageYOffset) { 1779 if (args.size() == 1 && args[0].is_number() && v_scrollbar_.get()) 1780 ScrollToY(GetScaled(args[0].AsInt())); 1781 return pp::Var(); 1782 } 1783 if (method_str == kJSPageXOffset) { 1784 return pp::Var(static_cast<int32_t>( 1785 h_scrollbar_.get() ? h_scrollbar_->GetValue() : 0)); 1786 } 1787 if (method_str == kJSSetPageXOffset) { 1788 if (args.size() == 1 && args[0].is_number() && h_scrollbar_.get()) 1789 ScrollToX(GetScaled(args[0].AsInt())); 1790 return pp::Var(); 1791 } 1792 if (method_str == kJSRemovePrintButton) { 1793 CreateToolbar(kPrintPreviewToolbarButtons, 1794 arraysize(kPrintPreviewToolbarButtons)); 1795 UpdateToolbarPosition(false); 1796 Invalidate(pp::Rect(plugin_size_)); 1797 return pp::Var(); 1798 } 1799 if (method_str == kJSGoToPage) { 1800 if (args.size() == 1 && args[0].is_string()) { 1801 ScrollToPage(atoi(args[0].AsString().c_str())); 1802 } 1803 return pp::Var(); 1804 } 1805 if (method_str == kJSAccessibility) { 1806 if (args.size() == 0) { 1807 base::DictionaryValue node; 1808 node.SetInteger(kAccessibleNumberOfPages, engine_->GetNumberOfPages()); 1809 node.SetBoolean(kAccessibleLoaded, 1810 document_load_state_ != LOAD_STATE_LOADING); 1811 bool has_permissions = 1812 engine_->HasPermission(PDFEngine::PERMISSION_COPY) || 1813 engine_->HasPermission(PDFEngine::PERMISSION_COPY_ACCESSIBLE); 1814 node.SetBoolean(kAccessibleCopyable, has_permissions); 1815 std::string json; 1816 base::JSONWriter::Write(&node, &json); 1817 return pp::Var(json); 1818 } else if (args[0].is_number()) { 1819 return pp::Var(engine_->GetPageAsJSON(args[0].AsInt())); 1820 } 1821 } 1822 if (method_str == kJSPrintPreviewPageCount) { 1823 if (args.size() == 1 && args[0].is_number()) 1824 SetPrintPreviewMode(args[0].AsInt()); 1825 return pp::Var(); 1826 } 1827 if (method_str == kJSLoadPreviewPage) { 1828 if (args.size() == 2 && args[0].is_string() && args[1].is_number()) 1829 ProcessPreviewPageInfo(args[0].AsString(), args[1].AsInt()); 1830 return pp::Var(); 1831 } 1832 if (method_str == kJSGetPageLocationNormalized) { 1833 const size_t kMaxLength = 30; 1834 char location_info[kMaxLength]; 1835 int page_idx = engine_->GetMostVisiblePage(); 1836 if (page_idx < 0) 1837 return pp::Var(std::string()); 1838 pp::Rect rect = engine_->GetPageContentsRect(page_idx); 1839 int v_scrollbar_reserved_thickness = 1840 v_scrollbar_.get() ? GetScaled(GetScrollbarReservedThickness()) : 0; 1841 1842 rect.set_x(rect.x() + ((plugin_size_.width() - 1843 v_scrollbar_reserved_thickness - available_area_.width()) / 2)); 1844 base::snprintf(location_info, 1845 kMaxLength, 1846 "%0.4f;%0.4f;%0.4f;%0.4f;", 1847 rect.x() / static_cast<float>(plugin_size_.width()), 1848 rect.y() / static_cast<float>(plugin_size_.height()), 1849 rect.width() / static_cast<float>(plugin_size_.width()), 1850 rect.height()/ static_cast<float>(plugin_size_.height())); 1851 return pp::Var(std::string(location_info)); 1852 } 1853 if (method_str == kJSSetPageNumbers) { 1854 if (args.size() != 1 || !args[0].is_string()) 1855 return pp::Var(); 1856 const int num_pages_signed = engine_->GetNumberOfPages(); 1857 if (num_pages_signed <= 0) 1858 return pp::Var(); 1859 scoped_ptr<base::ListValue> page_ranges(static_cast<base::ListValue*>( 1860 base::JSONReader::Read(args[0].AsString(), false))); 1861 const size_t num_pages = static_cast<size_t>(num_pages_signed); 1862 if (!page_ranges.get() || page_ranges->GetSize() != num_pages) 1863 return pp::Var(); 1864 1865 std::vector<int> print_preview_page_numbers; 1866 for (size_t index = 0; index < num_pages; ++index) { 1867 int page_number = 0; // |page_number| is 1-based. 1868 if (!page_ranges->GetInteger(index, &page_number) || page_number < 1) 1869 return pp::Var(); 1870 print_preview_page_numbers.push_back(page_number); 1871 } 1872 print_preview_page_numbers_ = print_preview_page_numbers; 1873 page_indicator_.set_current_page(GetPageNumberToDisplay()); 1874 return pp::Var(); 1875 } 1876 // This is here to work around https://bugs.webkit.org/show_bug.cgi?id=16735. 1877 // In JS, creating a synthetic keyboard event and dispatching it always 1878 // result in a keycode of 0. 1879 if (method_str == kJSSendKeyEvent) { 1880 if (args.size() == 1 && args[0].is_number()) { 1881 pp::KeyboardInputEvent event( 1882 this, // instance 1883 PP_INPUTEVENT_TYPE_KEYDOWN, // HandleInputEvent only care about this. 1884 0, // timestamp, not used for kbd events. 1885 0, // no modifiers. 1886 args[0].AsInt(), // keycode. 1887 pp::Var()); // no char text needed. 1888 HandleInputEvent(event); 1889 } 1890 } 1891 return pp::Var(); 1892 } 1893 1894 void Instance::OnGeometryChanged(double old_zoom, float old_device_scale) { 1895 bool force_no_horizontal_scrollbar = false; 1896 int scrollbar_thickness = GetScrollbarThickness(); 1897 1898 if (old_device_scale != device_scale_) { 1899 // Change in device scale forces us to recreate resources 1900 ConfigureNumberImageGenerator(); 1901 1902 CreateToolbar(current_tb_info_, current_tb_info_size_); 1903 // Load autoscroll anchor image. 1904 autoscroll_anchor_ = 1905 CreateResourceImage(PP_RESOURCEIMAGE_PDF_PAN_SCROLL_ICON); 1906 1907 ConfigurePageIndicator(); 1908 ConfigureProgressBar(); 1909 1910 pp::Point scroll_position = engine_->GetScrollPosition(); 1911 ScalePoint(device_scale_ / old_device_scale, &scroll_position); 1912 engine_->SetScrollPosition(scroll_position); 1913 } 1914 1915 UpdateZoomScale(); 1916 if (zoom_ != old_zoom || device_scale_ != old_device_scale) 1917 engine_->ZoomUpdated(zoom_ * device_scale_); 1918 if (zoom_ != old_zoom) 1919 ZoomChanged(zoom_); 1920 1921 available_area_ = pp::Rect(plugin_size_); 1922 if (GetDocumentPixelHeight() > plugin_size_.height()) { 1923 CreateVerticalScrollbar(); 1924 } else { 1925 DestroyVerticalScrollbar(); 1926 } 1927 1928 int v_scrollbar_reserved_thickness = 1929 v_scrollbar_.get() ? GetScaled(GetScrollbarReservedThickness()) : 0; 1930 1931 if (!force_no_horizontal_scrollbar && 1932 GetDocumentPixelWidth() > 1933 (plugin_size_.width() - v_scrollbar_reserved_thickness)) { 1934 CreateHorizontalScrollbar(); 1935 1936 // Adding the horizontal scrollbar now might cause us to need vertical 1937 // scrollbars. 1938 if (GetDocumentPixelHeight() > 1939 plugin_size_.height() - GetScaled(GetScrollbarReservedThickness())) { 1940 CreateVerticalScrollbar(); 1941 } 1942 1943 } else { 1944 DestroyHorizontalScrollbar(); 1945 } 1946 1947 #ifdef ENABLE_THUMBNAILS 1948 int thumbnails_pos = 0, thumbnails_total = 0; 1949 #endif 1950 if (v_scrollbar_.get()) { 1951 v_scrollbar_->SetScale(device_scale_); 1952 available_area_.set_width( 1953 std::max(0, plugin_size_.width() - v_scrollbar_reserved_thickness)); 1954 1955 #ifdef ENABLE_THUMBNAILS 1956 int height = plugin_size_.height(); 1957 #endif 1958 int height_dip = plugin_dip_size_.height(); 1959 1960 #if defined(OS_MACOSX) 1961 // Before Lion, Mac always had the resize at the bottom. After that, it 1962 // never did. 1963 if ((base::mac::IsOSSnowLeopard() && full_) || 1964 (base::mac::IsOSLionOrLater() && h_scrollbar_.get())) { 1965 #else 1966 if (h_scrollbar_.get()) { 1967 #endif // defined(OS_MACOSX) 1968 #ifdef ENABLE_THUMBNAILS 1969 height -= GetScaled(GetScrollbarThickness()); 1970 #endif 1971 height_dip -= GetScrollbarThickness(); 1972 } 1973 #ifdef ENABLE_THUMBNAILS 1974 int32 doc_height = GetDocumentPixelHeight(); 1975 #endif 1976 int32 doc_height_dip = 1977 static_cast<int32>(GetDocumentPixelHeight() / device_scale_); 1978 #if defined(OS_MACOSX) 1979 // On the Mac we always allow room for the resize button (whose width is 1980 // the same as that of the scrollbar) in full mode. However, if there is no 1981 // no horizontal scrollbar, the end of the scrollbar will scroll past the 1982 // end of the document. This is because the scrollbar assumes that its own 1983 // height (in the case of a vscroll bar) is the same as the height of the 1984 // viewport. Since the viewport is actually larger, we compensate by 1985 // adjusting the document height. Similar logic applies below for the 1986 // horizontal scrollbar. 1987 // For example, if the document size is 1000, and the viewport size is 200, 1988 // then the scrollbar position at the end will be 800. In this case the 1989 // viewport is actally 215 (assuming 15 as the scrollbar width) but the 1990 // scrollbar thinks it is 200. We want the scrollbar position at the end to 1991 // be 785. Making the document size 985 achieves this. 1992 if (full_ && !h_scrollbar_.get()) { 1993 #ifdef ENABLE_THUMBNAILS 1994 doc_height -= GetScaled(GetScrollbarThickness()); 1995 #endif 1996 doc_height_dip -= GetScrollbarThickness(); 1997 } 1998 #endif // defined(OS_MACOSX) 1999 2000 int32 position; 2001 position = v_scrollbar_->GetValue(); 2002 position = static_cast<int>(position * zoom_ / old_zoom); 2003 valid_v_range_ = doc_height_dip - height_dip; 2004 if (position > valid_v_range_) 2005 position = valid_v_range_; 2006 2007 v_scrollbar_->SetValue(position); 2008 2009 PP_Rect loc; 2010 loc.point.x = static_cast<int>(available_area_.right() / device_scale_); 2011 if (IsOverlayScrollbar()) 2012 loc.point.x -= scrollbar_thickness; 2013 loc.point.y = 0; 2014 loc.size.width = scrollbar_thickness; 2015 loc.size.height = height_dip; 2016 v_scrollbar_->SetLocation(loc); 2017 v_scrollbar_->SetDocumentSize(doc_height_dip); 2018 2019 #ifdef ENABLE_THUMBNAILS 2020 thumbnails_pos = position; 2021 thumbnails_total = doc_height - height; 2022 #endif 2023 } 2024 2025 if (h_scrollbar_.get()) { 2026 h_scrollbar_->SetScale(device_scale_); 2027 available_area_.set_height( 2028 std::max(0, plugin_size_.height() - 2029 GetScaled(GetScrollbarReservedThickness()))); 2030 2031 int width_dip = plugin_dip_size_.width(); 2032 2033 // See note above. 2034 #if defined(OS_MACOSX) 2035 if ((base::mac::IsOSSnowLeopard() && full_) || 2036 (base::mac::IsOSLionOrLater() && v_scrollbar_.get())) { 2037 #else 2038 if (v_scrollbar_.get()) { 2039 #endif 2040 width_dip -= GetScrollbarThickness(); 2041 } 2042 int32 doc_width_dip = 2043 static_cast<int32>(GetDocumentPixelWidth() / device_scale_); 2044 #if defined(OS_MACOSX) 2045 // See comment in the above if (v_scrollbar_.get()) block. 2046 if (full_ && !v_scrollbar_.get()) 2047 doc_width_dip -= GetScrollbarThickness(); 2048 #endif // defined(OS_MACOSX) 2049 2050 int32 position; 2051 position = h_scrollbar_->GetValue(); 2052 position = static_cast<int>(position * zoom_ / old_zoom); 2053 position = std::min(position, doc_width_dip - width_dip); 2054 2055 h_scrollbar_->SetValue(position); 2056 2057 PP_Rect loc; 2058 loc.point.x = 0; 2059 loc.point.y = static_cast<int>(available_area_.bottom() / device_scale_); 2060 if (IsOverlayScrollbar()) 2061 loc.point.y -= scrollbar_thickness; 2062 loc.size.width = width_dip; 2063 loc.size.height = scrollbar_thickness; 2064 h_scrollbar_->SetLocation(loc); 2065 h_scrollbar_->SetDocumentSize(doc_width_dip); 2066 } 2067 2068 int doc_width = GetDocumentPixelWidth(); 2069 if (doc_width < available_area_.width()) { 2070 available_area_.Offset((available_area_.width() - doc_width) / 2, 0); 2071 available_area_.set_width(doc_width); 2072 } 2073 int doc_height = GetDocumentPixelHeight(); 2074 if (doc_height < available_area_.height()) { 2075 available_area_.set_height(doc_height); 2076 } 2077 2078 // We'll invalidate the entire plugin anyways. 2079 UpdateToolbarPosition(false); 2080 UpdateProgressBarPosition(false); 2081 UpdatePageIndicatorPosition(false); 2082 2083 #ifdef ENABLE_THUMBNAILS 2084 // Update thumbnail control position. 2085 thumbnails_.SetPosition(thumbnails_pos, thumbnails_total, false); 2086 pp::Rect thumbnails_rc(plugin_size_.width() - GetScaled(kThumbnailsWidth), 0, 2087 GetScaled(kThumbnailsWidth), plugin_size_.height()); 2088 if (v_scrollbar_.get()) 2089 thumbnails_rc.Offset(-v_scrollbar_reserved_thickness, 0); 2090 if (h_scrollbar_.get()) 2091 thumbnails_rc.Inset(0, 0, 0, v_scrollbar_reserved_thickness); 2092 thumbnails_.SetRect(thumbnails_rc, false); 2093 #endif 2094 2095 CalculateBackgroundParts(); 2096 engine_->PageOffsetUpdated(available_area_.point()); 2097 engine_->PluginSizeUpdated(available_area_.size()); 2098 2099 if (!document_size_.GetArea()) 2100 return; 2101 paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_)); 2102 2103 if (on_plugin_size_changed_callback_.is_string()) 2104 ExecuteScript(on_plugin_size_changed_callback_); 2105 } 2106 2107 void Instance::CreateHorizontalScrollbar() { 2108 if (h_scrollbar_.get()) 2109 return; 2110 2111 h_scrollbar_.reset(new pp::Scrollbar_Dev(this, false)); 2112 } 2113 2114 void Instance::CreateVerticalScrollbar() { 2115 if (v_scrollbar_.get()) 2116 return; 2117 2118 v_scrollbar_.reset(new pp::Scrollbar_Dev(this, true)); 2119 } 2120 2121 void Instance::DestroyHorizontalScrollbar() { 2122 if (!h_scrollbar_.get()) 2123 return; 2124 if (h_scrollbar_->GetValue()) 2125 engine_->ScrolledToXPosition(0); 2126 h_scrollbar_.reset(); 2127 } 2128 2129 void Instance::DestroyVerticalScrollbar() { 2130 if (!v_scrollbar_.get()) 2131 return; 2132 if (v_scrollbar_->GetValue()) 2133 engine_->ScrolledToYPosition(0); 2134 v_scrollbar_.reset(); 2135 page_indicator_.Show(false, true); 2136 } 2137 2138 int Instance::GetScrollbarThickness() { 2139 if (scrollbar_thickness_ == -1) { 2140 pp::Scrollbar_Dev temp_scrollbar(this, false); 2141 scrollbar_thickness_ = temp_scrollbar.GetThickness(); 2142 scrollbar_reserved_thickness_ = 2143 temp_scrollbar.IsOverlay() ? 0 : scrollbar_thickness_; 2144 } 2145 2146 return scrollbar_thickness_; 2147 } 2148 2149 int Instance::GetScrollbarReservedThickness() { 2150 GetScrollbarThickness(); 2151 return scrollbar_reserved_thickness_; 2152 } 2153 2154 bool Instance::IsOverlayScrollbar() { 2155 return GetScrollbarReservedThickness() == 0; 2156 } 2157 2158 void Instance::CreateToolbar(const ToolbarButtonInfo* tb_info, size_t size) { 2159 toolbar_.reset(new FadingControls()); 2160 2161 DCHECK(tb_info); 2162 DCHECK(size >= 0); 2163 2164 // Remember the current toolbar information in case we need to recreate the 2165 // images later. 2166 current_tb_info_ = tb_info; 2167 current_tb_info_size_ = size; 2168 2169 int max_height = 0; 2170 pp::Point origin(kToolbarFadingOffsetLeft, kToolbarFadingOffsetTop); 2171 ScalePoint(device_scale_, &origin); 2172 2173 std::list<Button*> buttons; 2174 for (size_t i = 0; i < size; i++) { 2175 Button* btn = new Button; 2176 pp::ImageData normal_face = 2177 CreateResourceImage(tb_info[i].normal); 2178 btn->CreateButton(tb_info[i].id, 2179 origin, 2180 true, 2181 toolbar_.get(), 2182 tb_info[i].style, 2183 normal_face, 2184 CreateResourceImage(tb_info[i].highlighted), 2185 CreateResourceImage(tb_info[i].pressed)); 2186 buttons.push_back(btn); 2187 2188 origin += pp::Point(normal_face.size().width(), 0); 2189 max_height = std::max(max_height, normal_face.size().height()); 2190 } 2191 2192 pp::Rect rc_toolbar(0, 0, 2193 origin.x() + GetToolbarRightOffset(), 2194 origin.y() + max_height + GetToolbarBottomOffset()); 2195 toolbar_->CreateFadingControls( 2196 kToolbarId, rc_toolbar, false, this, kTransparentAlpha); 2197 2198 std::list<Button*>::iterator iter; 2199 for (iter = buttons.begin(); iter != buttons.end(); ++iter) { 2200 toolbar_->AddControl(*iter); 2201 } 2202 } 2203 2204 int Instance::GetToolbarRightOffset() { 2205 int scrollbar_thickness = GetScrollbarThickness(); 2206 return GetScaled(kToolbarFadingOffsetRight) + 2 * scrollbar_thickness; 2207 } 2208 2209 int Instance::GetToolbarBottomOffset() { 2210 int scrollbar_thickness = GetScrollbarThickness(); 2211 return GetScaled(kToolbarFadingOffsetBottom) + scrollbar_thickness; 2212 } 2213 2214 std::vector<pp::ImageData> Instance::GetThumbnailResources() { 2215 std::vector<pp::ImageData> num_images(10); 2216 num_images[0] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_0); 2217 num_images[1] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_1); 2218 num_images[2] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_2); 2219 num_images[3] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_3); 2220 num_images[4] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_4); 2221 num_images[5] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_5); 2222 num_images[6] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_6); 2223 num_images[7] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_7); 2224 num_images[8] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_8); 2225 num_images[9] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_9); 2226 return num_images; 2227 } 2228 2229 std::vector<pp::ImageData> Instance::GetProgressBarResources( 2230 pp::ImageData* background) { 2231 std::vector<pp::ImageData> result(9); 2232 result[0] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_0); 2233 result[1] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_1); 2234 result[2] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_2); 2235 result[3] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_3); 2236 result[4] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_4); 2237 result[5] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_5); 2238 result[6] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_6); 2239 result[7] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_7); 2240 result[8] = CreateResourceImage(PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_8); 2241 *background = CreateResourceImage( 2242 PP_RESOURCEIMAGE_PDF_PROGRESS_BAR_BACKGROUND); 2243 return result; 2244 } 2245 2246 void Instance::CreatePageIndicator(bool always_visible) { 2247 page_indicator_.CreatePageIndicator(kPageIndicatorId, false, this, 2248 number_image_generator(), always_visible); 2249 ConfigurePageIndicator(); 2250 } 2251 2252 void Instance::ConfigurePageIndicator() { 2253 pp::ImageData background = 2254 CreateResourceImage(PP_RESOURCEIMAGE_PDF_PAGE_INDICATOR_BACKGROUND); 2255 page_indicator_.Configure(pp::Point(), background); 2256 } 2257 2258 void Instance::CreateProgressBar() { 2259 pp::ImageData background; 2260 std::vector<pp::ImageData> images = GetProgressBarResources(&background); 2261 std::string text = GetLocalizedString(PP_RESOURCESTRING_PDFPROGRESSLOADING); 2262 progress_bar_.CreateProgressControl(kProgressBarId, false, this, 0.0, 2263 device_scale_, images, background, text); 2264 } 2265 2266 void Instance::ConfigureProgressBar() { 2267 pp::ImageData background; 2268 std::vector<pp::ImageData> images = GetProgressBarResources(&background); 2269 progress_bar_.Reconfigure(background, images, device_scale_); 2270 } 2271 2272 void Instance::CreateThumbnails() { 2273 thumbnails_.CreateThumbnailControl( 2274 kThumbnailsId, pp::Rect(), false, this, engine_.get(), 2275 number_image_generator()); 2276 } 2277 2278 void Instance::LoadUrl(const std::string& url) { 2279 LoadUrlInternal(url, &embed_loader_, &Instance::DidOpen); 2280 } 2281 2282 void Instance::LoadPreviewUrl(const std::string& url) { 2283 LoadUrlInternal(url, &embed_preview_loader_, &Instance::DidOpenPreview); 2284 } 2285 2286 void Instance::LoadUrlInternal(const std::string& url, pp::URLLoader* loader, 2287 void (Instance::* method)(int32_t)) { 2288 pp::URLRequestInfo request(this); 2289 request.SetURL(url); 2290 request.SetMethod("GET"); 2291 2292 *loader = CreateURLLoaderInternal(); 2293 pp::CompletionCallback callback = loader_factory_.NewCallback(method); 2294 int rv = loader->Open(request, callback); 2295 if (rv != PP_OK_COMPLETIONPENDING) 2296 callback.Run(rv); 2297 } 2298 2299 pp::URLLoader Instance::CreateURLLoaderInternal() { 2300 pp::URLLoader loader(this); 2301 2302 const PPB_URLLoaderTrusted* trusted_interface = 2303 reinterpret_cast<const PPB_URLLoaderTrusted*>( 2304 pp::Module::Get()->GetBrowserInterface( 2305 PPB_URLLOADERTRUSTED_INTERFACE)); 2306 if (trusted_interface) 2307 trusted_interface->GrantUniversalAccess(loader.pp_resource()); 2308 return loader; 2309 } 2310 2311 int Instance::GetInitialPage(const std::string& url) { 2312 #if defined(OS_NACL) 2313 return -1; 2314 #else 2315 size_t found_idx = url.find('#'); 2316 if (found_idx == std::string::npos) 2317 return -1; 2318 2319 const std::string& ref = url.substr(found_idx + 1); 2320 std::vector<std::string> fragments; 2321 Tokenize(ref, kDelimiters, &fragments); 2322 2323 // Page number to return, zero-based. 2324 int page = -1; 2325 2326 // Handle the case of http://foo.com/bar#NAMEDDEST. This is not explicitly 2327 // mentioned except by example in the Adobe "PDF Open Parameters" document. 2328 if ((fragments.size() == 1) && (fragments[0].find('=') == std::string::npos)) 2329 return engine_->GetNamedDestinationPage(fragments[0]); 2330 2331 for (size_t i = 0; i < fragments.size(); ++i) { 2332 std::vector<std::string> key_value; 2333 base::SplitString(fragments[i], '=', &key_value); 2334 if (key_value.size() != 2) 2335 continue; 2336 const std::string& key = key_value[0]; 2337 const std::string& value = key_value[1]; 2338 2339 if (base::strcasecmp(kPage, key.c_str()) == 0) { 2340 // |page_value| is 1-based. 2341 int page_value = -1; 2342 if (base::StringToInt(value, &page_value) && page_value > 0) 2343 page = page_value - 1; 2344 continue; 2345 } 2346 if (base::strcasecmp(kNamedDest, key.c_str()) == 0) { 2347 // |page_value| is 0-based. 2348 int page_value = engine_->GetNamedDestinationPage(value); 2349 if (page_value >= 0) 2350 page = page_value; 2351 continue; 2352 } 2353 } 2354 return page; 2355 #endif 2356 } 2357 2358 void Instance::UpdateToolbarPosition(bool invalidate) { 2359 pp::Rect ctrl_rc = toolbar_->GetControlsRect(); 2360 int min_toolbar_width = ctrl_rc.width() + GetToolbarRightOffset() + 2361 GetScaled(kToolbarFadingOffsetLeft); 2362 int min_toolbar_height = ctrl_rc.width() + GetToolbarBottomOffset() + 2363 GetScaled(kToolbarFadingOffsetBottom); 2364 2365 // Update toolbar position 2366 if (plugin_size_.width() < min_toolbar_width || 2367 plugin_size_.height() < min_toolbar_height) { 2368 // Disable toolbar if it does not fit on the screen. 2369 toolbar_->Show(false, invalidate); 2370 } else { 2371 pp::Point offset( 2372 plugin_size_.width() - GetToolbarRightOffset() - ctrl_rc.right(), 2373 plugin_size_.height() - GetToolbarBottomOffset() - ctrl_rc.bottom()); 2374 toolbar_->MoveBy(offset, invalidate); 2375 2376 int toolbar_width = std::max(plugin_size_.width() / 2, min_toolbar_width); 2377 toolbar_->ExpandLeft(toolbar_width - toolbar_->rect().width()); 2378 toolbar_->Show(painted_first_page_, invalidate); 2379 } 2380 } 2381 2382 void Instance::UpdateProgressBarPosition(bool invalidate) { 2383 // TODO(gene): verify we don't overlap with toolbar. 2384 int scrollbar_thickness = GetScrollbarThickness(); 2385 pp::Point progress_origin( 2386 scrollbar_thickness + GetScaled(kProgressOffsetLeft), 2387 plugin_size_.height() - progress_bar_.rect().height() - 2388 scrollbar_thickness - GetScaled(kProgressOffsetBottom)); 2389 progress_bar_.MoveTo(progress_origin, invalidate); 2390 } 2391 2392 void Instance::UpdatePageIndicatorPosition(bool invalidate) { 2393 int32 doc_height = static_cast<int>(document_size_.height() * zoom_); 2394 pp::Point origin( 2395 plugin_size_.width() - page_indicator_.rect().width() - 2396 GetScaled(GetScrollbarReservedThickness()), 2397 page_indicator_.GetYPosition(engine_->GetVerticalScrollbarYPosition(), 2398 doc_height, plugin_size_.height())); 2399 page_indicator_.MoveTo(origin, invalidate); 2400 } 2401 2402 void Instance::SetZoom(ZoomMode zoom_mode, double scale) { 2403 double old_zoom = zoom_; 2404 2405 zoom_mode_ = zoom_mode; 2406 if (zoom_mode_ == ZOOM_SCALE) 2407 zoom_ = scale; 2408 UpdateZoomScale(); 2409 2410 engine_->ZoomUpdated(zoom_ * device_scale_); 2411 OnGeometryChanged(old_zoom, device_scale_); 2412 2413 // If fit-to-height, snap to the beginning of the most visible page. 2414 if (zoom_mode_ == ZOOM_FIT_TO_PAGE) { 2415 ScrollToPage(engine_->GetMostVisiblePage()); 2416 } 2417 2418 // Update sticky buttons to the current zoom style. 2419 Button* ftp_btn = static_cast<Button*>( 2420 toolbar_->GetControl(kFitToPageButtonId)); 2421 Button* ftw_btn = static_cast<Button*>( 2422 toolbar_->GetControl(kFitToWidthButtonId)); 2423 switch (zoom_mode_) { 2424 case ZOOM_FIT_TO_PAGE: 2425 ftp_btn->SetPressedState(true); 2426 ftw_btn->SetPressedState(false); 2427 break; 2428 case ZOOM_FIT_TO_WIDTH: 2429 ftw_btn->SetPressedState(true); 2430 ftp_btn->SetPressedState(false); 2431 break; 2432 default: 2433 ftw_btn->SetPressedState(false); 2434 ftp_btn->SetPressedState(false); 2435 } 2436 } 2437 2438 void Instance::UpdateZoomScale() { 2439 switch (zoom_mode_) { 2440 case ZOOM_SCALE: 2441 break; // Keep current scale. 2442 case ZOOM_FIT_TO_PAGE: { 2443 int page_num = engine_->GetFirstVisiblePage(); 2444 if (page_num == -1) 2445 break; 2446 pp::Rect rc = engine_->GetPageRect(page_num); 2447 if (!rc.height()) 2448 break; 2449 // Calculate fit to width zoom level. 2450 double ftw_zoom = static_cast<double>(plugin_dip_size_.width() - 2451 GetScrollbarReservedThickness()) / document_size_.width(); 2452 // Calculate fit to height zoom level. If document will not fit 2453 // horizontally, adjust zoom level to allow space for horizontal 2454 // scrollbar. 2455 double fth_zoom = 2456 static_cast<double>(plugin_dip_size_.height()) / rc.height(); 2457 if (fth_zoom * document_size_.width() > 2458 plugin_dip_size_.width() - GetScrollbarReservedThickness()) 2459 fth_zoom = static_cast<double>(plugin_dip_size_.height() 2460 - GetScrollbarReservedThickness()) / rc.height(); 2461 zoom_ = std::min(ftw_zoom, fth_zoom); 2462 } break; 2463 case ZOOM_FIT_TO_WIDTH: 2464 case ZOOM_AUTO: 2465 if (!document_size_.width()) 2466 break; 2467 zoom_ = static_cast<double>(plugin_dip_size_.width() - 2468 GetScrollbarReservedThickness()) / document_size_.width(); 2469 if (zoom_mode_ == ZOOM_AUTO && zoom_ > 1.0) 2470 zoom_ = 1.0; 2471 break; 2472 } 2473 zoom_ = ClipToRange(zoom_, kMinZoom, kMaxZoom); 2474 } 2475 2476 double Instance::CalculateZoom(uint32 control_id) const { 2477 if (control_id == kZoomInButtonId) { 2478 for (size_t i = 0; i < chrome_page_zoom::kPresetZoomFactorsSize; ++i) { 2479 double current_zoom = chrome_page_zoom::kPresetZoomFactors[i]; 2480 if (current_zoom - content::kEpsilon > zoom_) 2481 return current_zoom; 2482 } 2483 } else { 2484 for (size_t i = chrome_page_zoom::kPresetZoomFactorsSize; i > 0; --i) { 2485 double current_zoom = chrome_page_zoom::kPresetZoomFactors[i - 1]; 2486 if (current_zoom + content::kEpsilon < zoom_) 2487 return current_zoom; 2488 } 2489 } 2490 return zoom_; 2491 } 2492 2493 pp::ImageData Instance::CreateResourceImage(PP_ResourceImage image_id) { 2494 if (hidpi_enabled_) 2495 return pp::PDF::GetResourceImageForScale(this, image_id, device_scale_); 2496 2497 return pp::PDF::GetResourceImage(this, image_id); 2498 } 2499 2500 std::string Instance::GetLocalizedString(PP_ResourceString id) { 2501 pp::Var rv(pp::PDF::GetLocalizedString(this, id)); 2502 if (!rv.is_string()) 2503 return std::string(); 2504 2505 return rv.AsString(); 2506 } 2507 2508 void Instance::DrawText(const pp::Point& top_center, PP_ResourceString id) { 2509 std::string str(GetLocalizedString(id)); 2510 2511 pp::FontDescription_Dev description; 2512 description.set_family(PP_FONTFAMILY_SANSSERIF); 2513 description.set_size(kMessageTextSize * device_scale_); 2514 pp::Font_Dev font(this, description); 2515 int length = font.MeasureSimpleText(str); 2516 pp::Point point(top_center); 2517 point.set_x(point.x() - length / 2); 2518 DCHECK(!image_data_.is_null()); 2519 font.DrawSimpleText(&image_data_, str, point, kMessageTextColor); 2520 } 2521 2522 void Instance::SetPrintPreviewMode(int page_count) { 2523 if (!IsPrintPreview() || page_count <= 0) { 2524 print_preview_page_count_ = 0; 2525 return; 2526 } 2527 2528 print_preview_page_count_ = page_count; 2529 ScrollToPage(0); 2530 engine_->AppendBlankPages(print_preview_page_count_); 2531 if (preview_pages_info_.size() > 0) 2532 LoadAvailablePreviewPage(); 2533 } 2534 2535 bool Instance::IsPrintPreview() { 2536 return IsPrintPreviewUrl(url_); 2537 } 2538 2539 int Instance::GetPageNumberToDisplay() { 2540 int page = engine_->GetMostVisiblePage(); 2541 if (IsPrintPreview() && !print_preview_page_numbers_.empty()) { 2542 page = ClipToRange<int>(page, 0, print_preview_page_numbers_.size() - 1); 2543 return print_preview_page_numbers_[page]; 2544 } 2545 return page + 1; 2546 } 2547 2548 void Instance::ProcessPreviewPageInfo(const std::string& url, 2549 int dst_page_index) { 2550 if (!IsPrintPreview() || print_preview_page_count_ < 0) 2551 return; 2552 2553 int src_page_index = ExtractPrintPreviewPageIndex(url); 2554 if (src_page_index < 1) 2555 return; 2556 2557 preview_pages_info_.push(std::make_pair(url, dst_page_index)); 2558 LoadAvailablePreviewPage(); 2559 } 2560 2561 void Instance::LoadAvailablePreviewPage() { 2562 if (preview_pages_info_.size() <= 0) 2563 return; 2564 2565 std::string url = preview_pages_info_.front().first; 2566 int dst_page_index = preview_pages_info_.front().second; 2567 int src_page_index = ExtractPrintPreviewPageIndex(url); 2568 if (src_page_index < 1 || 2569 dst_page_index >= print_preview_page_count_ || 2570 preview_document_load_state_ == LOAD_STATE_LOADING) { 2571 return; 2572 } 2573 2574 preview_document_load_state_ = LOAD_STATE_LOADING; 2575 LoadPreviewUrl(url); 2576 } 2577 2578 void Instance::EnableAutoscroll(const pp::Point& origin) { 2579 if (is_autoscroll_) 2580 return; 2581 2582 pp::Size client_size = plugin_size_; 2583 if (v_scrollbar_.get()) 2584 client_size.Enlarge(-GetScrollbarThickness(), 0); 2585 if (h_scrollbar_.get()) 2586 client_size.Enlarge(0, -GetScrollbarThickness()); 2587 2588 // Do not allow autoscroll if client area is too small. 2589 if (autoscroll_anchor_.size().width() > client_size.width() || 2590 autoscroll_anchor_.size().height() > client_size.height()) 2591 return; 2592 2593 autoscroll_rect_ = pp::Rect( 2594 pp::Point(origin.x() - autoscroll_anchor_.size().width() / 2, 2595 origin.y() - autoscroll_anchor_.size().height() / 2), 2596 autoscroll_anchor_.size()); 2597 2598 // Make sure autoscroll anchor is in the client area. 2599 if (autoscroll_rect_.right() > client_size.width()) { 2600 autoscroll_rect_.set_x( 2601 client_size.width() - autoscroll_anchor_.size().width()); 2602 } 2603 if (autoscroll_rect_.bottom() > client_size.height()) { 2604 autoscroll_rect_.set_y( 2605 client_size.height() - autoscroll_anchor_.size().height()); 2606 } 2607 2608 if (autoscroll_rect_.x() < 0) 2609 autoscroll_rect_.set_x(0); 2610 if (autoscroll_rect_.y() < 0) 2611 autoscroll_rect_.set_y(0); 2612 2613 is_autoscroll_ = true; 2614 Invalidate(kAutoScrollId, autoscroll_rect_); 2615 2616 ScheduleTimer(kAutoScrollId, kAutoScrollTimeoutMs); 2617 } 2618 2619 void Instance::DisableAutoscroll() { 2620 if (is_autoscroll_) { 2621 is_autoscroll_ = false; 2622 Invalidate(kAutoScrollId, autoscroll_rect_); 2623 } 2624 } 2625 2626 PP_CursorType_Dev Instance::CalculateAutoscroll(const pp::Point& mouse_pos) { 2627 // Scroll only if mouse pointer is outside of the anchor area. 2628 if (autoscroll_rect_.Contains(mouse_pos)) { 2629 autoscroll_x_ = 0; 2630 autoscroll_y_ = 0; 2631 return PP_CURSORTYPE_MIDDLEPANNING; 2632 } 2633 2634 // Relative position to the center of anchor area. 2635 pp::Point rel_pos = mouse_pos - autoscroll_rect_.CenterPoint(); 2636 2637 // Calculate angle from the X axis. Angle is in range from -pi to pi. 2638 double angle = atan2(static_cast<double>(rel_pos.y()), 2639 static_cast<double>(rel_pos.x())); 2640 2641 autoscroll_x_ = rel_pos.x() * kAutoScrollFactor; 2642 autoscroll_y_ = rel_pos.y() * kAutoScrollFactor; 2643 2644 // Angle is from -pi to pi. Screen Y is increasing toward bottom, 2645 // so negative angle represent north direction. 2646 if (angle < - (M_PI * 7.0 / 8.0)) { 2647 // going west 2648 return PP_CURSORTYPE_WESTPANNING; 2649 } else if (angle < - (M_PI * 5.0 / 8.0)) { 2650 // going north-west 2651 return PP_CURSORTYPE_NORTHWESTPANNING; 2652 } else if (angle < - (M_PI * 3.0 / 8.0)) { 2653 // going north. 2654 return PP_CURSORTYPE_NORTHPANNING; 2655 } else if (angle < - (M_PI * 1.0 / 8.0)) { 2656 // going north-east 2657 return PP_CURSORTYPE_NORTHEASTPANNING; 2658 } else if (angle < M_PI * 1.0 / 8.0) { 2659 // going east. 2660 return PP_CURSORTYPE_EASTPANNING; 2661 } else if (angle < M_PI * 3.0 / 8.0) { 2662 // going south-east 2663 return PP_CURSORTYPE_SOUTHEASTPANNING; 2664 } else if (angle < M_PI * 5.0 / 8.0) { 2665 // going south. 2666 return PP_CURSORTYPE_SOUTHPANNING; 2667 } else if (angle < M_PI * 7.0 / 8.0) { 2668 // going south-west 2669 return PP_CURSORTYPE_SOUTHWESTPANNING; 2670 } 2671 2672 // went around the circle, going west again 2673 return PP_CURSORTYPE_WESTPANNING; 2674 } 2675 2676 void Instance::ConfigureNumberImageGenerator() { 2677 std::vector<pp::ImageData> num_images = GetThumbnailResources(); 2678 pp::ImageData number_background = CreateResourceImage( 2679 PP_RESOURCEIMAGE_PDF_BUTTON_THUMBNAIL_NUM_BACKGROUND); 2680 number_image_generator_->Configure(number_background, 2681 num_images, 2682 device_scale_); 2683 } 2684 2685 NumberImageGenerator* Instance::number_image_generator() { 2686 if (!number_image_generator_.get()) { 2687 number_image_generator_.reset(new NumberImageGenerator(this)); 2688 ConfigureNumberImageGenerator(); 2689 } 2690 return number_image_generator_.get(); 2691 } 2692 2693 int Instance::GetScaled(int x) const { 2694 return static_cast<int>(x * device_scale_); 2695 } 2696 2697 void Instance::UserMetricsRecordAction(const std::string& action) { 2698 pp::PDF::UserMetricsRecordAction(this, pp::Var(action)); 2699 } 2700 2701 PDFScriptableObject::PDFScriptableObject(Instance* instance) 2702 : instance_(instance) { 2703 } 2704 2705 PDFScriptableObject::~PDFScriptableObject() { 2706 } 2707 2708 bool PDFScriptableObject::HasMethod(const pp::Var& name, pp::Var* exception) { 2709 return instance_->HasScriptableMethod(name, exception); 2710 } 2711 2712 pp::Var PDFScriptableObject::Call(const pp::Var& method, 2713 const std::vector<pp::Var>& args, 2714 pp::Var* exception) { 2715 return instance_->CallScriptableMethod(method, args, exception); 2716 } 2717 2718 } // namespace chrome_pdf 2719