Skip to content
This repository was archived by the owner on Apr 10, 2025. It is now read-only.

Commit a2b10e4

Browse files
hillspcrowell
authored andcommitted
Flatten experiment variant to int for ga.js.
If experiment variant is not an int, inject JS comment with a warning.
1 parent 6506752 commit a2b10e4

File tree

3 files changed

+80
-24
lines changed

3 files changed

+80
-24
lines changed

net/instaweb/rewriter/insert_ga_filter.cc

+30-13
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,15 @@ extern const char kContentExperimentsJsClientUrl[] =
141141
// before ga.js loads you need to call this. The first argument is the
142142
// variant id, the second is the experiment id.
143143
extern const char kContentExperimentsSetChosenVariationSnippet[] =
144-
"cxApi.setChosenVariation('%s', '%s');";
144+
"cxApi.setChosenVariation(%d, '%s');";
145+
146+
// When using content experiments with ga.js, the variant ID must be numeric.
147+
// If the user requests a non-numeric variant with ga.js, we inject this
148+
// comment. The string is bracketed with newlines because otherwise it's
149+
// invisible in a wall of JavaScript.
150+
extern const char kContentExperimentsNonNumericVariantComment[] =
151+
"\n/* mod_pagespeed cannot inject experiment variant '%s' "
152+
"because it's not a number */\n";
145153

146154
// When using content experiments with analytics.js, after ga('create', ..._)
147155
// and before ga('[...].send', 'pageview'), we need to insert:
@@ -246,13 +254,30 @@ InsertGAFilter::AnalyticsStatus InsertGAFilter::FindSnippetInScript(
246254
return kUnusableSnippetFound;
247255
}
248256

