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#ifndef CHROME_RENDERER_PRINTING_PRINT_WEB_VIEW_HELPER_H_
6#define CHROME_RENDERER_PRINTING_PRINT_WEB_VIEW_HELPER_H_
7
8#include <vector>
9
10#include "base/callback.h"
11#include "base/gtest_prod_util.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/memory/shared_memory.h"
14#include "base/memory/weak_ptr.h"
15#include "base/time/time.h"
16#include "content/public/renderer/render_view_observer.h"
17#include "content/public/renderer/render_view_observer_tracker.h"
18#include "printing/pdf_metafile_skia.h"
19#include "third_party/WebKit/public/platform/WebCanvas.h"
20#include "third_party/WebKit/public/web/WebNode.h"
21#include "third_party/WebKit/public/web/WebPrintParams.h"
22#include "ui/gfx/size.h"
23
24struct PrintMsg_Print_Params;
25struct PrintMsg_PrintPage_Params;
26struct PrintMsg_PrintPages_Params;
27struct PrintHostMsg_SetOptionsFromDocument_Params;
28
29namespace base {
30class DictionaryValue;
31}
32
33namespace blink {
34class WebFrame;
35class WebView;
36}
37
38namespace printing {
39
40struct PageSizeMargins;
41class PrepareFrameAndViewForPrint;
42
43// Stores reference to frame using WebVew and unique name.
44// Workaround to modal dialog issue on Linux. crbug.com/236147.
45// If WebFrame someday supports WeakPtr, we should use it here.
46class FrameReference {
47 public:
48  explicit FrameReference(blink::WebLocalFrame* frame);
49  FrameReference();
50  ~FrameReference();
51
52  void Reset(blink::WebLocalFrame* frame);
53
54  blink::WebLocalFrame* GetFrame();
55  blink::WebView* view();
56
57 private:
58  blink::WebView* view_;
59  blink::WebLocalFrame* frame_;
60};
61
62// PrintWebViewHelper handles most of the printing grunt work for RenderView.
63// We plan on making print asynchronous and that will require copying the DOM
64// of the document and creating a new WebView with the contents.
65class PrintWebViewHelper
66    : public content::RenderViewObserver,
67      public content::RenderViewObserverTracker<PrintWebViewHelper> {
68 public:
69  explicit PrintWebViewHelper(content::RenderView* render_view);
70  virtual ~PrintWebViewHelper();
71
72  // Disable print preview and switch to system dialog printing even if full
73  // printing is build-in. This method is used by CEF.
74  static void DisablePreview();
75
76  bool IsPrintingEnabled();
77
78  void PrintNode(const blink::WebNode& node);
79
80 private:
81  friend class PrintWebViewHelperTestBase;
82  FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperPreviewTest,
83                           BlockScriptInitiatedPrinting);
84  FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, OnPrintPages);
85  FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest,
86                           BlockScriptInitiatedPrinting);
87  FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest,
88                           BlockScriptInitiatedPrintingFromPopup);
89#if defined(OS_WIN) || defined(OS_MACOSX)
90  FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, PrintLayoutTest);
91  FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, PrintWithIframe);
92#endif  // defined(OS_WIN) || defined(OS_MACOSX)
93
94  enum PrintingResult {
95    OK,
96    FAIL_PRINT_INIT,
97    FAIL_PRINT,
98    FAIL_PREVIEW,
99  };
100
101  enum PrintPreviewErrorBuckets {
102    PREVIEW_ERROR_NONE,  // Always first.
103    PREVIEW_ERROR_BAD_SETTING,
104    PREVIEW_ERROR_METAFILE_COPY_FAILED,
105    PREVIEW_ERROR_METAFILE_INIT_FAILED,
106    PREVIEW_ERROR_ZERO_PAGES,
107    PREVIEW_ERROR_MAC_DRAFT_METAFILE_INIT_FAILED,
108    PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE,
109    PREVIEW_ERROR_INVALID_PRINTER_SETTINGS,
110    PREVIEW_ERROR_LAST_ENUM  // Always last.
111  };
112
113  enum PrintPreviewRequestType {
114    PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME,
115    PRINT_PREVIEW_USER_INITIATED_SELECTION,
116    PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE,
117    PRINT_PREVIEW_SCRIPTED  // triggered by window.print().
118  };
119
120  // RenderViewObserver implementation.
121  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
122  virtual void PrintPage(blink::WebLocalFrame* frame,
123                         bool user_initiated) OVERRIDE;
124  virtual void DidStartLoading() OVERRIDE;
125  virtual void DidStopLoading() OVERRIDE;
126
127  // Message handlers ---------------------------------------------------------
128#if !defined(DISABLE_BASIC_PRINTING)
129  void OnPrintPages();
130  void OnPrintForSystemDialog();
131#endif  // !DISABLE_BASIC_PRINTING
132  void OnInitiatePrintPreview(bool selection_only);
133  void OnPrintPreview(const base::DictionaryValue& settings);
134  void OnPrintForPrintPreview(const base::DictionaryValue& job_settings);
135  void OnPrintingDone(bool success);
136
137  // Get |page_size| and |content_area| information from
138  // |page_layout_in_points|.
139  void GetPageSizeAndContentAreaFromPageLayout(
140      const PageSizeMargins& page_layout_in_points,
141      gfx::Size* page_size,
142      gfx::Rect* content_area);
143
144  // Update |ignore_css_margins_| based on settings.
145  void UpdateFrameMarginsCssInfo(const base::DictionaryValue& settings);
146
147  // Returns true if the current destination printer is PRINT_TO_PDF.
148  bool IsPrintToPdfRequested(const base::DictionaryValue& settings);
149
150  // Prepare frame for creating preview document.
151  void PrepareFrameForPreviewDocument();
152
153  // Continue creating preview document.
154  void OnFramePreparedForPreviewDocument();
155
156  // Initialize the print preview document.
157  bool CreatePreviewDocument();
158
159  // Renders a print preview page. |page_number| is 0-based.
160  // Returns true if print preview should continue, false on failure.
161  bool RenderPreviewPage(int page_number,
162                         const PrintMsg_Print_Params& print_params);
163
164  // Finalize the print ready preview document.
165  bool FinalizePrintReadyDocument();
166
167  // Enable/Disable window.print calls.  If |blocked| is true window.print
168  // calls will silently fail.  Call with |blocked| set to false to reenable.
169  void SetScriptedPrintBlocked(bool blocked);
170
171  // Main printing code -------------------------------------------------------
172
173  void Print(blink::WebLocalFrame* frame, const blink::WebNode& node);
174
175  // Notification when printing is done - signal tear-down/free resources.
176  void DidFinishPrinting(PrintingResult result);
177
178  // Print Settings -----------------------------------------------------------
179
180  // Initialize print page settings with default settings.
181  // Used only for native printing workflow.
182  bool InitPrintSettings(bool fit_to_paper_size);
183
184  // Calculate number of pages in source document.
185  bool CalculateNumberOfPages(blink::WebLocalFrame* frame,
186                              const blink::WebNode& node,
187                              int* number_of_pages);
188
189  // Set options for print preset from source PDF document.
190  void SetOptionsFromDocument(
191      PrintHostMsg_SetOptionsFromDocument_Params& params);
192
193  // Update the current print settings with new |passed_job_settings|.
194  // |passed_job_settings| dictionary contains print job details such as printer
195  // name, number of copies, page range, etc.
196  bool UpdatePrintSettings(blink::WebLocalFrame* frame,
197                           const blink::WebNode& node,
198                           const base::DictionaryValue& passed_job_settings);
199
200  // Get final print settings from the user.
201  // Return false if the user cancels or on error.
202  bool GetPrintSettingsFromUser(blink::WebFrame* frame,
203                                const blink::WebNode& node,
204                                int expected_pages_count);
205
206  // Page Printing / Rendering ------------------------------------------------
207
208  void OnFramePreparedForPrintPages();
209  void PrintPages();
210  bool PrintPagesNative(blink::WebFrame* frame, int page_count);
211  void FinishFramePrinting();
212
213  // Prints the page listed in |params|.
214#if defined(OS_LINUX) || defined(OS_ANDROID)
215  void PrintPageInternal(const PrintMsg_PrintPage_Params& params,
216                         blink::WebFrame* frame,
217                         PdfMetafileSkia* metafile);
218#elif defined(OS_WIN)
219  void PrintPageInternal(const PrintMsg_PrintPage_Params& params,
220                         blink::WebFrame* frame,
221                         PdfMetafileSkia* metafile,
222                         gfx::Size* page_size_in_dpi,
223                         gfx::Rect* content_area_in_dpi);
224#else
225  void PrintPageInternal(const PrintMsg_PrintPage_Params& params,
226                         blink::WebFrame* frame);
227#endif
228
229  // Render the frame for printing.
230  bool RenderPagesForPrint(blink::WebLocalFrame* frame,
231                           const blink::WebNode& node);
232
233  // Platform specific helper function for rendering page(s) to |metafile|.
234#if defined(OS_MACOSX)
235  void RenderPage(const PrintMsg_Print_Params& params,
236                  int page_number,
237                  blink::WebFrame* frame,
238                  bool is_preview,
239                  PdfMetafileSkia* metafile,
240                  gfx::Size* page_size,
241                  gfx::Rect* content_rect);
242#endif  // defined(OS_MACOSX)
243
244  // Renders page contents from |frame| to |content_area| of |canvas|.
245  // |page_number| is zero-based.
246  // When method is called, canvas should be setup to draw to |canvas_area|
247  // with |scale_factor|.
248  static float RenderPageContent(blink::WebFrame* frame,
249                                 int page_number,
250                                 const gfx::Rect& canvas_area,
251                                 const gfx::Rect& content_area,
252                                 double scale_factor,
253                                 blink::WebCanvas* canvas);
254
255  // Helper methods -----------------------------------------------------------
256
257  bool CopyMetafileDataToSharedMem(PdfMetafileSkia* metafile,
258                                   base::SharedMemoryHandle* shared_mem_handle);
259
260  // Helper method to get page layout in points and fit to page if needed.
261  static void ComputePageLayoutInPointsForCss(
262      blink::WebFrame* frame,
263      int page_index,
264      const PrintMsg_Print_Params& default_params,
265      bool ignore_css_margins,
266      double* scale_factor,
267      PageSizeMargins* page_layout_in_points);
268
269  // Given the |device| and |canvas| to draw on, prints the appropriate headers
270  // and footers using strings from |header_footer_info| on to the canvas.
271  static void PrintHeaderAndFooter(blink::WebCanvas* canvas,
272                                   int page_number,
273                                   int total_pages,
274                                   const blink::WebFrame& source_frame,
275                                   float webkit_scale_factor,
276                                   const PageSizeMargins& page_layout_in_points,
277                                   const PrintMsg_Print_Params& params);
278
279  bool GetPrintFrame(blink::WebLocalFrame** frame);
280
281  // Script Initiated Printing ------------------------------------------------
282
283  // Return true if script initiated printing is currently
284  // allowed. |user_initiated| should be true when a user event triggered the
285  // script, most likely by pressing a print button on the page.
286  bool IsScriptInitiatedPrintAllowed(blink::WebFrame* frame,
287                                     bool user_initiated);
288
289  // Shows scripted print preview when options from plugin are available.
290  void ShowScriptedPrintPreview();
291
292  void RequestPrintPreview(PrintPreviewRequestType type);
293
294  // Checks whether print preview should continue or not.
295  // Returns true if canceling, false if continuing.
296  bool CheckForCancel();
297
298  // Notifies the browser a print preview page has been rendered.
299  // |page_number| is 0-based.
300  // For a valid |page_number| with modifiable content,
301  // |metafile| is the rendered page. Otherwise |metafile| is NULL.
302  // Returns true if print preview should continue, false on failure.
303  bool PreviewPageRendered(int page_number, PdfMetafileSkia* metafile);
304
305  void SetPrintPagesParams(const PrintMsg_PrintPages_Params& settings);
306
307  // WebView used only to print the selection.
308  scoped_ptr<PrepareFrameAndViewForPrint> prep_frame_view_;
309  bool reset_prep_frame_view_;
310
311  scoped_ptr<PrintMsg_PrintPages_Params> print_pages_params_;
312  bool is_print_ready_metafile_sent_;
313  bool ignore_css_margins_;
314
315  // Used for scripted initiated printing blocking.
316  bool is_scripted_printing_blocked_;
317
318  // Let the browser process know of a printing failure. Only set to false when
319  // the failure came from the browser in the first place.
320  bool notify_browser_of_print_failure_;
321
322  // True, when printing from print preview.
323  bool print_for_preview_;
324
325  // Keeps track of the state of print preview between messages.
326  // TODO(vitalybuka): Create PrintPreviewContext when needed and delete after
327  // use. Now it's interaction with various messages is confusing.
328  class PrintPreviewContext {
329   public:
330    PrintPreviewContext();
331    ~PrintPreviewContext();
332
333    // Initializes the print preview context. Need to be called to set
334    // the |web_frame| / |web_node| to generate the print preview for.
335    void InitWithFrame(blink::WebLocalFrame* web_frame);
336    void InitWithNode(const blink::WebNode& web_node);
337
338    // Does bookkeeping at the beginning of print preview.
339    void OnPrintPreview();
340
341    // Create the print preview document. |pages| is empty to print all pages.
342    // Takes ownership of |prepared_frame|.
343    bool CreatePreviewDocument(PrepareFrameAndViewForPrint* prepared_frame,
344                               const std::vector<int>& pages);
345
346    // Called after a page gets rendered. |page_time| is how long the
347    // rendering took.
348    void RenderedPreviewPage(const base::TimeDelta& page_time);
349
350    // Updates the print preview context when the required pages are rendered.
351    void AllPagesRendered();
352
353    // Finalizes the print ready preview document.
354    void FinalizePrintReadyDocument();
355
356    // Cleanup after print preview finishes.
357    void Finished();
358
359    // Cleanup after print preview fails.
360    void Failed(bool report_error);
361
362    // Helper functions
363    int GetNextPageNumber();
364    bool IsRendering() const;
365    bool IsModifiable();
366    bool HasSelection();
367    bool IsLastPageOfPrintReadyMetafile() const;
368    bool IsFinalPageRendered() const;
369
370    // Setters
371    void set_generate_draft_pages(bool generate_draft_pages);
372    void set_error(enum PrintPreviewErrorBuckets error);
373
374    // Getters
375    // Original frame for which preview was requested.
376    blink::WebLocalFrame* source_frame();
377    // Original node for which preview was requested.
378    const blink::WebNode& source_node() const;
379
380    // Frame to be use to render preview. May be the same as source_frame(), or
381    // generated from it, e.g. copy of selected block.
382    blink::WebLocalFrame* prepared_frame();
383    // Node to be use to render preview. May be the same as source_node(), or
384    // generated from it, e.g. copy of selected block.
385    const blink::WebNode& prepared_node() const;
386
387    int total_page_count() const;
388    bool generate_draft_pages() const;
389    PdfMetafileSkia* metafile();
390    int last_error() const;
391
392   private:
393    enum State {
394      UNINITIALIZED,  // Not ready to render.
395      INITIALIZED,    // Ready to render.
396      RENDERING,      // Rendering.
397      DONE            // Finished rendering.
398    };
399
400    // Reset some of the internal rendering context.
401    void ClearContext();
402
403    // Specifies what to render for print preview.
404    FrameReference source_frame_;
405    blink::WebNode source_node_;
406
407    scoped_ptr<PrepareFrameAndViewForPrint> prep_frame_view_;
408    scoped_ptr<PdfMetafileSkia> metafile_;
409
410    // Total page count in the renderer.
411    int total_page_count_;
412
413    // The current page to render.
414    int current_page_index_;
415
416    // List of page indices that need to be rendered.
417    std::vector<int> pages_to_render_;
418
419    // True, when draft pages needs to be generated.
420    bool generate_draft_pages_;
421
422    // Specifies the total number of pages in the print ready metafile.
423    int print_ready_metafile_page_count_;
424
425    base::TimeDelta document_render_time_;
426    base::TimeTicks begin_time_;
427
428    enum PrintPreviewErrorBuckets error_;
429
430    State state_;
431  };
432
433  class ScriptingThrottler {
434   public:
435    ScriptingThrottler();
436
437    // Returns false if script initiated printing occurs too often.
438    bool IsAllowed(blink::WebFrame* frame);
439
440    // Reset the counter for script initiated printing.
441    // Scripted printing will be allowed to continue.
442    void Reset();
443
444   private:
445    base::Time last_print_;
446    int count_ = 0;
447    DISALLOW_COPY_AND_ASSIGN(ScriptingThrottler);
448  };
449
450  ScriptingThrottler scripting_throttler_;
451
452  bool print_node_in_progress_;
453  PrintPreviewContext print_preview_context_;
454  bool is_loading_;
455  bool is_scripted_preview_delayed_;
456
457  // Used to fix a race condition where the source is a PDF and print preview
458  // hangs because RequestPrintPreview is called before DidStopLoading() is
459  // called. This is a store for the RequestPrintPreview() call and its
460  // parameters so that it can be invoked after DidStopLoading.
461  base::Closure on_stop_loading_closure_;
462
463  base::WeakPtrFactory<PrintWebViewHelper> weak_ptr_factory_;
464
465  DISALLOW_COPY_AND_ASSIGN(PrintWebViewHelper);
466};
467
468}  // namespace printing
469
470#endif  // CHROME_RENDERER_PRINTING_PRINT_WEB_VIEW_HELPER_H_
471