"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "add_instrumentation_filter.cc" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
1.13.35.2_vs_1.14.36.1.
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20
21 #include "net/instaweb/rewriter/public/add_instrumentation_filter.h"
22
23 #include "base/logging.h"
24 #include "net/instaweb/http/public/request_context.h"
25 #include "net/instaweb/http/public/request_timing_info.h"
26 #include "net/instaweb/rewriter/public/experiment_util.h"
27 #include "net/instaweb/rewriter/public/request_properties.h"
28 #include "net/instaweb/rewriter/public/rewrite_driver.h"
29 #include "net/instaweb/rewriter/public/rewrite_options.h"
30 #include "net/instaweb/rewriter/public/server_context.h"
31 #include "net/instaweb/rewriter/public/static_asset_manager.h"
32 #include "pagespeed/kernel/base/escaping.h"
33 #include "pagespeed/kernel/base/ref_counted_ptr.h"
34 #include "pagespeed/kernel/base/statistics.h"
35 #include "pagespeed/kernel/base/string.h"
36 #include "pagespeed/kernel/base/string_util.h"
37 #include "pagespeed/kernel/html/html_element.h"
38 #include "pagespeed/kernel/html/html_name.h"
39 #include "pagespeed/kernel/html/html_node.h"
40 #include "pagespeed/kernel/http/google_url.h"
41 #include "pagespeed/kernel/http/http_names.h"
42 #include "pagespeed/kernel/http/response_headers.h"
43
44 namespace net_instaweb {
45
46 namespace {
47
48 // The javascript tag to insert in the top of the <head> element. We want this
49 // as early as possible in the html. It must be short and fast.
50 const char kHeadScriptPedantic[] =
51 "<script type='text/javascript'>"
52 "window.mod_pagespeed_start = Number(new Date());"
53 "</script>";
54
55 // script tag without type attribute
56 const char kHeadScriptNonPedantic[] =
57 "<script>"
58 "window.mod_pagespeed_start = Number(new Date());"
59 "</script>";
60
61 } // namespace
62
63 // Timing tag for total page load time. Also embedded in kTailScriptFormat
64 // above via the second %s.
65 // TODO(jud): These values would be better set to "load" and "beforeunload".
66 const char AddInstrumentationFilter::kLoadTag[] = "load:";
67 const char AddInstrumentationFilter::kUnloadTag[] = "unload:";
68
69 // Counters.
70 const char AddInstrumentationFilter::kInstrumentationScriptAddedCount[] =
71 "instrumentation_filter_script_added_count";
72 AddInstrumentationFilter::AddInstrumentationFilter(RewriteDriver* driver)
73 : CommonFilter(driver),
74 found_head_(false),
75 added_head_script_(false),
76 added_unload_script_(false) {
77 Statistics* stats = driver->server_context()->statistics();
78 instrumentation_script_added_count_ = stats->GetVariable(
79 kInstrumentationScriptAddedCount);
80 }
81
82 AddInstrumentationFilter::~AddInstrumentationFilter() {}
83
84 void AddInstrumentationFilter::InitStats(Statistics* statistics) {
85 statistics->AddVariable(kInstrumentationScriptAddedCount);
86 }
87
88 void AddInstrumentationFilter::StartDocumentImpl() {
89 found_head_ = false;
90 added_head_script_ = false;
91 added_unload_script_ = false;
92 }
93
94 void AddInstrumentationFilter::AddHeadScript(HtmlElement* element) {
95 // IE doesn't like tags other than title or meta at the start of the
96 // head. The MSDN page says:
97 // The X-UA-Compatible header isn't case sensitive; however, it must appear
98 // in the header of the webpage (the HEAD section) before all other elements
99 // except for the title element and other meta elements.
100 // Reference: http://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx
101 if (element->keyword() != HtmlName::kTitle &&
102 element->keyword() != HtmlName::kMeta) {
103 added_head_script_ = true;
104 // TODO(abliss): add an actual element instead, so other filters can
105 // rewrite this JS
106 HtmlCharactersNode* script = nullptr;
107 if (driver()->options()->Enabled(RewriteOptions::kPedantic)) {
108 script = driver()->NewCharactersNode(nullptr, kHeadScriptPedantic);
109 } else {
110 script = driver()->NewCharactersNode(nullptr, kHeadScriptNonPedantic);
111 }
112 driver()->InsertNodeBeforeCurrent(script);
113 instrumentation_script_added_count_->Add(1);
114 }
115 }
116
117 void AddInstrumentationFilter::StartElementImpl(HtmlElement* element) {
118 if (found_head_ && !added_head_script_) {
119 AddHeadScript(element);
120 }
121 if (!found_head_ && element->keyword() == HtmlName::kHead) {
122 found_head_ = true;
123 }
124 }
125
126 void AddInstrumentationFilter::EndElementImpl(HtmlElement* element) {
127 if (found_head_ && element->keyword() == HtmlName::kHead) {
128 if (!added_head_script_) {
129 AddHeadScript(element);
130 }
131 if (driver()->options()->report_unload_time() &&
132 !added_unload_script_) {
133 GoogleString js = GetScriptJs(kUnloadTag);
134 HtmlElement* script = driver()->NewElement(element, HtmlName::kScript);
135 if (!driver()->defer_instrumentation_script()) {
136 driver()->AddAttribute(script, HtmlName::kDataPagespeedNoDefer,
137 StringPiece());
138 }
139 driver()->InsertNodeBeforeCurrent(script);
140 AddJsToElement(js, script);
141 added_unload_script_ = true;
142 }
143 }
144 }
145
146 void AddInstrumentationFilter::EndDocument() {
147 // We relied on the existence of a <head> element. This should have been
148 // assured by add_head_filter.
149 if (!found_head_) {
150 LOG(WARNING) << "No <head> found for URL " << driver()->url();
151 return;
152 }
153 GoogleString js = GetScriptJs(kLoadTag);
154 HtmlElement* script = driver()->NewElement(nullptr, HtmlName::kScript);
155 if (!driver()->defer_instrumentation_script()) {
156 driver()->AddAttribute(script, HtmlName::kDataPagespeedNoDefer,
157 StringPiece());
158 }
159 InsertNodeAtBodyEnd(script);
160 AddJsToElement(js, script);
161 }
162
163 GoogleString AddInstrumentationFilter::GetScriptJs(StringPiece event) {
164 GoogleString js;
165 StaticAssetManager* static_asset_manager =
166 driver()->server_context()->static_asset_manager();
167 // Only add the static JS once.
168 if (!added_unload_script_) {
169 if (driver()->options()->enable_extended_instrumentation()) {
170 js = static_asset_manager->GetAsset(
171 StaticAssetEnum::EXTENDED_INSTRUMENTATION_JS, driver()->options());
172 }
173 StrAppend(&js, static_asset_manager->GetAsset(
174 StaticAssetEnum::ADD_INSTRUMENTATION_JS, driver()->options()));
175 }
176
177 GoogleString js_event = (event == kLoadTag) ? "load" : "beforeunload";
178
179 const RewriteOptions::BeaconUrl& beacons = driver()->options()->beacon_url();
180 const GoogleString* beacon_url =
181 driver()->IsHttps() ? &beacons.https : &beacons.http;
182 GoogleString extra_params;
183 if (driver()->options()->running_experiment()) {
184 int experiment_state = driver()->options()->experiment_id();
185 if (experiment_state != experiment::kExperimentNotSet &&
186 experiment_state != experiment::kNoExperiment) {
187 StrAppend(&extra_params, "&exptid=",
188 IntegerToString(driver()->options()->experiment_id()));
189 }
190 }
191
192 const RequestTimingInfo& timing_info =
193 driver()->request_context()->timing_info();
194 int64 header_fetch_ms;
195 if (timing_info.GetFetchHeaderLatencyMs(&header_fetch_ms)) {
196 // If time taken to fetch the http header is not set, then the response
197 // came from cache.
198 StrAppend(&extra_params, "&hft=", Integer64ToString(header_fetch_ms));
199 }
200 int64 fetch_ms;
201 if (timing_info.GetFetchLatencyMs(&fetch_ms)) {
202 // If time taken to fetch the resource is not set, then the response
203 // came from cache.
204 StrAppend(&extra_params, "&ft=", Integer64ToString(fetch_ms));
205 }
206 int64 ttfb_ms;
207 if (timing_info.GetTimeToFirstByte(&ttfb_ms)) {
208 StrAppend(&extra_params, "&s_ttfb=", Integer64ToString(ttfb_ms));
209 }
210
211 // Append the http response code.
212 if (driver()->response_headers() != nullptr &&
213 driver()->response_headers()->status_code() > 0 &&
214 driver()->response_headers()->status_code() != HttpStatus::kOK) {
215 StrAppend(&extra_params, "&rc=", IntegerToString(
216 driver()->response_headers()->status_code()));
217 }
218 // Append the request id.
219 if (driver()->request_context()->request_id() > 0) {
220 StrAppend(&extra_params, "&id=", Integer64ToString(
221 driver()->request_context()->request_id()));
222 }
223
224 GoogleString html_url;
225 EscapeToJsStringLiteral(driver()->google_url().Spec(),
226 false, /* no quotes */
227 &html_url);
228
229 StrAppend(&js, "\npagespeed.addInstrumentationInit(");
230 StrAppend(&js, "'", *beacon_url, "', ");
231 StrAppend(&js, "'", js_event, "', ");
232 StrAppend(&js, "'", extra_params, "', ");
233 StrAppend(&js, "'", html_url, "');");
234
235 return js;
236 }
237
238 void AddInstrumentationFilter::DetermineEnabled(GoogleString* disabled_reason) {
239 set_is_enabled(!driver()->request_properties()->IsBot());
240 }
241
242 } // namespace net_instaweb