249-
GoogleString InsertGAFilter::AnalyticsJsExperimentSnippet() {
257+
GoogleString InsertGAFilter::AnalyticsJsExperimentSnippet() const {
250258
return StringPrintf(
251259
kContentExperimentsSetExpAndVariantSnippet,
252260
driver()->options()->content_experiment_id().c_str(),
253261
driver()->options()->content_experiment_variant_id().c_str());
254262
}
255263

264+
GoogleString InsertGAFilter::GaJsExperimentSnippet() const {
265+
// ga.js requires a numeric variant id. Attempt to convert the string
266+
// variant ID to int and use that.
267+
const char* variant_id =
268+
driver()->options()->content_experiment_variant_id().c_str();
269+
int numeric_variant_id;
270+
if (StringToInt(variant_id, &numeric_variant_id)) {
271+
return StringPrintf(
272+
kContentExperimentsSetChosenVariationSnippet, numeric_variant_id,
273+
driver()->options()->content_experiment_id().c_str());
274+
} else {
275+
// Variant ID was non-numeric, so inject a warning.
276+
return StringPrintf(kContentExperimentsNonNumericVariantComment,
277+
variant_id);
278+
}
279+
}
280+
256281
// * If we've already inserted any GA snippet or if we found a GA snippet in the
257282
// original page, don't do anything.
258283
// * If we haven't found anything, and haven't inserted anything yet, insert the
@@ -294,11 +319,7 @@ void InsertGAFilter::EndDocument() {
294319
driver()->AddAttribute(
295320
cxapi, HtmlName::kSrc, kContentExperimentsJsClientUrl);
296321
InsertNodeAtBodyEnd(cxapi);
297-
298-
experiment_snippet = StringPrintf(
299-
kContentExperimentsSetChosenVariationSnippet,
300-
driver()->options()->content_experiment_variant_id().c_str(),
301-
driver()->options()->content_experiment_id().c_str());
322+
experiment_snippet = GaJsExperimentSnippet();
302323
} else {
303324
experiment_snippet = StringPrintf(
304325
kGAExperimentSnippet,
@@ -515,15 +536,11 @@ void InsertGAFilter::RewriteInlineScript(HtmlCharactersNode* characters) {
515536
void InsertGAFilter::HandleEndScript(HtmlElement* script) {
516537
if (!postponed_script_body_.empty()) {
517538
DCHECK(script == script_element_);
518-
GoogleString snippet_text = StringPrintf(
519-
kContentExperimentsSetChosenVariationSnippet,
520-
driver()->options()->content_experiment_variant_id().c_str(),
521-
driver()->options()->content_experiment_id().c_str());
522-
523539
driver()->InsertScriptAfterCurrent(
524540
kContentExperimentsJsClientUrl, true /* external */);
525541
driver()->InsertScriptAfterCurrent(
526-
StrCat(snippet_text, postponed_script_body_), false /* inline */);
542+
StrCat(GaJsExperimentSnippet(), postponed_script_body_),
543+
false /* inline */);
527544
added_experiment_snippet_ = true;
528545
postponed_script_body_.clear();
529546
}

net/instaweb/rewriter/insert_ga_filter_test.cc

+46-8
Original file line numberDiff line numberDiff line change
@@ -252,19 +252,24 @@ class InsertGAFilterTest : public RewriteTestBase {
252252
}
253253

254254
void SetUpContentExperiment(bool use_analytics_js) {
255+
SetUpContentExperiment(use_analytics_js, "456");
256+
}
257+
258+
void SetUpContentExperiment(bool use_analytics_js,
259+
const GoogleString& variant_id) {
255260
NullMessageHandler handler;
256261
RewriteOptions* options = rewrite_driver()->options()->Clone();
257262
options->set_use_analytics_js(use_analytics_js);
258263
options->set_running_experiment(true);
259-
ASSERT_TRUE(options->AddExperimentSpec(
264+
ASSERT_TRUE(options->AddExperimentSpec(StringPrintf(
260265
"id=2;percent=10;slot=4;options="
261266
"ContentExperimentID=123,"
262-
"ContentExperimentVariantID=456", &handler));
267+
"ContentExperimentVariantID=%s", variant_id.c_str()), &handler));
263268
ASSERT_TRUE(options->AddExperimentSpec(
264269
"id=7;percent=10;level=CoreFilters;slot=4;options="
265270
"ContentExperimentID=123,"
266271
"ContentExperimentVariantID=789", &handler));
267-
options->SetExperimentState(2); // Expecting cxid=123, cxvid=456.
272+
options->SetExperimentState(2); // Expecting cxid=123, cxvid=variant_id.
268273

269274
// Setting up experiments automatically enables AddInstrumentation.
270275
// Turn it off so our output is easier to understand.
@@ -376,7 +381,25 @@ TEST_F(InsertGAFilterTest, ExperimentGaJsCx) {
376381
"\"></script>").c_str(),
377382
"",
378383
StrCat(StringPrintf(kContentExperimentsSetChosenVariationSnippet,
379-
"456", "123"),
384+
456, "123"),
385+
StringPrintf(kGAJsSnippet, kGaId, "test.com",
386+
kGASpeedTracking)).c_str());
387+
ValidateExpected("ga.js cx experiment", kHtmlInput, output);
388+
}
389+
390+
TEST_F(InsertGAFilterTest, ExperimentGaJsCxString) {
391+
// Show that an attempt to insert a ga.js snippet with a string variant ID
392+
// results in a warning message.
393+
const GoogleString& kVariantText("StringVariant");
394+
SetUpContentExperiment(false, kVariantText);
395+
GoogleString output = StringPrintf(
396+
kHtmlOutputFormat,
397+
StrCat("<script src=\"",
398+
kContentExperimentsJsClientUrl,
399+
"\"></script>").c_str(),
400+
"",
401+
StrCat(StringPrintf(kContentExperimentsNonNumericVariantComment,
402+
kVariantText.c_str()),
380403
StringPrintf(kGAJsSnippet, kGaId, "test.com",
381404
kGASpeedTracking)).c_str());
382405
ValidateExpected("ga.js cx experiment", kHtmlInput, output);
@@ -411,6 +434,21 @@ TEST_F(InsertGAFilterTest, ExperimentAnalyticsJsCx) {
411434
ValidateExpected("analytics.js cx experiment", kHtmlInput, output);
412435
}
413436

437+
TEST_F(InsertGAFilterTest, ExperimentAnalyticsJsCxString) {
438+
// Show that we can insert an anlytics.js snippet that includes content
439+
// experiment tracking where the variant is a string.
440+
const GoogleString& kVariantText("StringVariant");
441+
SetUpContentExperiment(true, kVariantText);
442+
GoogleString output = StringPrintf(
443+
kHtmlOutputFormat, "", "", StringPrintf(
444+
kAnalyticsJsSnippet,
445+
kGaId,
446+
kAnalyticsJsIncreaseSiteSpeedTracking,
447+
StringPrintf(kContentExperimentsSetExpAndVariantSnippet,
448+
"123", kVariantText.c_str()).c_str()).c_str());
449+
ValidateExpected("analytics.js cx experiment", kHtmlInput, output);
450+
}
451+
414452
const char kHtmlInputWithGASnippetFormat[] =
415453
"<head>\n<title>Something</title>\n"
416454
"</head><body> Hello World!"
@@ -529,7 +567,7 @@ TEST_F(InsertGAFilterTest, SynchronousGAContentExperiment) {
529567
"\"></script><script>",
530568
StringPrintf(
531569
kContentExperimentsSetChosenVariationSnippet,
532-
"456", "123")).c_str(),
570+
456, "123")).c_str(),
533571
kGaId);
534572
ValidateExpected("extend sync ga.js for content experiment", input, output);
535573
}
@@ -568,7 +606,7 @@ TEST_F(InsertGAFilterTest, AsynchronousGAContentExperiment) {
568606
"\"></script><script>",
569607
StringPrintf(
570608
kContentExperimentsSetChosenVariationSnippet,
571-
"456", "123")).c_str(),
609+
456, "123")).c_str(),
572610
kGaId);
573611
ValidateExpected("extend async ga.js for content experiment", input, output);
574612
}
@@ -764,7 +802,7 @@ TEST_F(InsertGAFilterTest, ExistingGaJsContentExperimentNoCloseAnything) {
764802
"\"></script>"
765803
"<script>",
766804
StringPrintf(kContentExperimentsSetChosenVariationSnippet,
767-
"456", "123"),
805+
456, "123"),
768806
StringPrintf(kGAJsSnippet, kGaId, "test.com",
769807
kGASpeedTracking)).c_str());
770808

