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#include "printing/print_destination_interface.h"
21
22using content::BrowserThread;
23
24DEFINE_WEB_CONTENTS_USER_DATA_KEY(printing::PrintViewManager);
25
26namespace {
27
28// Keeps track of pending scripted print preview closures.
29// No locking, only access on the UI thread.
30typedef std::map<content::RenderProcessHost*, base::Closure>
31    ScriptedPrintPreviewClosureMap;
32static base::LazyInstance<ScriptedPrintPreviewClosureMap>
33    g_scripted_print_preview_closure_map = LAZY_INSTANCE_INITIALIZER;
34
35}  // namespace
36
37namespace printing {
38
39PrintViewManager::PrintViewManager(content::WebContents* web_contents)
40    : PrintViewManagerBase(web_contents),
41      observer_(NULL),
42      print_preview_state_(NOT_PREVIEWING),
43      scripted_print_preview_rph_(NULL) {
44}
45
46PrintViewManager::~PrintViewManager() {
47  DCHECK_EQ(NOT_PREVIEWING, print_preview_state_);
48}
49
50bool PrintViewManager::PrintForSystemDialogNow() {
51  return PrintNowInternal(new PrintMsg_PrintForSystemDialog(routing_id()));
52}
53
54bool PrintViewManager::AdvancedPrintNow() {
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
73bool PrintViewManager::PrintToDestination() {
74  // TODO(mad): Remove this once we can send user metrics from the metro driver.
75  // crbug.com/142330
76  UMA_HISTOGRAM_ENUMERATION("Metro.Print", 0, 2);
77  // TODO(mad): Use a passed in destination interface instead.
78  g_browser_process->print_job_manager()->queue()->SetDestination(
79      printing::CreatePrintDestination());
80  return PrintNowInternal(new PrintMsg_PrintPages(routing_id()));
81}
82
83bool PrintViewManager::PrintPreviewNow(bool selection_only) {
84  // Users can send print commands all they want and it is beyond
85  // PrintViewManager's control. Just ignore the extra commands.
86  // See http://crbug.com/136842 for example.
87  if (print_preview_state_ != NOT_PREVIEWING)
88    return false;
89
90  if (!PrintNowInternal(new PrintMsg_InitiatePrintPreview(routing_id(),
91                                                          selection_only))) {
92    return false;
93  }
94
95  print_preview_state_ = USER_INITIATED_PREVIEW;
96  return true;
97}
98
99void PrintViewManager::PrintPreviewForWebNode() {
100  if (print_preview_state_ != NOT_PREVIEWING)
101    return;
102  print_preview_state_ = USER_INITIATED_PREVIEW;
103}
104
105void PrintViewManager::PrintPreviewDone() {
106  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
107  DCHECK_NE(NOT_PREVIEWING, print_preview_state_);
108
109  if (print_preview_state_ == SCRIPTED_PREVIEW) {
110    ScriptedPrintPreviewClosureMap& map =
111        g_scripted_print_preview_closure_map.Get();
112    ScriptedPrintPreviewClosureMap::iterator it =
113        map.find(scripted_print_preview_rph_);
114    CHECK(it != map.end());
115    it->second.Run();
116    map.erase(scripted_print_preview_rph_);
117    scripted_print_preview_rph_ = NULL;
118  }
119  print_preview_state_ = NOT_PREVIEWING;
120}
121
122void PrintViewManager::set_observer(PrintViewManagerObserver* observer) {
123  DCHECK(!observer || !observer_);
124  observer_ = observer;
125}
126
127void PrintViewManager::RenderProcessGone(base::TerminationStatus status) {
128  print_preview_state_ = NOT_PREVIEWING;
129  PrintViewManagerBase::RenderProcessGone(status);
130}
131
132void PrintViewManager::OnDidShowPrintDialog() {
133  if (observer_)
134    observer_->OnPrintDialogShown();
135}
136
137void PrintViewManager::OnSetupScriptedPrintPreview(IPC::Message* reply_msg) {
138  BrowserThread::CurrentlyOn(BrowserThread::UI);
139  ScriptedPrintPreviewClosureMap& map =
140      g_scripted_print_preview_closure_map.Get();
141  content::RenderProcessHost* rph = web_contents()->GetRenderProcessHost();
142
143  // This should always be 0 once we get modal window.print().
144  if (map.count(rph) != 0) {
145    // Renderer already handling window.print() in another View.
146    Send(reply_msg);
147    return;
148  }
149  if (print_preview_state_ != NOT_PREVIEWING) {
150    // If a user initiated print dialog is already open, ignore the scripted
151    // print message.
152    DCHECK_EQ(USER_INITIATED_PREVIEW, print_preview_state_);
153    Send(reply_msg);
154    return;
155  }
156
157  PrintPreviewDialogController* dialog_controller =
158      PrintPreviewDialogController::GetInstance();
159  if (!dialog_controller) {
160    Send(reply_msg);
161    return;
162  }
163
164  print_preview_state_ = SCRIPTED_PREVIEW;
165  base::Closure callback =
166      base::Bind(&PrintViewManager::OnScriptedPrintPreviewReply,
167                 base::Unretained(this),
168                 reply_msg);
169  map[rph] = callback;
170  scripted_print_preview_rph_ = rph;
171}
172
173void PrintViewManager::OnShowScriptedPrintPreview(bool source_is_modifiable) {
174  PrintPreviewDialogController* dialog_controller =
175      PrintPreviewDialogController::GetInstance();
176  if (!dialog_controller) {
177    PrintPreviewDone();
178    return;
179  }
180  dialog_controller->PrintPreview(web_contents());
181  PrintHostMsg_RequestPrintPreview_Params params;
182  params.is_modifiable = source_is_modifiable;
183  PrintPreviewUI::SetInitialParams(
184      dialog_controller->GetPrintPreviewForContents(web_contents()), params);
185}
186
187void PrintViewManager::OnScriptedPrintPreviewReply(IPC::Message* reply_msg) {
188  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
189  Send(reply_msg);
190}
191
192bool PrintViewManager::OnMessageReceived(const IPC::Message& message) {
193  bool handled = true;
194  IPC_BEGIN_MESSAGE_MAP(PrintViewManager, message)
195    IPC_MESSAGE_HANDLER(PrintHostMsg_DidShowPrintDialog, OnDidShowPrintDialog)
196    IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_SetupScriptedPrintPreview,
197                                    OnSetupScriptedPrintPreview)
198    IPC_MESSAGE_HANDLER(PrintHostMsg_ShowScriptedPrintPreview,
199                        OnShowScriptedPrintPreview)
200    IPC_MESSAGE_UNHANDLED(handled = false)
201  IPC_END_MESSAGE_MAP()
202
203  return handled ? true : PrintViewManagerBase::OnMessageReceived(message);
204}
205
206}  // namespace printing
207