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