1#!/usr/bin/env python 2# 3# Copyright (c) 2012 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Extract UserMetrics "actions" strings from the Chrome source. 8 9This program generates the list of known actions we expect to see in the 10user behavior logs. It walks the Chrome source, looking for calls to 11UserMetrics functions, extracting actions and warning on improper calls, 12as well as generating the lists of possible actions in situations where 13there are many possible actions. 14 15See also: 16 content/browser/user_metrics.h 17 http://wiki.corp.google.com/twiki/bin/view/Main/ChromeUserExperienceMetrics 18 19If run with a "--hash" argument, chromeactions.txt will be updated. 20""" 21 22__author__ = 'evanm (Evan Martin)' 23 24import hashlib 25from HTMLParser import HTMLParser 26import os 27import re 28import sys 29 30sys.path.insert(1, os.path.join(sys.path[0], '..', '..', 'python')) 31from google import path_utils 32 33# Files that are known to use content::RecordComputedAction(), which means 34# they require special handling code in this script. 35# To add a new file, add it to this list and add the appropriate logic to 36# generate the known actions to AddComputedActions() below. 37KNOWN_COMPUTED_USERS = ( 38 'back_forward_menu_model.cc', 39 'options_page_view.cc', 40 'render_view_host.cc', # called using webkit identifiers 41 'user_metrics.cc', # method definition 42 'new_tab_ui.cc', # most visited clicks 1-9 43 'extension_metrics_module.cc', # extensions hook for user metrics 44 'safe_browsing_blocking_page.cc', # various interstitial types and actions 45 'language_options_handler_common.cc', # languages and input methods in CrOS 46 'cros_language_options_handler.cc', # languages and input methods in CrOS 47 'about_flags.cc', # do not generate a warning; see AddAboutFlagsActions() 48 'external_metrics.cc', # see AddChromeOSActions() 49 'core_options_handler.cc', # see AddWebUIActions() 50 'browser_render_process_host.cc', # see AddRendererActions() 51 'render_thread_impl.cc', # impl of RenderThread::RecordComputedAction() 52 'render_process_host_impl.cc', # browser side impl for 53 # RenderThread::RecordComputedAction() 54 'mock_render_thread.cc', # mock of RenderThread::RecordComputedAction() 55 'ppb_pdf_impl.cc', # see AddClosedSourceActions() 56 'pepper_pdf_host.cc', # see AddClosedSourceActions() 57 'key_systems_support_uma.cc', # See AddKeySystemSupportActions() 58) 59 60# Language codes used in Chrome. The list should be updated when a new 61# language is added to app/l10n_util.cc, as follows: 62# 63# % (cat app/l10n_util.cc | \ 64# perl -n0e 'print $1 if /kAcceptLanguageList.*?\{(.*?)\}/s' | \ 65# perl -nle 'print $1, if /"(.*)"/'; echo 'es-419') | \ 66# sort | perl -pe "s/(.*)\n/'\$1', /" | \ 67# fold -w75 -s | perl -pe 's/^/ /;s/ $//'; echo 68# 69# The script extracts language codes from kAcceptLanguageList, but es-419 70# (Spanish in Latin America) is an exception. 71LANGUAGE_CODES = ( 72 'af', 'am', 'ar', 'az', 'be', 'bg', 'bh', 'bn', 'br', 'bs', 'ca', 'co', 73 'cs', 'cy', 'da', 'de', 'de-AT', 'de-CH', 'de-DE', 'el', 'en', 'en-AU', 74 'en-CA', 'en-GB', 'en-NZ', 'en-US', 'en-ZA', 'eo', 'es', 'es-419', 'et', 75 'eu', 'fa', 'fi', 'fil', 'fo', 'fr', 'fr-CA', 'fr-CH', 'fr-FR', 'fy', 76 'ga', 'gd', 'gl', 'gn', 'gu', 'ha', 'haw', 'he', 'hi', 'hr', 'hu', 'hy', 77 'ia', 'id', 'is', 'it', 'it-CH', 'it-IT', 'ja', 'jw', 'ka', 'kk', 'km', 78 'kn', 'ko', 'ku', 'ky', 'la', 'ln', 'lo', 'lt', 'lv', 'mk', 'ml', 'mn', 79 'mo', 'mr', 'ms', 'mt', 'nb', 'ne', 'nl', 'nn', 'no', 'oc', 'om', 'or', 80 'pa', 'pl', 'ps', 'pt', 'pt-BR', 'pt-PT', 'qu', 'rm', 'ro', 'ru', 'sd', 81 'sh', 'si', 'sk', 'sl', 'sn', 'so', 'sq', 'sr', 'st', 'su', 'sv', 'sw', 82 'ta', 'te', 'tg', 'th', 'ti', 'tk', 'to', 'tr', 'tt', 'tw', 'ug', 'uk', 83 'ur', 'uz', 'vi', 'xh', 'yi', 'yo', 'zh', 'zh-CN', 'zh-TW', 'zu', 84) 85 86# Input method IDs used in Chrome OS. The list should be updated when a 87# new input method is added to 88# chrome/browser/chromeos/input_method/input_methods.txt in the Chrome tree, as 89# follows: 90# 91# % sort chrome/browser/chromeos/input_method/input_methods.txt | \ 92# perl -ne "print \"'\$1', \" if /^([^#]+?)\s/" | \ 93# fold -w75 -s | perl -pe 's/^/ /;s/ $//'; echo 94# 95# The script extracts input method IDs from input_methods.txt. 96INPUT_METHOD_IDS = ( 97 'english-m', 'm17n:am:sera', 'm17n:ar:kbd', 'm17n:bn:itrans', 98 'm17n:fa:isiri', 'm17n:gu:itrans', 'm17n:hi:itrans', 'm17n:kn:itrans', 99 'm17n:ml:itrans', 'm17n:mr:itrans', 'm17n:ta:inscript', 'm17n:ta:itrans', 100 'm17n:ta:phonetic', 'm17n:ta:tamil99', 'm17n:ta:typewriter', 101 'm17n:te:itrans', 'm17n:th:kesmanee', 'm17n:th:pattachote', 102 'm17n:th:tis820', 'm17n:vi:tcvn', 'm17n:vi:telex', 'm17n:vi:viqr', 103 'm17n:vi:vni', 'm17n:zh:cangjie', 'm17n:zh:quick', 'mozc', 'mozc-chewing', 104 'mozc-dv', 'mozc-hangul', 'mozc-jp', 'pinyin', 'pinyin-dv', 'xkb:be::fra', 105 'xkb:be::ger', 'xkb:be::nld', 'xkb:bg::bul', 'xkb:bg:phonetic:bul', 106 'xkb:br::por', 'xkb:ca::fra', 'xkb:ca:eng:eng', 'xkb:ch::ger', 107 'xkb:ch:fr:fra', 'xkb:cz::cze', 'xkb:de::ger', 'xkb:de:neo:ger', 108 'xkb:dk::dan', 'xkb:ee::est', 'xkb:es::spa', 'xkb:es:cat:cat', 109 'xkb:fi::fin', 'xkb:fr::fra', 'xkb:gb:dvorak:eng', 'xkb:gb:extd:eng', 110 'xkb:gr::gre', 'xkb:hr::scr', 'xkb:hu::hun', 'xkb:il::heb', 'xkb:it::ita', 111 'xkb:jp::jpn', 'xkb:kr:kr104:kor', 'xkb:latam::spa', 'xkb:lt::lit', 112 'xkb:lv:apostrophe:lav', 'xkb:no::nob', 'xkb:pl::pol', 'xkb:pt::por', 113 'xkb:ro::rum', 'xkb:rs::srp', 'xkb:ru::rus', 'xkb:ru:phonetic:rus', 114 'xkb:se::swe', 'xkb:si::slv', 'xkb:sk::slo', 'xkb:tr::tur', 'xkb:ua::ukr', 115 'xkb:us::eng', 'xkb:us:altgr-intl:eng', 'xkb:us:colemak:eng', 116 'xkb:us:dvorak:eng', 'xkb:us:intl:eng', 117) 118 119# The path to the root of the repository. 120REPOSITORY_ROOT = os.path.join(path_utils.ScriptDir(), '..', '..', '..') 121 122number_of_files_total = 0 123 124 125def AddComputedActions(actions): 126 """Add computed actions to the actions list. 127 128 Arguments: 129 actions: set of actions to add to. 130 """ 131 132 # Actions for back_forward_menu_model.cc. 133 for dir in ('BackMenu_', 'ForwardMenu_'): 134 actions.add(dir + 'ShowFullHistory') 135 actions.add(dir + 'Popup') 136 for i in range(1, 20): 137 actions.add(dir + 'HistoryClick' + str(i)) 138 actions.add(dir + 'ChapterClick' + str(i)) 139 140 # Actions for new_tab_ui.cc. 141 for i in range(1, 10): 142 actions.add('MostVisited%d' % i) 143 144 # Actions for safe_browsing_blocking_page.cc. 145 for interstitial in ('Phishing', 'Malware', 'Multiple'): 146 for action in ('Show', 'Proceed', 'DontProceed', 'ForcedDontProceed'): 147 actions.add('SBInterstitial%s%s' % (interstitial, action)) 148 149 # Actions for language_options_handler.cc (Chrome OS specific). 150 for input_method_id in INPUT_METHOD_IDS: 151 actions.add('LanguageOptions_DisableInputMethod_%s' % input_method_id) 152 actions.add('LanguageOptions_EnableInputMethod_%s' % input_method_id) 153 actions.add('InputMethodOptions_Open_%s' % input_method_id) 154 for language_code in LANGUAGE_CODES: 155 actions.add('LanguageOptions_UiLanguageChange_%s' % language_code) 156 actions.add('LanguageOptions_SpellCheckLanguageChange_%s' % language_code) 157 158def AddWebKitEditorActions(actions): 159 """Add editor actions from editor_client_impl.cc. 160 161 Arguments: 162 actions: set of actions to add to. 163 """ 164 action_re = re.compile(r'''\{ [\w']+, +\w+, +"(.*)" +\},''') 165 166 editor_file = os.path.join(REPOSITORY_ROOT, 'webkit', 'api', 'src', 167 'EditorClientImpl.cc') 168 for line in open(editor_file): 169 match = action_re.search(line) 170 if match: # Plain call to RecordAction 171 actions.add(match.group(1)) 172 173def AddClosedSourceActions(actions): 174 """Add actions that are in code which is not checked out by default 175 176 Arguments 177 actions: set of actions to add to. 178 """ 179 actions.add('PDF.FitToHeightButton') 180 actions.add('PDF.FitToWidthButton') 181 actions.add('PDF.LoadFailure') 182 actions.add('PDF.LoadSuccess') 183 actions.add('PDF.PreviewDocumentLoadFailure') 184 actions.add('PDF.PrintButton') 185 actions.add('PDF.PrintPage') 186 actions.add('PDF.SaveButton') 187 actions.add('PDF.ZoomFromBrowser') 188 actions.add('PDF.ZoomInButton') 189 actions.add('PDF.ZoomOutButton') 190 actions.add('PDF_Unsupported_3D') 191 actions.add('PDF_Unsupported_Attachment') 192 actions.add('PDF_Unsupported_Bookmarks') 193 actions.add('PDF_Unsupported_Digital_Signature') 194 actions.add('PDF_Unsupported_Movie') 195 actions.add('PDF_Unsupported_Portfolios_Packages') 196 actions.add('PDF_Unsupported_Rights_Management') 197 actions.add('PDF_Unsupported_Screen') 198 actions.add('PDF_Unsupported_Shared_Form') 199 actions.add('PDF_Unsupported_Shared_Review') 200 actions.add('PDF_Unsupported_Sound') 201 actions.add('PDF_Unsupported_XFA') 202 203def AddAndroidActions(actions): 204 """Add actions that are used by Chrome on Android. 205 206 Arguments 207 actions: set of actions to add to. 208 """ 209 actions.add('Cast_Sender_CastEnterFullscreen'); 210 actions.add('Cast_Sender_CastDeviceSelected'); 211 actions.add('Cast_Sender_YouTubeDeviceSelected'); 212 actions.add('Cast_Sender_CastPlayRequested'); 213 actions.add('DataReductionProxy_PromoDisplayed'); 214 actions.add('DataReductionProxy_PromoLearnMore'); 215 actions.add('DataReductionProxy_TurnedOn'); 216 actions.add('DataReductionProxy_TurnedOnFromPromo'); 217 actions.add('DataReductionProxy_TurnedOff'); 218 actions.add('MobileActionBarShown') 219 actions.add('MobileBeamCallbackSuccess') 220 actions.add('MobileBeamInvalidAppState') 221 actions.add('MobileBreakpadUploadAttempt') 222 actions.add('MobileBreakpadUploadFailure') 223 actions.add('MobileBreakpadUploadSuccess') 224 actions.add('MobileContextMenuCopyImageLinkAddress') 225 actions.add('MobileContextMenuCopyLinkAddress') 226 actions.add('MobileContextMenuCopyLinkText') 227 actions.add('MobileContextMenuDownloadImage') 228 actions.add('MobileContextMenuDownloadLink') 229 actions.add('MobileContextMenuDownloadVideo') 230 actions.add('MobileContextMenuImage') 231 actions.add('MobileContextMenuLink') 232 actions.add('MobileContextMenuOpenImageInNewTab') 233 actions.add('MobileContextMenuOpenLink') 234 actions.add('MobileContextMenuOpenLinkInIncognito') 235 actions.add('MobileContextMenuOpenLinkInNewTab') 236 actions.add('MobileContextMenuSaveImage') 237 actions.add('MobileContextMenuSearchByImage') 238 actions.add('MobileContextMenuShareLink') 239 actions.add('MobileContextMenuText') 240 actions.add('MobileContextMenuVideo') 241 actions.add('MobileContextMenuViewImage') 242 actions.add('MobileFocusedFakeboxOnNtp') 243 actions.add('MobileFocusedOmniboxNotOnNtp') 244 actions.add('MobileFocusedOmniboxOnNtp') 245 actions.add('MobileFreAttemptSignIn') 246 actions.add('MobileFreSignInSuccessful') 247 actions.add('MobileFreSkipSignIn') 248 actions.add('MobileMenuAddToBookmarks') 249 actions.add('MobileMenuAllBookmarks') 250 actions.add('MobileMenuBack') 251 actions.add('MobileMenuCloseAllTabs') 252 actions.add('MobileMenuCloseTab') 253 actions.add('MobileMenuFeedback') 254 actions.add('MobileMenuFindInPage') 255 actions.add('MobileMenuForward') 256 actions.add('MobileMenuFullscreen') 257 actions.add('MobileMenuNewIncognitoTab') 258 actions.add('MobileMenuNewTab') 259 actions.add('MobileMenuOpenTabs') 260 actions.add('MobileMenuQuit') 261 actions.add('MobileMenuReload') 262 actions.add('MobileMenuSettings') 263 actions.add('MobileMenuShare') 264 actions.add('MobileMenuShow') 265 actions.add('MobileNTPBookmark') 266 actions.add('MobileNTPForeignSession') 267 actions.add('MobileNTPMostVisited') 268 actions.add('MobileNTPRecentlyClosed') 269 actions.add('MobileNTPSwitchToBookmarks') 270 actions.add('MobileNTPSwitchToIncognito') 271 actions.add('MobileNTPSwitchToMostVisited') 272 actions.add('MobileNTPSwitchToOpenTabs') 273 actions.add('MobileNewTabOpened') 274 actions.add('MobileOmniboxSearch') 275 actions.add('MobileOmniboxVoiceSearch') 276 actions.add('MobileOmniboxRefineSuggestion') 277 actions.add('MobilePageLoaded') 278 actions.add('MobilePageLoadedDesktopUserAgent') 279 actions.add('MobilePageLoadedWithKeyboard') 280 actions.add('MobileReceivedExternalIntent') 281 actions.add('MobileRendererCrashed') 282 actions.add('MobileShortcutAllBookmarks') 283 actions.add('MobileShortcutFindInPage') 284 actions.add('MobileShortcutNewIncognitoTab') 285 actions.add('MobileShortcutNewTab') 286 actions.add('MobileSideSwipeFinished') 287 actions.add('MobileStackViewCloseTab') 288 actions.add('MobileStackViewSwipeCloseTab') 289 actions.add('MobileTabClobbered') 290 actions.add('MobileTabClosed') 291 actions.add('MobileTabStripCloseTab') 292 actions.add('MobileTabStripNewTab') 293 actions.add('MobileTabSwitched') 294 actions.add('MobileToolbarBack') 295 actions.add('MobileToolbarForward') 296 actions.add('MobileToolbarNewTab') 297 actions.add('MobileToolbarReload') 298 actions.add('MobileToolbarShowMenu') 299 actions.add('MobileToolbarShowStackView') 300 actions.add('MobileToolbarStackViewNewTab') 301 actions.add('MobileToolbarToggleBookmark') 302 actions.add('MobileUsingMenuByHwButtonDragging') 303 actions.add('MobileUsingMenuByHwButtonTap') 304 actions.add('MobileUsingMenuBySwButtonDragging') 305 actions.add('MobileUsingMenuBySwButtonTap') 306 actions.add('SystemBack') 307 actions.add('SystemBackForNavigation') 308 309def AddAboutFlagsActions(actions): 310 """This parses the experimental feature flags for UMA actions. 311 312 Arguments: 313 actions: set of actions to add to. 314 """ 315 about_flags = os.path.join(REPOSITORY_ROOT, 'chrome', 'browser', 316 'about_flags.cc') 317 flag_name_re = re.compile(r'\s*"([0-9a-zA-Z\-_]+)",\s*// FLAGS:RECORD_UMA') 318 for line in open(about_flags): 319 match = flag_name_re.search(line) 320 if match: 321 actions.add("AboutFlags_" + match.group(1)) 322 # If the line contains the marker but was not matched by the regex, put up 323 # an error if the line is not a comment. 324 elif 'FLAGS:RECORD_UMA' in line and line[0:2] != '//': 325 print >>sys.stderr, 'WARNING: This line is marked for recording ' + \ 326 'about:flags metrics, but is not in the proper format:\n' + line 327 328def AddBookmarkManagerActions(actions): 329 """Add actions that are used by BookmarkManager. 330 331 Arguments 332 actions: set of actions to add to. 333 """ 334 actions.add('BookmarkManager_Command_AddPage') 335 actions.add('BookmarkManager_Command_Copy') 336 actions.add('BookmarkManager_Command_Cut') 337 actions.add('BookmarkManager_Command_Delete') 338 actions.add('BookmarkManager_Command_Edit') 339 actions.add('BookmarkManager_Command_Export') 340 actions.add('BookmarkManager_Command_Import') 341 actions.add('BookmarkManager_Command_NewFolder') 342 actions.add('BookmarkManager_Command_OpenIncognito') 343 actions.add('BookmarkManager_Command_OpenInNewTab') 344 actions.add('BookmarkManager_Command_OpenInNewWindow') 345 actions.add('BookmarkManager_Command_OpenInSame') 346 actions.add('BookmarkManager_Command_Paste') 347 actions.add('BookmarkManager_Command_ShowInFolder') 348 actions.add('BookmarkManager_Command_Sort') 349 actions.add('BookmarkManager_Command_UndoDelete') 350 actions.add('BookmarkManager_Command_UndoGlobal') 351 actions.add('BookmarkManager_Command_UndoNone') 352 353 actions.add('BookmarkManager_NavigateTo_BookmarkBar') 354 actions.add('BookmarkManager_NavigateTo_Mobile') 355 actions.add('BookmarkManager_NavigateTo_Other') 356 actions.add('BookmarkManager_NavigateTo_Recent') 357 actions.add('BookmarkManager_NavigateTo_Search') 358 actions.add('BookmarkManager_NavigateTo_SubFolder') 359 360def AddChromeOSActions(actions): 361 """Add actions reported by non-Chrome processes in Chrome OS. 362 363 Arguments: 364 actions: set of actions to add to. 365 """ 366 # Actions sent by Chrome OS update engine. 367 actions.add('Updater.ServerCertificateChanged') 368 actions.add('Updater.ServerCertificateFailed') 369 370 # Actions sent by Chrome OS cryptohome. 371 actions.add('Cryptohome.PKCS11InitFail') 372 373def AddExtensionActions(actions): 374 """Add actions reported by extensions via chrome.metricsPrivate API. 375 376 Arguments: 377 actions: set of actions to add to. 378 """ 379 # Actions sent by Chrome OS File Browser. 380 actions.add('FileBrowser.CreateNewFolder') 381 actions.add('FileBrowser.PhotoEditor.Edit') 382 actions.add('FileBrowser.PhotoEditor.View') 383 actions.add('FileBrowser.SuggestApps.ShowDialog') 384 385 # Actions sent by Google Now client. 386 actions.add('GoogleNow.MessageClicked') 387 actions.add('GoogleNow.ButtonClicked0') 388 actions.add('GoogleNow.ButtonClicked1') 389 actions.add('GoogleNow.Dismissed') 390 391 # Actions sent by Chrome Connectivity Diagnostics. 392 actions.add('ConnectivityDiagnostics.LaunchSource.OfflineChromeOS') 393 actions.add('ConnectivityDiagnostics.LaunchSource.WebStore') 394 actions.add('ConnectivityDiagnostics.UA.LogsShown') 395 actions.add('ConnectivityDiagnostics.UA.PassingTestsShown') 396 actions.add('ConnectivityDiagnostics.UA.SettingsShown') 397 actions.add('ConnectivityDiagnostics.UA.TestResultExpanded') 398 actions.add('ConnectivityDiagnostics.UA.TestSuiteRun') 399 400def GrepForActions(path, actions): 401 """Grep a source file for calls to UserMetrics functions. 402 403 Arguments: 404 path: path to the file 405 actions: set of actions to add to 406 """ 407 global number_of_files_total 408 number_of_files_total = number_of_files_total + 1 409 # we look for the UserMetricsAction structure constructor 410 # this should be on one line 411 action_re = re.compile(r'[^a-zA-Z]UserMetricsAction\("([^"]*)') 412 malformed_action_re = re.compile(r'[^a-zA-Z]UserMetricsAction\([^"]') 413 computed_action_re = re.compile(r'RecordComputedAction') 414 line_number = 0 415 for line in open(path): 416 line_number = line_number + 1 417 match = action_re.search(line) 418 if match: # Plain call to RecordAction 419 actions.add(match.group(1)) 420 elif malformed_action_re.search(line): 421 # Warn if this line is using RecordAction incorrectly. 422 print >>sys.stderr, ('WARNING: %s has malformed call to RecordAction' 423 ' at %d' % (path, line_number)) 424 elif computed_action_re.search(line): 425 # Warn if this file shouldn't be calling RecordComputedAction. 426 if os.path.basename(path) not in KNOWN_COMPUTED_USERS: 427 print >>sys.stderr, ('WARNING: %s has RecordComputedAction at %d' % 428 (path, line_number)) 429 430class WebUIActionsParser(HTMLParser): 431 """Parses an HTML file, looking for all tags with a 'metric' attribute. 432 Adds user actions corresponding to any metrics found. 433 434 Arguments: 435 actions: set of actions to add to 436 """ 437 def __init__(self, actions): 438 HTMLParser.__init__(self) 439 self.actions = actions 440 441 def handle_starttag(self, tag, attrs): 442 # We only care to examine tags that have a 'metric' attribute. 443 attrs = dict(attrs) 444 if not 'metric' in attrs: 445 return 446 447 # Boolean metrics have two corresponding actions. All other metrics have 448 # just one corresponding action. By default, we check the 'dataType' 449 # attribute. 450 is_boolean = ('dataType' in attrs and attrs['dataType'] == 'boolean') 451 if 'type' in attrs and attrs['type'] in ('checkbox', 'radio'): 452 if attrs['type'] == 'checkbox': 453 is_boolean = True 454 else: 455 # Radio buttons are boolean if and only if their values are 'true' or 456 # 'false'. 457 assert(attrs['type'] == 'radio') 458 if 'value' in attrs and attrs['value'] in ['true', 'false']: 459 is_boolean = True 460 461 if is_boolean: 462 self.actions.add(attrs['metric'] + '_Enable') 463 self.actions.add(attrs['metric'] + '_Disable') 464 else: 465 self.actions.add(attrs['metric']) 466 467def GrepForWebUIActions(path, actions): 468 """Grep a WebUI source file for elements with associated metrics. 469 470 Arguments: 471 path: path to the file 472 actions: set of actions to add to 473 """ 474 close_called = False 475 try: 476 parser = WebUIActionsParser(actions) 477 parser.feed(open(path).read()) 478 # An exception can be thrown by parser.close(), so do it in the try to 479 # ensure the path of the file being parsed gets printed if that happens. 480 close_called = True 481 parser.close() 482 except Exception, e: 483 print "Error encountered for path %s" % path 484 raise e 485 finally: 486 if not close_called: 487 parser.close() 488 489def WalkDirectory(root_path, actions, extensions, callback): 490 for path, dirs, files in os.walk(root_path): 491 if '.svn' in dirs: 492 dirs.remove('.svn') 493 if '.git' in dirs: 494 dirs.remove('.git') 495 for file in files: 496 ext = os.path.splitext(file)[1] 497 if ext in extensions: 498 callback(os.path.join(path, file), actions) 499 500def AddLiteralActions(actions): 501 """Add literal actions specified via calls to UserMetrics functions. 502 503 Arguments: 504 actions: set of actions to add to. 505 """ 506 EXTENSIONS = ('.cc', '.mm', '.c', '.m') 507 508 # Walk the source tree to process all .cc files. 509 ash_root = os.path.normpath(os.path.join(REPOSITORY_ROOT, 'ash')) 510 WalkDirectory(ash_root, actions, EXTENSIONS, GrepForActions) 511 chrome_root = os.path.normpath(os.path.join(REPOSITORY_ROOT, 'chrome')) 512 WalkDirectory(chrome_root, actions, EXTENSIONS, GrepForActions) 513 content_root = os.path.normpath(os.path.join(REPOSITORY_ROOT, 'content')) 514 WalkDirectory(content_root, actions, EXTENSIONS, GrepForActions) 515 webkit_root = os.path.normpath(os.path.join(REPOSITORY_ROOT, 'webkit')) 516 WalkDirectory(os.path.join(webkit_root, 'glue'), actions, EXTENSIONS, 517 GrepForActions) 518 WalkDirectory(os.path.join(webkit_root, 'port'), actions, EXTENSIONS, 519 GrepForActions) 520 521def AddWebUIActions(actions): 522 """Add user actions defined in WebUI files. 523 524 Arguments: 525 actions: set of actions to add to. 526 """ 527 resources_root = os.path.join(REPOSITORY_ROOT, 'chrome', 'browser', 528 'resources') 529 WalkDirectory(resources_root, actions, ('.html'), GrepForWebUIActions) 530 531def AddHistoryPageActions(actions): 532 """Add actions that are used in History page. 533 534 Arguments 535 actions: set of actions to add to. 536 """ 537 actions.add('HistoryPage_BookmarkStarClicked') 538 actions.add('HistoryPage_EntryMenuRemoveFromHistory') 539 actions.add('HistoryPage_EntryLinkClick') 540 actions.add('HistoryPage_EntryLinkRightClick') 541 actions.add('HistoryPage_SearchResultClick') 542 actions.add('HistoryPage_EntryMenuShowMoreFromSite') 543 actions.add('HistoryPage_NewestHistoryClick') 544 actions.add('HistoryPage_NewerHistoryClick') 545 actions.add('HistoryPage_OlderHistoryClick') 546 actions.add('HistoryPage_Search') 547 actions.add('HistoryPage_InitClearBrowsingData') 548 actions.add('HistoryPage_RemoveSelected') 549 actions.add('HistoryPage_SearchResultRemove') 550 actions.add('HistoryPage_ConfirmRemoveSelected') 551 actions.add('HistoryPage_CancelRemoveSelected') 552 553def AddKeySystemSupportActions(actions): 554 """Add actions that are used for key system support metrics. 555 556 Arguments 557 actions: set of actions to add to. 558 """ 559 actions.add('KeySystemSupport.Widevine.Queried') 560 actions.add('KeySystemSupport.WidevineWithType.Queried') 561 actions.add('KeySystemSupport.Widevine.Supported') 562 actions.add('KeySystemSupport.WidevineWithType.Supported') 563 564def AddAutomaticResetBannerActions(actions): 565 """Add actions that are used for the automatic profile settings reset banner 566 in chrome://settings. 567 568 Arguments 569 actions: set of actions to add to. 570 """ 571 actions.add('AutomaticReset_WebUIBanner_BannerShown') 572 actions.add('AutomaticReset_WebUIBanner_ManuallyClosed') 573 actions.add('AutomaticReset_WebUIBanner_ResetClicked') 574 575def main(argv): 576 if '--hash' in argv: 577 hash_output = True 578 else: 579 hash_output = False 580 print >>sys.stderr, "WARNING: If you added new UMA tags, you must" + \ 581 " use the --hash option to update chromeactions.txt." 582 # if we do a hash output, we want to only append NEW actions, and we know 583 # the file we want to work on 584 actions = set() 585 586 chromeactions_path = os.path.join(path_utils.ScriptDir(), "chromeactions.txt") 587 588 if hash_output: 589 f = open(chromeactions_path) 590 for line in f: 591 part = line.rpartition("\t") 592 part = part[2].strip() 593 actions.add(part) 594 f.close() 595 596 597 AddComputedActions(actions) 598 # TODO(fmantek): bring back webkit editor actions. 599 # AddWebKitEditorActions(actions) 600 AddAboutFlagsActions(actions) 601 AddWebUIActions(actions) 602 603 AddLiteralActions(actions) 604 605 # print "Scanned {0} number of files".format(number_of_files_total) 606 # print "Found {0} entries".format(len(actions)) 607 608 AddAndroidActions(actions) 609 AddAutomaticResetBannerActions(actions) 610 AddBookmarkManagerActions(actions) 611 AddChromeOSActions(actions) 612 AddClosedSourceActions(actions) 613 AddExtensionActions(actions) 614 AddHistoryPageActions(actions) 615 AddKeySystemSupportActions(actions) 616 617 if hash_output: 618 f = open(chromeactions_path, "wb") 619 620 621 # Print out the actions as a sorted list. 622 for action in sorted(actions): 623 if hash_output: 624 hash = hashlib.md5() 625 hash.update(action) 626 print >>f, '0x%s\t%s' % (hash.hexdigest()[:16], action) 627 else: 628 print action 629 630 if hash_output: 631 print "Done. Do not forget to add chromeactions.txt to your changelist" 632 return 0 633 634 635if '__main__' == __name__: 636 sys.exit(main(sys.argv)) 637