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 "chrome/browser/printing/print_view_manager.h"
6
7#include <map>
8
9#include "base/bind.h"
10#include "base/lazy_instance.h"
11#include "base/metrics/histogram.h"
12#include "chrome/browser/browser_process.h"
13#include "chrome/browser/printing/print_job_manager.h"
14#include "chrome/browser/printing/print_preview_dialog_controller.h"
15#include "chrome/browser/printing/print_view_manager_observer.h"
16#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
17#include "chrome/common/print_messages.h"
18#include "content/public/browser/browser_thread.h"
19#include "content/public/browser/web_contents.h"
20
21using content::BrowserThread;
22
23DEFINE_WEB_CONTENTS_USER_DATA_KEY(printing::PrintViewManager);
24
25namespace {
26
27// Keeps track of pending scripted print preview closures.
28// No locking, only access on the UI thread.
29typedef std::map<content::RenderProcessHost*, base::Closure>
30    ScriptedPrintPreviewClosureMap;
31static base::LazyInstance<ScriptedPrintPreviewClosureMap>
32    g_scripted_print_preview_closure_map = LAZY_INSTANCE_INITIALIZER;
33
34}  // namespace
35
36namespace printing {
37
38PrintViewManager::PrintViewManager(content::WebContents* web_contents)
39    : PrintViewManagerBase(web_contents),
40      observer_(NULL),
41      print_preview_state_(NOT_PREVIEWING),
42      scripted_print_preview_rph_(NULL) {
43}
44
45PrintViewManager::~PrintViewManager() {
46  DCHECK_EQ(NOT_PREVIEWING, print_preview_state_);
47}
48
49#if !defined(DISABLE_BASIC_PRINTING)
50bool PrintViewManager::PrintForSystemDialogNow() {
51  return PrintNowInternal(new PrintMsg_PrintForSystemDialog(routing_id()));
52}
53
54bool PrintViewManager::BasicPrint() {
55  PrintPreviewDialogController* dialog_controller =
56      PrintPreviewDialogController::GetInstance();
57  if (!dialog_controller)
58    return false;
59  content::WebContents* print_preview_dialog =
60      dialog_controller->GetPrintPreviewForContents(web_contents());
61  if (print_preview_dialog) {
62    if (!print_preview_dialog->GetWebUI())
63      return false;
64    PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
65        print_preview_dialog->GetWebUI()->GetController());
66    print_preview_ui->OnShowSystemDialog();
67    return true;
68  } else {
69    return PrintNow();
70  }
71}
72#endif  // !DISABLE_BASIC_PRINTING
73bool PrintViewManager::PrintPreviewNow(bool selection_only) {
74  // Users can send print commands all they want and it is beyond
75  // PrintViewManager's control. Just ignore the extra commands.
76  // See http://crbug.com/136842 for example.
77  if (print_preview_state_ != NOT_PREVIEWING)
78    return false;
79
80  if (!PrintNowInternal(new PrintMsg_InitiatePrintPreview(routing_id(),
81                                                          selection_only))) {
82    return false;
83  }
84
85  print_preview_state_ = USER_INITIATED_PREVIEW;
86  return true;
87}
88
89void PrintViewManager::PrintPreviewForWebNode() {
90  if (print_preview_state_ != NOT_PREVIEWING)
91    return;
92  print_preview_state_ = USER_INITIATED_PREVIEW;
93}
94
95void PrintViewManager::PrintPreviewDone() {
96  DCHECK_CURRENTLY_ON(BrowserThread::UI);
97  DCHECK_NE(NOT_PREVIEWING, print_preview_state_);
98
99  if (print_preview_state_ == SCRIPTED_PREVIEW) {
100    ScriptedPrintPreviewClosureMap& map =
101        g_scripted_print_preview_closure_map.Get();
102    ScriptedPrintPreviewClosureMap::iterator it =
103        map.find(scripted_print_preview_rph_);
104    CHECK(it != map.end());
105    it->second.Run();
106    map.erase(scripted_print_preview_rph_);
107    scripted_print_preview_rph_ = NULL;
108  }
109  print_preview_state_ = NOT_PREVIEWING;
110}
111
112void PrintViewManager::set_observer(PrintViewManagerObserver* observer) {
113  DCHECK(!observer || !observer_);
114  observer_ = observer;
115}
116
117void PrintViewManager::RenderProcessGone(base::TerminationStatus status) {
118  print_preview_state_ = NOT_PREVIEWING;
119  PrintViewManagerBase::RenderProcessGone(status);
120}
121
122void PrintViewManager::OnDidShowPrintDialog() {
123  if (observer_)
124    observer_->OnPrintDialogShown();
125}
126
127void PrintViewManager::OnSetupScriptedPrintPreview(IPC::Message* reply_msg) {
128  DCHECK_CURRENTLY_ON(BrowserThread::UI);
129  ScriptedPrintPreviewClosureMap& map =
130      g_scripted_print_preview_closure_map.Get();
131  content::RenderProcessHost* rph = web_contents()->GetRenderProcessHost();
132
133  // This should always be 0 once we get modal window.print().
134  if (map.count(rph) != 0) {
135    // Renderer already handling window.print() in another View.
136    Send(reply_msg);
137    return;
138  }
139  if (print_preview_state_ != NOT_PREVIEWING) {
140    // If a user initiated print dialog is already open, ignore the scripted
141    // print message.
142    DCHECK_EQ(USER_INITIATED_PREVIEW, print_preview_state_);
143    Send(reply_msg);
144    return;
145  }
146
147  PrintPreviewDialogController* dialog_controller =
148      PrintPreviewDialogController::GetInstance();
149  if (!dialog_controller) {
150    Send(reply_msg);
151    return;
152  }
153
154  print_preview_state_ = SCRIPTED_PREVIEW;
155  base::Closure callback =
156      base::Bind(&PrintViewManager::OnScriptedPrintPreviewReply,
157                 base::Unretained(this),
158                 reply_msg);
159  map[rph] = callback;
160  scripted_print_preview_rph_ = rph;
161}
162
163void PrintViewManager::OnShowScriptedPrintPreview(bool source_is_modifiable) {
164  PrintPreviewDialogController* dialog_controller =
165      PrintPreviewDialogController::GetInstance();
166  if (!dialog_controller) {
167    PrintPreviewDone();
168    return;
169  }
170  dialog_controller->PrintPreview(web_contents());
171  PrintHostMsg_RequestPrintPreview_Params params;
172  params.is_modifiable = source_is_modifiable;
173  PrintPreviewUI::SetInitialParams(
174      dialog_controller->GetPrintPreviewForContents(web_contents()), params);
175}
176
177void PrintViewManager::OnScriptedPrintPreviewReply(IPC::Message* reply_msg) {
178  DCHECK_CURRENTLY_ON(BrowserThread::UI);
179  Send(reply_msg);
180}
181
182bool PrintViewManager::OnMessageReceived(const IPC::Message& message) {
183  bool handled = true;
184  IPC_BEGIN_MESSAGE_MAP(PrintViewManager, message)
185    IPC_MESSAGE_HANDLER(PrintHostMsg_DidShowPrintDialog, OnDidShowPrintDialog)
186    IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_SetupScriptedPrintPreview,
187                                    OnSetupScriptedPrintPreview)
188    IPC_MESSAGE_HANDLER(PrintHostMsg_ShowScriptedPrintPreview,
189                        OnShowScriptedPrintPreview)
190    IPC_MESSAGE_UNHANDLED(handled = false)
191  IPC_END_MESSAGE_MAP()
192
193  return handled ? true : PrintViewManagerBase::OnMessageReceived(message);
194}
195
196}  // namespace printing
197