Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / dom_distiller / core / distiller_page.cc
blob97b86e84d3ed7ca62407ef3b37ea3aac792871b8
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"
7 #include "base/bind.h"
8 #include "base/json/json_writer.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/time/time.h"
17 #include "grit/components_resources.h"
18 #include "third_party/dom_distiller_js/dom_distiller.pb.h"
19 #include "third_party/dom_distiller_js/dom_distiller_json_converter.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "url/gurl.h"
23 namespace dom_distiller {
25 namespace {
27 const char* kOptionsPlaceholder = "$$OPTIONS";
28 const char* kStringifyPlaceholder = "$$STRINGIFY";
29 const char* kNewContextPlaceholder = "$$NEW_CONTEXT";
31 std::string GetDistillerScriptWithOptions(
32 const dom_distiller::proto::DomDistillerOptions& options,
33 bool stringify_output,
34 bool create_new_context) {
35 std::string script = ResourceBundle::GetSharedInstance()
36 .GetRawDataResource(IDR_DISTILLER_JS)
37 .as_string();
38 if (script.empty()) {
39 return "";
42 scoped_ptr<base::Value> options_value(
43 dom_distiller::proto::json::DomDistillerOptions::WriteToValue(options));
44 std::string options_json;
45 if (!base::JSONWriter::Write(*options_value, &options_json)) {
46 NOTREACHED();
48 size_t options_offset = script.find(kOptionsPlaceholder);
49 DCHECK_NE(std::string::npos, options_offset);
50 DCHECK_EQ(std::string::npos,
51 script.find(kOptionsPlaceholder, options_offset + 1));
52 script =
53 script.replace(options_offset, strlen(kOptionsPlaceholder), options_json);
55 std::string stringify = stringify_output ? "true" : "false";
56 size_t stringify_offset = script.find(kStringifyPlaceholder);
57 DCHECK_NE(std::string::npos, stringify_offset);
58 DCHECK_EQ(std::string::npos,
59 script.find(kStringifyPlaceholder, stringify_offset + 1));
60 script = script.replace(stringify_offset,
61 strlen(kStringifyPlaceholder),
62 stringify);
64 std::string new_context = create_new_context ? "true" : "false";
65 size_t new_context_offset = script.find(kNewContextPlaceholder);
66 DCHECK_NE(std::string::npos, new_context_offset);
67 DCHECK_EQ(std::string::npos,
68 script.find(kNewContextPlaceholder, new_context_offset + 1));
69 script = script.replace(new_context_offset,
70 strlen(kNewContextPlaceholder),
71 new_context);
73 return script;
78 DistillerPageFactory::~DistillerPageFactory() {}
80 DistillerPage::DistillerPage() : ready_(true) {}
82 DistillerPage::~DistillerPage() {}
84 void DistillerPage::DistillPage(
85 const GURL& gurl,
86 const dom_distiller::proto::DomDistillerOptions options,
87 const DistillerPageCallback& callback) {
88 DCHECK(ready_);
89 // It is only possible to distill one page at a time. |ready_| is reset when
90 // the callback to OnDistillationDone happens.
91 ready_ = false;
92 distiller_page_callback_ = callback;
93 distillation_start_ = base::TimeTicks::Now();
94 DistillPageImpl(gurl, GetDistillerScriptWithOptions(options,
95 StringifyOutput(),
96 CreateNewContext()));
99 void DistillerPage::OnDistillationDone(const GURL& page_url,
100 const base::Value* value) {
101 DCHECK(!ready_);
102 ready_ = true;
104 scoped_ptr<dom_distiller::proto::DomDistillerResult> distiller_result(
105 new dom_distiller::proto::DomDistillerResult());
106 bool found_content;
107 if (value->IsType(base::Value::TYPE_NULL)) {
108 found_content = false;
109 } else {
110 found_content =
111 dom_distiller::proto::json::DomDistillerResult::ReadFromValue(
112 value, distiller_result.get());
113 if (!found_content) {
114 DVLOG(1) << "Unable to parse DomDistillerResult.";
115 } else {
116 base::TimeDelta distillation_time =
117 base::TimeTicks::Now() - distillation_start_;
118 UMA_HISTOGRAM_TIMES("DomDistiller.Time.DistillPage", distillation_time);
119 VLOG(1) << "DomDistiller.Time.DistillPage = " << distillation_time;
121 if (distiller_result->has_timing_info()) {
122 const dom_distiller::proto::TimingInfo& timing =
123 distiller_result->timing_info();
124 if (timing.has_markup_parsing_time()) {
125 UMA_HISTOGRAM_TIMES(
126 "DomDistiller.Time.MarkupParsing",
127 base::TimeDelta::FromMillisecondsD(timing.markup_parsing_time()));
129 if (timing.has_document_construction_time()) {
130 UMA_HISTOGRAM_TIMES(
131 "DomDistiller.Time.DocumentConstruction",
132 base::TimeDelta::FromMillisecondsD(
133 timing.document_construction_time()));
135 if (timing.has_article_processing_time()) {
136 UMA_HISTOGRAM_TIMES(
137 "DomDistiller.Time.ArticleProcessing",
138 base::TimeDelta::FromMillisecondsD(
139 timing.article_processing_time()));
141 if (timing.has_formatting_time()) {
142 UMA_HISTOGRAM_TIMES(
143 "DomDistiller.Time.Formatting",
144 base::TimeDelta::FromMillisecondsD(timing.formatting_time()));
146 if (timing.has_total_time()) {
147 UMA_HISTOGRAM_TIMES(
148 "DomDistiller.Time.DistillationTotal",
149 base::TimeDelta::FromMillisecondsD(timing.total_time()));
150 VLOG(1) << "DomDistiller.Time.DistillationTotal = " <<
151 base::TimeDelta::FromMillisecondsD(timing.total_time());
154 if (distiller_result->has_statistics_info()) {
155 const dom_distiller::proto::StatisticsInfo& statistics =
156 distiller_result->statistics_info();
157 if (statistics.has_word_count()) {
158 UMA_HISTOGRAM_CUSTOM_COUNTS(
159 "DomDistiller.Statistics.WordCount",
160 statistics.word_count(),
161 1, 4000, 50);
167 base::ThreadTaskRunnerHandle::Get()->PostTask(
168 FROM_HERE, base::Bind(distiller_page_callback_,
169 base::Passed(&distiller_result), found_content));
172 } // namespace dom_distiller