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/mac_util.h" 7#include "base/string_util.h" 8#include "base/sys_string_conversions.h" 9#include "base/time.h" 10#include "chrome/browser/browser_process.h" 11#import "chrome/browser/ui/cocoa/about_ipc_controller.h" 12 13#if defined(IPC_MESSAGE_LOG_ENABLED) 14 15@implementation CocoaLogData 16 17- (id)initWithLogData:(const IPC::LogData&)data { 18 if ((self = [super init])) { 19 data_ = data; 20 // data_.message_name may not have been filled in if it originated 21 // somewhere other than the browser process. 22 IPC::Logging::GetMessageText(data_.type, &data_.message_name, NULL, NULL); 23 } 24 return self; 25} 26 27- (NSString*)time { 28 base::Time t = base::Time::FromInternalValue(data_.sent); 29 base::Time::Exploded exploded; 30 t.LocalExplode(&exploded); 31 return [NSString stringWithFormat:@"%02d:%02d:%02d.%03d", 32 exploded.hour, exploded.minute, 33 exploded.second, exploded.millisecond]; 34} 35 36- (NSString*)channel { 37 return base::SysUTF8ToNSString(data_.channel); 38} 39 40- (NSString*)message { 41 if (data_.message_name == "") { 42 int high = data_.type >> 12; 43 int low = data_.type - (high<<12); 44 return [NSString stringWithFormat:@"type=(%d,%d) 0x%x,0x%x", 45 high, low, high, low]; 46 } 47 else { 48 return base::SysUTF8ToNSString(data_.message_name); 49 } 50} 51 52- (NSString*)flags { 53 return base::SysUTF8ToNSString(data_.flags); 54} 55 56- (NSString*)dispatch { 57 base::Time sent = base::Time::FromInternalValue(data_.sent); 58 int64 delta = (base::Time::FromInternalValue(data_.receive) - 59 sent).InMilliseconds(); 60 return [NSString stringWithFormat:@"%d", delta ? (int)delta : 0]; 61} 62 63- (NSString*)process { 64 base::TimeDelta delta = (base::Time::FromInternalValue(data_.dispatch) - 65 base::Time::FromInternalValue(data_.receive)); 66 int64 t = delta.InMilliseconds(); 67 return [NSString stringWithFormat:@"%d", t ? (int)t : 0]; 68} 69 70- (NSString*)parameters { 71 return base::SysUTF8ToNSString(data_.params); 72} 73 74@end 75 76namespace { 77AboutIPCController* gSharedController = nil; 78} 79 80@implementation AboutIPCController 81 82+ (AboutIPCController*)sharedController { 83 if (gSharedController == nil) 84 gSharedController = [[AboutIPCController alloc] init]; 85 return gSharedController; 86} 87 88- (id)init { 89 NSString* nibpath = [base::mac::MainAppBundle() pathForResource:@"AboutIPC" 90 ofType:@"nib"]; 91 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { 92 // Default to all on 93 appCache_ = view_ = utilityHost_ = viewHost_ = plugin_ = 94 npObject_ = devTools_ = pluginProcessing_ = userString1_ = 95 userString2_ = userString3_ = YES; 96 } 97 return self; 98} 99 100- (void)dealloc { 101 if (gSharedController == self) 102 gSharedController = nil; 103 if (g_browser_process) 104 g_browser_process->SetIPCLoggingEnabled(false); // just in case... 105 IPC::Logging::GetInstance()->SetConsumer(NULL); 106 [super dealloc]; 107} 108 109- (void)awakeFromNib { 110 // Running Chrome with the --ipc-logging switch might cause it to 111 // be enabled before the about:ipc window comes up; accomodate. 112 [self updateVisibleRunState]; 113 114 // We are now able to display information, so let'er rip. 115 bridge_.reset(new AboutIPCBridge(self)); 116 IPC::Logging::GetInstance()->SetConsumer(bridge_.get()); 117} 118 119// Delegate callback. Closing the window means there is no more need 120// for the me, the controller. 121- (void)windowWillClose:(NSNotification*)notification { 122 [self autorelease]; 123} 124 125- (void)updateVisibleRunState { 126 if (IPC::Logging::GetInstance()->Enabled()) 127 [startStopButton_ setTitle:@"Stop"]; 128 else 129 [startStopButton_ setTitle:@"Start"]; 130} 131 132- (IBAction)startStop:(id)sender { 133 g_browser_process->SetIPCLoggingEnabled( 134 !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:@"%d", count]]; 190} 191 192- (void)setDisplayViewMessages:(BOOL)display { 193 view_ = display; 194} 195 196@end 197 198#endif // IPC_MESSAGE_LOG_ENABLED 199 200