@@ -786,7 +824,7 @@ TEST_F(InsertGAFilterTest, AsynchronousGAContentExperimentFlush) {
786824
"\"></script><script>",
787825
StringPrintf(
788826
kContentExperimentsSetChosenVariationSnippet,
789-
"456", "123")).c_str(),
827+
456, "123")).c_str(),
790828
kGaId);
791829

792830
SetupWriter();

net/instaweb/rewriter/public/insert_ga_filter.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ extern const char kAnalyticsJsSnippet[];
4242
extern const char kAnalyticsJsIncreaseSiteSpeedTracking[];
4343
extern const char kAnalyticsJsIncreaseSiteSpeedTrackingMinimal[];
4444
extern const char kContentExperimentsJsClientUrl[];
45+
extern const char kContentExperimentsNonNumericVariantComment[];
4546
extern const char kContentExperimentsSetChosenVariationSnippet[];
4647
extern const char kContentExperimentsSetExpAndVariantSnippet[];
4748
extern const char kGASpeedTracking[];
@@ -112,9 +113,9 @@ class InsertGAFilter : public CommonFilter {
112113
// snippet depends in part on whether we've already seen a ga.js library load.
113114
AnalyticsStatus FindSnippetInScript(const GoogleString& s);
114115

115-
// Determine the snippet of JS we need to log a content experiment to
116-
// analytics.js.
117-
GoogleString AnalyticsJsExperimentSnippet();
116+
// Determine the snippet of JS we need to log a content experiment.
117+
GoogleString AnalyticsJsExperimentSnippet() const;
118+
GoogleString GaJsExperimentSnippet() const;
118119

119120
// Note: logs a warning if we're running with analytics.js and have asked it
120121
// to log to a custom variable (which isn't possible).

0 commit comments

Comments
 (0)