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