1// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/logging.h"
6#include "base/mac/bundle_locations.h"
7#include "base/mac/mac_util.h"
8#include "base/strings/string_util.h"
9#include "base/strings/sys_string_conversions.h"
10#include "base/time/time.h"
11#import "chrome/browser/ui/cocoa/about_ipc_controller.h"
12#include "content/public/browser/browser_ipc_logging.h"
13
14#if defined(IPC_MESSAGE_LOG_ENABLED)
15
16@implementation CocoaLogData
17
18- (id)initWithLogData:(const IPC::LogData&)data {
19  if ((self = [super init])) {
20    data_ = data;
21    // data_.message_name may not have been filled in if it originated
22    // somewhere other than the browser process.
23    if (data_.message_name == "")
24      IPC::Logging::GetMessageText(data_.type, &data_.message_name, NULL, NULL);
25  }
26  return self;
27}
28
29- (NSString*)time {
30  base::Time t = base::Time::FromInternalValue(data_.sent);
31  base::Time::Exploded exploded;
32  t.LocalExplode(&exploded);
33  return [NSString stringWithFormat:@"%02d:%02d:%02d.%03d",
34                   exploded.hour, exploded.minute,
35                   exploded.second, exploded.millisecond];
36}
37
38- (NSString*)channel {
39  return base::SysUTF8ToNSString(data_.channel);
40}
41
42- (NSString*)message {
43  if (data_.message_name == "") {
44    int high = data_.type >> 12;
45    int low = data_.type - (high<<12);
46    return [NSString stringWithFormat:@"type=(%d,%d) 0x%x,0x%x",
47                     high, low, high, low];
48  }
49  else {
50    return base::SysUTF8ToNSString(data_.message_name);
51  }
52}
53
54- (NSString*)flags {
55  return base::SysUTF8ToNSString(data_.flags);
56}
57
58- (NSString*)dispatch {
59  base::Time sent = base::Time::FromInternalValue(data_.sent);
60  int64 delta = (base::Time::FromInternalValue(data_.receive) -
61                 sent).InMilliseconds();
62  return [NSString stringWithFormat:@"%d", delta ? (int)delta : 0];
63}
64
65- (NSString*)process {
66  base::TimeDelta delta = (base::Time::FromInternalValue(data_.dispatch) -
67                           base::Time::FromInternalValue(data_.receive));
68  int64 t = delta.InMilliseconds();
69  return [NSString stringWithFormat:@"%d", t ? (int)t : 0];
70}
71
72- (NSString*)parameters {
73  return base::SysUTF8ToNSString(data_.params);
74}
75
76@end
77
78namespace {
79AboutIPCController* gSharedController = nil;
80}
81
82@implementation AboutIPCController
83
84+ (AboutIPCController*)sharedController {
85  if (gSharedController == nil)
86    gSharedController = [[AboutIPCController alloc] init];
87  return gSharedController;
88}
89
90- (id)init {
91  NSString* nibpath = [base::mac::FrameworkBundle() pathForResource:@"AboutIPC"
92                                                             ofType:@"nib"];
93  if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
94    // Default to all on
95    appCache_ = view_ = utilityHost_ = viewHost_ = plugin_ =
96      npObject_ = devTools_ = pluginProcessing_ = userString1_ =
97      userString2_ = userString3_ = YES;
98  }
99  return self;
100}
101
102- (void)dealloc {
103  if (gSharedController == self)
104    gSharedController = nil;
105  content::EnableIPCLogging(false);  // just in case...
106  IPC::Logging::GetInstance()->SetConsumer(NULL);
107  [super dealloc];
108}
109
110- (void)awakeFromNib {
111  // Running Chrome with the --ipc-logging switch might cause it to
112  // be enabled before the about:ipc window comes up; accomodate.
113  [self updateVisibleRunState];
114
115  // We are now able to display information, so let'er rip.
116  bridge_.reset(new AboutIPCBridge(self));
117  IPC::Logging::GetInstance()->SetConsumer(bridge_.get());
118}
119
120// Delegate callback.  Closing the window means there is no more need
121// for the me, the controller.
122- (void)windowWillClose:(NSNotification*)notification {
123  [self autorelease];
124}
125
126- (void)updateVisibleRunState {
127  if (IPC::Logging::GetInstance()->Enabled())
128    [startStopButton_ setTitle:@"Stop"];
129  else
130    [startStopButton_ setTitle:@"Start"];
131}
132
133- (IBAction)startStop:(id)sender {
134  content::EnableIPCLogging(!IPC::Logging::GetInstance()->Enabled());
135  [self updateVisibleRunState];
136}
137
138- (IBAction)clear:(id)sender {
139  [dataController_ setContent:[NSMutableArray array]];
140  [eventCount_ setStringValue:@"0"];
141  [filteredEventCount_ setStringValue:@"0"];
142  filteredEventCounter_ = 0;
143}
144
145// Return YES if we should filter this out; else NO.
146// Just to be clear, [@"any string" hasPrefix:@""] returns NO.
147- (BOOL)filterOut:(CocoaLogData*)data {
148  NSString* name = [data message];
149  if ((appCache_) && [name hasPrefix:@"AppCache"])
150    return NO;
151  if ((view_) && [name hasPrefix:@"ViewMsg"])
152    return NO;
153  if ((utilityHost_) && [name hasPrefix:@"UtilityHost"])
154    return NO;
155  if ((viewHost_) && [name hasPrefix:@"ViewHost"])
156    return NO;
157  if ((plugin_) && [name hasPrefix:@"PluginMsg"])
158    return NO;
159  if ((npObject_) && [name hasPrefix:@"NPObject"])
160    return NO;
161  if ((devTools_) && [name hasPrefix:@"DevTools"])
162    return NO;
163  if ((pluginProcessing_) && [name hasPrefix:@"PluginProcessing"])
164    return NO;
165  if ((userString1_) && ([name hasPrefix:[userStringTextField1_ stringValue]]))
166    return NO;
167  if ((userString2_) && ([name hasPrefix:[userStringTextField2_ stringValue]]))
168    return NO;
169  if ((userString3_) && ([name hasPrefix:[userStringTextField3_ stringValue]]))
170    return NO;
171
172  // Special case the unknown type.
173  if ([name hasPrefix:@"type="])
174    return NO;
175
176  return YES;  // filter out.
177}
178
179- (void)log:(CocoaLogData*)data {
180  if ([self filterOut:data]) {
181    [filteredEventCount_ setStringValue:[NSString stringWithFormat:@"%d",
182                                                  ++filteredEventCounter_]];
183    return;
184  }
185  [dataController_ addObject:data];
186  NSUInteger count = [[dataController_ arrangedObjects] count];
187  // Uncomment if you want scroll-to-end behavior... but seems expensive.
188  // [tableView_ scrollRowToVisible:count-1];
189  [eventCount_ setStringValue:[NSString stringWithFormat:@"%ld",
190                               static_cast<long>(count)]];
191}
192
193- (void)setDisplayViewMessages:(BOOL)display {
194  view_ = display;
195}
196
197@end
198
199#endif  // IPC_MESSAGE_LOG_ENABLED
200
201