1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/dom_distiller/core/distiller_page.h"
8 #include "base/json/json_writer.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h"
15 #include "grit/components_resources.h"
16 #include "third_party/dom_distiller_js/dom_distiller.pb.h"
17 #include "third_party/dom_distiller_js/dom_distiller_json_converter.h"
18 #include "ui/base/resource/resource_bundle.h"
21 namespace dom_distiller
{
25 const char* kOptionsPlaceholder
= "$$OPTIONS";
26 const char* kStringifyPlaceholder
= "$$STRINGIFY";
27 const char* kNewContextPlaceholder
= "$$NEW_CONTEXT";
29 std::string
GetDistillerScriptWithOptions(
30 const dom_distiller::proto::DomDistillerOptions
& options
,
31 bool stringify_output
,
32 bool create_new_context
) {
33 std::string script
= ResourceBundle::GetSharedInstance()
34 .GetRawDataResource(IDR_DISTILLER_JS
)
40 scoped_ptr
<base::Value
> options_value(
41 dom_distiller::proto::json::DomDistillerOptions::WriteToValue(options
));
42 std::string options_json
;
43 if (!base::JSONWriter::Write(*options_value
, &options_json
)) {
46 size_t options_offset
= script
.find(kOptionsPlaceholder
);
47 DCHECK_NE(std::string::npos
, options_offset
);
48 DCHECK_EQ(std::string::npos
,
49 script
.find(kOptionsPlaceholder
, options_offset
+ 1));
51 script
.replace(options_offset
, strlen(kOptionsPlaceholder
), options_json
);
53 std::string stringify
= stringify_output
? "true" : "false";
54 size_t stringify_offset
= script
.find(kStringifyPlaceholder
);
55 DCHECK_NE(std::string::npos
, stringify_offset
);
56 DCHECK_EQ(std::string::npos
,
57 script
.find(kStringifyPlaceholder
, stringify_offset
+ 1));
58 script
= script
.replace(stringify_offset
,
59 strlen(kStringifyPlaceholder
),
62 std::string new_context
= create_new_context
? "true" : "false";
63 size_t new_context_offset
= script
.find(kNewContextPlaceholder
);
64 DCHECK_NE(std::string::npos
, new_context_offset
);
65 DCHECK_EQ(std::string::npos
,
66 script
.find(kNewContextPlaceholder
, new_context_offset
+ 1));
67 script
= script
.replace(new_context_offset
,
68 strlen(kNewContextPlaceholder
),
76 DistillerPageFactory::~DistillerPageFactory() {}
78 DistillerPage::DistillerPage() : ready_(true) {}
80 DistillerPage::~DistillerPage() {}
82 void DistillerPage::DistillPage(
84 const dom_distiller::proto::DomDistillerOptions options
,
85 const DistillerPageCallback
& callback
) {
87 // It is only possible to distill one page at a time. |ready_| is reset when
88 // the callback to OnDistillationDone happens.
90 distiller_page_callback_
= callback
;
91 distillation_start_
= base::TimeTicks::Now();
92 DistillPageImpl(gurl
, GetDistillerScriptWithOptions(options
,
97 void DistillerPage::OnDistillationDone(const GURL
& page_url
,
98 const base::Value
* value
) {
102 scoped_ptr
<dom_distiller::proto::DomDistillerResult
> distiller_result(
103 new dom_distiller::proto::DomDistillerResult());
105 if (value
->IsType(base::Value::TYPE_NULL
)) {
106 found_content
= false;
109 dom_distiller::proto::json::DomDistillerResult::ReadFromValue(
110 value
, distiller_result
.get());
111 if (!found_content
) {
112 DVLOG(1) << "Unable to parse DomDistillerResult.";
114 base::TimeDelta distillation_time
=
115 base::TimeTicks::Now() - distillation_start_
;
116 UMA_HISTOGRAM_TIMES("DomDistiller.Time.DistillPage", distillation_time
);
117 VLOG(1) << "DomDistiller.Time.DistillPage = " << distillation_time
;
119 if (distiller_result
->has_timing_info()) {
120 const dom_distiller::proto::TimingInfo
& timing
=
121 distiller_result
->timing_info();
122 if (timing
.has_markup_parsing_time()) {
124 "DomDistiller.Time.MarkupParsing",
125 base::TimeDelta::FromMillisecondsD(timing
.markup_parsing_time()));
127 if (timing
.has_document_construction_time()) {
129 "DomDistiller.Time.DocumentConstruction",
130 base::TimeDelta::FromMillisecondsD(
131 timing
.document_construction_time()));
133 if (timing
.has_article_processing_time()) {
135 "DomDistiller.Time.ArticleProcessing",
136 base::TimeDelta::FromMillisecondsD(
137 timing
.article_processing_time()));
139 if (timing
.has_formatting_time()) {
141 "DomDistiller.Time.Formatting",
142 base::TimeDelta::FromMillisecondsD(timing
.formatting_time()));
144 if (timing
.has_total_time()) {
146 "DomDistiller.Time.DistillationTotal",
147 base::TimeDelta::FromMillisecondsD(timing
.total_time()));
148 VLOG(1) << "DomDistiller.Time.DistillationTotal = " <<
149 base::TimeDelta::FromMillisecondsD(timing
.total_time());
152 if (distiller_result
->has_statistics_info()) {
153 const dom_distiller::proto::StatisticsInfo
& statistics
=
154 distiller_result
->statistics_info();
155 if (statistics
.has_word_count()) {
156 UMA_HISTOGRAM_CUSTOM_COUNTS(
157 "DomDistiller.Statistics.WordCount",
158 statistics
.word_count(),
165 base::MessageLoop::current()->PostTask(
167 base::Bind(distiller_page_callback_
,
168 base::Passed(&distiller_result
),
172 } // namespace dom_distiller