summaryrefslogtreecommitdiffstats
path: root/chrome/browser/printing
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/printing
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/printing')
-rw-r--r--chrome/browser/printing/page_number.cc104
-rw-r--r--chrome/browser/printing/page_number.h100
-rw-r--r--chrome/browser/printing/page_overlays.cc182
-rw-r--r--chrome/browser/printing/page_overlays.h101
-rw-r--r--chrome/browser/printing/page_range.cc48
-rw-r--r--chrome/browser/printing/page_range.h56
-rw-r--r--chrome/browser/printing/page_setup.cc151
-rw-r--r--chrome/browser/printing/page_setup.h107
-rw-r--r--chrome/browser/printing/page_setup_unittest.cc171
-rw-r--r--chrome/browser/printing/print_job.cc454
-rw-r--r--chrome/browser/printing/print_job.h249
-rw-r--r--chrome/browser/printing/print_job_manager.cc220
-rw-r--r--chrome/browser/printing/print_job_manager.h111
-rw-r--r--chrome/browser/printing/print_job_worker.cc325
-rw-r--r--chrome/browser/printing/print_job_worker.h133
-rw-r--r--chrome/browser/printing/print_job_worker_owner.h70
-rw-r--r--chrome/browser/printing/print_settings.cc195
-rw-r--r--chrome/browser/printing/print_settings.h142
-rw-r--r--chrome/browser/printing/print_view_manager.cc578
-rw-r--r--chrome/browser/printing/print_view_manager.h192
-rw-r--r--chrome/browser/printing/printed_document.cc401
-rw-r--r--chrome/browser/printing/printed_document.h220
-rw-r--r--chrome/browser/printing/printed_page.cc51
-rw-r--r--chrome/browser/printing/printed_page.h78
-rw-r--r--chrome/browser/printing/printed_pages_source.h59
-rw-r--r--chrome/browser/printing/printer_query.cc141
-rw-r--r--chrome/browser/printing/printer_query.h127
-rw-r--r--chrome/browser/printing/printing_layout_uitest.cc643
-rw-r--r--chrome/browser/printing/printing_test.h61
-rw-r--r--chrome/browser/printing/units.cc67
-rw-r--r--chrome/browser/printing/units.h52
-rw-r--r--chrome/browser/printing/win_printing_context.cc629
-rw-r--r--chrome/browser/printing/win_printing_context.h163
-rw-r--r--chrome/browser/printing/win_printing_context_unittest.cc54
34 files changed, 6435 insertions, 0 deletions
diff --git a/chrome/browser/printing/page_number.cc b/chrome/browser/printing/page_number.cc
new file mode 100644
index 0000000..041ddaf
--- /dev/null
+++ b/chrome/browser/printing/page_number.cc
@@ -0,0 +1,104 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/page_number.h"
+
+#include "base/logging.h"
+#include "chrome/browser/printing/print_settings.h"
+
+namespace printing {
+
+PageNumber::PageNumber(const PrintSettings& settings, int document_page_count) {
+ Init(settings, document_page_count);
+}
+
+PageNumber::PageNumber()
+ : ranges_(NULL),
+ page_number_(-1),
+ page_range_index_(-1),
+ document_page_count_(0) {
+}
+
+void PageNumber::operator=(const PageNumber& other) {
+ ranges_ = other.ranges_;
+ page_number_ = other.page_number_;
+ page_range_index_ = other.page_range_index_;
+ document_page_count_ = other.document_page_count_;
+}
+
+void PageNumber::Init(const PrintSettings& settings, int document_page_count) {
+ DCHECK(document_page_count);
+ ranges_ = settings.ranges.empty() ? NULL : &settings.ranges;
+ document_page_count_ = document_page_count;
+ if (ranges_) {
+ page_range_index_ = 0;
+ page_number_ = (*ranges_)[0].from;
+ } else {
+ if (document_page_count) {
+ page_number_ = 0;
+ } else {
+ page_number_ = -1;
+ }
+ page_range_index_ = -1;
+ }
+}
+
+int PageNumber::operator++() {
+ if (!ranges_) {
+ // Switch to next page.
+ if (++page_number_ == document_page_count_) {
+ // Finished.
+ *this = npos();
+ }
+ } else {
+ // Switch to next page.
+ ++page_number_;
+ // Page ranges are inclusive.
+ if (page_number_ > (*ranges_)[page_range_index_].to) {
+ if (++page_range_index_ == ranges_->size()) {
+ // Finished.
+ *this = npos();
+ } else {
+ page_number_ = (*ranges_)[page_range_index_].from;
+ }
+ }
+ }
+ return ToInt();
+}
+
+bool PageNumber::operator==(const PageNumber& other) const {
+ return page_number_ == other.page_number_ &&
+ page_range_index_ == other.page_range_index_;
+}
+bool PageNumber::operator!=(const PageNumber& other) const {
+ return page_number_ != other.page_number_ ||
+ page_range_index_ != other.page_range_index_;
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/page_number.h b/chrome/browser/printing/page_number.h
new file mode 100644
index 0000000..398de42
--- /dev/null
+++ b/chrome/browser/printing/page_number.h
@@ -0,0 +1,100 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PAGE_NUMBER_H__
+#define CHROME_BROWSER_PRINTING_PAGE_NUMBER_H__
+
+#ifdef _DEBUG
+#include <ostream>
+#endif
+
+#include "chrome/browser/printing/page_range.h"
+
+namespace printing {
+
+class PrintSettings;
+
+// Represents a page series following the array of page ranges defined in a
+// PrintSettings.
+class PageNumber {
+ public:
+ // Initializes the page to the first page in the settings's range or 0.
+ PageNumber(const PrintSettings& settings, int document_page_count);
+
+ PageNumber();
+
+ void operator=(const PageNumber& other);
+
+ // Initializes the page to the first page in the setting's range or 0. It
+ // initialize to npos if the range is empty and document_page_count is 0.
+ void Init(const PrintSettings& settings, int document_page_count);
+
+ // Converts to a page numbers.
+ int ToInt() const {
+ return page_number_;
+ }
+
+ // Calculates the next page in the serie.
+ int operator++();
+
+ // Returns an instance that represents the end of a serie.
+ static const PageNumber npos() {
+ return PageNumber();
+ }
+
+ // Equality operator. Only the current page number is verified so that
+ // "page != PageNumber::npos()" works.
+ bool operator==(const PageNumber& other) const;
+ bool operator!=(const PageNumber& other) const;
+
+ private:
+ // The page range to follow.
+ const PageRanges* ranges_;
+
+ // The next page to be printed. -1 when not printing.
+ int page_number_;
+
+ // The next page to be printed. -1 when not used. Valid only if
+ // document()->settings().range.empty() is false.
+ int page_range_index_;
+
+ // Number of expected pages in the document. Used when ranges_ is NULL.
+ int document_page_count_;
+};
+
+// Debug output support.
+template<class E, class T>
+inline std::basic_ostream<E,T>& operator<<(std::basic_ostream<E,T>& ss,
+ const PageNumber& page) {
+ return ss << page.ToInt();
+}
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PAGE_NUMBER_H__
diff --git a/chrome/browser/printing/page_overlays.cc b/chrome/browser/printing/page_overlays.cc
new file mode 100644
index 0000000..6f9d8c3
--- /dev/null
+++ b/chrome/browser/printing/page_overlays.cc
@@ -0,0 +1,182 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/page_overlays.h"
+
+#include "base/string_util.h"
+#include "chrome/browser/printing/printed_document.h"
+#include "chrome/browser/printing/printed_page.h"
+#include "chrome/common/gfx/url_elider.h"
+
+namespace {
+
+// Replaces a subpart of a string by other value, and returns the position right
+// after the new value.
+size_t ReplaceKey(std::wstring* string,
+ size_t offset,
+ size_t old_string_len,
+ const std::wstring& new_string) {
+ string->replace(offset, old_string_len, new_string);
+ return offset + new_string.size();
+}
+
+} // namespace
+
+namespace printing {
+
+const wchar_t* const PageOverlays::kTitle = L"{title}";
+const wchar_t* const PageOverlays::kTime = L"{time}";
+const wchar_t* const PageOverlays::kDate = L"{date}";
+const wchar_t* const PageOverlays::kPage = L"{page}";
+const wchar_t* const PageOverlays::kPageCount = L"{pagecount}";
+const wchar_t* const PageOverlays::kPageOnTotal = L"{pageontotal}";
+const wchar_t* const PageOverlays::kUrl = L"{url}";
+
+PageOverlays::PageOverlays()
+ : top_left(kDate),
+ top_center(kTitle),
+ top_right(),
+ bottom_left(kUrl),
+ bottom_center(),
+ bottom_right(kPageOnTotal) {
+}
+
+bool PageOverlays::Equals(const PageOverlays& rhs) const {
+ return top_left == rhs.top_left &&
+ top_center == rhs.top_center &&
+ top_right == rhs.top_right &&
+ bottom_left == rhs.bottom_left &&
+ bottom_center == rhs.bottom_center &&
+ bottom_right == rhs.bottom_right;
+}
+
+const std::wstring& PageOverlays::GetOverlay(HorizontalPosition x,
+ VerticalPosition y) const {
+ switch (x) {
+ case LEFT:
+ switch (y) {
+ case TOP:
+ return top_left;
+ case BOTTOM:
+ return bottom_left;
+ }
+ break;
+ case CENTER:
+ switch (y) {
+ case TOP:
+ return top_center;
+ case BOTTOM:
+ return bottom_center;
+ }
+ break;
+ case RIGHT:
+ switch (y) {
+ case TOP:
+ return top_right;
+ case BOTTOM:
+ return bottom_right;
+ }
+ break;
+ }
+ NOTREACHED();
+ return EmptyWString();
+}
+
+//static
+std::wstring PageOverlays::ReplaceVariables(const std::wstring& input,
+ const PrintedDocument& document,
+ const PrintedPage& page) {
+ std::wstring output(input);
+ for (size_t offset = output.find(L'{', 0);
+ offset != std::wstring::npos;
+ offset = output.find(L'{', offset)) {
+
+ if (0 == output.compare(offset,
+ wcslen(kTitle),
+ kTitle)) {
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kTitle),
+ document.name());
+ } else if (0 == output.compare(offset,
+ wcslen(kTime),
+ kTime)) {
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kTime),
+ document.time());
+ } else if (0 == output.compare(offset,
+ wcslen(kDate),
+ kDate)) {
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kDate),
+ document.date());
+ } else if (0 == output.compare(offset,
+ wcslen(kPage),
+ kPage)) {
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kPage),
+ IntToWString(page.page_number()));
+ } else if (0 == output.compare(offset,
+ wcslen(kPageCount),
+ kPageCount)) {
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kPageCount),
+ IntToWString(document.page_count()));
+ } else if (0 == output.compare(offset,
+ wcslen(kPageOnTotal),
+ kPageOnTotal)) {
+ std::wstring replacement;
+ replacement = IntToWString(page.page_number());
+ replacement += L"/";
+ replacement += IntToWString(document.page_count());
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kPageOnTotal),
+ replacement);
+ } else if (0 == output.compare(offset,
+ wcslen(kUrl),
+ kUrl)) {
+ // TODO(maruel): http://b/1126373 gfx::ElideUrl(document.url(), ...)
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kUrl),
+ UTF8ToWide(document.url().spec()));
+ } else {
+ // There is just a { in the string.
+ ++offset;
+ }
+ }
+ return output;
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/page_overlays.h b/chrome/browser/printing/page_overlays.h
new file mode 100644
index 0000000..905b917
--- /dev/null
+++ b/chrome/browser/printing/page_overlays.h
@@ -0,0 +1,101 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PAGE_OVERLAYS_H__
+#define CHROME_BROWSER_PRINTING_PAGE_OVERLAYS_H__
+
+#include <string>
+
+namespace printing {
+
+class PrintedDocument;
+class PrintedPage;
+
+// Page's overlays, i.e. headers and footers. Contains the strings that will be
+// printed in the overlays, with actual values as variables. The variables are
+// replaced by their actual values with ReplaceVariables().
+class PageOverlays {
+ public:
+ // Position of the header/footer.
+ enum HorizontalPosition {
+ LEFT,
+ CENTER,
+ RIGHT,
+ };
+
+ // Position of the header/footer.
+ enum VerticalPosition {
+ TOP,
+ BOTTOM,
+ };
+
+ PageOverlays();
+
+ // Equality operator.
+ bool Equals(const PageOverlays& rhs) const;
+
+ // Returns the string of an overlay according to its x,y position.
+ const std::wstring& GetOverlay(HorizontalPosition x,
+ VerticalPosition y) const;
+
+ // Replaces the variables in |input| with their actual values according to the
+ // properties of the current printed document and the current printed page.
+ static std::wstring ReplaceVariables(const std::wstring& input,
+ const PrintedDocument& document,
+ const PrintedPage& page);
+
+ // Keys that are dynamically replaced in the header and footer by their actual
+ // values.
+ // Web page's title.
+ static const wchar_t* const kTitle;
+ // Print job's start time.
+ static const wchar_t* const kTime;
+ // Print job's start date.
+ static const wchar_t* const kDate;
+ // Printed page's number.
+ static const wchar_t* const kPage;
+ // Print job's total page count.
+ static const wchar_t* const kPageCount;
+ // Printed page's number on total page count.
+ static const wchar_t* const kPageOnTotal;
+ // Web page's displayed url.
+ static const wchar_t* const kUrl;
+
+ // Actual headers and footers.
+ std::wstring top_left;
+ std::wstring top_center;
+ std::wstring top_right;
+ std::wstring bottom_left;
+ std::wstring bottom_center;
+ std::wstring bottom_right;
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PAGE_OVERLAYS_H__
diff --git a/chrome/browser/printing/page_range.cc b/chrome/browser/printing/page_range.cc
new file mode 100644
index 0000000..f967877
--- /dev/null
+++ b/chrome/browser/printing/page_range.cc
@@ -0,0 +1,48 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/page_range.h"
+
+#include "chrome/common/stl_util-inl.h"
+
+namespace printing {
+
+std::vector<int> PageRange::GetPages(const PageRanges& ranges) {
+ std::set<int> pages;
+ for (unsigned i = 0; i < ranges.size(); ++i) {
+ const PageRange& range = ranges[i];
+ // Ranges are inclusive.
+ for (int i = range.from; i <= range.to; ++i) {
+ pages.insert(i);
+ }
+ }
+ return SetToVector(pages);
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/page_range.h b/chrome/browser/printing/page_range.h
new file mode 100644
index 0000000..59d808a
--- /dev/null
+++ b/chrome/browser/printing/page_range.h
@@ -0,0 +1,56 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PAGE_RANGE_H__
+#define CHROME_BROWSER_PRINTING_PAGE_RANGE_H__
+
+#include <vector>
+
+namespace printing {
+
+struct PageRange;
+
+typedef std::vector<PageRange> PageRanges;
+
+// Print range is inclusive. To select one page, set from == to.
+struct PageRange {
+ int from;
+ int to;
+
+ bool operator==(const PageRange& rhs) const {
+ return from == rhs.from && to == rhs.to;
+ }
+
+ // Retrieves the sorted list of unique pages in the page ranges.
+ static std::vector<int> GetPages(const PageRanges& ranges);
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PAGE_RANGE_H__
diff --git a/chrome/browser/printing/page_setup.cc b/chrome/browser/printing/page_setup.cc
new file mode 100644
index 0000000..6d6c42a
--- /dev/null
+++ b/chrome/browser/printing/page_setup.cc
@@ -0,0 +1,151 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/page_setup.h"
+
+#include "base/logging.h"
+
+namespace printing {
+
+PageMargins::PageMargins()
+ : header(0),
+ footer(0),
+ left(0),
+ right(0),
+ top(0),
+ bottom(0) {
+}
+
+void PageMargins::Clear() {
+ header = 0;
+ footer = 0;
+ left = 0;
+ right = 0;
+ top = 0;
+ bottom = 0;
+}
+
+bool PageMargins::Equals(const PageMargins& rhs) const {
+ return header == rhs.header &&
+ footer == rhs.footer &&
+ left == rhs.left &&
+ top == rhs.top &&
+ right == rhs.right &&
+ bottom == rhs.bottom;
+}
+
+PageSetup::PageSetup() : text_height_(0) {
+}
+
+void PageSetup::Clear() {
+ physical_size_.SetSize(0, 0);
+ printable_area_.SetRect(0, 0, 0, 0);
+ overlay_area_.SetRect(0, 0, 0, 0);
+ content_area_.SetRect(0, 0, 0, 0);
+ effective_margins_.Clear();
+ text_height_ = 0;
+}
+
+bool PageSetup::Equals(const PageSetup& rhs) const {
+ return physical_size_ == rhs.physical_size_ &&
+ printable_area_ == rhs.printable_area_ &&
+ overlay_area_ == rhs.overlay_area_ &&
+ content_area_ == rhs.content_area_ &&
+ effective_margins_.Equals(rhs.effective_margins_) &&
+ requested_margins_.Equals(rhs.requested_margins_) &&
+ text_height_ == rhs.text_height_;
+}
+
+void PageSetup::Init(const gfx::Size& physical_size,
+ const gfx::Rect& printable_area,
+ int text_height) {
+ DCHECK_LE(printable_area.right(), physical_size.width());
+ // I've seen this assert triggers on Canon GP160PF PCL 5e.
+ // 28092 vs. 27940 @ 600 dpi = ~.25 inch.
+ DCHECK_LE(printable_area.bottom(), physical_size.height());
+ DCHECK_GE(printable_area.x(), 0);
+ DCHECK_GE(printable_area.y(), 0);
+ DCHECK_GE(text_height, 0);
+ physical_size_ = physical_size;
+ printable_area_ = printable_area;
+ text_height_ = text_height;
+
+ // Calculate the effective margins. The tricky part.
+ effective_margins_.header = std::max(requested_margins_.header,
+ printable_area_.y());
+ effective_margins_.footer = std::max(requested_margins_.footer,
+ physical_size.height() -
+ printable_area_.bottom());
+ effective_margins_.left = std::max(requested_margins_.left,
+ printable_area_.x());
+ effective_margins_.top = std::max(std::max(requested_margins_.top,
+ printable_area_.y()),
+ effective_margins_.header + text_height);
+ effective_margins_.right = std::max(requested_margins_.right,
+ physical_size.width() -
+ printable_area_.right());
+ effective_margins_.bottom = std::max(std::max(requested_margins_.bottom,
+ physical_size.height() -
+ printable_area_.bottom()),
+ effective_margins_.footer + text_height);
+
+ // Calculate the overlay area. If the margins are excessive, the overlay_area
+ // size will be (0, 0).
+ overlay_area_.set_x(effective_margins_.left);
+ overlay_area_.set_y(effective_margins_.header);
+ overlay_area_.set_width(std::max(0,
+ physical_size.width() -
+ effective_margins_.right -
+ overlay_area_.x()));
+ overlay_area_.set_height(std::max(0,
+ physical_size.height() -
+ effective_margins_.footer -
+ overlay_area_.y()));
+
+ // Calculate the content area. If the margins are excessive, the content_area
+ // size will be (0, 0).
+ content_area_.set_x(effective_margins_.left);
+ content_area_.set_y(effective_margins_.top);
+ content_area_.set_width(std::max(0,
+ physical_size.width() -
+ effective_margins_.right -
+ content_area_.x()));
+ content_area_.set_height(std::max(0,
+ physical_size.height() -
+ effective_margins_.bottom -
+ content_area_.y()));
+}
+
+void PageSetup::SetRequestedMargins(const PageMargins& requested_margins) {
+ requested_margins_ = requested_margins;
+ if (physical_size_.width() && physical_size_.height())
+ Init(physical_size_, printable_area_, text_height_);
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/page_setup.h b/chrome/browser/printing/page_setup.h
new file mode 100644
index 0000000..4902843
--- /dev/null
+++ b/chrome/browser/printing/page_setup.h
@@ -0,0 +1,107 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PAGE_SETUP_H__
+#define CHROME_BROWSER_PRINTING_PAGE_SETUP_H__
+
+#include "base/gfx/rect.h"
+
+namespace printing {
+
+// Margins for a page setup.
+class PageMargins {
+ public:
+ PageMargins();
+
+ void Clear();
+
+ // Equality operator.
+ bool Equals(const PageMargins& rhs) const;
+
+ // Vertical space for the overlay from the top of the sheet.
+ int header;
+ // Vertical space for the overlay from the bottom of the sheet.
+ int footer;
+ // Margin on each side of the sheet.
+ int left;
+ int top;
+ int right;
+ int bottom;
+};
+
+// Settings that define the size and printable areas of a page. Unit is
+// unspecified.
+class PageSetup {
+ public:
+ PageSetup();
+
+ void Clear();
+
+ // Equality operator.
+ bool Equals(const PageSetup& rhs) const;
+
+ void Init(const gfx::Size& physical_size, const gfx::Rect& printable_area,
+ int text_height);
+
+ void SetRequestedMargins(const PageMargins& requested_margins);
+
+ const gfx::Size& physical_size() const { return physical_size_; }
+ const gfx::Rect& overlay_area() const { return overlay_area_; }
+ const gfx::Rect& content_area() const { return content_area_; }
+ const PageMargins& effective_margins() const {
+ return effective_margins_;
+ }
+
+ private:
+ // Physical size of the page, including non-printable margins.
+ gfx::Size physical_size_;
+
+ // The printable area as specified by the printer driver. We can't get
+ // larger than this.
+ gfx::Rect printable_area_;
+
+ // The printable area for headers and footers.
+ gfx::Rect overlay_area_;
+
+ // The printable area as selected by the user's margins.
+ gfx::Rect content_area_;
+
+ // Effective margins.
+ PageMargins effective_margins_;
+
+ // Requested margins.
+ PageMargins requested_margins_;
+
+ // Space that must be kept free for the overlays.
+ int text_height_;
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PAGE_SETUP_H__
diff --git a/chrome/browser/printing/page_setup_unittest.cc b/chrome/browser/printing/page_setup_unittest.cc
new file mode 100644
index 0000000..7128aed
--- /dev/null
+++ b/chrome/browser/printing/page_setup_unittest.cc
@@ -0,0 +1,171 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/page_setup.h"
+
+#include <stdlib.h>
+#include <time.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(PageSetupTest, Random) {
+ time_t seed = time(NULL);
+ int kMax = 10;
+ srand(static_cast<unsigned>(seed));
+
+ // Margins.
+ printing::PageMargins margins;
+ margins.header = rand() % kMax;
+ margins.footer = rand() % kMax;
+ margins.left = rand() % kMax;
+ margins.top = rand() % kMax;
+ margins.right = rand() % kMax;
+ margins.bottom = rand() % kMax;
+ int kTextHeight = rand() % kMax;
+
+ // Page description.
+ gfx::Size page_size(100 + rand() % kMax, 200 + rand() % kMax);
+ gfx::Rect printable_area(rand() % kMax, rand() % kMax, 0, 0);
+ printable_area.set_width(page_size.width() - (rand() % kMax) -
+ printable_area.x());
+ printable_area.set_height(page_size.height() - (rand() % kMax) -
+ printable_area.y());
+
+ // Make the calculations.
+ printing::PageSetup setup;
+ setup.SetRequestedMargins(margins);
+ setup.Init(page_size, printable_area, kTextHeight);
+
+ // Calculate the effective margins.
+ printing::PageMargins effective_margins;
+ effective_margins.header = std::max(margins.header, printable_area.y());
+ effective_margins.left = std::max(margins.left, printable_area.x());
+ effective_margins.top = std::max(margins.top,
+ effective_margins.header + kTextHeight);
+ effective_margins.footer = std::max(margins.footer,
+ page_size.height() -
+ printable_area.bottom());
+ effective_margins.right = std::max(margins.right,
+ page_size.width() -
+ printable_area.right());
+ effective_margins.bottom = std::max(margins.bottom,
+ effective_margins.footer + kTextHeight);
+
+ // Calculate the overlay area.
+ gfx::Rect overlay_area(effective_margins.left, effective_margins.header,
+ page_size.width() - effective_margins.right -
+ effective_margins.left,
+ page_size.height() - effective_margins.footer -
+ effective_margins.header);
+
+ // Calculate the content area.
+ gfx::Rect content_area(overlay_area.x(),
+ effective_margins.top,
+ overlay_area.width(),
+ page_size.height() - effective_margins.bottom -
+ effective_margins.top);
+
+ // Test values.
+ EXPECT_EQ(page_size, setup.physical_size()) << seed << " " << page_size <<
+ " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(overlay_area, setup.overlay_area()) << seed << " " << page_size <<
+ " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(content_area, setup.content_area()) << seed << " " << page_size <<
+ " " << printable_area << " " << kTextHeight;
+
+ EXPECT_EQ(effective_margins.header, setup.effective_margins().header) <<
+ seed << " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.footer, setup.effective_margins().footer) <<
+ seed << " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.left, setup.effective_margins().left) << seed <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.top, setup.effective_margins().top) << seed <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.right, setup.effective_margins().right) << seed <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.bottom, setup.effective_margins().bottom) <<
+ seed << " " << page_size << " " << printable_area << " " << kTextHeight;
+}
+
+TEST(PageSetupTest, HardCoded) {
+ // Margins.
+ printing::PageMargins margins;
+ margins.header = 2;
+ margins.footer = 2;
+ margins.left = 4;
+ margins.top = 4;
+ margins.right = 4;
+ margins.bottom = 4;
+ int kTextHeight = 3;
+
+ // Page description.
+ gfx::Size page_size(100, 100);
+ gfx::Rect printable_area(3, 3, 94, 94);
+
+ // Make the calculations.
+ printing::PageSetup setup;
+ setup.SetRequestedMargins(margins);
+ setup.Init(page_size, printable_area, kTextHeight);
+
+ // Calculate the effective margins.
+ printing::PageMargins effective_margins;
+ effective_margins.header = 3;
+ effective_margins.left = 4;
+ effective_margins.top = 6;
+ effective_margins.footer = 3;
+ effective_margins.right = 4;
+ effective_margins.bottom = 6;
+
+ // Calculate the overlay area.
+ gfx::Rect overlay_area(4, 3, 92, 94);
+
+ // Calculate the content area.
+ gfx::Rect content_area(4, 6, 92, 88);
+
+ // Test values.
+ EXPECT_EQ(page_size, setup.physical_size()) << " " << page_size <<
+ " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(overlay_area, setup.overlay_area()) << " " << page_size <<
+ " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(content_area, setup.content_area()) << " " << page_size <<
+ " " << printable_area << " " << kTextHeight;
+
+ EXPECT_EQ(effective_margins.header, setup.effective_margins().header) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.footer, setup.effective_margins().footer) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.left, setup.effective_margins().left) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.top, setup.effective_margins().top) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.right, setup.effective_margins().right) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.bottom, setup.effective_margins().bottom) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+}
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
new file mode 100644
index 0000000..54713cf
--- /dev/null
+++ b/chrome/browser/printing/print_job.cc
@@ -0,0 +1,454 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/print_job.h"
+
+#include "base/message_loop.h"
+#include "chrome/browser/printing/print_job_worker.h"
+#include "chrome/browser/printing/printed_document.h"
+#include "chrome/browser/printing/printed_page.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+namespace printing {
+
+PrintJob::PrintJob(PrintedPagesSource* source)
+ : ui_message_loop_(MessageLoop::current()),
+ worker_(new PrintJobWorker(this)),
+ source_(source),
+ is_job_pending_(false),
+ is_print_dialog_box_shown_(false),
+ is_blocking_(false),
+ is_canceling_(false) {
+}
+
+PrintJob::PrintJob()
+ : ui_message_loop_(MessageLoop::current()),
+ worker_(),
+ source_(NULL),
+ settings_(),
+ is_job_pending_(false),
+ is_print_dialog_box_shown_(false),
+ is_blocking_(false),
+ is_canceling_(false) {
+}
+
+PrintJob::~PrintJob() {
+ // The job should be finished (or at least canceled) when it is destroyed.
+ DCHECK(!is_job_pending_);
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!is_blocking_);
+ DCHECK(!is_canceling_);
+ DCHECK(worker_->message_loop() == NULL);
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+}
+
+void PrintJob::Initialize(PrintJobWorkerOwner* job,
+ PrintedPagesSource* source) {
+ DCHECK(!source_);
+ DCHECK(!worker_.get());
+ DCHECK(!is_job_pending_);
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!is_blocking_);
+ DCHECK(!is_canceling_);
+ DCHECK(!document_.get());
+ source_ = source;
+ worker_.reset(job->DetachWorker(this));
+ settings_ = job->settings();
+
+ UpdatePrintedDocument(new PrintedDocument(settings_, source_, job->cookie()));
+
+ // Don't forget to register to our own messages.
+ NotificationService::current()->AddObserver(
+ this, NOTIFY_PRINT_JOB_EVENT, Source<PrintJob>(this));
+}
+
+void PrintJob::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+ switch (type) {
+ case NOTIFY_PRINTED_DOCUMENT_UPDATED: {
+ DCHECK(Source<PrintedDocument>(source).ptr() ==
+ document_.get());
+
+ // This notification may happens even if no job is started (i.e. print
+ // preview)
+ if (is_job_pending_ == true &&
+ Source<PrintedDocument>(source).ptr() == document_.get() &&
+ Details<PrintedPage>(details).ptr() != NULL) {
+ // Are we waiting for a page to print? The worker will know.
+ worker_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::OnNewPage));
+ }
+ break;
+ }
+ case NOTIFY_PRINT_JOB_EVENT: {
+ OnNotifyPrintJobEvent(*Details<JobEventDetails>(details).ptr());
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+void PrintJob::GetSettingsDone(const PrintSettings& new_settings,
+ PrintingContext::Result result) {
+ DCHECK(!is_job_pending_);
+ DCHECK(!is_blocking_);
+
+ if (!source_ || result == PrintingContext::FAILED) {
+ // The source is gone, there's nothing to do.
+ Cancel();
+ return;
+ }
+
+ // Only create a new PrintedDocument if the settings have changed or if
+ // there was no printed document.
+ if (!document_.get() || !new_settings.Equals(settings_)) {
+ UpdatePrintedDocument(new PrintedDocument(new_settings, source_,
+ PrintSettings::NewCookie()));
+ }
+
+ JobEventDetails::Type type;
+ if (is_print_dialog_box_shown_) {
+ type = (result == PrintingContext::OK) ?
+ JobEventDetails::USER_INIT_DONE :
+ JobEventDetails::USER_INIT_CANCELED;
+ // Dialog box is not shown anymore.
+ is_print_dialog_box_shown_ = false;
+ } else {
+ DCHECK_EQ(result, PrintingContext::OK);
+ type = JobEventDetails::DEFAULT_INIT_DONE;
+ }
+ scoped_refptr<JobEventDetails> details(
+ new JobEventDetails(type, document_.get(), NULL));
+ NotificationService::current()->Notify(
+ NOTIFY_PRINT_JOB_EVENT,
+ Source<PrintJob>(this),
+ Details<JobEventDetails>(details.get()));
+}
+
+PrintJobWorker* PrintJob::DetachWorker(PrintJobWorkerOwner* new_owner) {
+ NOTREACHED();
+ return NULL;
+}
+
+int PrintJob::cookie() const {
+ if (!document_.get())
+ // Always use an invalid cookie in this case.
+ return 0;
+ return document_->cookie();
+}
+
+void PrintJob::GetSettings(GetSettingsAskParam ask_user_for_settings,
+ HWND parent_window) {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+ DCHECK(!is_job_pending_);
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!is_blocking_);
+ // Is not reentrant.
+ if (is_job_pending_ || is_blocking_)
+ return;
+
+ // Lazy create the worker thread. There is one worker thread per print job.
+ if (!worker_->message_loop()) {
+ if (!worker_->Start())
+ return;
+
+ // Don't re-register if we were already registered.
+ NotificationService::current()->AddObserver(
+ this, NOTIFY_PRINT_JOB_EVENT, Source<PrintJob>(this));
+ }
+
+ int page_count = 0;
+ if (document_.get()) {
+ page_count = document_->page_count();
+ }
+
+ // Real work is done in PrintJobWorker::Init().
+ is_print_dialog_box_shown_ = ask_user_for_settings == ASK_USER;
+ worker_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::GetSettings, is_print_dialog_box_shown_,
+ parent_window, page_count));
+}
+
+void PrintJob::StartPrinting() {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+ DCHECK(worker_->message_loop());
+ DCHECK(!is_job_pending_);
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!is_blocking_);
+ if (!worker_->message_loop() || is_job_pending_ || is_blocking_)
+ return;
+
+ // Real work is done in PrintJobWorker::StartPrinting().
+ worker_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::StartPrinting, document_));
+ // Set the flag right now.
+ is_job_pending_ = true;
+
+ // Tell everyone!
+ scoped_refptr<JobEventDetails> details(
+ new JobEventDetails(JobEventDetails::NEW_DOC, document_.get(), NULL));
+ NotificationService::current()->Notify(
+ NOTIFY_PRINT_JOB_EVENT,
+ Source<PrintJob>(this),
+ Details<JobEventDetails>(details.get()));
+}
+
+void PrintJob::Stop() {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+
+ // Be sure to live long enough.
+ scoped_refptr<PrintJob> handle(this);
+
+ MessageLoop* worker_loop = worker_->message_loop();
+ if (worker_loop) {
+ if (is_print_dialog_box_shown_) {
+ // Make sure there is no dialog box.
+ worker_loop->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::DismissDialog));
+ is_print_dialog_box_shown_ = false;
+ }
+ // It will wait infinitely for the worker thread to quit.
+ worker_->NonBlockingStop();
+ is_job_pending_ = false;
+ NotificationService::current()->RemoveObserver(
+ this, NOTIFY_PRINT_JOB_EVENT, Source<PrintJob>(this));
+ }
+ // Flush the cached document.
+ UpdatePrintedDocument(NULL);
+
+ if (is_blocking_) {
+ // Make sure we don't get stuck in an inner message loop.
+ MessageLoop::current()->Quit();
+ is_blocking_ = false;
+ }
+}
+
+void PrintJob::Cancel() {
+ if (is_canceling_)
+ return;
+ is_canceling_ = true;
+
+ // Be sure to live long enough.
+ scoped_refptr<PrintJob> handle(this);
+
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+ MessageLoop* worker_loop = worker_.get() ? worker_->message_loop() : NULL;
+ if (worker_loop) {
+ // Call this right now so it renders the context invalid. Do not use
+ // InvokeLater since it would take too much time.
+ worker_->Cancel();
+ }
+ // Make sure a Cancel() is broadcast.
+ scoped_refptr<JobEventDetails> details(
+ new JobEventDetails(JobEventDetails::FAILED, NULL, NULL));
+ NotificationService::current()->Notify(
+ NOTIFY_PRINT_JOB_EVENT,
+ Source<PrintJob>(this),
+ Details<JobEventDetails>(details.get()));
+ Stop();
+ is_canceling_ = false;
+}
+
+bool PrintJob::RequestMissingPages() {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!is_blocking_);
+ if (!is_job_pending_ || is_print_dialog_box_shown_ || is_blocking_)
+ return false;
+
+ MessageLoop* worker_loop = worker_.get() ? worker_->message_loop() : NULL;
+ if (!worker_loop)
+ return false;
+
+ worker_loop->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::RequestMissingPages));
+ return true;
+}
+
+bool PrintJob::FlushJob(int timeout_ms) {
+ if (!RequestMissingPages())
+ return false;
+
+ // Make sure the object outlive this message loop.
+ scoped_refptr<PrintJob> handle(this);
+
+ MessageLoop::QuitTask timeout_task;
+ scoped_ptr<Timer> timeout;
+ if (timeout_ms) {
+ timeout.reset(MessageLoop::current()->timer_manager()->StartTimer(
+ timeout_ms,
+ &timeout_task,
+ false));
+ }
+
+ is_blocking_ = true;
+ // Stop() will eventually be called, which will get out of the inner message
+ // loop. But, don't take it for granted and set a timer in case something goes
+ // wrong.
+
+ bool old_state = MessageLoop::current()->NestableTasksAllowed();
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ // Restore task state.
+ MessageLoop::current()->SetNestableTasksAllowed(old_state);
+
+ if (timeout.get()) {
+ MessageLoop::current()->timer_manager()->StopTimer(timeout.get());
+ }
+ return true;
+}
+
+void PrintJob::DisconnectSource() {
+ source_ = NULL;
+ if (document_.get())
+ document_->DisconnectSource();
+}
+
+bool PrintJob::is_job_pending() const {
+ return is_job_pending_;
+}
+
+bool PrintJob::is_print_dialog_box_shown() const {
+ return is_print_dialog_box_shown_;
+}
+
+PrintedDocument* PrintJob::document() const {
+ return document_.get();
+}
+
+void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
+ if (document_.get() == new_document)
+ return;
+ // Unregisters.
+ if (document_.get()) {
+ NotificationService::current()->
+ RemoveObserver(this,
+ NOTIFY_PRINTED_DOCUMENT_UPDATED,
+ Source<PrintedDocument>(document_.get()));
+ }
+ document_ = new_document;
+
+ // Registers.
+ if (document_.get()) {
+ NotificationService::current()->
+ AddObserver(this,
+ NOTIFY_PRINTED_DOCUMENT_UPDATED,
+ Source<PrintedDocument>(document_.get()));
+ settings_ = document_->settings();
+ }
+
+ if (worker_.get() && worker_->message_loop()) {
+ DCHECK(!is_job_pending_);
+ // Sync the document with the worker.
+ worker_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::OnDocumentChanged, document_));
+ }
+}
+
+void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) {
+ switch (event_details.type()) {
+ case JobEventDetails::FAILED: {
+ settings_.Clear();
+ // Update internal state.
+ is_print_dialog_box_shown_ = false;
+ // No need to cancel since the worker already canceled itself.
+ Stop();
+ break;
+ }
+ case JobEventDetails::USER_INIT_DONE:
+ case JobEventDetails::DEFAULT_INIT_DONE:
+ case JobEventDetails::USER_INIT_CANCELED: {
+ DCHECK_EQ(event_details.document(), document_.get());
+ break;
+ }
+ case JobEventDetails::NEW_DOC:
+ case JobEventDetails::NEW_PAGE:
+ case JobEventDetails::PAGE_DONE:
+ case JobEventDetails::JOB_DONE:
+ case JobEventDetails::ALL_PAGES_REQUESTED: {
+ // Don't care.
+ break;
+ }
+ case JobEventDetails::DOC_DONE: {
+ // This will call Stop() and broadcast a JOB_DONE message.
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &PrintJob::OnDocumentDone));
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+void PrintJob::OnDocumentDone() {
+ // Be sure to live long enough. The instance could be destroyed by the
+ // JOB_DONE broadcast.
+ scoped_refptr<PrintJob> handle(this);
+
+ // Stop the worker thread.
+ Stop();
+
+ scoped_refptr<JobEventDetails> details(
+ new JobEventDetails(JobEventDetails::JOB_DONE, document_.get(), NULL));
+ NotificationService::current()->Notify(
+ NOTIFY_PRINT_JOB_EVENT,
+ Source<PrintJob>(this),
+ Details<JobEventDetails>(details.get()));
+}
+
+// Takes settings_ ownership and will be deleted in the receiving thread.
+JobEventDetails::JobEventDetails(Type type,
+ PrintedDocument* document,
+ PrintedPage* page)
+ : document_(document),
+ page_(page),
+ type_(type) {
+}
+
+JobEventDetails::~JobEventDetails() {
+}
+
+PrintedDocument* JobEventDetails::document() const {
+ return document_;
+}
+
+PrintedPage* JobEventDetails::page() const {
+ return page_;
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/print_job.h b/chrome/browser/printing/print_job.h
new file mode 100644
index 0000000..6f0accd
--- /dev/null
+++ b/chrome/browser/printing/print_job.h
@@ -0,0 +1,249 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_H__
+#define CHROME_BROWSER_PRINTING_PRINT_JOB_H__
+
+#include "base/ref_counted.h"
+#include "chrome/browser/printing/print_job_worker_owner.h"
+#include "chrome/common/notification_service.h"
+
+class ChromeFont;
+class GURL;
+class Thread;
+
+namespace printing {
+
+// See definition below.
+class JobEventDetails;
+
+class PrintedDocument;
+class PrintedPage;
+class PrintedPagesSource;
+class PrintJobWorker;
+class PrinterQuery;
+
+// Manages the print work for a specific document. Talks to the printer through
+// PrintingContext though PrintJob::Worker. Hides access to PrintingContext in a
+// worker thread so the caller never blocks. PrintJob will send notifications on
+// any state change. While printing, the PrintJobManager instance keeps a
+// reference to the job to be sure it is kept alive. All the code in this class
+// runs in the UI thread.
+class PrintJob : public base::RefCountedThreadSafe<PrintJob>,
+ public NotificationObserver,
+ public PrintJobWorkerOwner {
+ public:
+ // GetSettings() UI parameter.
+ enum GetSettingsAskParam {
+ DEFAULTS,
+ ASK_USER,
+ };
+
+ // Create a standalone PrintJob. When initializing with this constructor,
+ // Initialize() must not be called.
+ PrintJob(PrintedPagesSource* source);
+ // Create a empty PrintJob. When initializing with this constructor,
+ // post-constructor initialization must be done with Initialize().
+ PrintJob();
+ virtual ~PrintJob();
+
+ void Initialize(PrintJobWorkerOwner* job, PrintedPagesSource* source);
+
+ // NotificationObserver
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // PrintJobWorkerOwner
+ virtual void AddRef() {
+ return base::RefCountedThreadSafe<PrintJob>::AddRef();
+ }
+ virtual void Release() {
+ return base::RefCountedThreadSafe<PrintJob>::Release();
+ }
+ virtual void GetSettingsDone(const PrintSettings& new_settings,
+ PrintingContext::Result result);
+ virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner);
+ virtual MessageLoop* message_loop() { return ui_message_loop_; }
+ virtual const PrintSettings& settings() const { return settings_; }
+ virtual int cookie() const;
+
+ // Initializes the printing context. This can be done synchronously or not. It
+ // is fine to call this function multiple times to reinitialize the settings.
+ // |parent_window| parameter will be the owner of the print setting dialog
+ // box. It is unused when |ask_for_user_settings| is DEFAULTS. No-op if a
+ // print job is active.
+ void GetSettings(GetSettingsAskParam ask_user_for_settings,
+ HWND parent_window);
+
+ // Starts the actual printing. Signals the worker that it should begin to
+ // spool as soon as data is available.
+ void StartPrinting();
+
+ // Waits for the worker thread to finish its queued tasks and disconnects the
+ // delegate object. The PrintJobManager will remove it reference. This may
+ // have the side-effect of destroying the object if the caller doesn't have a
+ // handle to the object.
+ void Stop();
+
+ // Cancels printing job and stops the worker thread. Takes effect immediately.
+ void Cancel();
+
+ // Requests all the missing pages in the PrintedDocument. Returns true if at
+ // least one page has been requested. Returns false if there was not enough
+ // information to request the missing pages, i.e.
+ // document()->document_page_count() is not initialized or no page
+ // has been requested.
+ bool RequestMissingPages();
+
+ // Synchronously wait for the job to finish. It is mainly useful when the
+ // process is about to be shut down and we're waiting for the spooler to eat
+ // our data.
+ bool FlushJob(int timeout_ms);
+
+ // Disconnects the PrintedPage source (PrintedPagesSource). It is done when
+ // the source is being destroyed.
+ void DisconnectSource();
+
+ // Returns true if the print job is pending, i.e. between a StartPrinting()
+ // and the end of the spooling.
+ bool is_job_pending() const;
+
+ // Returns true if the Print... dialog box is currently displayed.
+ bool is_print_dialog_box_shown() const;
+
+ // Access the current printed document. Warning: may be NULL.
+ PrintedDocument* document() const;
+
+
+ private:
+ // Updates document_ to a new instance.
+ void UpdatePrintedDocument(PrintedDocument* new_document);
+
+ // Processes a NOTIFY_PRINT_JOB_EVENT notification.
+ void OnNotifyPrintJobEvent(const JobEventDetails& event_details);
+
+ // Releases the worker thread by calling Stop(), then broadcasts a JOB_DONE
+ // notification.
+ void OnDocumentDone();
+
+ // Main message loop reference. Used to send notifications in the right
+ // thread.
+ MessageLoop* const ui_message_loop_;
+
+ // Source that generates the PrintedPage's (i.e. a WebContents). It will be
+ // set back to NULL if the source is deleted before this object.
+ PrintedPagesSource* source_;
+
+ // All the UI is done in a worker thread because many Win32 print functions
+ // are blocking and enters a message loop without your consent. There is one
+ // worker thread per print job.
+ scoped_ptr<PrintJobWorker> worker_;
+
+ // Cache of the print context settings for access in the UI thread.
+ PrintSettings settings_;
+
+ // The printed document.
+ scoped_refptr<PrintedDocument> document_;
+
+ // Is the worker thread printing.
+ bool is_job_pending_;
+
+ // Is the Print... dialog box currently shown.
+ bool is_print_dialog_box_shown_;
+
+ // Is GetSettings() blocking to act like a synchronous function?
+ bool is_blocking_;
+
+ // Is Canceling? If so, try to not cause recursion if on FAILED notification,
+ // the notified calls Cancel() again.
+ bool is_canceling_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintJob);
+};
+
+// Details for a NOTIFY_PRINT_JOB_EVENT notification. The members may be NULL.
+class JobEventDetails : public base::RefCountedThreadSafe<JobEventDetails> {
+ public:
+ // Event type.
+ enum Type {
+ // Print... dialog box has been closed with OK button.
+ USER_INIT_DONE,
+
+ // Print... dialog box has been closed with CANCEL button.
+ USER_INIT_CANCELED,
+
+ // An automated initialization has been done, e.g. Init(false, NULL).
+ DEFAULT_INIT_DONE,
+
+ // A new document started printing.
+ NEW_DOC,
+
+ // A new page started printing.
+ NEW_PAGE,
+
+ // A page is done printing.
+ PAGE_DONE,
+
+ // A document is done printing. The worker thread is still alive. Warning:
+ // not a good moment to release the handle to PrintJob.
+ DOC_DONE,
+
+ // The worker thread is finished. A good moment to release the handle to
+ // PrintJob.
+ JOB_DONE,
+
+ // All missing pages have been requested.
+ ALL_PAGES_REQUESTED,
+
+ // An error occured. Printing is canceled.
+ FAILED,
+ };
+
+ JobEventDetails(Type type, PrintedDocument* document, PrintedPage* page);
+ ~JobEventDetails();
+
+ // Getters.
+ PrintedDocument* document() const;
+ PrintedPage* page() const;
+ Type type() const {
+ return type_;
+ }
+
+ private:
+ scoped_refptr<PrintedDocument> document_;
+ scoped_refptr<PrintedPage> page_;
+ const Type type_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(JobEventDetails);
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_H__
diff --git a/chrome/browser/printing/print_job_manager.cc b/chrome/browser/printing/print_job_manager.cc
new file mode 100644
index 0000000..794af34
--- /dev/null
+++ b/chrome/browser/printing/print_job_manager.cc
@@ -0,0 +1,220 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/print_job_manager.h"
+
+#include "base/file_util.h"
+#include "base/string_util.h"
+#include "chrome/browser/printing/print_job.h"
+#include "chrome/browser/printing/printer_query.h"
+#include "chrome/browser/printing/printed_document.h"
+#include "chrome/browser/printing/printed_page.h"
+#include "chrome/common/gfx/emf.h"
+
+namespace printing {
+
+PrintJobManager::PrintJobManager()
+ : debug_dump_path_() {
+ NotificationService::current()->AddObserver(
+ this,
+ NOTIFY_PRINT_JOB_EVENT,
+ NotificationService::AllSources());
+ NotificationService::current()->AddObserver(
+ this,
+ NOTIFY_PRINTED_DOCUMENT_UPDATED,
+ NotificationService::AllSources());
+}
+
+PrintJobManager::~PrintJobManager() {
+ // When this object is destroyed, the shared NotificationService instance is
+ // already destroyed.
+ AutoLock lock(lock_);
+ queued_queries_.clear();
+ NotificationService::current()->RemoveObserver(
+ this,
+ NOTIFY_PRINT_JOB_EVENT,
+ NotificationService::AllSources());
+ NotificationService::current()->RemoveObserver(
+ this,
+ NOTIFY_PRINTED_DOCUMENT_UPDATED,
+ NotificationService::AllSources());
+}
+
+void PrintJobManager::OnQuit() {
+ // Common case, no print job pending.
+ if (current_jobs_.size() == 0)
+ return;
+ {
+ // Don't take a chance and copy the array since it can be modified in transit.
+ PrintJobs current_jobs(current_jobs_);
+ // Wait for every jobs to finish.
+ for (size_t i = 0; i < current_jobs.size(); ++i) {
+ PrintJob* job = current_jobs[i];
+ if (!job)
+ continue;
+ // Wait for 120 seconds for the print job to be spooled.
+ job->FlushJob(120000);
+ job->Stop();
+ }
+ }
+ current_jobs_.clear();
+ NotificationService::current()->RemoveObserver(
+ this,
+ NOTIFY_PRINT_JOB_EVENT,
+ NotificationService::AllSources());
+ NotificationService::current()->RemoveObserver(
+ this,
+ NOTIFY_PRINTED_DOCUMENT_UPDATED,
+ NotificationService::AllSources());
+ DCHECK_EQ(current_jobs_.size(), 0);
+}
+
+void PrintJobManager::QueuePrinterQuery(PrinterQuery* job) {
+ AutoLock lock(lock_);
+ DCHECK(job);
+ queued_queries_.push_back(job);
+ DCHECK(job->is_valid());
+}
+
+void PrintJobManager::PopPrinterQuery(int document_cookie,
+ scoped_refptr<PrinterQuery>* job) {
+ AutoLock lock(lock_);
+ for (PrinterQueries::iterator itr = queued_queries_.begin();
+ itr != queued_queries_.end();
+ ++itr) {
+ PrinterQuery* current_query = *itr;
+ if (current_query->cookie() == document_cookie &&
+ !current_query->is_callback_pending()) {
+ *job = current_query;
+ queued_queries_.erase(itr);
+ DCHECK(current_query->is_valid());
+ return;
+ }
+ }
+}
+
+
+void PrintJobManager::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFY_PRINT_JOB_EVENT: {
+ OnPrintJobEvent(Source<PrintJob>(source).ptr(),
+ *Details<JobEventDetails>(details).ptr());
+ break;
+ }
+ case NOTIFY_PRINTED_DOCUMENT_UPDATED: {
+ PrintedPage* printed_page = Details<PrintedPage>(details).ptr();
+ if (printed_page)
+ OnPrintedDocumentUpdated(*Source<PrintedDocument>(source).ptr(),
+ *printed_page);
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+void PrintJobManager::OnPrintJobEvent(
+ PrintJob* print_job,
+ const JobEventDetails& event_details) {
+ switch (event_details.type()) {
+ case JobEventDetails::NEW_DOC: {
+ DCHECK(current_jobs_.end() == std::find(current_jobs_.begin(),
+ current_jobs_.end(),
+ print_job));
+ // Causes a AddRef().
+ current_jobs_.push_back(print_job);
+ break;
+ }
+ case JobEventDetails::JOB_DONE: {
+ PrintJobs::iterator itr = std::find(current_jobs_.begin(),
+ current_jobs_.end(),
+ print_job);
+ DCHECK(current_jobs_.end() != itr);
+ current_jobs_.erase(itr);
+ DCHECK(current_jobs_.end() == std::find(current_jobs_.begin(),
+ current_jobs_.end(),
+ print_job));
+ break;
+ }
+ case JobEventDetails::FAILED: {
+ PrintJobs::iterator itr = std::find(current_jobs_.begin(),
+ current_jobs_.end(),
+ print_job);
+ // A failed job may have never started.
+ if (current_jobs_.end() != itr) {
+ current_jobs_.erase(itr);
+ DCHECK(current_jobs_.end() ==
+ std::find(current_jobs_.begin(),
+ current_jobs_.end(),
+ print_job));
+ }
+ break;
+ }
+ case JobEventDetails::USER_INIT_DONE:
+ case JobEventDetails::USER_INIT_CANCELED:
+ case JobEventDetails::DEFAULT_INIT_DONE:
+ case JobEventDetails::NEW_PAGE:
+ case JobEventDetails::PAGE_DONE:
+ case JobEventDetails::DOC_DONE:
+ case JobEventDetails::ALL_PAGES_REQUESTED: {
+ // Don't care.
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+void PrintJobManager::OnPrintedDocumentUpdated(const PrintedDocument& document,
+ const PrintedPage& page) {
+ if (debug_dump_path_.empty())
+ return;
+
+ std::wstring filename;
+ filename += document.date();
+ filename += L"_";
+ filename += document.time();
+ filename += L"_";
+ filename += document.name();
+ filename += L"_";
+ filename += StringPrintf(L"%02d", page.page_number());
+ filename += L"_.emf";
+ file_util::ReplaceIllegalCharacters(&filename, '_');
+ std::wstring path(debug_dump_path_);
+ file_util::AppendToPath(&path, filename);
+ page.emf()->SaveTo(path);
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/print_job_manager.h b/chrome/browser/printing/print_job_manager.h
new file mode 100644
index 0000000..ee95367
--- /dev/null
+++ b/chrome/browser/printing/print_job_manager.h
@@ -0,0 +1,111 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H__
+#define CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H__
+
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "chrome/common/notification_service.h"
+
+namespace printing {
+
+class JobEventDetails;
+class PrintedDocument;
+class PrintJob;
+class PrintedPage;
+class PrinterQuery;
+
+class PrintJobManager : public NotificationObserver {
+ public:
+ PrintJobManager();
+ ~PrintJobManager();
+
+ // On browser quit, we should wait to have the print job finished.
+ void OnQuit();
+
+ // Queues a semi-initialized worker thread. Can be called from any thread.
+ // Current use case is queuing from the I/O thread.
+ // TODO(maruel): Have them vanish after a timeout (~5 minutes?)
+ void QueuePrinterQuery(PrinterQuery* job);
+
+ // Pops a queued PrintJobWorkerOwner object that was previously queued. Can be
+ // called from any thread. Current use case is poping from the browser thread.
+ void PopPrinterQuery(int document_cookie, scoped_refptr<PrinterQuery>* job);
+
+ // NotificationObserver
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Sets a path where to dump EMF data files. This enables debug behavior where
+ // every rendered pages are dumped as-is. By default the path is empty, which
+ // disables the dumping.
+ // TODO(maruel): Remove me once printing is awesome.
+ void set_debug_dump_path(const std::wstring& debug_dump_path) {
+ debug_dump_path_ = debug_dump_path;
+ }
+
+ const std::wstring& debug_dump_path() const {
+ return debug_dump_path_;
+ }
+
+ private:
+ typedef std::vector<scoped_refptr<PrintJob> > PrintJobs;
+ typedef std::vector<scoped_refptr<PrinterQuery> > PrinterQueries;
+
+ // Processes a NOTIFY_PRINT_JOB_EVENT notification.
+ void OnPrintJobEvent(PrintJob* print_job,
+ const JobEventDetails& event_details);
+
+ // Processes a NOTIFY_PRINTED_DOCUMENT_UPDATED notification. When
+ // debug_dump_path_ is not empty, it is processed to detect newly rendered
+ // pages and to dump their EMF buffer.
+ void OnPrintedDocumentUpdated(const PrintedDocument& document,
+ const PrintedPage& page);
+
+ // Used to serialize access to queued_workers_.
+ Lock lock_;
+
+ PrinterQueries queued_queries_;
+
+ // Current print jobs that are active.
+ PrintJobs current_jobs_;
+
+ // Path where debug dump of EMF buffer are saved. Empty by default. When
+ // empty, EMF dumping is disabled.
+ // TODO(maruel): Remove me once printing is awesome.
+ std::wstring debug_dump_path_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintJobManager);
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H__
diff --git a/chrome/browser/printing/print_job_worker.cc b/chrome/browser/printing/print_job_worker.cc
new file mode 100644
index 0000000..5a26ef91
--- /dev/null
+++ b/chrome/browser/printing/print_job_worker.cc
@@ -0,0 +1,325 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/print_job_worker.h"
+
+#include "base/message_loop.h"
+#include "chrome/browser/printing/print_job.h"
+#include "chrome/browser/printing/printed_document.h"
+#include "chrome/browser/printing/printed_page.h"
+#include "chrome/common/gfx/emf.h"
+
+namespace printing {
+
+class PrintJobWorker::NotificationTask : public Task {
+ public:
+ NotificationTask() : print_job_(NULL), details_(NULL) {
+ }
+ ~NotificationTask() {
+ }
+
+ // Initializes the object. This object can't be initialized in the constructor
+ // since it is not created directly.
+ void Init(PrintJobWorkerOwner* print_job,
+ JobEventDetails::Type detail_type,
+ PrintedDocument* document,
+ PrintedPage* page) {
+ DCHECK(!print_job_);
+ DCHECK(!details_);
+ print_job_ = print_job;
+ details_ = new JobEventDetails(detail_type, document, page);
+ }
+
+ virtual void Run() {
+ // Send the notification in the right thread.
+ NotificationService::current()->Notify(
+ NOTIFY_PRINT_JOB_EVENT,
+ // We know that is is a PrintJob object in this circumstance.
+ Source<PrintJob>(static_cast<PrintJob*>(print_job_.get())),
+ Details<JobEventDetails>(details_));
+ }
+
+ // The job which originates this notification.
+ scoped_refptr<PrintJobWorkerOwner> print_job_;
+ scoped_refptr<JobEventDetails> details_;
+ NotificationType type_;
+};
+
+
+PrintJobWorker::PrintJobWorker(PrintJobWorkerOwner* owner)
+ : Thread("Printing_Worker"),
+ owner_(owner) {
+ // The object is created in the UI thread.
+ DCHECK_EQ(owner_->message_loop(), MessageLoop::current());
+}
+
+PrintJobWorker::~PrintJobWorker() {
+ // The object is deleted in the UI thread.
+ DCHECK_EQ(owner_->message_loop(), MessageLoop::current());
+}
+
+void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
+ DCHECK(page_number_ == PageNumber::npos());
+ owner_ = new_owner;
+}
+
+void PrintJobWorker::GetSettings(bool ask_user_for_settings,
+ HWND parent_window,
+ int document_page_count) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_EQ(page_number_, PageNumber::npos());
+
+ // Recursive task processing is needed for the dialog in case it needs to be
+ // destroyed by a task.
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+
+ PrintingContext::Result result;
+ if (ask_user_for_settings) {
+ result = printing_context_.AskUserForSettings(parent_window,
+ document_page_count);
+ } else {
+ result = printing_context_.UseDefaultSettings();
+ }
+
+ // Most PrintingContext functions may start a message loop and process
+ // message recursively, so disable recursive task processing.
+ MessageLoop::current()->SetNestableTasksAllowed(false);
+
+ // We can't use OnFailure() here since owner_ may not support notifications.
+
+ // PrintJob will create the new PrintedDocument.
+ owner_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ owner_,
+ &PrintJobWorkerOwner::GetSettingsDone,
+ printing_context_.settings(),
+ result));
+}
+
+void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_EQ(page_number_, PageNumber::npos());
+ DCHECK_EQ(document_, new_document);
+ DCHECK(document_.get());
+ DCHECK(new_document->settings().Equals(printing_context_.settings()));
+ DCHECK(printing_context_.context());
+ if (!document_.get() || page_number_ != PageNumber::npos() ||
+ document_ != new_document) {
+ return;
+ }
+
+ PrintingContext::Result result =
+ printing_context_.NewDocument(document_->name());
+ if (result != PrintingContext::OK) {
+ OnFailure();
+ return;
+ }
+
+ // Try to print already cached data. It may already have been generated for
+ // the print preview.
+ OnNewPage();
+ // Don't touch this anymore since the instance could be destroyed. It happens
+ // if all the pages are printed a one sweep and the client doesn't have a
+ // handle to us anymore. There's a timing issue involved between the worker
+ // thread and the UI thread. Take no chance.
+}
+
+void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_EQ(page_number_, PageNumber::npos());
+ DCHECK(!new_document ||
+ new_document->settings().Equals(printing_context_.settings()));
+ DCHECK(printing_context_.context());
+ if (page_number_ != PageNumber::npos())
+ return;
+
+ document_ = new_document;
+}
+
+void PrintJobWorker::OnNewPage() {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+ if (!document_.get()) {
+ // Spurious message.
+ return;
+ }
+ DCHECK(printing_context_.context());
+ if (!printing_context_.context())
+ return;
+
+ if (page_number_ == PageNumber::npos()) {
+ // Find first page to print.
+ int page_count = document_->page_count();
+ if (!page_count) {
+ // We still don't know how many pages the document contains. We can't
+ // start to print the document yet since the header/footer may refer to
+ // the document's page count.
+ return;
+ }
+ // We have enough information to initialize page_number_.
+ page_number_.Init(document_->settings(), page_count);
+ }
+ DCHECK_NE(page_number_, PageNumber::npos());
+
+ for (;;) {
+ // Is the page available?
+ scoped_refptr<PrintedPage> page;
+ if (!document_->GetPage(page_number_.ToInt(), &page)) {
+ // The page is implictly requested.
+ break;
+ }
+ // The page is there, print it.
+ SpoolPage(*page);
+ ++page_number_;
+ if (page_number_ == PageNumber::npos()) {
+ OnDocumentDone();
+ // Don't touch this anymore since the instance could be destroyed.
+ break;
+ }
+ }
+}
+
+void PrintJobWorker::Cancel() {
+ // This is the only function that can be called from any thread.
+ printing_context_.Cancel();
+ // Cannot touch any member variable since we don't know in which thread
+ // context we run.
+}
+
+void PrintJobWorker::DismissDialog() {
+ printing_context_.DismissDialog();
+}
+
+void PrintJobWorker::RequestMissingPages() {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+ // It may arrive out of order. Don't mind about it.
+ if (page_number_ != PageNumber::npos()) {
+ // We are printing.
+ document_->RequestMissingPages();
+ }
+ NotificationTask* task = new NotificationTask();
+ task->Init(owner_,
+ JobEventDetails::ALL_PAGES_REQUESTED,
+ document_.get(),
+ NULL);
+ owner_->message_loop()->PostTask(FROM_HERE, task);
+}
+
+void PrintJobWorker::OnDocumentDone() {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_EQ(page_number_, PageNumber::npos());
+ DCHECK(document_.get());
+ DCHECK(printing_context_.context());
+
+ if (printing_context_.DocumentDone() != PrintingContext::OK) {
+ OnFailure();
+ return;
+ }
+
+ // Tell everyone!
+ NotificationTask* task = new NotificationTask();
+ task->Init(owner_,
+ JobEventDetails::DOC_DONE,
+ document_.get(),
+ NULL);
+ owner_->message_loop()->PostTask(FROM_HERE, task);
+
+ // Makes sure the variables are reinitialized.
+ document_ = NULL;
+}
+
+void PrintJobWorker::SpoolPage(PrintedPage& page) {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+ DCHECK_NE(page_number_, PageNumber::npos());
+ DCHECK(printing_context_.context());
+ // Signal everyone that the page is about to be printed.
+ NotificationTask* task = new NotificationTask();
+ task->Init(owner_,
+ JobEventDetails::NEW_PAGE,
+ document_.get(),
+ &page);
+ owner_->message_loop()->PostTask(FROM_HERE, task);
+
+ // Preprocess.
+ if (printing_context_.NewPage() != PrintingContext::OK) {
+ OnFailure();
+ return;
+ }
+
+ // Actual printing.
+ document_->RenderPrintedPage(page, printing_context_.context());
+
+ // Postprocess.
+ if (printing_context_.PageDone() != PrintingContext::OK) {
+ OnFailure();
+ return;
+ }
+
+ // Signal everyone that the page is printed.
+ task = new NotificationTask();
+ task->Init(owner_,
+ JobEventDetails::PAGE_DONE,
+ document_.get(),
+ &page);
+ owner_->message_loop()->PostTask(FROM_HERE, task);
+}
+
+void PrintJobWorker::OnFailure() {
+ DCHECK_EQ(message_loop(), MessageLoop::current());
+
+ // We may loose our last reference by broadcasting the FAILED event.
+ scoped_refptr<PrintJobWorkerOwner> handle(owner_);
+
+ NotificationTask* task = new NotificationTask();
+ task->Init(owner_,
+ JobEventDetails::FAILED,
+ document_.get(),
+ NULL);
+ owner_->message_loop()->PostTask(FROM_HERE, task);
+ Cancel();
+
+ // Makes sure the variables are reinitialized.
+ document_ = NULL;
+ page_number_ = PageNumber::npos();
+}
+
+} // namespace printing
+
+RunnableMethodTraits<printing::PrintJobWorker>::RunnableMethodTraits() {
+}
+
+void RunnableMethodTraits<printing::PrintJobWorker>::RetainCallee(
+ printing::PrintJobWorker* obj) {
+ DCHECK(!owner_.get());
+ owner_ = obj->owner_;
+}
+
+void RunnableMethodTraits<printing::PrintJobWorker>::ReleaseCallee(
+ printing::PrintJobWorker* obj) {
+ DCHECK_EQ(owner_, obj->owner_);
+ owner_ = NULL;
+}
diff --git a/chrome/browser/printing/print_job_worker.h b/chrome/browser/printing/print_job_worker.h
new file mode 100644
index 0000000..c7064f7
--- /dev/null
+++ b/chrome/browser/printing/print_job_worker.h
@@ -0,0 +1,133 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H__
+#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H__
+
+#include "base/task.h"
+#include "base/thread.h"
+#include "chrome/browser/printing/page_number.h"
+#include "chrome/browser/printing/win_printing_context.h"
+#include "googleurl/src/gurl.h"
+
+namespace printing {
+
+class PrintedDocument;
+class PrintedPage;
+class PrintJob;
+class PrintJobWorkerOwner;
+
+// Worker thread code. All this code, except for the constructor, is executed in
+// the worker thread. It manages the PrintingContext, which can be blocking
+// and/or run a message loop. This is the object that generates most
+// NOTIFY_PRINT_JOB_EVENT notifications, but they are generated through a
+// NotificationTask task to be executed from the right thread, the UI thread.
+// PrintJob always outlives its worker instance.
+class PrintJobWorker : public Thread {
+ public:
+ PrintJobWorker(PrintJobWorkerOwner* owner);
+ ~PrintJobWorker();
+
+ void SetNewOwner(PrintJobWorkerOwner* new_owner);
+
+ // Initializes the print settings. If |ask_user_for_settings| is true, a
+ // Print... dialog box will be shown to ask the user his preference.
+ void GetSettings(bool ask_user_for_settings,
+ HWND parent_window,
+ int document_page_count);
+
+ // Starts the printing loop. Every pages are printed as soon as the data is
+ // available. Makes sure the new_document is the right one.
+ void StartPrinting(PrintedDocument* new_document);
+
+ // Updates the printed document.
+ void OnDocumentChanged(PrintedDocument* new_document);
+
+ // Unqueues waiting pages. Called when PrintJob receives a
+ // NOTIFY_PRINTED_DOCUMENT_UPDATED notification. It's time to look again if
+ // the next page can be printed.
+ void OnNewPage();
+
+ // This is the only function that can be called in a thread.
+ void Cancel();
+
+ // Cancels the Print... dialog box if shown, noop otherwise.
+ void DismissDialog();
+
+ // Requests the missing pages in rendered_document_. Sends back a
+ // ALL_PAGES_REQUESTED notification once done.
+ void RequestMissingPages();
+
+ private:
+ // The shared NotificationService service can only be accessed from the UI
+ // thread, so this class encloses the necessary information to send the
+ // notification from the right thread. Most NOTIFY_PRINT_JOB_EVENT
+ // notifications are sent this way, except USER_INIT_DONE, USER_INIT_CANCELED
+ // and DEFAULT_INIT_DONE. These three are sent through PrintJob::InitDone().
+ class NotificationTask;
+ friend struct RunnableMethodTraits<PrintJobWorker>;
+
+ // Renders a page in the printer.
+ void SpoolPage(PrintedPage& page);
+
+ // Closes the job since spooling is done.
+ void OnDocumentDone();
+
+ // Discards the current document, the current page and cancels the printing
+ // context.
+ void OnFailure();
+
+ // Information about the printer setting.
+ PrintingContext printing_context_;
+
+ // The printed document. Only has read-only access.
+ scoped_refptr<PrintedDocument> document_;
+
+ // The print job owning this worker thread. It is guaranteed to outlive this
+ // object.
+ PrintJobWorkerOwner* owner_;
+
+ // Current page number to print.
+ PageNumber page_number_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintJobWorker);
+};
+
+} // namespace printing
+
+template <>
+struct RunnableMethodTraits<printing::PrintJobWorker> {
+ RunnableMethodTraits();
+ void RetainCallee(printing::PrintJobWorker* obj);
+ void ReleaseCallee(printing::PrintJobWorker* obj);
+ private:
+ scoped_refptr<printing::PrintJobWorkerOwner> owner_;
+};
+
+#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H__
diff --git a/chrome/browser/printing/print_job_worker_owner.h b/chrome/browser/printing/print_job_worker_owner.h
new file mode 100644
index 0000000..c74b819
--- /dev/null
+++ b/chrome/browser/printing/print_job_worker_owner.h
@@ -0,0 +1,70 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__
+#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__
+
+#include "chrome/browser/printing/win_printing_context.h"
+
+class MessageLoop;
+
+namespace printing {
+
+class PrintJobWorker;
+class PrintSettings;
+
+class PrintJobWorkerOwner {
+ public:
+ virtual ~PrintJobWorkerOwner() {
+ }
+ virtual void AddRef() = 0;
+ virtual void Release() = 0;
+
+ // Finishes the initialization began by PrintJobWorker::Init(). Creates a
+ // new PrintedDocument if necessary. Solely meant to be called by
+ // PrintJobWorker.
+ virtual void GetSettingsDone(const PrintSettings& new_settings,
+ PrintingContext::Result result) = 0;
+
+ // Detach the PrintJobWorker associated to this object.
+ virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) = 0;
+
+ // Retrieves the message loop that is expected to process GetSettingsDone.
+ virtual MessageLoop* message_loop() = 0;
+
+ // Access the current settings.
+ virtual const PrintSettings& settings() const = 0;
+
+ // Cookie uniquely identifying the PrintedDocument and/or loaded settings.
+ virtual int cookie() const = 0;
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__
diff --git a/chrome/browser/printing/print_settings.cc b/chrome/browser/printing/print_settings.cc
new file mode 100644
index 0000000..308e306
--- /dev/null
+++ b/chrome/browser/printing/print_settings.cc
@@ -0,0 +1,195 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/print_settings.h"
+
+#include "base/logging.h"
+#include "chrome/browser/printing/units.h"
+#include "chrome/common/render_messages.h"
+
+namespace printing {
+
+int PrintSettings::s_cookie_;
+
+PrintSettings::PrintSettings()
+ : min_shrink(1.25),
+ max_shrink(2.0),
+ desired_dpi(72),
+ dpi_(0),
+ landscape_(false) {
+}
+
+void PrintSettings::Clear() {
+ ranges.clear();
+ min_shrink = 1.25;
+ max_shrink = 2.;
+ desired_dpi = 72;
+ printer_name_.clear();
+ device_name_.clear();
+ page_setup_cmm_.Clear();
+ page_setup_pixels_.Clear();
+ dpi_ = 0;
+ landscape_ = false;
+}
+
+#ifdef WIN32
+void PrintSettings::Init(HDC hdc,
+ const DEVMODE& dev_mode,
+ const PageRanges& new_ranges,
+ const std::wstring& new_device_name) {
+ DCHECK(hdc);
+ printer_name_ = dev_mode.dmDeviceName;
+ device_name_ = new_device_name;
+ ranges = new_ranges;
+ landscape_ = dev_mode.dmOrientation == DMORIENT_LANDSCAPE;
+
+ int old_dpi = dpi_;
+ dpi_ = GetDeviceCaps(hdc, LOGPIXELSX);
+ // No printer device is known to advertise different dpi in X and Y axis; even
+ // the fax device using the 200x100 dpi setting. It's ought to break so many
+ // applications that it's not even needed to care about. WebKit doesn't
+ // support different dpi settings in X and Y axis.
+ DCHECK_EQ(dpi_, GetDeviceCaps(hdc, LOGPIXELSY));
+
+ DCHECK_EQ(GetDeviceCaps(hdc, SCALINGFACTORX), 0);
+ DCHECK_EQ(GetDeviceCaps(hdc, SCALINGFACTORY), 0);
+
+ // Initialize page_setup_pixels_.
+ gfx::Size physical_size_pixels(GetDeviceCaps(hdc, PHYSICALWIDTH),
+ GetDeviceCaps(hdc, PHYSICALHEIGHT));
+ gfx::Rect printable_area_pixels(GetDeviceCaps(hdc, PHYSICALOFFSETX),
+ GetDeviceCaps(hdc, PHYSICALOFFSETY),
+ GetDeviceCaps(hdc, HORZRES),
+ GetDeviceCaps(hdc, VERTRES));
+ // Hard-code text_height = 0.5cm = ~1/5 of inch
+ page_setup_pixels_.Init(physical_size_pixels, printable_area_pixels,
+ ConvertUnit(500, kHundrethsMMPerInch, dpi_));
+
+ // Initialize page_setup_cmm_.
+ // In theory, we should be using HORZSIZE and VERTSIZE but their value is
+ // so wrong it's useless. So read the values in dpi unit and convert them back
+ // in 0.01 mm.
+ gfx::Size physical_size_cmm(
+ ConvertUnit(physical_size_pixels.width(), dpi_, kHundrethsMMPerInch),
+ ConvertUnit(physical_size_pixels.height(), dpi_, kHundrethsMMPerInch));
+ gfx::Rect printable_area_cmm(
+ ConvertUnit(printable_area_pixels.x(), dpi_, kHundrethsMMPerInch),
+ ConvertUnit(printable_area_pixels.y(), dpi_, kHundrethsMMPerInch),
+ ConvertUnit(printable_area_pixels.width(), dpi_, kHundrethsMMPerInch),
+ ConvertUnit(printable_area_pixels.bottom(), dpi_, kHundrethsMMPerInch));
+
+ static const int kRoundingTolerance = 5;
+ // Some printers may advertise a slightly larger printable area than the
+ // physical area. This is mostly due to integer calculation and rounding.
+ if (physical_size_cmm.height() > printable_area_cmm.bottom() &&
+ physical_size_cmm.height() <= (printable_area_cmm.bottom() +
+ kRoundingTolerance)) {
+ physical_size_cmm.set_height(printable_area_cmm.bottom());
+ }
+ if (physical_size_cmm.width() > printable_area_cmm.right() &&
+ physical_size_cmm.width() <= (printable_area_cmm.right() +
+ kRoundingTolerance)) {
+ physical_size_cmm.set_width(printable_area_cmm.right());
+ }
+ page_setup_cmm_.Init(physical_size_cmm, printable_area_cmm, 500);
+}
+#endif
+
+void PrintSettings::UpdateMarginsMetric(const PageMargins& new_margins) {
+ // Apply the new margins in 0.01 mm unit.
+ page_setup_cmm_.SetRequestedMargins(new_margins);
+
+ // Converts the margins in dpi unit and apply those too.
+ PageMargins pixels_margins;
+ pixels_margins.header = ConvertUnit(new_margins.header, kHundrethsMMPerInch,
+ dpi_);
+ pixels_margins.footer = ConvertUnit(new_margins.footer, kHundrethsMMPerInch,
+ dpi_);
+ pixels_margins.left = ConvertUnit(new_margins.left, kHundrethsMMPerInch,
+ dpi_);
+ pixels_margins.top = ConvertUnit(new_margins.top, kHundrethsMMPerInch, dpi_);
+ pixels_margins.right = ConvertUnit(new_margins.right, kHundrethsMMPerInch,
+ dpi_);
+ pixels_margins.bottom = ConvertUnit(new_margins.bottom, kHundrethsMMPerInch,
+ dpi_);
+ page_setup_pixels_.SetRequestedMargins(pixels_margins);
+}
+
+void PrintSettings::UpdateMarginsMilliInch(const PageMargins& new_margins) {
+ // Convert margins from thousandth inches to cmm (0.01mm).
+ PageMargins cmm_margins;
+ cmm_margins.header =
+ ConvertMilliInchToHundredThousanthMeter(new_margins.header);
+ cmm_margins.footer =
+ ConvertMilliInchToHundredThousanthMeter(new_margins.footer);
+ cmm_margins.left = ConvertMilliInchToHundredThousanthMeter(new_margins.left);
+ cmm_margins.top = ConvertMilliInchToHundredThousanthMeter(new_margins.top);
+ cmm_margins.right =
+ ConvertMilliInchToHundredThousanthMeter(new_margins.right);
+ cmm_margins.bottom =
+ ConvertMilliInchToHundredThousanthMeter(new_margins.bottom);
+ UpdateMarginsMetric(cmm_margins);
+}
+
+void PrintSettings::RenderParams(ViewMsg_Print_Params* params) const {
+ DCHECK(params);
+ params->printable_size.SetSize(page_setup_pixels_.content_area().width(),
+ page_setup_pixels_.content_area().height());
+ params->dpi = dpi_;
+ // Currently hardcoded at 1.25. See PrintSettings' constructor.
+ params->min_shrink = min_shrink;
+ // Currently hardcoded at 2.0. See PrintSettings' constructor.
+ params->max_shrink = max_shrink;
+ // Currently hardcoded at 72dpi. See PrintSettings' constructor.
+ params->desired_dpi = desired_dpi;
+ // Always use an invalid cookie.
+ params->document_cookie = 0;
+}
+
+bool PrintSettings::Equals(const PrintSettings& rhs) const {
+ // Do not test the display device name (printer_name_) for equality since it
+ // may sometimes be chopped off at 30 chars. As long as device_name is the
+ // same, that's fine.
+ return ranges == rhs.ranges &&
+ min_shrink == rhs.min_shrink &&
+ max_shrink == rhs.max_shrink &&
+ desired_dpi == rhs.desired_dpi &&
+ overlays.Equals(rhs.overlays) &&
+ device_name_ == rhs.device_name_ &&
+ page_setup_pixels_.Equals(rhs.page_setup_pixels_) &&
+ page_setup_cmm_.Equals(rhs.page_setup_cmm_) &&
+ dpi_ == rhs.dpi_ &&
+ landscape_ == rhs.landscape_;
+}
+
+int PrintSettings::NewCookie() {
+ return base::AtomicIncrement(&s_cookie_);
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/print_settings.h b/chrome/browser/printing/print_settings.h
new file mode 100644
index 0000000..4e9416a
--- /dev/null
+++ b/chrome/browser/printing/print_settings.h
@@ -0,0 +1,142 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINT_SETTINGS_H__
+#define CHROME_BROWSER_PRINTING_PRINT_SETTINGS_H__
+
+#include "base/gfx/rect.h"
+#include "chrome/browser/printing/page_overlays.h"
+#include "chrome/browser/printing/page_range.h"
+#include "chrome/browser/printing/page_setup.h"
+
+struct ViewMsg_Print_Params;
+typedef struct HDC__* HDC;
+typedef struct _devicemodeW DEVMODE;
+
+namespace printing {
+
+// OS-independent print settings.
+class PrintSettings {
+ public:
+ PrintSettings();
+
+ // Reinitialize the settings to the default values.
+ void Clear();
+
+#ifdef WIN32
+ // Reads the settings from the selected device context. Calculates derived
+ // values like printable_area_.
+ void Init(HDC hdc,
+ const DEVMODE& dev_mode,
+ const PageRanges& new_ranges,
+ const std::wstring& new_device_name);
+#endif
+
+ // Sets margins in 0.01 millimeter unit.
+ void UpdateMarginsMetric(const PageMargins& new_margins);
+
+ // Sets margins in thousandth of inch.
+ void UpdateMarginsMilliInch(const PageMargins& new_margins);
+
+ // Initializes the print parameters that needs to be sent to the renderer
+ // process.
+ void RenderParams(ViewMsg_Print_Params* params) const;
+
+ // Equality operator.
+ // NOTE: printer_name is NOT tested for equality since it doesn't affect the
+ // output.
+ bool Equals(const PrintSettings& rhs) const;
+
+ const std::wstring& printer_name() const { return printer_name_; }
+ void set_device_name(const std::wstring& device_name) {
+ device_name_ = device_name;
+ }
+ const std::wstring& device_name() const { return device_name_; }
+ int dpi() const { return dpi_; }
+ const PageSetup& page_setup_cmm() const { return page_setup_cmm_; }
+ const PageSetup& page_setup_pixels() const { return page_setup_pixels_; }
+
+ // Multipage printing. Each PageRange describes a from-to page combinaison.
+ // This permits printing some selected pages only.
+ PageRanges ranges;
+
+ // By imaging to a width a little wider than the available pixels, thin pages
+ // will be scaled down a little, matching the way they print in IE and Camino.
+ // This lets them use fewer sheets than they would otherwise, which is
+ // presumably why other browsers do this. Wide pages will be scaled down more
+ // than this.
+ double min_shrink;
+
+ // This number determines how small we are willing to reduce the page content
+ // in order to accommodate the widest line. If the page would have to be
+ // reduced smaller to make the widest line fit, we just clip instead (this
+ // behavior matches MacIE and Mozilla, at least)
+ double max_shrink;
+
+ // Desired visible dots per inch rendering for output. Printing should be
+ // scaled to ScreenDpi/dpix*desired_dpi.
+ int desired_dpi;
+
+ // The various overlays (headers and footers).
+ PageOverlays overlays;
+
+ // Generates a new cookie to uniquely identify a PrintedDocument.
+ static int NewCookie();
+
+ private:
+ //////////////////////////////////////////////////////////////////////////////
+ // Settings that can't be changed without side-effects.
+
+ // Printer name as shown to the user.
+ std::wstring printer_name_;
+
+ // Printer device name as opened by the OS.
+ std::wstring device_name_;
+
+ // Page setup in centimillimeter (0.01 mm) units.
+ PageSetup page_setup_cmm_;
+
+ // Page setup in pixel units, dpi adjusted.
+ PageSetup page_setup_pixels_;
+
+ // Printer's device effective dots per inch in both axis.
+ int dpi_;
+
+ // Is the orientation landscape or portrait.
+ bool landscape_;
+
+ // Cookie generator. It is used to initialize PrintedDocument with its
+ // associated PrintSettings, to be sure that each generated PrintedPage is
+ // correctly associated with its corresponding PrintedDocument.
+ static int s_cookie_;
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PRINT_SETTINGS_H__
diff --git a/chrome/browser/printing/print_view_manager.cc b/chrome/browser/printing/print_view_manager.cc
new file mode 100644
index 0000000..9c299b1
--- /dev/null
+++ b/chrome/browser/printing/print_view_manager.cc
@@ -0,0 +1,578 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/print_view_manager.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/printing/print_job.h"
+#include "chrome/browser/printing/print_job_manager.h"
+#include "chrome/browser/printing/printed_document.h"
+#include "chrome/browser/printing/printer_query.h"
+#include "chrome/browser/render_view_host.h"
+#include "chrome/browser/web_contents.h"
+#include "chrome/common/gfx/emf.h"
+#include "chrome/common/l10n_util.h"
+
+#include "generated_resources.h"
+
+namespace printing {
+
+PrintViewManager::PrintViewManager(WebContents& owner)
+ : owner_(owner),
+ waiting_to_print_(false),
+ inside_inner_message_loop_(false),
+ waiting_to_show_print_dialog_(false) {
+ memset(&print_params_, 0, sizeof(print_params_));
+}
+
+void PrintViewManager::Destroy() {
+ DisconnectFromCurrentPrintJob();
+}
+
+void PrintViewManager::Stop() {
+ // Cancel the current job, wait for the worker to finish.
+ TerminatePrintJob(true);
+}
+
+void PrintViewManager::ShowPrintDialog() {
+ if (!CreateNewPrintJob(NULL))
+ return;
+
+ // Retrieve default settings. PrintJob will send back a
+ // NOTIFY_PRINT_JOB_EVENT with either INIT_DONE, INIT_CANCELED or FAILED.
+ // On failure, simply back off. Otherwise, request the number of pages to
+ // the renderer. Wait for its response (DidGetPrintedPagesCount), which will
+ // give the value used to initialize the Print... dialog. PrintJob will send
+ // back (again) a NOTIFY_PRINT_JOB_EVENT with either INIT_DONE, INIT_CANCELED
+ // or FAILED. The result is to call PrintNowInternal or to back off.
+ waiting_to_show_print_dialog_ = true;
+ print_job_->GetSettings(PrintJob::DEFAULTS, NULL);
+}
+
+bool PrintViewManager::PrintNow() {
+ if (!CreateNewPrintJob(NULL))
+ return false;
+
+ // Retrieve default settings. PrintJob will send back a NOTIFY_PRINT_JOB_EVENT
+ // with either DEFAULT_INIT_DONE or FAILED. On failure, simply back off.
+ // Otherwise, call PrintNowInternal() again to start the print job.
+ waiting_to_print_ = true;
+ print_job_->GetSettings(PrintJob::DEFAULTS, NULL);
+ return true;
+}
+
+bool PrintViewManager::OnRendererGone(RenderViewHost* render_view_host) {
+ if (!print_job_.get())
+ return true;
+
+ if (render_view_host != owner_.render_view_host())
+ return false;
+
+ scoped_refptr<PrintedDocument> document(print_job_->document());
+ if (document) {
+ // If IsComplete() returns false, the document isn't completely renderered.
+ // Since our renderer is gone, there's nothing to do, cancel it. Otherwise,
+ // the print job may finish without problem.
+ TerminatePrintJob(!document->IsComplete());
+ }
+ return true;
+}
+
+void PrintViewManager::DidGetPrintedPagesCount(int cookie, int number_pages) {
+ DCHECK_GT(cookie, 0);
+ if (!OpportunisticallyCreatePrintJob(cookie))
+ return;
+
+ PrintedDocument* document = print_job_->document();
+ if (!document || cookie != document->cookie()) {
+ // Out of sync. It may happens since we are completely asynchronous. Old
+ // spurious message can happen if one of the processes is overloaded.
+ return;
+ }
+
+ // Time to inform our print job. Make sure it is for the right document.
+ if (!document->page_count()) {
+ document->set_page_count(number_pages);
+ if (waiting_to_show_print_dialog_) {
+ // Ask for user settings. There's a timing issue since we may not have
+ // received the INIT_DONE notification yet. If so, the dialog will be
+ // shown in Observe() since the page count arrived before the settings.
+ print_job_->GetSettings(PrintJob::ASK_USER,
+ ::GetParent(owner_.GetContainerHWND()));
+ waiting_to_show_print_dialog_ = false;
+ }
+ }
+}
+
+void PrintViewManager::DidPrintPage(
+ const ViewHostMsg_DidPrintPage_Params& params) {
+ DCHECK(!inside_inner_message_loop_);
+ if (!OpportunisticallyCreatePrintJob(params.document_cookie))
+ return;
+
+ PrintedDocument* document = print_job_->document();
+ if (!document || params.document_cookie != document->cookie()) {
+ // Out of sync. It may happens since we are completely asynchronous. Old
+ // spurious message can happen if one of the processes is overloaded.
+ return;
+ }
+
+ // http://msdn2.microsoft.com/en-us/library/ms535522.aspx
+ // Windows 2000/XP: When a page in a spooled file exceeds approximately 350
+ // MB, it can fail to print and not send an error message.
+ if (params.data_size && params.data_size >= 350*1024*1024) {
+ NOTREACHED() << "size:" << params.data_size;
+ TerminatePrintJob(true);
+ owner_.Stop();
+ return;
+ }
+
+ SharedMemory shared_buf(params.emf_data_handle, true);
+ if (!shared_buf.Map(params.data_size)) {
+ NOTREACHED() << "couldn't map";
+ owner_.Stop();
+ return;
+ }
+
+ gfx::Emf* emf = new gfx::Emf;
+ if (!emf->CreateFromData(shared_buf.memory(), params.data_size)) {
+ NOTREACHED() << "Invalid EMF header";
+ delete emf;
+ owner_.Stop();
+ return;
+ }
+
+ // Update the rendered document. It will send notifications to the listener.
+ document->SetPage(params.page_number, emf, params.actual_shrink);
+ ShouldQuitFromInnerMessageLoop();
+}
+
+void PrintViewManager::RenderOnePrintedPage(PrintedDocument* document,
+ int page_number) {
+ // Currently a no-op. Rationale: printing is now completely synchronous and is
+ // handled by PrintAllPages. The reason is that PrintPreview is not used
+ // anymore and to make sure to not corrupt the screen, the whole generation is
+ // done synchronously. To make this work completely asynchronously, a
+ // duplicate copy of RenderView must be made to have an "innert" web page.
+ // Once this object is created, we'll have all the leasure to do whatever we
+ // want.
+}
+
+std::wstring PrintViewManager::RenderSourceName() {
+ std::wstring name(owner_.GetTitle());
+ if (name.empty())
+ name = l10n_util::GetString(IDS_DEFAULT_PRINT_DOCUMENT_TITLE);
+ return name;
+}
+
+GURL PrintViewManager::RenderSourceUrl() {
+ NavigationEntry* entry = owner_.controller()->GetActiveEntry();
+ if (entry)
+ return entry->GetDisplayURL();
+ else
+ return GURL();
+}
+
+void PrintViewManager::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFY_PRINT_JOB_EVENT: {
+ OnNotifyPrintJobEvent(*Details<JobEventDetails>(details).ptr());
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+void PrintViewManager::OnNotifyPrintJobEvent(
+ const JobEventDetails& event_details) {
+ switch (event_details.type()) {
+ case JobEventDetails::FAILED: {
+ // TODO(maruel): bug 1123882 Show some kind of notification.
+ TerminatePrintJob(true);
+ break;
+ }
+ case JobEventDetails::USER_INIT_DONE:
+ case JobEventDetails::DEFAULT_INIT_DONE:
+ case JobEventDetails::USER_INIT_CANCELED: {
+ OnNotifyPrintJobInitEvent(event_details);
+ break;
+ }
+ case JobEventDetails::ALL_PAGES_REQUESTED: {
+ ShouldQuitFromInnerMessageLoop();
+ break;
+ }
+ case JobEventDetails::NEW_DOC:
+ case JobEventDetails::NEW_PAGE:
+ case JobEventDetails::PAGE_DONE: {
+ // Don't care about the actual printing process.
+ break;
+ }
+ case JobEventDetails::DOC_DONE: {
+ waiting_to_print_ = false;
+ break;
+ }
+ case JobEventDetails::JOB_DONE: {
+ // Printing is done, we don't need it anymore.
+ // print_job_->is_job_pending() may still be true, depending on the order
+ // of object registration.
+ ReleasePrintJob();
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+void PrintViewManager::OnNotifyPrintJobInitEvent(
+ const JobEventDetails& event_details) {
+ ViewMsg_Print_Params old_print_params(print_params_);
+
+ // Backup the print settings relevant to the renderer.
+ DCHECK_EQ(print_job_->document(), event_details.document());
+ event_details.document()->settings().RenderParams(&print_params_);
+ print_params_.document_cookie = event_details.document()->cookie();
+ DCHECK_GT(print_params_.document_cookie, 0);
+
+ // If settings changed
+ DCHECK(owner_.render_view_host());
+ // Equals() doesn't compare the cookie value.
+ if (owner_.render_view_host() &&
+ owner_.render_view_host()->IsRenderViewLive() &&
+ (!old_print_params.Equals(print_params_) ||
+ !event_details.document()->page_count())) {
+ // This will generate a DidGetPrintedPagesCount() callback.
+ if (!owner_.render_view_host()->GetPrintedPagesCount(print_params_)) {
+ NOTREACHED();
+ if (inside_inner_message_loop_) {
+ MessageLoop::current()->Quit();
+ return;
+ }
+ }
+ }
+
+ // Continue even if owner_.render_view_host() is dead because we may already
+ // have buffered all the necessary pages.
+ switch (event_details.type()) {
+ case JobEventDetails::USER_INIT_DONE: {
+ // The user clicked the "Print" button in the Print... dialog.
+ // Time to print.
+ DCHECK_EQ(waiting_to_print_, false);
+ DCHECK_EQ(waiting_to_show_print_dialog_, false);
+ waiting_to_print_ = true;
+ PrintNowInternal();
+ break;
+ }
+ case JobEventDetails::USER_INIT_CANCELED: {
+ DCHECK(!waiting_to_show_print_dialog_);
+ // The print dialog box has been dismissed (Cancel button or the X).
+ TerminatePrintJob(false);
+ break;
+ }
+ case JobEventDetails::DEFAULT_INIT_DONE: {
+ // Init(false) returned.
+ if (waiting_to_print_) {
+ // PrintNow() is pending.
+ DCHECK_EQ(waiting_to_show_print_dialog_, false);
+ PrintNowInternal();
+ } else if (waiting_to_show_print_dialog_ &&
+ event_details.document()->page_count()) {
+ // Time to ask the user for the print settings.
+ print_job_->GetSettings(PrintJob::ASK_USER,
+ ::GetParent(owner_.GetContainerHWND()));
+ waiting_to_show_print_dialog_ = false;
+ } else {
+ // event_details.document()->page_count() is false. This simply means
+ // that the renderer was slower to calculate the number of pages than
+ // the print_job_ to initialize the default settings. If so, the dialog
+ // will be shown in DidGetPrintedPagesCount() since the settings arrived
+ // before the page count.
+ DCHECK_EQ(waiting_to_show_print_dialog_, true);
+ }
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+bool PrintViewManager::RenderAllMissingPagesNow() {
+ if (waiting_to_show_print_dialog_) {
+ // TODO(maruel): http://b/1186708 This happens in one of these case:
+ // - javascript:window.print();window.close(); which closes the window very
+ // fast.
+ // - The worker thread is hung, like the network printer failed, the network
+ // print server failed or the network cable is disconnected.
+ // In the first case we want to wait, but not on the second case.
+
+ if (!RunInnerMessageLoop()) {
+ // This function is always called from DisconnectFromCurrentPrintJob()
+ // so we know that the job will be stopped/canceled in any case.
+ return false;
+ }
+ }
+
+ if (!print_job_.get() || !print_job_->is_job_pending()) {
+ DCHECK_EQ(waiting_to_print_, false);
+ return false;
+ }
+
+ // We can't print if there is no renderer.
+ if (!owner_.render_view_host() ||
+ !owner_.render_view_host()->IsRenderViewLive()) {
+ waiting_to_print_ = false;
+ return false;
+ }
+
+ // Is the document already complete?
+ if (print_job_->document() && print_job_->document()->IsComplete()) {
+ waiting_to_print_ = false;
+ return true;
+ }
+
+ // WebContents is either dying or a second consecutive request to print
+ // happened before the first had time to finish. We need to render all the
+ // pages in an hurry if a print_job_ is still pending. No need to wait for it
+ // to actually spool the pages, only to have the renderer generate them. Run
+ // a message loop until we get our signal that the print job is satisfied.
+ // PrintJob will send a ALL_PAGES_REQUESTED after having received all the
+ // pages it needs. MessageLoop::current()->Quit() will be called as soon as
+ // print_job_->document()->IsComplete() is true on either ALL_PAGES_REQUESTED
+ // or in DidPrintPage(). The check is done in
+ // ShouldQuitFromInnerMessageLoop().
+ // BLOCKS until all the pages are received. (Need to enable recursive task)
+ if (!RunInnerMessageLoop()) {
+ // This function is always called from DisconnectFromCurrentPrintJob() so we
+ // know that the job will be stopped/canceled in any case.
+ return false;
+ }
+ return true;
+}
+
+void PrintViewManager::ShouldQuitFromInnerMessageLoop() {
+ // Look at the reason.
+ DCHECK(print_job_->document());
+ if (print_job_->document() &&
+ print_job_->document()->IsComplete() &&
+ inside_inner_message_loop_) {
+ // We are in a message loop created by RenderAllMissingPagesNow. Quit from
+ // it.
+ MessageLoop::current()->Quit();
+ inside_inner_message_loop_ = false;
+ waiting_to_print_ = false;
+ }
+}
+
+bool PrintViewManager::CreateNewPrintJob(PrintJobWorkerOwner* job) {
+ DCHECK(!inside_inner_message_loop_);
+ if (waiting_to_print_ || waiting_to_show_print_dialog_) {
+ // We can't help; we are waiting for a print job initialization. The user is
+ // button bashing. The only thing we could do is to batch up the requests.
+ return false;
+ }
+
+ // Disconnect the current print_job_.
+ DisconnectFromCurrentPrintJob();
+
+ // We can't print if there is no renderer.
+ if (!owner_.render_view_host() ||
+ !owner_.render_view_host()->IsRenderViewLive()) {
+ return false;
+ }
+
+ // Ask the renderer to generate the print preview, create the print preview
+ // view and switch to it, initialize the printer and show the print dialog.
+ DCHECK(!print_job_.get());
+ if (job) {
+ print_job_ = new PrintJob();
+ print_job_->Initialize(job, this);
+ } else {
+ print_job_ = new PrintJob(this);
+ }
+ NotificationService::current()->
+ AddObserver(this,
+ NOTIFY_PRINT_JOB_EVENT,
+ Source<PrintJob>(print_job_.get()));
+ return true;
+}
+
+void PrintViewManager::DisconnectFromCurrentPrintJob() {
+ // Make sure all the necessary rendered page are done. Don't bother with the
+ // return value.
+ bool result = RenderAllMissingPagesNow();
+
+ // Verify that assertion.
+ if (print_job_.get() &&
+ print_job_->document() &&
+ !print_job_->document()->IsComplete()) {
+ DCHECK(!result);
+ // That failed.
+ TerminatePrintJob(true);
+ } else {
+ // DO NOT wait for the job to finish.
+ ReleasePrintJob();
+ }
+}
+
+void PrintViewManager::TerminatePrintJob(bool cancel) {
+ if (!print_job_.get())
+ return;
+
+ if (cancel) {
+ // We don't need the EMF data anymore because the printing is canceled.
+ print_job_->Cancel();
+ waiting_to_print_ = false;
+ waiting_to_show_print_dialog_ = false;
+ inside_inner_message_loop_ = false;
+ } else {
+ DCHECK(!inside_inner_message_loop_);
+ DCHECK(!waiting_to_show_print_dialog_);
+ DCHECK(!print_job_->document() || print_job_->document()->IsComplete() ||
+ !waiting_to_print_);
+
+ // WebContents is either dying or navigating elsewhere. We need to render
+ // all the pages in an hurry if a print job is still pending. This does the
+ // trick since it runs a blocking message loop:
+ print_job_->Stop();
+ }
+ ReleasePrintJob();
+}
+
+void PrintViewManager::ReleasePrintJob() {
+ DCHECK_EQ(waiting_to_print_, false);
+ if (!print_job_.get())
+ return;
+ NotificationService::current()->RemoveObserver(
+ this,
+ NOTIFY_PRINT_JOB_EVENT,
+ Source<PrintJob>(print_job_.get()));
+
+ print_job_->DisconnectSource();
+ // Don't close the worker thread.
+ print_job_ = NULL;
+ memset(&print_params_, 0, sizeof(print_params_));
+}
+
+void PrintViewManager::PrintNowInternal() {
+ DCHECK(waiting_to_print_);
+
+ // Settings are already loaded. Go ahead. This will set
+ // print_job_->is_job_pending() to true.
+ print_job_->StartPrinting();
+
+ if (!print_job_->document() ||
+ !print_job_->document()->IsComplete()) {
+ ViewMsg_PrintPages_Params params;
+ params.params = print_params_;
+ params.pages = PageRange::GetPages(print_job_->settings().ranges);
+ owner_.render_view_host()->PrintPages(params);
+ }
+}
+
+bool PrintViewManager::RunInnerMessageLoop() {
+ // This value may actually be too low:
+ //
+ // - If we're looping because of printer settings initializaton, the premise
+ // here is that some poor users have their print server away on a VPN over
+ // dialup. In this situation, the simple fact of opening the printer can be
+ // dead slow. On the other side, we don't want to die infinitely for a real
+ // network error. Give the printer 60 seconds to comply.
+ //
+ // - If we're looping because of renderer page generation, the renderer could
+ // be cpu bound, the page overly complex/large or the system just
+ // memory-bound.
+ static const int kPrinterSettingsTimeout = 60000;
+ MessageLoop::QuitTask timeout_task;
+ Timer* timeout = MessageLoop::current()->timer_manager()->StartTimer(
+ kPrinterSettingsTimeout,
+ &timeout_task,
+ false);
+ inside_inner_message_loop_ = true;
+ // Need to enable recursive task.
+ bool old_state = MessageLoop::current()->NestableTasksAllowed();
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ // Restore task state.
+ MessageLoop::current()->SetNestableTasksAllowed(old_state);
+
+ bool success = true;
+ if (inside_inner_message_loop_) {
+ // Ok we timed out. That's sad.
+ inside_inner_message_loop_ = false;
+ success = false;
+ }
+
+ if (timeout) {
+ MessageLoop::current()->timer_manager()->StopTimer(timeout);
+ delete timeout;
+ timeout = NULL;
+ }
+ return success;
+}
+
+bool PrintViewManager::OpportunisticallyCreatePrintJob(int cookie) {
+ if (print_job_.get())
+ return true;
+
+ if (!cookie) {
+ // Out of sync. It may happens since we are completely asynchronous. Old
+ // spurious message can happen if one of the processes is overloaded.
+ return false;
+ }
+
+ // The job was initiated by a script. Time to get the corresponding worker
+ // thread.
+ scoped_refptr<PrinterQuery> queued_query;
+ g_browser_process->print_job_manager()->PopPrinterQuery(cookie,
+ &queued_query);
+ DCHECK(queued_query.get());
+ if (!queued_query.get())
+ return false;
+
+ if (!CreateNewPrintJob(queued_query.get())) {
+ // Don't kill anything.
+ return false;
+ }
+
+ // Settings are already loaded. Go ahead. This will set
+ // print_job_->is_job_pending() to true.
+ print_job_->StartPrinting();
+ return true;
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/print_view_manager.h b/chrome/browser/printing/print_view_manager.h
new file mode 100644
index 0000000..b0a154e
--- /dev/null
+++ b/chrome/browser/printing/print_view_manager.h
@@ -0,0 +1,192 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_H__
+#define CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_H__
+
+#include "chrome/browser/printing/printed_pages_source.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/render_messages.h"
+
+class RenderViewHost;
+class WebContents;
+
+namespace printing {
+
+class JobEventDetails;
+class PrintJob;
+class PrintJobWorkerOwner;
+
+// Manages the print commands in relation to a WebContents. WebContents
+// delegates a few printing related commands to this instance.
+class PrintViewManager : public NotificationObserver,
+ public PrintedPagesSource {
+ public:
+ PrintViewManager(WebContents& owner);
+
+ // Destroys the "Print..." dialog, makes sure the pages are finished rendering
+ // and release the print job.
+ void Destroy();
+
+ // Cancels the print job.
+ void Stop();
+
+ // Shows the "Print..." dialog if none is shown and if no rendering is
+ // pending. This is done asynchronously.
+ void ShowPrintDialog();
+
+ // Initiates a print job immediately. This is done asynchronously. Returns
+ // false if printing is impossible at the moment.
+ bool PrintNow();
+
+ // Terminates or cancels the print job if one was pending, depending on the
+ // current state. Returns false if the renderer was not valuable.
+ bool OnRendererGone(RenderViewHost* render_view_host);
+
+ // Received a notification from the renderer that the number of printed page
+ // has just been calculated..
+ void DidGetPrintedPagesCount(int cookie, int number_pages);
+
+ // Received a notification from the renderer that a printed page page is
+ // finished renderering.
+ void DidPrintPage(const ViewHostMsg_DidPrintPage_Params& params);
+
+ // PrintedPagesSource implementation.
+ virtual void RenderOnePrintedPage(PrintedDocument* document, int page_number);
+ virtual std::wstring RenderSourceName();
+ virtual GURL RenderSourceUrl();
+
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ // Processes a NOTIFY_PRINT_JOB_EVENT notification.
+ void OnNotifyPrintJobEvent(const JobEventDetails& event_details);
+
+ // Processes a xxx_INIT_xxx type of NOTIFY_PRINT_JOB_EVENT notification.
+ void OnNotifyPrintJobInitEvent(const JobEventDetails& event_details);
+
+ // Requests the RenderView to render all the missing pages for the print job.
+ // Noop if no print job is pending. Returns true if at least one page has been
+ // requested to the renderer.
+ bool RenderAllMissingPagesNow();
+
+ // Quits the current message loop if these conditions hold true: a document is
+ // loaded and is complete and waiting_for_pages_to_be_rendered_ is true. This
+ // function is called in DidPrintPage() or on ALL_PAGES_REQUESTED
+ // notification. The inner message loop is created was created by
+ // RenderAllMissingPagesNow().
+ void ShouldQuitFromInnerMessageLoop();
+
+ // Creates a new empty print job. It has no settings loaded. If there is
+ // currently a print job, safely disconnect from it. Returns false if it is
+ // impossible to safely disconnect from the current print job or it is
+ // impossible to create a new print job.
+ bool CreateNewPrintJob(PrintJobWorkerOwner* job);
+
+ // Makes sure the current print_job_ has all its data before continuing, and
+ // disconnect from it.
+ void DisconnectFromCurrentPrintJob();
+
+ // Terminates the print job. Noop if no print job has been created. If
+ // |cancel| is true, cancel it instead of waiting for the job to finish. Will
+ // call ReleasePrintJob().
+ void TerminatePrintJob(bool cancel);
+
+ // Releases print_job_. Correctly deregisters from notifications. Noop if
+ // no print job has been created.
+ void ReleasePrintJob();
+
+ // Prints the document. Starts the actual print job. Requests asynchronously
+ // the renderered pages from the renderer. Is called once the printing context
+ // is initialized, on a DEFAULT_INIT_DONE notification when waiting_to_print_
+ // is true.
+ void PrintNowInternal();
+
+ // Runs an inner message loop. It will set inside_inner_message_loop_ to true
+ // while the blocking inner message loop is running. This is useful in cases
+ // where the RenderView is about to be destroyed while a printing job isn't
+ // finished.
+ bool RunInnerMessageLoop();
+
+ // In the case of Scripted Printing, where the renderer is controlling the
+ // control flow, print_job_ is initialized whenever possible. No-op is
+ // print_job_ is initialized.
+ bool OpportunisticallyCreatePrintJob(int cookie);
+
+ // Cache the last print settings requested to the renderer.
+ ViewMsg_Print_Params print_params_;
+
+ // Manages the low-level talk to the printer.
+ scoped_refptr<PrintJob> print_job_;
+
+ // Waiting for print_job_ initialization to be completed to start printing.
+ // Specifically the DEFAULT_INIT_DONE notification. Set when PrintNow() is
+ // called.
+ bool waiting_to_print_;
+
+ // Running an inner message loop inside RenderAllMissingPagesNow(). This means
+ // we are _blocking_ until all the necessary pages have been rendered or the
+ // print settings are being loaded.
+ bool inside_inner_message_loop_;
+
+ // The object is waiting for some information to call print_job_->Init(true).
+ // It is either a DEFAULT_INIT_DONE notification or the
+ // DidGetPrintedPagesCount() callback.
+ // Showing the Print... dialog box is a multi-step operation:
+ // - print_job_->Init(false) to get the default settings. Set
+ // waiting_to_show_print_dialog_ = true.
+ // - on DEFAULT_INIT_DONE, gathers new settings.
+ // - did settings() or document() change since the last intialization?
+ // - Call SwitchCssToPrintMediaType()
+ // - On DidGetPrintedPagesCount() call, if
+ // waiting_to_show_print_dialog_ = true
+ // - calls print_job_->Init(true).
+ // - waiting_to_show_print_dialog_ = false.
+ // - DONE.
+ // - else (It may happens when redisplaying the dialog box with settings that
+ // haven't changed)
+ // - if waiting_to_show_print_dialog_ = true && page_count is initialized.
+ // - calls print_job_->Init(true).
+ // - waiting_to_show_print_dialog_ = false.
+ bool waiting_to_show_print_dialog_;
+
+ // PrintViewManager is created as an extension of WebContent specialized for
+ // printing-related behavior. Still, access to the renderer is needed so a
+ // back reference is kept the the "parent object".
+ WebContents& owner_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintViewManager);
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_H__
diff --git a/chrome/browser/printing/printed_document.cc b/chrome/browser/printing/printed_document.cc
new file mode 100644
index 0000000..fb4d13a
--- /dev/null
+++ b/chrome/browser/printing/printed_document.cc
@@ -0,0 +1,401 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/printed_document.h"
+
+#include <set>
+
+#include "base/gfx/platform_device.h"
+#include "base/message_loop.h"
+#include "base/time.h"
+#include "chrome/browser/printing/page_number.h"
+#include "chrome/browser/printing/page_overlays.h"
+#include "chrome/browser/printing/printed_pages_source.h"
+#include "chrome/browser/printing/printed_page.h"
+#include "chrome/browser/printing/units.h"
+#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/common/gfx/emf.h"
+#include "chrome/common/gfx/url_elider.h"
+#include "chrome/common/time_format.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/win_util.h"
+
+namespace printing {
+
+PrintedDocument::PrintedDocument(const PrintSettings& settings,
+ PrintedPagesSource* source,
+ int cookie)
+ : mutable_(source),
+ immutable_(settings, source, cookie) {
+
+ // Records the expected page count if a range is setup.
+ if (!settings.ranges.empty()) {
+ // If there is a range, set the number of page
+ for (unsigned i = 0; i < settings.ranges.size(); ++i) {
+ const PageRange& range = settings.ranges[i];
+ mutable_.expected_page_count_ += range.to - range.from + 1;
+ }
+ }
+}
+
+PrintedDocument::~PrintedDocument() {
+}
+
+void PrintedDocument::SetPage(int page_number, gfx::Emf* emf, double shrink) {
+ // Notice the page_number + 1, the reason is that this is the value that will
+ // be shown. Users dislike 0-based counting.
+ scoped_refptr<PrintedPage> page(
+ new PrintedPage(page_number + 1,
+ emf, immutable_.settings_.page_setup_pixels().physical_size()));
+ {
+ AutoLock lock(lock_);
+ mutable_.pages_[page_number] = page;
+ if (mutable_.shrink_factor == 0) {
+ mutable_.shrink_factor = shrink;
+ } else {
+ DCHECK_EQ(mutable_.shrink_factor, shrink);
+ }
+ }
+ NotificationService::current()->Notify(
+ NOTIFY_PRINTED_DOCUMENT_UPDATED,
+ Source<PrintedDocument>(this),
+ Details<PrintedPage>(page));
+}
+
+bool PrintedDocument::GetPage(int page_number,
+ scoped_refptr<PrintedPage>* page) {
+ bool request = false;
+ {
+ AutoLock lock(lock_);
+ PrintedPages::const_iterator itr = mutable_.pages_.find(page_number);
+ if (itr != mutable_.pages_.end()) {
+ if (itr->second.get()) {
+ *page = itr->second;
+ return true;
+ }
+ request = false;
+ } else {
+ request = true;
+ // Force the creation to not repeatedly request the same page.
+ mutable_.pages_[page_number];
+ }
+ }
+ if (request) {
+ PrintPage_ThreadJump(page_number);
+ }
+ return false;
+}
+
+void PrintedDocument::RenderPrintedPage(const PrintedPage& page,
+ HDC context) const {
+#ifdef _DEBUG
+ {
+ // Make sure the page is from our list.
+ AutoLock lock(lock_);
+ DCHECK(&page == mutable_.pages_.find(page.page_number() - 1)->second.get());
+ }
+#endif
+
+ // Save the state to make sure the context this function call does not modify
+ // the device context.
+ int saved_state = SaveDC(context);
+ DCHECK_NE(saved_state, 0);
+ gfx::PlatformDevice::InitializeDC(context);
+ {
+ // Save the state (again) to apply the necessary world transformation.
+ int saved_state = SaveDC(context);
+ DCHECK_NE(saved_state, 0);
+
+ // Setup the matrix to translate and scale to the right place. Take in
+ // account the actual shrinking factor.
+ XFORM xform = { 0 };
+ xform.eDx = static_cast<float>(
+ immutable_.settings_.page_setup_pixels().content_area().x());
+ xform.eDy = static_cast<float>(
+ immutable_.settings_.page_setup_pixels().content_area().y());
+ xform.eM11 = static_cast<float>(1. / mutable_.shrink_factor);
+ xform.eM22 = static_cast<float>(1. / mutable_.shrink_factor);
+ BOOL res = ModifyWorldTransform(context, &xform, MWT_LEFTMULTIPLY);
+ DCHECK_NE(res, 0);
+
+ if (!page.emf()->SafePlayback(context)) {
+ NOTREACHED();
+ }
+
+ res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+ }
+
+ // Print the header and footer.
+ int base_font_size = ChromeFont().height();
+ int new_font_size = ConvertUnit(10, 72, immutable_.settings_.dpi());
+ DCHECK_GT(new_font_size, base_font_size);
+ ChromeFont font(ChromeFont().DeriveFont(new_font_size - base_font_size));
+ HGDIOBJ old_font = SelectObject(context, font.hfont());
+ DCHECK(old_font != NULL);
+ // We don't want a white square around the text ever if overflowing.
+ SetBkMode(context, TRANSPARENT);
+ PrintHeaderFooter(context, page, PageOverlays::LEFT, PageOverlays::TOP,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::CENTER, PageOverlays::TOP,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::RIGHT, PageOverlays::TOP,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::LEFT, PageOverlays::BOTTOM,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::CENTER, PageOverlays::BOTTOM,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::RIGHT, PageOverlays::BOTTOM,
+ font);
+ int res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+}
+
+bool PrintedDocument::RenderPrintedPageNumber(int page_number, HDC context) {
+ scoped_refptr<PrintedPage> page;
+ if (!GetPage(page_number, &page))
+ return false;
+ RenderPrintedPage(*page.get(), context);
+ return true;
+}
+
+bool PrintedDocument::IsComplete() const {
+ AutoLock lock(lock_);
+ if (!mutable_.page_count_)
+ return false;
+ PageNumber page(immutable_.settings_, mutable_.page_count_);
+ if (page == PageNumber::npos())
+ return false;
+ for (; page != PageNumber::npos(); ++page) {
+ PrintedPages::const_iterator itr = mutable_.pages_.find(page.ToInt());
+ if (itr == mutable_.pages_.end() || !itr->second.get() ||
+ !itr->second->emf())
+ return false;
+ }
+ return true;
+}
+
+bool PrintedDocument::RequestMissingPages() {
+ typedef std::set<int> PageNumbers;
+ PageNumbers missing_pages;
+ {
+ AutoLock lock(lock_);
+ PageNumber page(immutable_.settings_, mutable_.page_count_);
+ if (page == PageNumber::npos())
+ return false;
+ for (; page != PageNumber::npos(); ++page) {
+ PrintedPage* printed_page = mutable_.pages_[page.ToInt()].get();
+ if (!printed_page || !printed_page->emf())
+ missing_pages.insert(page.ToInt());
+ }
+ }
+ if (!missing_pages.size())
+ return true;
+ PageNumbers::const_iterator end = missing_pages.end();
+ for (PageNumbers::const_iterator itr = missing_pages.begin();
+ itr != end;
+ ++itr) {
+ int page_number = *itr;
+ PrintPage_ThreadJump(page_number);
+ }
+ return true;
+}
+
+void PrintedDocument::DisconnectSource() {
+ AutoLock lock(lock_);
+ mutable_.source_ = NULL;
+}
+
+size_t PrintedDocument::MemoryUsage() const {
+ std::vector<scoped_refptr<PrintedPage>> pages_copy;
+ {
+ AutoLock lock(lock_);
+ pages_copy.reserve(mutable_.pages_.size());
+ PrintedPages::const_iterator end = mutable_.pages_.end();
+ for (PrintedPages::const_iterator itr = mutable_.pages_.begin();
+ itr != end; ++itr) {
+ if (itr->second.get()) {
+ pages_copy.push_back(itr->second);
+ }
+ }
+ }
+ size_t total = 0;
+ for (size_t i = 0; i < pages_copy.size(); ++i) {
+ total += pages_copy[i]->emf()->GetDataSize();
+ }
+ return total;
+}
+
+void PrintedDocument::set_page_count(int max_page) {
+ {
+ AutoLock lock(lock_);
+ DCHECK_EQ(0, mutable_.page_count_);
+ mutable_.page_count_ = max_page;
+ if (immutable_.settings_.ranges.empty()) {
+ mutable_.expected_page_count_ = max_page;
+ } else {
+ // If there is a range, don't bother since expected_page_count_ is already
+ // initialized.
+ DCHECK_NE(mutable_.expected_page_count_, 0);
+ }
+ }
+ NotificationService::current()->Notify(
+ NOTIFY_PRINTED_DOCUMENT_UPDATED,
+ Source<PrintedDocument>(this),
+ NotificationService::NoDetails());
+}
+
+int PrintedDocument::page_count() const {
+ AutoLock lock(lock_);
+ return mutable_.page_count_;
+}
+
+int PrintedDocument::expected_page_count() const {
+ AutoLock lock(lock_);
+ return mutable_.expected_page_count_;
+}
+
+void PrintedDocument::PrintHeaderFooter(HDC context,
+ const PrintedPage& page,
+ PageOverlays::HorizontalPosition x,
+ PageOverlays::VerticalPosition y,
+ const ChromeFont& font) const {
+ const PrintSettings& settings = immutable_.settings_;
+ const std::wstring& line = settings.overlays.GetOverlay(x, y);
+ if (line.empty()) {
+ return;
+ }
+ std::wstring output(PageOverlays::ReplaceVariables(line, *this, page));
+ if (output.empty()) {
+ // May happens if document name or url is empty.
+ return;
+ }
+ const gfx::Size string_size(font.GetStringWidth(output), font.height());
+ gfx::Rect bounding;
+ bounding.set_height(string_size.height());
+ const gfx::Rect& overlay_area(settings.page_setup_pixels().overlay_area());
+ // Hard code .25 cm interstice between overlays. Make sure that some space is
+ // kept between each headers.
+ const int interstice = ConvertUnit(250, kHundrethsMMPerInch, settings.dpi());
+ const int max_width = overlay_area.width() / 3 - interstice;
+ const int actual_width = std::min(string_size.width(), max_width);
+ switch (x) {
+ case PageOverlays::LEFT:
+ bounding.set_x(overlay_area.x());
+ bounding.set_width(max_width);
+ break;
+ case PageOverlays::CENTER:
+ bounding.set_x(overlay_area.x() +
+ (overlay_area.width() - actual_width) / 2);
+ bounding.set_width(actual_width);
+ break;
+ case PageOverlays::RIGHT:
+ bounding.set_x(overlay_area.right() - actual_width);
+ bounding.set_width(actual_width);
+ break;
+ }
+
+ DCHECK_LE(bounding.right(), overlay_area.right());
+
+ switch (y) {
+ case PageOverlays::BOTTOM:
+ bounding.set_y(overlay_area.bottom() - string_size.height());
+ break;
+ case PageOverlays::TOP:
+ bounding.set_y(overlay_area.y());
+ break;
+ }
+
+ if (string_size.width() > bounding.width())
+ output = gfx::ElideText(output, font, bounding.width());
+
+ // Save the state (again) for the clipping region.
+ int saved_state = SaveDC(context);
+ DCHECK_NE(saved_state, 0);
+
+ int result = IntersectClipRect(context, bounding.x(), bounding.y(),
+ bounding.right() + 1, bounding.bottom() + 1);
+ DCHECK(result == SIMPLEREGION || result == COMPLEXREGION);
+ TextOut(context,
+ bounding.x(), bounding.y(),
+ output.c_str(),
+ static_cast<int>(output.size()));
+ int res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+}
+
+void PrintedDocument::PrintPage_ThreadJump(int page_number) {
+ if (MessageLoop::current() != immutable_.source_message_loop_) {
+ immutable_.source_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &PrintedDocument::PrintPage_ThreadJump, page_number));
+ } else {
+ PrintedPagesSource* source = NULL;
+ {
+ AutoLock lock(lock_);
+ source = mutable_.source_;
+ }
+ if (source) {
+ // Don't render with the lock held.
+ source->RenderOnePrintedPage(this, page_number);
+ } else {
+ // Printing has probably been canceled already.
+ }
+ }
+}
+
+PrintedDocument::Mutable::Mutable(PrintedPagesSource* source)
+ : source_(source),
+ expected_page_count_(0),
+ page_count_(0),
+ shrink_factor(0) {
+}
+
+PrintedDocument::Immutable::Immutable(const PrintSettings& settings,
+ PrintedPagesSource* source,
+ int cookie)
+ : settings_(settings),
+ source_message_loop_(MessageLoop::current()),
+ name_(source->RenderSourceName()),
+ url_(source->RenderSourceUrl()),
+ cookie_(cookie) {
+ // Setup the document's date.
+#ifdef WIN32
+ // On Windows, use the native time formatting for printing.
+ SYSTEMTIME systemtime;
+ GetLocalTime(&systemtime);
+ date_ = win_util::FormatSystemDate(systemtime, std::wstring());
+ time_ = win_util::FormatSystemTime(systemtime, std::wstring());
+#else
+ Time now = Time::Now();
+ date_ = TimeFormat::ShortDateNumeric(now);
+ time_ = TimeFormat::TimeOfDay(now);
+#endif // WIN32
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/printed_document.h b/chrome/browser/printing/printed_document.h
new file mode 100644
index 0000000..43ed635
--- /dev/null
+++ b/chrome/browser/printing/printed_document.h
@@ -0,0 +1,220 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINTED_DOCUMENT_H__
+#define CHROME_BROWSER_PRINTING_PRINTED_DOCUMENT_H__
+
+#include <map>
+
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/printing/print_settings.h"
+#include "googleurl/src/gurl.h"
+
+class ChromeFont;
+class MessageLoop;
+
+namespace gfx {
+class Emf;
+}
+
+namespace printing {
+
+class PrintedPage;
+class PrintedPagesSource;
+
+// A collection of rendered pages. The settings are immuable. If the print
+// settings are changed, a new PrintedDocument must be created.
+// Warning: May be accessed from many threads at the same time. Only one thread
+// will have write access. Sensible functions are protected by a lock.
+// Warning: Once a page is loaded, it cannot be replaced. Pages may be discarded
+// under low memory conditions.
+class PrintedDocument : public base::RefCountedThreadSafe<PrintedDocument> {
+ public:
+ // The cookie shall be unique and has a specific relationship with its
+ // originating source and settings.
+ PrintedDocument(const PrintSettings& settings,
+ PrintedPagesSource* source,
+ int cookie);
+ ~PrintedDocument();
+
+ // Sets a page's data. 0-based. Takes emf ownership.
+ // Note: locks for a short amount of time.
+ void SetPage(int page_number, gfx::Emf* emf, double shrink);
+
+ // Retrieves a page. If the page is not available right now, it
+ // requests to have this page be rendered and returns false.
+ // Note: locks for a short amount of time.
+ bool GetPage(int page_number, scoped_refptr<PrintedPage>* page);
+
+ // Draws the page in the context.
+ // Note: locks for a short amount of time in debug only.
+ void RenderPrintedPage(const PrintedPage& page, HDC context) const;
+
+ // Draws the page in the context. If the page is not available right now, it
+ // requests to have this page be rendered and returns false.
+ // Note: locks for a short amount of time.
+ bool RenderPrintedPageNumber(int page_number, HDC context);
+
+ // Returns true if all the necessary pages for the settings are already
+ // rendered.
+ // Note: locks while parsing the whole tree.
+ bool IsComplete() const;
+
+ // Requests all the missing pages. Returns true if at least one page has been
+ // requested. Returns false if there was not enough information to request the
+ // missing pages, i.e. document_page_count_ is not initialized or no page has
+ // been requested.
+ // Note: locks while parsing the whole tree.
+ bool RequestMissingPages();
+
+ // Disconnects the PrintedPage source (PrintedPagesSource). It is done when
+ // the source is being destroyed.
+ void DisconnectSource();
+
+ // Retrieves the current memory usage of the renderer pages.
+ // Note: locks for a short amount of time.
+ size_t MemoryUsage() const;
+
+ // Sets the number of pages in the document to be rendered. Can only be set
+ // once.
+ // Note: locks for a short amount of time.
+ void set_page_count(int max_page);
+
+ // Number of pages in the document. Used for headers/footers.
+ // Note: locks for a short amount of time.
+ int page_count() const;
+
+ // Returns the number of expected pages to be rendered. It is a non-linear
+ // series if settings().ranges is not empty. It is the same value as
+ // document_page_count() otherwise.
+ // Note: locks for a short amount of time.
+ int expected_page_count() const;
+
+ // Getters. All these items are immuable hence thread-safe.
+ const PrintSettings& settings() const { return immutable_.settings_; }
+ const std::wstring& name() const {
+ return immutable_.name_;
+ }
+ const GURL& url() const { return immutable_.url_; }
+ const std::wstring& date() const { return immutable_.date_; }
+ const std::wstring& time() const { return immutable_.time_; }
+ const int cookie() const { return immutable_.cookie_; }
+
+ private:
+ // Array of EMF data for each print previewed page.
+ typedef std::map<int, scoped_refptr<PrintedPage>> PrintedPages;
+
+ // Contains all the mutable stuff. All this stuff MUST be accessed with the
+ // lock held.
+ struct Mutable {
+ Mutable(PrintedPagesSource* source);
+
+ // Source that generates the PrintedPage's (i.e. a WebContents). It will be
+ // set back to NULL if the source is deleted before this object.
+ PrintedPagesSource* source_;
+
+ // Contains the pages' representation. This is a collection of PrintedPage.
+ // Warning: Lock must be held when accessing this member.
+ PrintedPages pages_;
+
+ // Number of expected pages to be rendered.
+ // Warning: Lock must be held when accessing this member.
+ int expected_page_count_;
+
+ // The total number of pages in the document.
+ int page_count_;
+
+ // Shrink done in comparison to desired_dpi.
+ double shrink_factor;
+ };
+
+ // Contains all the immutable stuff. All this stuff can be accessed without
+ // any lock held. This is because it can't be changed after the object's
+ // construction.
+ struct Immutable {
+ Immutable(const PrintSettings& settings, PrintedPagesSource* source,
+ int cookie);
+
+ // Print settings used to generate this document. Immuable.
+ PrintSettings settings_;
+
+ // Native thread for the render source.
+ MessageLoop* source_message_loop_;
+
+ // Document name. Immuable.
+ std::wstring name_;
+
+ // URL that generated this document. Immuable.
+ GURL url_;
+
+ // The date on which this job started. Immuable.
+ std::wstring date_;
+
+ // The time at which this job started. Immuable.
+ std::wstring time_;
+
+ // Cookie to uniquely identify this document. It is used to make sure that a
+ // PrintedPage is correctly belonging to the PrintedDocument. Since
+ // PrintedPage generation is completely asynchronous, it could be easy to
+ // mess up and send the page to the wrong document. It can be viewed as a
+ // simpler hash of PrintSettings since a new document is made each time the
+ // print settings change.
+ int cookie_;
+ };
+
+ // Prints the headers and footers for one page in the specified context
+ // according to the current settings.
+ void PrintHeaderFooter(HDC context,
+ const PrintedPage& page,
+ PageOverlays::HorizontalPosition x,
+ PageOverlays::VerticalPosition y,
+ const ChromeFont& font) const;
+
+ // Calls the render source to render a page. Makes sure to execute the call in
+ // the right thread context.
+ void PrintPage_ThreadJump(int page_number);
+
+ // All writable data member access must be guarded by this lock. Needs to be
+ // mutable since it can be acquired from const member functions.
+ mutable Lock lock_;
+
+ // All the mutable members.
+ Mutable mutable_;
+
+ // All the immutable members.
+ const Immutable immutable_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintedDocument);
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PRINTED_DOCUMENT_H__
diff --git a/chrome/browser/printing/printed_page.cc b/chrome/browser/printing/printed_page.cc
new file mode 100644
index 0000000..588d973
--- /dev/null
+++ b/chrome/browser/printing/printed_page.cc
@@ -0,0 +1,51 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/printed_page.h"
+
+#include "chrome/common/gfx/emf.h"
+
+namespace printing {
+
+PrintedPage::PrintedPage(int page_number,
+ gfx::Emf* emf,
+ const gfx::Size& page_size)
+ : page_number_(page_number),
+ emf_(emf),
+ page_size_(page_size) {
+}
+
+PrintedPage::~PrintedPage() {
+}
+
+const gfx::Emf* PrintedPage::emf() const {
+ return emf_.get();
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/printed_page.h b/chrome/browser/printing/printed_page.h
new file mode 100644
index 0000000..b15a452
--- /dev/null
+++ b/chrome/browser/printing/printed_page.h
@@ -0,0 +1,78 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINTED_PAGE_H__
+#define CHROME_BROWSER_PRINTING_PRINTED_PAGE_H__
+
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+
+namespace gfx {
+class Emf;
+}
+
+namespace printing {
+
+// Contains the data to reproduce a printed page, either on screen or on
+// paper. Once created, this object is immuable. It has no reference to the
+// PrintedDocument containing this page.
+// Note: May be accessed from many threads at the same time. This is an non
+// issue since this object is immuable. The reason is that a page may be printed
+// and be displayed at the same time.
+class PrintedPage : public base::RefCountedThreadSafe<PrintedPage> {
+ public:
+ PrintedPage(int page_number,
+ gfx::Emf* emf,
+ const gfx::Size& page_size);
+ ~PrintedPage();
+
+ // Getters
+ int page_number() const { return page_number_; }
+ const gfx::Emf* emf() const;
+ const gfx::Size& page_size() const { return page_size_; }
+
+ private:
+ // Page number inside the printed document.
+ const int page_number_;
+
+ // Actual paint data.
+ const scoped_ptr<gfx::Emf> emf_;
+
+ // The physical page size. To support multiple page formats inside on print
+ // job.
+ const gfx::Size page_size_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintedPage);
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PRINTED_PAGE_H__
diff --git a/chrome/browser/printing/printed_pages_source.h b/chrome/browser/printing/printed_pages_source.h
new file mode 100644
index 0000000..6012519
--- /dev/null
+++ b/chrome/browser/printing/printed_pages_source.h
@@ -0,0 +1,59 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINTED_PAGES_SOURCE_H__
+#define CHROME_BROWSER_PRINTING_PRINTED_PAGES_SOURCE_H__
+
+#include <string>
+
+class GURL;
+class MessageLoop;
+
+namespace printing {
+
+class PrintedDocument;
+
+// Source of printed pages.
+class PrintedPagesSource {
+ public:
+ // Renders a printed page. It is not necessary to be synchronous. It must call
+ // document->SetPage() once the source is done rendering the requested page.
+ virtual void RenderOnePrintedPage(PrintedDocument* document,
+ int page_number) = 0;
+
+ // Returns the document title.
+ virtual std::wstring RenderSourceName() = 0;
+
+ // Returns the URL's source of the document if applicable.
+ virtual GURL RenderSourceUrl() = 0;
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PRINTED_PAGES_SOURCE_H__
diff --git a/chrome/browser/printing/printer_query.cc b/chrome/browser/printing/printer_query.cc
new file mode 100644
index 0000000..0e440b7
--- /dev/null
+++ b/chrome/browser/printing/printer_query.cc
@@ -0,0 +1,141 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/printer_query.h"
+
+#include "base/message_loop.h"
+#include "chrome/browser/printing/print_job_worker.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+namespace printing {
+
+PrinterQuery::PrinterQuery()
+ : ui_message_loop_(MessageLoop::current()),
+ worker_(new PrintJobWorker(this)),
+ is_print_dialog_box_shown_(false),
+ last_status_(PrintingContext::FAILED),
+ cookie_(PrintSettings::NewCookie()) {
+}
+
+PrinterQuery::~PrinterQuery() {
+ // The job should be finished (or at least canceled) when it is destroyed.
+ DCHECK(!is_print_dialog_box_shown_);
+ // If this fires, it is that this pending printer context has leaked.
+ DCHECK(!worker_.get());
+ if (callback_.get()) {
+ // Be sure to cancel it.
+ callback_->Cancel();
+ }
+ // It may get deleted in a different thread that the one that created it.
+ // That's fine so don't DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+}
+
+void PrinterQuery::GetSettingsDone(const PrintSettings& new_settings,
+ PrintingContext::Result result) {
+ is_print_dialog_box_shown_ = false;
+ last_status_ = result;
+ if (result != PrintingContext::FAILED) {
+ settings_ = new_settings;
+ cookie_ = PrintSettings::NewCookie();
+ } else {
+ // Failure.
+ cookie_ = 0;
+ }
+ if (callback_.get()) {
+ // This may cause reentrancy like to call StopWorker().
+ callback_->Run();
+ callback_.reset(NULL);
+ }
+}
+
+PrintJobWorker* PrinterQuery::DetachWorker(PrintJobWorkerOwner* new_owner) {
+ DCHECK(!callback_.get());
+ DCHECK(worker_.get());
+ if (!worker_.get())
+ return NULL;
+ worker_->SetNewOwner(new_owner);
+ return worker_.release();
+}
+
+void PrinterQuery::GetSettings(GetSettingsAskParam ask_user_for_settings,
+ HWND parent_window,
+ int expected_page_count,
+ CancelableTask* callback) {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!callback_.get());
+ DCHECK(worker_.get());
+ if (!worker_.get())
+ return;
+ // Lazy create the worker thread. There is one worker thread per print job.
+ if (!worker_->message_loop()) {
+ if (!worker_->Start()) {
+ if (callback) {
+ callback->Cancel();
+ delete callback;
+ }
+ NOTREACHED();
+ return;
+ }
+ }
+
+ callback_.reset(callback);
+ // Real work is done in PrintJobWorker::Init().
+ is_print_dialog_box_shown_ = ask_user_for_settings == ASK_USER;
+ worker_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(),
+ &PrintJobWorker::GetSettings,
+ is_print_dialog_box_shown_,
+ parent_window,
+ expected_page_count));
+}
+
+void PrinterQuery::StopWorker() {
+ if (worker_.get()) {
+ worker_->Stop();
+ worker_.reset();
+ }
+}
+
+bool PrinterQuery::is_print_dialog_box_shown() const {
+ return is_print_dialog_box_shown_;
+}
+
+bool PrinterQuery::is_callback_pending() const {
+ return callback_.get() != NULL;
+}
+
+bool PrinterQuery::is_valid() const {
+ return worker_.get() != NULL;
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/printer_query.h b/chrome/browser/printing/printer_query.h
new file mode 100644
index 0000000..9b1e50b
--- /dev/null
+++ b/chrome/browser/printing/printer_query.h
@@ -0,0 +1,127 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINTER_QUERY_H__
+#define CHROME_BROWSER_PRINTING_PRINTER_QUERY_H__
+
+#include "base/ref_counted.h"
+#include "chrome/browser/printing/print_job_worker_owner.h"
+
+class CancelableTask;
+class MessageLoop;
+class Thread;
+
+namespace printing {
+
+class PrintJobWorker;
+
+// Query the printer for settings.
+class PrinterQuery : public base::RefCountedThreadSafe<PrinterQuery>,
+ public PrintJobWorkerOwner {
+ public:
+ // GetSettings() UI parameter.
+ enum GetSettingsAskParam {
+ DEFAULTS,
+ ASK_USER,
+ };
+
+ PrinterQuery();
+ virtual ~PrinterQuery();
+
+ // PrintJobWorkerOwner
+ virtual void AddRef() {
+ return base::RefCountedThreadSafe<PrinterQuery>::AddRef();
+ }
+ virtual void Release() {
+ return base::RefCountedThreadSafe<PrinterQuery>::Release();
+ }
+ virtual void GetSettingsDone(const PrintSettings& new_settings,
+ PrintingContext::Result result);
+ virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner);
+ virtual MessageLoop* message_loop() {
+ return ui_message_loop_;
+ }
+ virtual const PrintSettings& settings() const { return settings_; }
+
+ virtual int cookie() const { return cookie_; }
+
+ // Initializes the printing context. It is fine to call this function multiple
+ // times to reinitialize the settings. |parent_window| parameter will be the
+ // owner of the print setting dialog box. It is unused when
+ // |ask_for_user_settings| is DEFAULTS.
+ void GetSettings(GetSettingsAskParam ask_user_for_settings,
+ HWND parent_window,
+ int expected_page_count,
+ CancelableTask* callback);
+
+ // Stops the worker thread since the client is done with this object.
+ void StopWorker();
+
+ // Returns true if the Print... dialog box is currently displayed.
+ bool is_print_dialog_box_shown() const;
+
+ // Returns true if a GetSettings() call is pending completion.
+ bool is_callback_pending() const;
+
+ PrintingContext::Result last_status() const { return last_status_; }
+
+ // Returns if a worker thread is still associated to this instance.
+ bool is_valid() const;
+
+ private:
+ // Main message loop reference. Used to send notifications in the right
+ // thread.
+ MessageLoop* const ui_message_loop_;
+
+ // All the UI is done in a worker thread because many Win32 print functions
+ // are blocking and enters a message loop without your consent. There is one
+ // worker thread per print job.
+ scoped_ptr<PrintJobWorker> worker_;
+
+ // Cache of the print context settings for access in the UI thread.
+ PrintSettings settings_;
+
+ // Is the Print... dialog box currently shown.
+ bool is_print_dialog_box_shown_;
+
+ // Cookie that make this instance unique.
+ int cookie_;
+
+ // Results from the last GetSettingsDone() callback.
+ PrintingContext::Result last_status_;
+
+ // Task waiting to be executed.
+ scoped_ptr<CancelableTask> callback_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrinterQuery);
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_PRINTER_QUERY_H__
diff --git a/chrome/browser/printing/printing_layout_uitest.cc b/chrome/browser/printing/printing_layout_uitest.cc
new file mode 100644
index 0000000..9a6d04c
--- /dev/null
+++ b/chrome/browser/printing/printing_layout_uitest.cc
@@ -0,0 +1,643 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/command_line.h"
+#include "base/gfx/bitmap_header.h"
+#include "base/gfx/platform_device.h"
+#include "base/gfx/png_decoder.h"
+#include "base/gfx/png_encoder.h"
+#include "base/time.h"
+#include "base/win_util.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/gfx/emf.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/automation/window_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+#include "chrome/browser/printing/printing_test.h"
+#include "net/url_request/url_request_unittest.h"
+
+namespace {
+
+const wchar_t* const kGenerateSwitch = L"print-layout-generate";
+const wchar_t kDocRoot[] = L"chrome/test/data";
+
+// Lightweight raw-bitmap management. The image, once initialized, is immuable.
+// It is mainly used for comparison.
+class Image {
+ public:
+ // Creates the image from the given filename on disk.
+ Image(const std::wstring& filename) : ignore_alpha_(true) {
+ std::string data;
+ file_util::ReadFileToString(filename, &data);
+ EXPECT_TRUE(data.size());
+ std::wstring ext = file_util::GetFileExtensionFromPath(filename);
+ if (LowerCaseEqualsASCII(ext, "png")) {
+ LoadPng(data);
+ } else if (LowerCaseEqualsASCII(ext, "emf")) {
+ LoadEmf(data);
+ } else {
+ EXPECT_TRUE(false);
+ }
+ }
+
+ const gfx::Size& size() const {
+ return size_;
+ }
+
+ // Used to create the initial test files.
+ void SaveToPng(const std::wstring& filename) {
+ ASSERT_FALSE(data_.empty());
+ std::vector<unsigned char> compressed;
+ ASSERT_TRUE(PNGEncoder::Encode(&*data_.begin(),
+ PNGEncoder::FORMAT_BGRA,
+ size_.width(),
+ size_.height(),
+ row_length_,
+ true,
+ &compressed));
+ ASSERT_TRUE(compressed.size());
+ FILE* f;
+ ASSERT_EQ(_wfopen_s(&f, filename.c_str(), L"wbS"), 0);
+ ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
+ compressed.size());
+ fclose(f);
+ }
+
+ double PercentageDifferent(const Image& rhs) const {
+ if (size_.width() == 0 || size_.height() == 0 ||
+ rhs.size_.width() == 0 || rhs.size_.height() == 0)
+ return 100.;
+
+ int width = std::min(size_.width(), rhs.size_.width());
+ int height = std::min(size_.height(), rhs.size_.height());
+ // Compute pixels different in the overlap
+ int pixels_different = 0;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ uint32 lhs_pixel = pixel_at(x, y);
+ uint32 rhs_pixel = rhs.pixel_at(x, y);
+ if (lhs_pixel != rhs_pixel)
+ ++pixels_different;
+ }
+
+ // Look for extra right lhs pixels. They should be white.
+ for (int x = width; x < size_.width(); ++x) {
+ uint32 lhs_pixel = pixel_at(x, y);
+ if (lhs_pixel != Color(SK_ColorWHITE))
+ ++pixels_different;
+ }
+
+ // Look for extra right rhs pixels. They should be white.
+ for (int x = width; x < rhs.size_.width(); ++x) {
+ uint32 rhs_pixel = rhs.pixel_at(x, y);
+ if (rhs_pixel != Color(SK_ColorWHITE))
+ ++pixels_different;
+ }
+ }
+
+ // Look for extra bottom lhs pixels. They should be white.
+ for (int y = height; y < size_.height(); ++y) {
+ for (int x = 0; x < size_.width(); ++x) {
+ uint32 lhs_pixel = pixel_at(x, y);
+ if (lhs_pixel != Color(SK_ColorWHITE))
+ ++pixels_different;
+ }
+ }
+
+ // Look for extra bottom rhs pixels. They should be white.
+ for (int y = height; y < rhs.size_.height(); ++y) {
+ for (int x = 0; x < rhs.size_.width(); ++x) {
+ uint32 rhs_pixel = rhs.pixel_at(x, y);
+ if (rhs_pixel != Color(SK_ColorWHITE))
+ ++pixels_different;
+ }
+ }
+
+ // Like the WebKit ImageDiff tool, we define percentage different in terms
+ // of the size of the 'actual' bitmap.
+ double total_pixels = static_cast<double>(size_.width()) *
+ static_cast<double>(height);
+ return static_cast<double>(pixels_different) / total_pixels * 100.;
+ }
+
+ // Returns the 0x0RGB or 0xARGB value of the pixel at the given location,
+ // depending on ignore_alpha_.
+ uint32 Color(uint32 color) const {
+ if (ignore_alpha_)
+ return color & 0xFFFFFF; // Strip out A.
+ else
+ return color;
+ }
+
+ uint32 pixel_at(int x, int y) const {
+ EXPECT_TRUE(x >= 0 && x < size_.width());
+ EXPECT_TRUE(y >= 0 && y < size_.height());
+ const uint32* data = reinterpret_cast<const uint32*>(&*data_.begin());
+ const uint32* data_row = data + y * row_length_ / sizeof(uint32);
+ return Color(data_row[x]);
+ }
+
+ private:
+ void LoadPng(const std::string& compressed) {
+ int w;
+ int h;
+ EXPECT_TRUE(PNGDecoder::Decode(
+ reinterpret_cast<const unsigned char*>(compressed.c_str()),
+ compressed.size(), PNGDecoder::FORMAT_BGRA, &data_, &w, &h));
+ size_.SetSize(w, h);
+ row_length_ = size_.width() * sizeof(uint32);
+ }
+
+ void LoadEmf(const std::string& data) {
+ ASSERT_FALSE(data.empty());
+ gfx::Emf emf;
+ emf.CreateFromData(data.data(), data.size());
+ gfx::Rect rect(emf.GetBounds());
+ // Create a temporary HDC and bitmap to retrieve the renderered data.
+ HDC hdc = CreateCompatibleDC(NULL);
+ BITMAPV4HEADER hdr;
+ EXPECT_FALSE(rect.x());
+ EXPECT_FALSE(rect.y());
+ EXPECT_NE(rect.width(), 0);
+ EXPECT_NE(rect.height(), 0);
+ size_ = rect.size();
+ gfx::CreateBitmapV4Header(rect.width(), rect.height(), &hdr);
+ void* bits;
+ HBITMAP bitmap = CreateDIBSection(hdc,
+ reinterpret_cast<BITMAPINFO*>(&hdr), 0,
+ &bits, NULL, 0);
+ EXPECT_TRUE(bitmap);
+ EXPECT_TRUE(SelectObject(hdc, bitmap));
+ gfx::PlatformDevice::InitializeDC(hdc);
+ EXPECT_TRUE(emf.Playback(hdc, NULL));
+ row_length_ = size_.width() * sizeof(uint32);
+ size_t bytes = row_length_ * size_.height();
+ ASSERT_TRUE(bytes);
+ data_.resize(bytes);
+ memcpy(&*data_.begin(), bits, bytes);
+ DeleteDC(hdc);
+ DeleteObject(bitmap);
+ }
+
+ // Pixel dimensions of the image.
+ gfx::Size size_;
+
+ // Length of a line in bytes.
+ int row_length_;
+
+ // Actual bitmap data in arrays of RGBAs (so when loaded as uint32, it's
+ // 0xABGR).
+ std::vector<unsigned char> data_;
+
+ // Flag to signal if the comparison functions should ignore the alpha channel.
+ const bool ignore_alpha_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Image);
+};
+
+class PrintingLayoutTest : public PrintingTest<UITest> {
+ public:
+ PrintingLayoutTest() {
+ emf_path_ = browser_directory_;
+ file_util::AppendToPath(&emf_path_, L"emf_dumps");
+ std::wstring arg(L" --debug-print=\"");
+ arg += emf_path_;
+ arg += L"\"";
+ launch_arguments_.append(arg);
+ show_window_ = true;
+ }
+
+ virtual void SetUp() {
+ // Make sure there is no left overs.
+ CleanupDumpDirectory();
+ UITest::SetUp();
+ }
+
+ virtual void TearDown() {
+ UITest::TearDown();
+ file_util::Delete(emf_path_, true);
+ }
+
+ protected:
+ void PrintNowTab() {
+ scoped_ptr<TabProxy> tab_proxy(GetActiveTab());
+ ASSERT_TRUE(tab_proxy.get());
+ if (!tab_proxy.get())
+ return;
+
+ ASSERT_TRUE(tab_proxy->PrintNow());
+ }
+
+ // Finds the dump for the last print job and compares it to the data named
+ // |verification_name|. Compares the saved printed job pixels with the test
+ // data pixels and returns the percentage of different pixels; 0 for success,
+ // ]0, 100] for failure.
+ double CompareWithResult(const std::wstring& verification_name) {
+ std::wstring test_result(ScanFiles(verification_name));
+ if (test_result.empty()) {
+ // 100% different, the print job buffer is not there.
+ return 100.;
+ }
+
+ std::wstring verification_file(test_data_directory_);
+ file_util::AppendToPath(&verification_file, L"printing");
+ file_util::AppendToPath(&verification_file, verification_name);
+ std::wstring emf(verification_file + L".emf");
+ std::wstring png(verification_file + L".png");
+
+ // Looks for Cleartype override.
+ if (file_util::PathExists(verification_file + L"_cleartype.png") &&
+ IsClearTypeEnabled()) {
+ png = verification_file + L"_cleartype.png";
+ }
+
+ if (GenerateFiles()) {
+ // Copy the .emf and generate an .png.
+ file_util::CopyFile(test_result, emf);
+ Image emf_content(emf);
+ emf_content.SaveToPng(png);
+ // Saving is always fine.
+ return 0;
+ } else {
+ // File compare between test and result.
+ Image emf_content(emf);
+ Image test_content(test_result);
+ Image png_content(png);
+ double diff_emf = emf_content.PercentageDifferent(test_content);
+
+ EXPECT_EQ(0., diff_emf) << verification_name <<
+ L" original size:" << emf_content.size() <<
+ L" result size:" << test_content.size();
+ if (diff_emf) {
+ // Backup the result emf file.
+ file_util::CopyFile(test_result, verification_file + L"_failed.emf");
+ }
+
+ // This verification is only to know that the EMF rendering stays
+ // immutable.
+ double diff_png = emf_content.PercentageDifferent(png_content);
+ EXPECT_EQ(0., diff_png) << verification_name <<
+ L" original size:" << emf_content.size() <<
+ L" result size:" << test_content.size();
+ if (diff_png) {
+ // Backup the rendered emf file to detect the rendering difference.
+ emf_content.SaveToPng(verification_file + L"_rendering.png");
+ }
+ return std::max(diff_png, diff_emf);
+ }
+ }
+
+ // Makes sure the directory exists and is empty.
+ void CleanupDumpDirectory() {
+ // Tries to delete the dumping directory for around 10 seconds.
+ for (int i = 0; i < 100 && file_util::PathExists(emf_path()); ++i) {
+ // It's fine fail sometimes because of opened left over .PRN file.
+ // Explanation:
+ // When calling PrintNowTab(), it makes sure the page is rendered and
+ // sent to the spooler. It does *not* wait for the spooler to flush the
+ // job. It is completely unnecessary to wait for that. So the printer
+ // may write the file too late. Since the printer holds an exclusive
+ // access to the file, it can't be deleted until the printer is done.
+ if (file_util::Delete(emf_path(), true)) {
+ break;
+ }
+ Sleep(100);
+ }
+ file_util::CreateDirectory(emf_path());
+ }
+
+ // Returns if Clear Type is currently enabled.
+ static bool IsClearTypeEnabled() {
+ BOOL ct_enabled = 0;
+ if (SystemParametersInfo(SPI_GETCLEARTYPE, 0, &ct_enabled, 0) && ct_enabled)
+ return true;
+ UINT smoothing = 0;
+ if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smoothing, 0) &&
+ smoothing == FE_FONTSMOOTHINGCLEARTYPE)
+ return true;
+ return false;
+ }
+
+ private:
+ // Verifies that there is one .emf and one .prn file in the dump directory.
+ // Returns the path of the .emf file and deletes the .prn file.
+ std::wstring ScanFiles(const std::wstring& verification_name) {
+ // Try to 10 seconds.
+ std::wstring emf_file;
+ std::wstring prn_file;
+ bool found_emf = false;
+ bool found_prn = false;
+ for (int i = 0; i < 100; ++i) {
+ file_util::FileEnumerator enumerator(emf_path(), false,
+ file_util::FileEnumerator::FILES);
+ emf_file.clear();
+ prn_file.clear();
+ found_emf = false;
+ found_prn = false;
+ std::wstring file;
+ while (!(file = enumerator.Next()).empty()) {
+ std::wstring ext = file_util::GetFileExtensionFromPath(file);
+ if (!_wcsicmp(ext.c_str(), L"emf")) {
+ EXPECT_FALSE(found_emf) << "Found a leftover .EMF file: \"" <<
+ emf_file << "\" and \"" << file << "\" when looking for \"" <<
+ verification_name << "\"";
+ found_emf = true;
+ emf_file = file;
+ continue;
+ }
+ if (!_wcsicmp(ext.c_str(), L"prn")) {
+ EXPECT_FALSE(found_prn) << "Found a leftover .PRN file: \"" <<
+ prn_file << "\" and \"" << file << "\" when looking for \"" <<
+ verification_name << "\"";
+ prn_file = file;
+ found_prn = true;
+ file_util::Delete(file, false);
+ continue;
+ }
+ EXPECT_TRUE(false);
+ }
+ if (found_emf && found_prn)
+ break;
+ Sleep(100);
+ }
+ EXPECT_TRUE(found_emf) << ".PRN file is: " << prn_file;
+ EXPECT_TRUE(found_prn) << ".EMF file is: " << emf_file;
+ return emf_file;
+ }
+
+ static bool GenerateFiles() {
+ return CommandLine().HasSwitch(kGenerateSwitch);
+ }
+
+ const std::wstring& emf_path() const { return emf_path_; }
+
+ std::wstring emf_path_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintingLayoutTest);
+};
+
+// Tests that don't need UI access.
+class PrintingLayoutTestHidden : public PrintingLayoutTest {
+ public:
+ PrintingLayoutTestHidden() {
+ show_window_ = false;
+ }
+};
+
+class PrintingLayoutTextTest : public PrintingLayoutTest {
+ typedef PrintingLayoutTest Parent;
+ public:
+ // Returns if the test is disabled.
+ // TODO(maruel): http://b/1157665 Until the issue is fixed, disable the test
+ // if ClearType is enabled.
+ static bool IsTestCaseDisabled() {
+ return Parent::IsTestCaseDisabled() || IsClearTypeEnabled();
+ }
+};
+
+// Dismiss the first dialog box child of owner_window by "executing" the
+// default button.
+class DismissTheWindow : public Task {
+ public:
+ DismissTheWindow(DWORD owner_process)
+ : owner_process_(owner_process),
+ dialog_was_found_(false),
+ dialog_window_(NULL),
+ other_thread_(MessageLoop::current()),
+ timer_(NULL),
+ start_time_(Time::Now()) {
+ }
+ virtual void Run() {
+ // A bit twisted code that runs in 2 passes or more. First it tries to find
+ // a dialog box, if it finds it, it will execute the default action. If it
+ // still works, it will loop again but then it will try to *not* find the
+ // window. Once this is right, it will stop the timer and unlock the
+ // other_thread_ message loop.
+ if (!timer_)
+ return;
+
+ if (!dialog_window_) {
+ HWND dialog_window = NULL;
+ for (;;) {
+ dialog_window = FindWindowEx(NULL,
+ dialog_window,
+ MAKEINTATOM(32770),
+ NULL);
+ if (!dialog_window)
+ break;
+
+ // In some corner case, the Print... dialog box may not have any owner.
+ // Trap that case. Too bad if the user has a dialog opened.
+ if (Time::Now() - start_time_ > TimeDelta::FromSeconds(3))
+ break;
+
+ DWORD process_id = 0;
+ GetWindowThreadProcessId(dialog_window, &process_id);
+ if (process_id == owner_process_)
+ break;
+ }
+ if (dialog_window) {
+ LRESULT res = SendMessage(dialog_window, DM_GETDEFID, 0, 0);
+ if (!res)
+ return;
+ EXPECT_EQ(DC_HASDEFID, HIWORD(res));
+ WORD print_button_id = LOWORD(res);
+ res = SendMessage(
+ dialog_window,
+ WM_COMMAND,
+ print_button_id,
+ reinterpret_cast<LPARAM>(GetDlgItem(dialog_window, print_button_id)));
+ // Try again.
+ if (res)
+ return;
+
+ // Ok it succeeded.
+ dialog_window_ = dialog_window;
+ dialog_was_found_ = true;
+ return;
+ }
+ if (!dialog_was_found_)
+ return;
+ }
+
+ // Now verify that it indeed closed itself.
+ if (!IsWindow(dialog_window_)) {
+ MessageLoop::current()->timer_manager()->StopTimer(timer_);
+ timer_ = NULL;
+ // Unlock the other thread.
+ other_thread_->Quit();
+ } else {
+ // Maybe it's time to try to click it again. Restart from the begining.
+ dialog_window_ = NULL;
+ }
+ }
+ void SetTimer(Timer* timer) {
+ timer_ = timer;
+ }
+ private:
+ DWORD owner_process_;
+ bool dialog_was_found_;
+ HWND dialog_window_;
+ MessageLoop* other_thread_;
+ Timer* timer_;
+ Time start_time_;
+};
+
+} // namespace
+
+TEST_F(PrintingLayoutTextTest, Complex) {
+ if (IsTestCaseDisabled())
+ return;
+
+ // Print a document, check its output.
+ TestServer server(kDocRoot);
+ NavigateToURL(server.TestServerPage("files/printing/test1.html"));
+ PrintNowTab();
+ EXPECT_EQ(0., CompareWithResult(L"test1"));
+}
+
+struct TestPool {
+ const wchar_t* source;
+ const wchar_t* result;
+};
+
+const TestPool kTestPool[] = {
+ // ImagesB&W
+ L"files/printing/test2.html", L"test2",
+ // ImagesTransparent
+ L"files/printing/test3.html", L"test3",
+ // ImageColor
+ L"files/printing/test4.html", L"test4",
+ // TODO(maruel): http://b/1171450 Transparent overlays are drawn opaque
+ // L"files/printing/test5.html", L"test5",
+};
+
+TEST_F(PrintingLayoutTestHidden, ManyTimes) {
+ if (IsTestCaseDisabled())
+ return;
+
+ TestServer server(kDocRoot);
+ ASSERT_GT(arraysize(kTestPool), 0u);
+ for (int i = 0; i < arraysize(kTestPool); ++i) {
+ if (i)
+ CleanupDumpDirectory();
+ const TestPool& test = kTestPool[i % arraysize(kTestPool)];
+ NavigateToURL(server.TestServerPageW(test.source));
+ PrintNowTab();
+ EXPECT_EQ(0., CompareWithResult(test.result)) << test.result;
+ CleanupDumpDirectory();
+ PrintNowTab();
+ EXPECT_EQ(0., CompareWithResult(test.result)) << test.result;
+ CleanupDumpDirectory();
+ PrintNowTab();
+ EXPECT_EQ(0., CompareWithResult(test.result)) << test.result;
+ CleanupDumpDirectory();
+ PrintNowTab();
+ EXPECT_EQ(0., CompareWithResult(test.result)) << test.result;
+ }
+}
+
+// Prints a popup and immediately closes it.
+TEST_F(PrintingLayoutTest, DISABLED_Delayed) {
+ if (IsTestCaseDisabled())
+ return;
+
+ TestServer server(kDocRoot);
+
+ {
+ scoped_ptr<TabProxy> tab_proxy(GetActiveTab());
+ ASSERT_TRUE(tab_proxy.get());
+ bool is_timeout = true;
+ GURL url = server.TestServerPage("files/printing/popup_delayed_print.htm");
+ EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab_proxy->NavigateToURL(url));
+
+
+ scoped_ptr<Thread> worker(new Thread("PrintingLayoutTest_worker"));
+ DismissTheWindow dismiss_task(process_util::GetProcId(process()));
+ // We need to start the thread to be able to set the timer.
+ worker->Start();
+ scoped_ptr<Timer> timer(worker->message_loop()->timer_manager()->StartTimer(
+ 250,
+ &dismiss_task,
+ true));
+ dismiss_task.SetTimer(timer.get());
+ MessageLoop::current()->Run();
+
+ worker->Stop();
+
+ // Force a navigation elsewhere to verify that it's fine with it.
+ url = server.TestServerPage("files/printing/test1.html");
+ EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab_proxy->NavigateToURL(url));
+ }
+ CloseBrowserAndServer();
+
+ EXPECT_EQ(0., CompareWithResult(L"popup_delayed_print"))
+ << L"popup_delayed_print";
+}
+
+// Prints a popup and immediately closes it.
+TEST_F(PrintingLayoutTest, DISABLED_IFrame) {
+ if (IsTestCaseDisabled())
+ return;
+
+ TestServer server(kDocRoot);
+
+ {
+ scoped_ptr<TabProxy> tab_proxy(GetActiveTab());
+ ASSERT_TRUE(tab_proxy.get());
+ GURL url = server.TestServerPage("files/printing/iframe.htm");
+ EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab_proxy->NavigateToURL(url));
+
+ scoped_ptr<Thread> worker(new Thread("PrintingLayoutTest_worker"));
+ DismissTheWindow dismiss_task(process_util::GetProcId(process()));
+ // We need to start the thread to be able to set the timer.
+ worker->Start();
+ scoped_ptr<Timer> timer(worker->message_loop()->timer_manager()->StartTimer(
+ 250,
+ &dismiss_task,
+ true));
+ dismiss_task.SetTimer(timer.get());
+ MessageLoop::current()->Run();
+
+ worker->Stop();
+
+ // Force a navigation elsewhere to verify that it's fine with it.
+ url = server.TestServerPage("files/printing/test1.html");
+ EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab_proxy->NavigateToURL(url));
+ }
+ CloseBrowserAndServer();
+
+ EXPECT_EQ(0., CompareWithResult(L"iframe"))
+ << L"iframe";
+}
diff --git a/chrome/browser/printing/printing_test.h b/chrome/browser/printing/printing_test.h
new file mode 100644
index 0000000..c2344dd
--- /dev/null
+++ b/chrome/browser/printing/printing_test.h
@@ -0,0 +1,61 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINTING_TEST_H__
+#define CHROME_BROWSER_PRINTING_PRINTING_TEST_H__
+
+#include <windows.h>
+#include <winspool.h>
+
+// Disable the whole test case when executing on a computer that has no printer
+// installed.
+// Note: Parent should be testing::Test or UITest.
+template<typename Parent>
+class PrintingTest : public Parent {
+ public:
+ static bool IsTestCaseDisabled() {
+ return GetDefaultPrinter().empty();
+ }
+ static std::wstring GetDefaultPrinter() {
+ wchar_t printer_name[MAX_PATH];
+ DWORD size = arraysize(printer_name);
+ BOOL result = ::GetDefaultPrinter(printer_name, &size);
+ if (result == 0) {
+ if (GetLastError() == ERROR_FILE_NOT_FOUND) {
+ printf("There is no printer installed, printing can't be tested!\n");
+ return std::wstring();
+ }
+ printf("INTERNAL PRINTER ERROR!\n");
+ return std::wstring();
+ }
+ return printer_name;
+ }
+};
+
+#endif // CHROME_BROWSER_PRINTING_PRINTING_TEST_H__
diff --git a/chrome/browser/printing/units.cc b/chrome/browser/printing/units.cc
new file mode 100644
index 0000000..881b819
--- /dev/null
+++ b/chrome/browser/printing/units.cc
@@ -0,0 +1,67 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/units.h"
+
+#include "base/logging.h"
+
+namespace printing {
+
+int ConvertUnit(int value, int old_unit, int new_unit) {
+ DCHECK_GT(new_unit, 0);
+ DCHECK_GT(old_unit, 0);
+ // With integer arithmetic, to divide a value with correct rounding, you need
+ // to add half of the divisor value to the dividend value. You need to do the
+ // reverse with negative number.
+ if (value >= 0) {
+ return ((value * new_unit) + (old_unit / 2)) / old_unit;
+ } else {
+ return ((value * new_unit) - (old_unit / 2)) / old_unit;
+ }
+}
+
+double ConvertUnitDouble(double value, double old_unit, double new_unit) {
+ DCHECK_GT(new_unit, 0);
+ DCHECK_GT(old_unit, 0);
+ return value * new_unit / old_unit;
+}
+
+int ConvertMilliInchToHundredThousanthMeter(int milli_inch) {
+ // 1" == 25.4 mm
+ // 1" == 25400 um
+ // 0.001" == 25.4 um
+ // 0.001" == 2.54 cmm
+ return ConvertUnit(milli_inch, 100, 254);
+}
+
+int ConvertHundredThousanthMeterToMilliInch(int cmm) {
+ return ConvertUnit(cmm, 254, 100);
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/units.h b/chrome/browser/printing/units.h
new file mode 100644
index 0000000..1501509
--- /dev/null
+++ b/chrome/browser/printing/units.h
@@ -0,0 +1,52 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_UNITS_H__
+#define CHROME_BROWSER_PRINTING_UNITS_H__
+
+namespace printing {
+
+// Length of a thousanth of inches in 0.01mm unit.
+const int kHundrethsMMPerInch = 2540;
+
+// Converts from one unit system to another using integer arithmetics.
+int ConvertUnit(int value, int old_unit, int new_unit);
+
+// Converts from one unit system to another using doubles.
+double ConvertUnitDouble(double value, double old_unit, double new_unit);
+
+// Converts from 0.001 inch unit to 0.00001 meter.
+int ConvertMilliInchToHundredThousanthMeter(int milli_inch);
+
+// Converts from 0.00001 meter unit to 0.001 inch.
+int ConvertHundredThousanthMeterToMilliInch(int cmm);
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_UNITS_H__
diff --git a/chrome/browser/printing/win_printing_context.cc b/chrome/browser/printing/win_printing_context.cc
new file mode 100644
index 0000000..81315f3
--- /dev/null
+++ b/chrome/browser/printing/win_printing_context.cc
@@ -0,0 +1,629 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/win_printing_context.h"
+
+#include <winspool.h>
+
+#include "base/file_util.h"
+#include "base/gfx/platform_device.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/printing/print_job_manager.h"
+#include "chrome/common/gfx/emf.h"
+#include "chrome/common/time_format.h"
+
+namespace {
+
+// Retrieves the content of a GetPrinter call.
+void GetPrinterHelper(HANDLE printer, int level, scoped_array<uint8>* buffer) {
+ DWORD buf_size = 0;
+ GetPrinter(printer, level, NULL, 0, &buf_size);
+ if (buf_size) {
+ buffer->reset(new uint8[buf_size]);
+ memset(buffer->get(), 0, buf_size);
+ if (!GetPrinter(printer, level, buffer->get(), buf_size, &buf_size)) {
+ buffer->reset();
+ }
+ }
+}
+
+} // namespace
+
+namespace printing {
+
+class PrintingContext::CallbackHandler
+ : public IPrintDialogCallback,
+ public IObjectWithSite {
+ public:
+ CallbackHandler(PrintingContext& owner, HWND owner_hwnd)
+ : owner_(owner),
+ owner_hwnd_(owner_hwnd),
+ services_(NULL) {
+ }
+
+ ~CallbackHandler() {
+ if (services_)
+ services_->Release();
+ }
+
+ IUnknown* ToIUnknown() {
+ return static_cast<IUnknown*>(static_cast<IPrintDialogCallback*>(this));
+ }
+
+ // IUnknown
+ virtual HRESULT WINAPI QueryInterface(REFIID riid, void**object) {
+ if (riid == IID_IUnknown) {
+ *object = ToIUnknown();
+ } else if (riid == IID_IPrintDialogCallback) {
+ *object = static_cast<IPrintDialogCallback*>(this);
+ } else if (riid == IID_IObjectWithSite) {
+ *object = static_cast<IObjectWithSite*>(this);
+ } else {
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+ }
+
+ // No real ref counting.
+ virtual ULONG WINAPI AddRef() {
+ return 1;
+ }
+ virtual ULONG WINAPI Release() {
+ return 1;
+ }
+
+ // IPrintDialogCallback methods
+ virtual HRESULT WINAPI InitDone() {
+ return S_OK;
+ }
+
+ virtual HRESULT WINAPI SelectionChange() {
+ if (services_) {
+ // TODO(maruel): Get the devmode for the new printer with
+ // services_->GetCurrentDevMode(&devmode, &size), send that information
+ // back to our client and continue. The client needs to recalculate the
+ // number of rendered pages and send back this information here.
+ }
+ return S_OK;
+ }
+
+ virtual HRESULT WINAPI HandleMessage(HWND dialog,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ LRESULT* result) {
+ // Cheap way to retrieve the window handle.
+ if (!owner_.dialog_box_) {
+ // The handle we receive is the one of the groupbox in the General tab. We
+ // need to get the grand-father to get the dialog box handle.
+ owner_.dialog_box_ = GetAncestor(dialog, GA_ROOT);
+ // Trick to enable the owner window. This can cause issues with navigation
+ // events so it may have to be disabled if we don't fix the side-effects.
+ EnableWindow(owner_hwnd_, TRUE);
+ }
+ return S_FALSE;
+ }
+
+ virtual HRESULT WINAPI SetSite(IUnknown* site) {
+ if (!site) {
+ DCHECK(services_);
+ services_->Release();
+ services_ = NULL;
+ // The dialog box is destroying, PrintJob::Worker don't need the handle
+ // anymore.
+ owner_.dialog_box_ = NULL;
+ } else {
+ DCHECK(services_ == NULL);
+ HRESULT hr = site->QueryInterface(IID_IPrintDialogServices,
+ reinterpret_cast<void**>(&services_));
+ DCHECK(SUCCEEDED(hr));
+ }
+ return S_OK;
+ }
+
+ virtual HRESULT WINAPI GetSite(REFIID riid, void** site) {
+ return E_NOTIMPL;
+ }
+
+ private:
+ PrintingContext& owner_;
+ HWND owner_hwnd_;
+ IPrintDialogServices* services_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CallbackHandler);
+};
+
+PrintingContext::PrintingContext()
+ : hdc_(NULL),
+#ifdef _DEBUG
+ page_number_(-1),
+#endif
+ dialog_box_(NULL),
+ dialog_box_dismissed_(false),
+ abort_printing_(false),
+ in_print_job_(false) {
+}
+
+PrintingContext::~PrintingContext() {
+ ResetSettings();
+}
+
+PrintingContext::Result PrintingContext::AskUserForSettings(HWND window,
+ int max_pages) {
+ DCHECK(window);
+ DCHECK(!in_print_job_);
+ dialog_box_dismissed_ = false;
+ // Show the OS-dependent dialog box.
+ // If the user press
+ // - OK, the settings are reset and reinitialized with the new settings. OK is
+ // returned.
+ // - Apply then Cancel, the settings are reset and reinitialized with the new
+ // settings. CANCEL is returned.
+ // - Cancel, the settings are not changed, the previous setting, if it was
+ // initialized before, are kept. CANCEL is returned.
+ // On failure, the settings are reset and FAILED is returned.
+ PRINTDLGEX dialog_options = { sizeof(PRINTDLGEX) };
+ dialog_options.hwndOwner = window;
+ // Disables the Current Page and Selection radio buttons since WebKit can't
+ // print a part of the webpage and we don't know which page is the current
+ // one.
+ // TODO(maruel): Reuse the previously loaded settings!
+ dialog_options.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE |
+ PD_NOSELECTION | PD_NOCURRENTPAGE | PD_HIDEPRINTTOFILE;
+ PRINTPAGERANGE ranges[32];
+ dialog_options.nStartPage = START_PAGE_GENERAL;
+ if (max_pages) {
+ // Default initialize to print all the pages.
+ memset(ranges, 0, sizeof(ranges));
+ ranges[0].nFromPage = 1;
+ ranges[0].nToPage = max_pages;
+ dialog_options.nPageRanges = 1;
+ dialog_options.nMaxPageRanges = arraysize(ranges);
+ dialog_options.nMaxPage = max_pages;
+ dialog_options.lpPageRanges = ranges;
+ } else {
+ // No need to bother, we don't know how many pages are available.
+ dialog_options.Flags |= PD_NOPAGENUMS;
+ }
+
+ {
+ CallbackHandler handler(*this, window);
+ dialog_options.lpCallback = handler.ToIUnknown();
+ if (PrintDlgEx(&dialog_options) != S_OK) {
+ ResetSettings();
+ return FAILED;
+ }
+ }
+ // TODO(maruel): Support PD_PRINTTOFILE.
+ return ParseDialogResultEx(dialog_options);
+}
+
+PrintingContext::Result PrintingContext::UseDefaultSettings() {
+ DCHECK(!in_print_job_);
+
+ PRINTDLG dialog_options = { sizeof(PRINTDLG) };
+ dialog_options.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
+ if (PrintDlg(&dialog_options) == 0) {
+ ResetSettings();
+ return FAILED;
+ }
+ return ParseDialogResult(dialog_options);
+}
+
+PrintingContext::Result PrintingContext::InitWithSettings(
+ const PrintSettings& settings) {
+ DCHECK(!in_print_job_);
+ settings_ = settings;
+ // TODO(maruel): settings_->ToDEVMODE()
+ HANDLE printer;
+ if (!OpenPrinter(const_cast<wchar_t*>(settings_.device_name().c_str()),
+ &printer,
+ NULL))
+ return FAILED;
+
+ Result status = OK;
+
+ if (!GetPrinterSettings(printer, settings_.device_name()))
+ status = FAILED;
+
+ // Close the printer after retrieving the context.
+ ClosePrinter(printer);
+
+ if (status != OK)
+ ResetSettings();
+ return status;
+}
+
+void PrintingContext::ResetSettings() {
+ if (hdc_ != NULL) {
+ DeleteDC(hdc_);
+ hdc_ = NULL;
+ }
+ settings_.Clear();
+ in_print_job_ = false;
+
+#ifdef _DEBUG
+ page_number_ = -1;
+#endif
+}
+
+PrintingContext::Result PrintingContext::NewDocument(
+ const std::wstring& document_name) {
+ DCHECK(!in_print_job_);
+ if (!hdc_)
+ return OnErrror();
+
+ // Set the flag used by the AbortPrintJob dialog procedure.
+ abort_printing_ = false;
+
+ in_print_job_ = true;
+
+ // Register the application's AbortProc function with GDI.
+ if (SP_ERROR == SetAbortProc(hdc_, &AbortProc))
+ return OnErrror();
+
+ DOCINFO di = { sizeof(DOCINFO) };
+ di.lpszDocName = document_name.c_str();
+
+ // Is there a debug dump directory specified? If so, force to print to a file.
+ std::wstring debug_dump_path;
+ if (!g_browser_process || !g_browser_process->print_job_manager()) {
+ // Happens only inside a unit test.
+ debug_dump_path = L".";
+ } else {
+ debug_dump_path = g_browser_process->print_job_manager()->debug_dump_path();
+ }
+
+ if (!debug_dump_path.empty()) {
+ // Create a filename.
+ std::wstring filename;
+ Time now(Time::Now());
+ filename = TimeFormat::ShortDateNumeric(now);
+ filename += L"_";
+ filename += TimeFormat::TimeOfDay(now);
+ filename += L"_";
+ filename += document_name;
+ filename += L"_";
+ filename += L"buffer.prn";
+ file_util::ReplaceIllegalCharacters(&filename, '_');
+ file_util::AppendToPath(&debug_dump_path, filename);
+ di.lpszOutput = debug_dump_path.c_str();
+ }
+
+ DCHECK_EQ(MessageLoop::current()->NestableTasksAllowed(), false);
+ // Begin a print job by calling the StartDoc function.
+ // NOTE: StartDoc() starts a message loop. That causes a lot of problems with
+ // IPC. Make sure recursive task processing is disabled.
+ if (StartDoc(hdc_, &di) <= 0)
+ return OnErrror();
+
+#ifdef _DEBUG
+ page_number_ = 0;
+#endif
+ return OK;
+}
+
+PrintingContext::Result PrintingContext::NewPage() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ // Inform the driver that the application is about to begin sending data.
+ if (StartPage(hdc_) <= 0)
+ return OnErrror();
+
+#ifdef _DEBUG
+ ++page_number_;
+#endif
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContext::PageDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ if (EndPage(hdc_) <= 0)
+ return OnErrror();
+ return OK;
+}
+
+PrintingContext::Result PrintingContext::DocumentDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ // Inform the driver that document has ended.
+ if (EndDoc(hdc_) <= 0)
+ return OnErrror();
+
+ ResetSettings();
+ return OK;
+}
+
+void PrintingContext::Cancel() {
+ abort_printing_ = true;
+ in_print_job_ = false;
+ if (hdc_)
+ CancelDC(hdc_);
+ DismissDialog();
+}
+
+void PrintingContext::DismissDialog() {
+ if (dialog_box_) {
+ DestroyWindow(dialog_box_);
+ dialog_box_dismissed_ = true;
+ }
+}
+
+PrintingContext::Result PrintingContext::OnErrror() {
+ // This will close hdc_ and clear settings_.
+ ResetSettings();
+ return abort_printing_ ? CANCEL : FAILED;
+}
+
+// static
+BOOL PrintingContext::AbortProc(HDC hdc, int nCode) {
+ if (nCode) {
+ // TODO(maruel): Need a way to find the right instance to set. Should
+ // leverage PrintJobManager here?
+ // abort_printing_ = true;
+ }
+ return true;
+}
+
+bool PrintingContext::InitializeSettings(const DEVMODE& dev_mode,
+ const std::wstring& new_device_name,
+ const PRINTPAGERANGE* ranges,
+ int number_ranges) {
+ gfx::PlatformDevice::InitializeDC(hdc_);
+ DCHECK(GetDeviceCaps(hdc_, CLIPCAPS));
+ DCHECK(GetDeviceCaps(hdc_, RASTERCAPS) & RC_STRETCHDIB);
+ DCHECK(GetDeviceCaps(hdc_, RASTERCAPS) & RC_BITMAP64);
+ // Some printers don't advertise these.
+ // DCHECK(GetDeviceCaps(hdc_, RASTERCAPS) & RC_SCALING);
+ // DCHECK(GetDeviceCaps(hdc_, SHADEBLENDCAPS) & SB_CONST_ALPHA);
+ // DCHECK(GetDeviceCaps(hdc_, SHADEBLENDCAPS) & SB_PIXEL_ALPHA);
+
+ // StretchDIBits() support is needed for printing.
+ if (!(GetDeviceCaps(hdc_, RASTERCAPS) & RC_STRETCHDIB) ||
+ !(GetDeviceCaps(hdc_, RASTERCAPS) & RC_BITMAP64)) {
+ NOTREACHED();
+ ResetSettings();
+ return false;
+ }
+
+ DCHECK(!in_print_job_);
+ DCHECK(hdc_);
+ // Convert the PRINTPAGERANGE array to a PrintSettings::PageRanges vector.
+ PageRanges ranges_vector;
+ ranges_vector.reserve(number_ranges);
+ for (int i = 0; i < number_ranges; ++i) {
+ PageRange range;
+ // Transfert from 1-based to 0-based.
+ range.from = ranges[i].nFromPage - 1;
+ range.to = ranges[i].nToPage - 1;
+ ranges_vector.push_back(range);
+ }
+ settings_.Init(hdc_, dev_mode, ranges_vector, new_device_name);
+ PageMargins margins;
+ margins.header = 500;
+ margins.footer = 500;
+ margins.left = 500;
+ margins.top = 500;
+ margins.right = 500;
+ margins.bottom = 500;
+ settings_.UpdateMarginsMilliInch(margins);
+ return true;
+}
+
+bool PrintingContext::GetPrinterSettings(HANDLE printer,
+ const std::wstring& device_name) {
+ DCHECK(!in_print_job_);
+ scoped_array<uint8> buffer;
+
+ // A PRINTER_INFO_9 structure specifying the per-user default printer
+ // settings.
+ GetPrinterHelper(printer, 9, &buffer);
+ if (buffer.get()) {
+ PRINTER_INFO_9* info_9 = reinterpret_cast<PRINTER_INFO_9*>(buffer.get());
+ if (info_9->pDevMode != NULL) {
+ if (!AllocateContext(device_name, info_9->pDevMode)) {
+ ResetSettings();
+ return false;
+ }
+ return InitializeSettings(*info_9->pDevMode, device_name, NULL, 0);
+ }
+ buffer.reset();
+ }
+
+ // A PRINTER_INFO_8 structure specifying the global default printer settings.
+ GetPrinterHelper(printer, 8, &buffer);
+ if (buffer.get()) {
+ PRINTER_INFO_8* info_8 = reinterpret_cast<PRINTER_INFO_8*>(buffer.get());
+ if (info_8->pDevMode != NULL) {
+ if (!AllocateContext(device_name, info_8->pDevMode)) {
+ ResetSettings();
+ return false;
+ }
+ return InitializeSettings(*info_8->pDevMode, device_name, NULL, 0);
+ }
+ buffer.reset();
+ }
+
+ // A PRINTER_INFO_2 structure specifying the driver's default printer
+ // settings.
+ GetPrinterHelper(printer, 2, &buffer);
+ if (buffer.get()) {
+ PRINTER_INFO_2* info_2 = reinterpret_cast<PRINTER_INFO_2*>(buffer.get());
+ if (info_2->pDevMode != NULL) {
+ if (!AllocateContext(device_name, info_2->pDevMode)) {
+ ResetSettings();
+ return false;
+ }
+ return InitializeSettings(*info_2->pDevMode, device_name, NULL, 0);
+ }
+ buffer.reset();
+ }
+ // Failed to retrieve the printer settings.
+ ResetSettings();
+ return false;
+}
+
+bool PrintingContext::AllocateContext(const std::wstring& printer_name,
+ const DEVMODE* dev_mode) {
+ hdc_ = CreateDC(L"WINSPOOL", printer_name.c_str(), NULL, dev_mode);
+ DCHECK(hdc_);
+ return hdc_ != NULL;
+}
+
+PrintingContext::Result PrintingContext::ParseDialogResultEx(
+ const PRINTDLGEX& dialog_options) {
+ // If the user clicked OK or Apply then Cancel, but not only Cancel.
+ if (dialog_options.dwResultAction != PD_RESULT_CANCEL) {
+ // Start fresh.
+ ResetSettings();
+
+ DEVMODE* dev_mode = NULL;
+ if (dialog_options.hDevMode) {
+ dev_mode =
+ reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
+ DCHECK(dev_mode);
+ }
+
+ std::wstring device_name;
+ if (dialog_options.hDevNames) {
+ DEVNAMES* dev_names =
+ reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
+ DCHECK(dev_names);
+ if (dev_names) {
+ device_name =
+ reinterpret_cast<const wchar_t*>(
+ reinterpret_cast<const wchar_t*>(dev_names) +
+ dev_names->wDeviceOffset);
+ GlobalUnlock(dialog_options.hDevNames);
+ }
+ }
+
+ bool success = false;
+ if (dev_mode && !device_name.empty()) {
+ hdc_ = dialog_options.hDC;
+ if (dialog_options.Flags & PD_PAGENUMS) {
+ success = InitializeSettings(*dev_mode,
+ device_name,
+ dialog_options.lpPageRanges,
+ dialog_options.nPageRanges);
+ } else {
+ success = InitializeSettings(*dev_mode, device_name, NULL, 0);
+ }
+ }
+
+ if (!success && dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ hdc_ = NULL;
+ }
+
+ if (dev_mode) {
+ GlobalUnlock(dialog_options.hDevMode);
+ }
+ } else {
+ if (dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ }
+ }
+
+ if (dialog_options.hDevMode != NULL)
+ GlobalFree(dialog_options.hDevMode);
+ if (dialog_options.hDevNames != NULL)
+ GlobalFree(dialog_options.hDevNames);
+
+ switch (dialog_options.dwResultAction) {
+ case PD_RESULT_PRINT:
+ return hdc_ ? OK : FAILED;
+ case PD_RESULT_APPLY:
+ return hdc_ ? CANCEL : FAILED;
+ case PD_RESULT_CANCEL:
+ return CANCEL;
+ default:
+ return FAILED;
+ }
+}
+
+PrintingContext::Result PrintingContext::ParseDialogResult(
+ const PRINTDLG& dialog_options) {
+ // If the user clicked OK or Apply then Cancel, but not only Cancel.
+ // Start fresh.
+ ResetSettings();
+
+ DEVMODE* dev_mode = NULL;
+ if (dialog_options.hDevMode) {
+ dev_mode =
+ reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
+ DCHECK(dev_mode);
+ }
+
+ std::wstring device_name;
+ if (dialog_options.hDevNames) {
+ DEVNAMES* dev_names =
+ reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
+ DCHECK(dev_names);
+ if (dev_names) {
+ device_name =
+ reinterpret_cast<const wchar_t*>(
+ reinterpret_cast<const wchar_t*>(dev_names) +
+ dev_names->wDeviceOffset);
+ GlobalUnlock(dialog_options.hDevNames);
+ }
+ }
+
+ bool success = false;
+ if (dev_mode && !device_name.empty()) {
+ hdc_ = dialog_options.hDC;
+ success = InitializeSettings(*dev_mode, device_name, NULL, 0);
+ }
+
+ if (!success && dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ hdc_ = NULL;
+ }
+
+ if (dev_mode) {
+ GlobalUnlock(dialog_options.hDevMode);
+ }
+
+ if (dialog_options.hDevMode != NULL)
+ GlobalFree(dialog_options.hDevMode);
+ if (dialog_options.hDevNames != NULL)
+ GlobalFree(dialog_options.hDevNames);
+
+ return hdc_ ? OK : FAILED;
+}
+
+} // namespace printing
diff --git a/chrome/browser/printing/win_printing_context.h b/chrome/browser/printing/win_printing_context.h
new file mode 100644
index 0000000..63b8bcb
--- /dev/null
+++ b/chrome/browser/printing/win_printing_context.h
@@ -0,0 +1,163 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_BROWSER_PRINTING_WIN_PRINTING_CONTEXT_H__
+#define CHROME_BROWSER_PRINTING_WIN_PRINTING_CONTEXT_H__
+
+#include <ocidl.h>
+#include <commdlg.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "chrome/browser/printing/print_settings.h"
+
+namespace printing {
+
+// Describe the user selected printing context for Windows. This includes the
+// OS-dependent UI to ask the user about the print settings. This class directly
+// talk to the printer and manages the document and pages breaks.
+class PrintingContext {
+ public:
+ // Tri-state result for user behavior-dependent functions.
+ enum Result {
+ OK,
+ CANCEL,
+ FAILED,
+ };
+
+ PrintingContext();
+ ~PrintingContext();
+
+ // Asks the user what printer and format should be used to print. Updates the
+ // context with the select device settings.
+ Result AskUserForSettings(HWND window, int max_pages);
+
+ // Selects the user's default printer and format. Updates the context with the
+ // default device settings.
+ Result UseDefaultSettings();
+
+ // Initializes with predefined settings.
+ Result InitWithSettings(const PrintSettings& settings);
+
+ // Reinitializes the settings to uninitialized for object reuse.
+ void ResetSettings();
+
+ // Does platform specific setup of the printer before the printing. Signal the
+ // printer that a document is about to be spooled.
+ // Warning: This function enters a message loop. That may cause side effects
+ // like IPC message processing! Some printers have side-effects on this call
+ // like virtual printers that ask the user for the path of the saved document;
+ // for example a PDF printer.
+ Result NewDocument(const std::wstring& document_name);
+
+ // Starts a new page.
+ Result NewPage();
+
+ // Closes the printed page.
+ Result PageDone();
+
+ // Closes the printing job. After this call the object is ready to start a new
+ // document.
+ Result DocumentDone();
+
+ // Cancels printing. Can be used in a multithreaded context. Takes effect
+ // immediately.
+ void Cancel();
+
+ // Dismiss the Print... dialog box if shown.
+ void DismissDialog();
+
+ HDC context() {
+ return hdc_;
+ }
+
+ const PrintSettings& settings() const {
+ return settings_;
+ }
+
+ private:
+ // Class that manages the PrintDlgEx() callbacks. This is meant to be a
+ // temporary object used during the Print... dialog display.
+ class CallbackHandler;
+
+ // Does bookeeping when an error occurs.
+ PrintingContext::Result OnErrror();
+
+ // Used in response to the user cancelling the printing.
+ static BOOL CALLBACK AbortProc(HDC hdc, int nCode);
+
+ // Reads the settings from the selected device context. Updates settings_ and
+ // its margins.
+ bool InitializeSettings(const DEVMODE& dev_mode,
+ const std::wstring& new_device_name,
+ const PRINTPAGERANGE* ranges,
+ int number_ranges);
+
+ // Retrieves the printer's default low-level settings. hdc_ is allocated with
+ // this call.
+ bool GetPrinterSettings(HANDLE printer,
+ const std::wstring& device_name);
+
+ // Allocates the HDC for a specific DEVMODE.
+ bool AllocateContext(const std::wstring& printer_name,
+ const DEVMODE* dev_mode);
+
+ // Parses the result of a PRINTDLGEX result.
+ Result ParseDialogResultEx(const PRINTDLGEX& dialog_options);
+ Result ParseDialogResult(const PRINTDLG& dialog_options);
+
+ // The selected printer context.
+ HDC hdc_;
+
+ // Complete print context settings.
+ PrintSettings settings_;
+
+#ifdef _DEBUG
+ // Current page number in the print job.
+ int page_number_;
+#endif
+
+ // The dialog box for the time it is shown.
+ volatile HWND dialog_box_;
+
+ // The dialog box has been dismissed.
+ volatile bool dialog_box_dismissed_;
+
+ // Is a print job being done.
+ volatile bool in_print_job_;
+
+ // Did the user cancel the print job.
+ volatile bool abort_printing_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintingContext);
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_PRINTING_WIN_PRINTING_CONTEXT_H__ \ No newline at end of file
diff --git a/chrome/browser/printing/win_printing_context_unittest.cc b/chrome/browser/printing/win_printing_context_unittest.cc
new file mode 100644
index 0000000..a324425
--- /dev/null
+++ b/chrome/browser/printing/win_printing_context_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/win_printing_context.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "chrome/browser/printing/printing_test.h"
+#include "chrome/browser/printing/print_settings.h"
+
+// This test is automatically disabled if no printer is available.
+class PrintingContextTest : public PrintingTest<testing::Test> {
+};
+
+TEST_F(PrintingContextTest, Base) {
+ printing::PrintSettings settings;
+
+ settings.set_device_name(GetDefaultPrinter());
+ // Initialize it.
+ printing::PrintingContext context;
+ EXPECT_EQ(context.InitWithSettings(settings), printing::PrintingContext::OK);
+
+ ;
+ // The print may lie to use and may not support world transformation.
+ // Verify right now.
+ XFORM random_matrix = { 1, 0.1f, 0, 1.5f, 0, 1 };
+ EXPECT_TRUE(SetWorldTransform(context.context(), &random_matrix));
+ EXPECT_TRUE(ModifyWorldTransform(context.context(), NULL, MWT_IDENTITY));
+}