Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(179)

Side by Side Diff: chrome/browser/resources/google_now/background_unittest.gtestjs

Issue 19822007: Updated Google Now to Check the Geolocation Access Preference (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@r213016
Patch Set: CR Feedback Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * Test fixture for ../background.js. 6 * Test fixture for ../background.js.
7 * @constructor 7 * @constructor
8 * @extends {testing.Test} 8 * @extends {testing.Test}
9 */ 9 */
10 function GoogleNowBackgroundUnitTest () { 10 function GoogleNowBackgroundUnitTest () {
(...skipping 12 matching lines...) Expand all
23 23
24 TEST_F('GoogleNowBackgroundUnitTest', 'AreTasksConflicting', function() { 24 TEST_F('GoogleNowBackgroundUnitTest', 'AreTasksConflicting', function() {
25 function testTaskPair(newTaskName, scheduledTaskName, expected) { 25 function testTaskPair(newTaskName, scheduledTaskName, expected) {
26 assertTrue(areTasksConflicting(newTaskName, scheduledTaskName) == expected, 26 assertTrue(areTasksConflicting(newTaskName, scheduledTaskName) == expected,
27 '(' + newTaskName + ', ' + scheduledTaskName + ')'); 27 '(' + newTaskName + ', ' + scheduledTaskName + ')');
28 } 28 }
29 29
30 testTaskPair(UPDATE_CARDS_TASK_NAME, UPDATE_CARDS_TASK_NAME, true); 30 testTaskPair(UPDATE_CARDS_TASK_NAME, UPDATE_CARDS_TASK_NAME, true);
31 testTaskPair(UPDATE_CARDS_TASK_NAME, DISMISS_CARD_TASK_NAME, false); 31 testTaskPair(UPDATE_CARDS_TASK_NAME, DISMISS_CARD_TASK_NAME, false);
32 testTaskPair(UPDATE_CARDS_TASK_NAME, RETRY_DISMISS_TASK_NAME, false); 32 testTaskPair(UPDATE_CARDS_TASK_NAME, RETRY_DISMISS_TASK_NAME, false);
33 testTaskPair(UPDATE_CARDS_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
33 34
34 testTaskPair(DISMISS_CARD_TASK_NAME, UPDATE_CARDS_TASK_NAME, false); 35 testTaskPair(DISMISS_CARD_TASK_NAME, UPDATE_CARDS_TASK_NAME, false);
35 testTaskPair(DISMISS_CARD_TASK_NAME, DISMISS_CARD_TASK_NAME, false); 36 testTaskPair(DISMISS_CARD_TASK_NAME, DISMISS_CARD_TASK_NAME, false);
36 testTaskPair(DISMISS_CARD_TASK_NAME, RETRY_DISMISS_TASK_NAME, false); 37 testTaskPair(DISMISS_CARD_TASK_NAME, RETRY_DISMISS_TASK_NAME, false);
38 testTaskPair(DISMISS_CARD_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
37 39
38 testTaskPair(RETRY_DISMISS_TASK_NAME, UPDATE_CARDS_TASK_NAME, true); 40 testTaskPair(RETRY_DISMISS_TASK_NAME, UPDATE_CARDS_TASK_NAME, true);
39 testTaskPair(RETRY_DISMISS_TASK_NAME, DISMISS_CARD_TASK_NAME, true); 41 testTaskPair(RETRY_DISMISS_TASK_NAME, DISMISS_CARD_TASK_NAME, true);
40 testTaskPair(RETRY_DISMISS_TASK_NAME, RETRY_DISMISS_TASK_NAME, true); 42 testTaskPair(RETRY_DISMISS_TASK_NAME, RETRY_DISMISS_TASK_NAME, true);
43 testTaskPair(RETRY_DISMISS_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
44
45 testTaskPair(STATE_CHANGED_TASK_NAME, UPDATE_CARDS_TASK_NAME, false);
46 testTaskPair(STATE_CHANGED_TASK_NAME, DISMISS_CARD_TASK_NAME, false);
47 testTaskPair(STATE_CHANGED_TASK_NAME, RETRY_DISMISS_TASK_NAME, false);
48 testTaskPair(STATE_CHANGED_TASK_NAME, STATE_CHANGED_TASK_NAME, false);
41 }); 49 });
42 50
43 /** 51 /**
44 * Mocks global functions and APIs that initialize() depends upon. 52 * Mocks global functions and APIs that initialize() depends upon.
45 * @param {Test} fixture Test fixture. 53 * @param {Test} fixture Test fixture.
46 */ 54 */
47 function mockInitializeDependencies(fixture) { 55 function mockInitializeDependencies(fixture) {
48 fixture.makeAndRegisterMockGlobals([ 56 fixture.makeAndRegisterMockGlobals([
49 'recordEvent', 57 'recordEvent',
50 'showWelcomeToast', 58 'showWelcomeToast',
51 'startPollingCards']); 59 'startPollingCards']);
52 fixture.makeAndRegisterMockApis( 60 fixture.makeAndRegisterMockApis([
53 ['storage.get', 'chrome.identity.getAuthToken']); 61 'chrome.identity.getAuthToken',
62 'chrome.location.clearWatch',
63 'chrome.notifications.getAll',
64 'chrome.preferencesPrivate.googleGeolocationAccessEnabled.get',
65 'storage.get',
66 'storage.set',
67 'tasks.add',
68 'updateCardsAttempts.isRunning',
69 'updateCardsAttempts.stop']);
vadimt 2013/07/26 01:00:37 Square bracket should be on a new line.
robliao 2013/07/26 01:12:17 Done.
70 }
71
72 /**
73 * Sets up the test to expect the state machine calls and send
74 * the specified state machine state. Currently used to test initialize().
75 * Note that this CAN NOT be used if any of the methods below are called
76 * outside of this context with the same argument matchers.
77 * expects() calls cannot be chained with the same argument matchers.
78 * @param {object} mockApisObj Mock APIs Object.
79 * @param {string} testIdentityToken getAuthToken callback token.
80 * @param {boolean} testGeolocationPref Geolocation Preference callback value.
81 * @param {boolean} testUserRespondedToToast User Response to toast
82 & callback value.
83 */
84 function expectStateMachineCalls(
85 mockApisObj,
86 testIdentityToken,
87 testGeolocationPref,
88 testUserRespondedToToast) {
89 var chromeIdentityGetAuthTokenSavedArgs = new SaveMockArguments();
90 mockApisObj.expects(once()).
91 chrome_identity_getAuthToken(
92 chromeIdentityGetAuthTokenSavedArgs.match(
93 eqJSON({interactive: false})),
94 chromeIdentityGetAuthTokenSavedArgs.match(ANYTHING)).
95 will(invokeCallback(
96 chromeIdentityGetAuthTokenSavedArgs, 1, testIdentityToken));
97
98 var googleGeolocationPrefGetSavedArgs = new SaveMockArguments();
99 mockApisObj.expects(once()).
100 chrome_preferencesPrivate_googleGeolocationAccessEnabled_get(
101 googleGeolocationPrefGetSavedArgs.match(eqJSON({})),
102 googleGeolocationPrefGetSavedArgs.match(ANYTHING)).
103 will(invokeCallback(
104 googleGeolocationPrefGetSavedArgs, 1, {value: testGeolocationPref}));
105
106 var storageGetSavedArgs = new SaveMockArguments();
107 mockApisObj.expects(once()).
108 storage_get(
109 storageGetSavedArgs.match(eq('userRespondedToToast')),
110 storageGetSavedArgs.match(ANYTHING)).
111 will(invokeCallback(storageGetSavedArgs, 1, testUserRespondedToToast));
112 }
113
114 /**
115 * Sets up the test to expect the initialization calls.
vadimt 2013/07/26 01:00:37 Please mention what function we are testing.
robliao 2013/07/26 01:12:17 Done.
116 * Note that this CAN NOT be used if any of the methods below are called
117 * outside of this context with the same argument matchers.
118 * expects() calls cannot be chained with the same argument matchers.
119 */
120 function expectInitialization(mockApisObj) {
121 mockApisObj.expects(once()).
122 chrome_location_clearWatch(ANYTHING);
123 mockApisObj.expects(once()).
124 updateCardsAttempts_stop();
125 mockApisObj.expects(once()).
126 storage_set(eqJSON({notificationsData: {}}));
127 var tasksAddSavedArgs = new SaveMockArguments();
128 mockApisObj.expects(once()).
129 tasks_add(
130 tasksAddSavedArgs.match(ANYTHING),
131 tasksAddSavedArgs.match(ANYTHING)).
132 will(invokeCallback(tasksAddSavedArgs, 1, function() {}));
133 var updateCardsAttemptsIsRunningSavedArgs = new SaveMockArguments();
134 mockApisObj.expects(once()).
135 updateCardsAttempts_isRunning(
136 updateCardsAttemptsIsRunningSavedArgs.match(ANYTHING)).
137 will(
138 invokeCallback(
139 updateCardsAttemptsIsRunningSavedArgs, 0, false));
54 } 140 }
55 141
56 TEST_F( 142 TEST_F(
57 'GoogleNowBackgroundUnitTest', 143 'GoogleNowBackgroundUnitTest',
58 'Initialize_ToastStateEmpty1', 144 'Initialize_ToastStateEmpty1',
59 function() { 145 function() {
60 // Tests the case when toast state is empty and NOTIFICATION_CARDS_URL is 146 // Tests the case when the user isn't signed in and NOTIFICATION_CARDS_URL
61 // not set. In this case, the function should quietly exit after finding 147 // is not set. Since NOTIFICATION_CARDS_URL is empty,
62 // out that NOTIFICATION_CARDS_URL is empty. 148 // nothing should start.
63 149
64 // Setup and expectations. 150 // Setup and expectations.
65 var testToastState = {};
66 NOTIFICATION_CARDS_URL = undefined; 151 NOTIFICATION_CARDS_URL = undefined;
152 var testIdentityToken = undefined;
153 var testGeolocationPref = false;
154 var testUserRespondedToToast = {};
67 155
68 mockInitializeDependencies(this); 156 mockInitializeDependencies(this);
69 157
70 this.mockGlobals.expects(once()).recordEvent( 158 this.mockGlobals.expects(once()).recordEvent(
71 DiagnosticEvent.EXTENSION_START); 159 DiagnosticEvent.EXTENSION_START);
72 var storageGetSavedArgs = new SaveMockArguments(); 160
73 this.mockApis.expects(once()). 161 expectInitialization(this.mockApis);
74 storage_get( 162
75 storageGetSavedArgs.match(eq('toastState')), 163 expectStateMachineCalls(
76 storageGetSavedArgs.match(ANYTHING)). 164 this.mockApis,
77 will(invokeCallback(storageGetSavedArgs, 1, testToastState)); 165 testIdentityToken,
166 testGeolocationPref,
167 testUserRespondedToToast);
168
169 var chromeNotificationGetAllSavedArgs = new SaveMockArguments();
170 this.mockApis.expects(exactly(2)).
171 chrome_notifications_getAll(
172 chromeNotificationGetAllSavedArgs.match(ANYTHING)).
173 will(
174 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}),
175 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}));
176
177 // TODO(robliao,vadimt): Determine the granularity of testing to perform.
78 178
79 // Invoking the tested function. 179 // Invoking the tested function.
80 initialize(); 180 initialize();
81 }); 181 });
82 182
83 TEST_F( 183 TEST_F(
84 'GoogleNowBackgroundUnitTest', 184 'GoogleNowBackgroundUnitTest',
85 'Initialize_ToastStateEmpty2', 185 'Initialize_ToastStateEmpty2',
86 function() { 186 function() {
87 // Tests the case when toast state is empty and NOTIFICATION_CARDS_URL is 187 // Tests the case when NOTIFICATION_CARDS_URL is but getAuthToken fails
88 // set, but getAuthToken fails most likely because the user is not signed 188 // most likely because the user is not signed in. In this case, the
89 // in. In this case, the function should quietly exit after finding out 189 // function should quietly exit after finding out that getAuthToken fails.
90 // that getAuthToken fails.
91 190
92 // Setup and expectations. 191 // Setup and expectations.
93 var testToastState = {};
94 NOTIFICATION_CARDS_URL = 'https://some.server.url.com'; 192 NOTIFICATION_CARDS_URL = 'https://some.server.url.com';
95 var testIdentityToken = undefined; 193 var testIdentityToken = undefined;
194 var testGeolocationPref = false;
195 var testUserRespondedToToast = {};
96 196
97 mockInitializeDependencies(this); 197 mockInitializeDependencies(this);
98 198
99 this.mockGlobals.expects(once()).recordEvent( 199 this.mockGlobals.expects(once()).recordEvent(
100 DiagnosticEvent.EXTENSION_START); 200 DiagnosticEvent.EXTENSION_START);
101 var storageGetSavedArgs = new SaveMockArguments(); 201
102 this.mockApis.expects(once()). 202 expectInitialization(this.mockApis);
103 storage_get( 203
104 storageGetSavedArgs.match(eq('toastState')), 204 expectStateMachineCalls(
105 storageGetSavedArgs.match(ANYTHING)). 205 this.mockApis,
106 will(invokeCallback(storageGetSavedArgs, 1, testToastState)); 206 testIdentityToken,
107 var chromeIdentityGetAuthTokenSavedArgs = new SaveMockArguments(); 207 testGeolocationPref,
108 this.mockApis.expects(once()). 208 testUserRespondedToToast);
109 chrome_identity_getAuthToken( 209
110 chromeIdentityGetAuthTokenSavedArgs.match( 210 var chromeNotificationGetAllSavedArgs = new SaveMockArguments();
111 eqJSON({interactive: false})), 211 this.mockApis.expects(exactly(2)).
112 chromeIdentityGetAuthTokenSavedArgs.match(ANYTHING)). 212 chrome_notifications_getAll(
113 will(invokeCallback( 213 chromeNotificationGetAllSavedArgs.match(ANYTHING)).
114 chromeIdentityGetAuthTokenSavedArgs, 1, testIdentityToken)); 214 will(
215 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}),
216 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}));
115 217
116 // Invoking the tested function. 218 // Invoking the tested function.
117 initialize(); 219 initialize();
118 }); 220 });
119 221
120 TEST_F( 222 TEST_F(
121 'GoogleNowBackgroundUnitTest', 223 'GoogleNowBackgroundUnitTest',
122 'Initialize_ToastStateEmpty3', 224 'Initialize_ToastStateEmpty3',
123 function() { 225 function() {
124 // Tests the case when toast state is empty and NOTIFICATION_CARDS_URL is 226 // Tests the case when NOTIFICATION_CARDS_URL is set, getAuthToken
125 // set, and getAuthToken succeeds. In this case, the function should 227 // succeeds, and the user has never responded to the toast.
126 // invoke showWelcomeToast(). 228 // In this case, the function should invoke showWelcomeToast().
127 229
128 // Setup and expectations. 230 // Setup and expectations.
129 var testToastState = {};
130 NOTIFICATION_CARDS_URL = 'https://some.server.url.com'; 231 NOTIFICATION_CARDS_URL = 'https://some.server.url.com';
131 var testIdentityToken = 'some identity token'; 232 var testIdentityToken = 'some identity token';
233 var testGeolocationPref = false;
234 var testUserRespondedToToast = {};
132 235
133 mockInitializeDependencies(this); 236 mockInitializeDependencies(this);
134 237
135 this.mockGlobals.expects(once()).recordEvent( 238 this.mockGlobals.expects(once()).recordEvent(
136 DiagnosticEvent.EXTENSION_START); 239 DiagnosticEvent.EXTENSION_START);
137 var storageGetSavedArgs = new SaveMockArguments(); 240
138 this.mockApis.expects(once()). 241 expectInitialization(this.mockApis);
139 storage_get( 242
140 storageGetSavedArgs.match(eq('toastState')), 243 expectStateMachineCalls(
141 storageGetSavedArgs.match(ANYTHING)). 244 this.mockApis,
142 will(invokeCallback(storageGetSavedArgs, 1, testToastState)); 245 testIdentityToken,
143 var chromeIdentityGetAuthTokenSavedArgs = new SaveMockArguments(); 246 testGeolocationPref,
144 this.mockApis.expects(once()). 247 testUserRespondedToToast);
145 chrome_identity_getAuthToken( 248
146 chromeIdentityGetAuthTokenSavedArgs.match( 249 var chromeNotificationGetAllSavedArgs = new SaveMockArguments();
147 eqJSON({interactive: false})), 250 this.mockApis.expects(exactly(2)).
148 chromeIdentityGetAuthTokenSavedArgs.match(ANYTHING)). 251 chrome_notifications_getAll(
149 will(invokeCallback( 252 chromeNotificationGetAllSavedArgs.match(ANYTHING)).
150 chromeIdentityGetAuthTokenSavedArgs, 1, testIdentityToken)); 253 will(
254 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}),
255 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}));
256
151 this.mockGlobals.expects(once()).showWelcomeToast(); 257 this.mockGlobals.expects(once()).showWelcomeToast();
152 258
153 // Invoking the tested function. 259 // Invoking the tested function.
154 initialize(); 260 initialize();
155 }); 261 });
156 262
157 TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_ToastStateYes', function() { 263 TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_ToastAnswerYes', function() {
158 // Tests the case when the user has answered "yes" to the toast in the past. 264 // Tests the case when the user has answered "yes" to the toast in the past.
159 // In this case, the function should invoke startPollingCards(). 265 // In this case, the function should invoke startPollingCards().
160 266
161 // Setup and expectations. 267 // Setup and expectations.
162 var testToastState = {toastState: ToastOptionResponse.CHOSE_YES}; 268 NOTIFICATION_CARDS_URL = 'https://some.server.url.com';
269 var testIdentityToken = 'some identity token';
270 var testGeolocationPref = true;
271 var testUserRespondedToToast = {userRespondedToToast: true};
163 272
164 mockInitializeDependencies(this); 273 mockInitializeDependencies(this);
165 274
166 this.mockGlobals.expects(once()).recordEvent(DiagnosticEvent.EXTENSION_START); 275 this.mockGlobals.expects(once()).recordEvent(
167 var storageGetSavedArgs = new SaveMockArguments(); 276 DiagnosticEvent.EXTENSION_START);
168 this.mockApis.expects(once()).
169 storage_get(
170 storageGetSavedArgs.match(eq('toastState')),
171 storageGetSavedArgs.match(ANYTHING)).
172 will(invokeCallback(storageGetSavedArgs, 1, testToastState));
173 this.mockGlobals.expects(once()).startPollingCards();
174 277
175 // Invoking the tested function. 278 expectInitialization(this.mockApis);
176 initialize(); 279
280 expectStateMachineCalls(
281 this.mockApis,
282 testIdentityToken,
283 testGeolocationPref,
284 testUserRespondedToToast);
285
286 var chromeNotificationGetAllSavedArgs = new SaveMockArguments();
287 this.mockApis.expects(exactly(2)).
288 chrome_notifications_getAll(
289 chromeNotificationGetAllSavedArgs.match(ANYTHING)).
290 will(
291 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}),
292 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}));
293
294 this.mockGlobals.expects(once()).startPollingCards();
295
296 // Invoking the tested function.
297 initialize();
177 }); 298 });
178 299
179 TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_ToastStateNo', function() { 300 TEST_F('GoogleNowBackgroundUnitTest', 'Initialize_ToastAnswerNo', function() {
180 // Tests the case when the user has answered "no" to the toast in the past. 301 // Tests the case when the user has answered "no" to the toast in the past.
181 // In this case, the function should do nothing. 302 // In this case, the function should do nothing.
182 303
183 // Setup and expectations. 304 // Setup and expectations.
184 var testToastState = {toastState: ToastOptionResponse.CHOSE_NO}; 305 // Setup and expectations.
vadimt 2013/07/26 01:00:37 Duplication of // Setup and expectations.
robliao 2013/07/26 01:12:17 Done.
306 NOTIFICATION_CARDS_URL = 'https://some.server.url.com';
307 var testIdentityToken = 'some identity token';
308 var testGeolocationPref = false;
309 var testUserRespondedToToast = {userRespondedToToast: true};
185 310
186 mockInitializeDependencies(this); 311 mockInitializeDependencies(this);
187 312
188 this.mockGlobals.expects(once()).recordEvent(DiagnosticEvent.EXTENSION_START); 313 this.mockGlobals.expects(once()).recordEvent(
189 var storageGetSavedArgs = new SaveMockArguments(); 314 DiagnosticEvent.EXTENSION_START);
190 this.mockApis.expects(once()).
191 storage_get(
192 storageGetSavedArgs.match(eq('toastState')),
193 storageGetSavedArgs.match(ANYTHING)).
194 will(invokeCallback(storageGetSavedArgs, 1, testToastState));
195 315
196 // Invoking the tested function. 316 expectInitialization(this.mockApis);
197 initialize(); 317
318 expectStateMachineCalls(
319 this.mockApis,
vadimt 2013/07/26 01:00:37 Indent by 4.
robliao 2013/07/26 01:12:17 Done.
320 testIdentityToken,
321 testGeolocationPref,
322 testUserRespondedToToast);
323
324 var chromeNotificationGetAllSavedArgs = new SaveMockArguments();
325 this.mockApis.expects(exactly(2)).
326 chrome_notifications_getAll(
327 chromeNotificationGetAllSavedArgs.match(ANYTHING)).
328 will(
329 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}),
330 invokeCallback(chromeNotificationGetAllSavedArgs, 0, {}));
331
332 // Invoking the tested function.
333 initialize();
198 }); 334 });
199 335
200 /** 336 /**
201 * Mocks global functions and APIs that onNotificationClicked() depends upon. 337 * Mocks global functions and APIs that onNotificationClicked() depends upon.
202 * @param {Test} fixture Test fixture. 338 * @param {Test} fixture Test fixture.
203 */ 339 */
204 function mockOnNotificationClickedDependencies(fixture) { 340 function mockOnNotificationClickedDependencies(fixture) {
205 fixture.makeAndRegisterMockApis([ 341 fixture.makeAndRegisterMockApis([
206 'storage.get', 342 'storage.get',
207 'chrome.tabs.create', 343 'chrome.tabs.create',
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
369 chromeTabsCreateSavedArgs.match(eqJSON({url: testActionUrl})), 505 chromeTabsCreateSavedArgs.match(eqJSON({url: testActionUrl})),
370 chromeTabsCreateSavedArgs.match(ANYTHING)). 506 chromeTabsCreateSavedArgs.match(ANYTHING)).
371 will(invokeCallback(chromeTabsCreateSavedArgs, 1, testCreatedTab)); 507 will(invokeCallback(chromeTabsCreateSavedArgs, 1, testCreatedTab));
372 this.mockApis.expects(once()).chrome_windows_create( 508 this.mockApis.expects(once()).chrome_windows_create(
373 eqJSON({url: testActionUrl})); 509 eqJSON({url: testActionUrl}));
374 510
375 // Invoking the tested function. 511 // Invoking the tested function.
376 onNotificationClicked( 512 onNotificationClicked(
377 testNotificationId, this.mockLocalFunctions.functions().selector); 513 testNotificationId, this.mockLocalFunctions.functions().selector);
378 }); 514 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698