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