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