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