diff options
author | jrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-28 21:08:04 +0000 |
---|---|---|
committer | jrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-28 21:08:04 +0000 |
commit | d55aaa139da94452ebd6812bfa9241ba050978d9 (patch) | |
tree | c5fd3ad94b96114b9109fc92610cae3b9688014b /chrome/browser/cocoa | |
parent | 50e8299eb26f3a289c35badb7e740eb109981cd1 (diff) | |
download | chromium_src-d55aaa139da94452ebd6812bfa9241ba050978d9.zip chromium_src-d55aaa139da94452ebd6812bfa9241ba050978d9.tar.gz chromium_src-d55aaa139da94452ebd6812bfa9241ba050978d9.tar.bz2 |
Implement about:ipc dialog for Mac.
Convert IPC logging trigger from x-process waitable event to a messages
sent to all processes.
Review URL: http://codereview.chromium.org/192070
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27405 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa')
-rw-r--r-- | chrome/browser/cocoa/about_ipc_bridge.h | 32 | ||||
-rw-r--r-- | chrome/browser/cocoa/about_ipc_bridge.mm | 21 | ||||
-rw-r--r-- | chrome/browser/cocoa/about_ipc_controller.h | 83 | ||||
-rw-r--r-- | chrome/browser/cocoa/about_ipc_controller.mm | 198 | ||||
-rw-r--r-- | chrome/browser/cocoa/about_ipc_controller_unittest.mm | 51 | ||||
-rw-r--r-- | chrome/browser/cocoa/about_ipc_dialog.h | 23 | ||||
-rw-r--r-- | chrome/browser/cocoa/about_ipc_dialog.mm | 21 |
7 files changed, 429 insertions, 0 deletions
diff --git a/chrome/browser/cocoa/about_ipc_bridge.h b/chrome/browser/cocoa/about_ipc_bridge.h new file mode 100644 index 0000000..6ac1ec0 --- /dev/null +++ b/chrome/browser/cocoa/about_ipc_bridge.h @@ -0,0 +1,32 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_COCOA_ABOUT_IPC_BRIDGE_H_ +#define CHROME_BROWSER_COCOA_ABOUT_IPC_BRIDGE_H_ + +#include "ipc/ipc_logging.h" +#include "ipc/ipc_message_utils.h" + +#if defined(IPC_MESSAGE_LOG_ENABLED) + +@class AboutIPCController; + +// On Windows, the AboutIPCDialog is a views::View. On Mac we have a +// Cocoa dialog. This class bridges from C++ to ObjC. +class AboutIPCBridge : public IPC::Logging::Consumer { + public: + AboutIPCBridge(AboutIPCController* controller) : controller_(controller) { } + virtual ~AboutIPCBridge() { } + + // IPC::Logging::Consumer implementation. + virtual void Log(const IPC::LogData& data); + + private: + AboutIPCController* controller_; // weak; owns me + DISALLOW_COPY_AND_ASSIGN(AboutIPCBridge); +}; + +#endif // IPC_MESSAGE_LOG_ENABLED + +#endif // CHROME_BROWSER_COCOA_ABOUT_IPC_BRIDGE_H_ diff --git a/chrome/browser/cocoa/about_ipc_bridge.mm b/chrome/browser/cocoa/about_ipc_bridge.mm new file mode 100644 index 0000000..ee02e94 --- /dev/null +++ b/chrome/browser/cocoa/about_ipc_bridge.mm @@ -0,0 +1,21 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/cocoa/about_ipc_bridge.h" +#include "chrome/browser/cocoa/about_ipc_controller.h" + +#if defined(IPC_MESSAGE_LOG_ENABLED) + +void AboutIPCBridge::Log(const IPC::LogData& data) { + CocoaLogData* cocoa_data = [[CocoaLogData alloc] initWithLogData:data]; + if ([NSThread isMainThread]) { + [controller_ log:cocoa_data]; + } else { + [controller_ performSelectorOnMainThread:@selector(log:) + withObject:cocoa_data + waitUntilDone:NO]; + } +} + +#endif diff --git a/chrome/browser/cocoa/about_ipc_controller.h b/chrome/browser/cocoa/about_ipc_controller.h new file mode 100644 index 0000000..e907af7 --- /dev/null +++ b/chrome/browser/cocoa/about_ipc_controller.h @@ -0,0 +1,83 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_COCOA_ABOUT_IPC_CONTROLLER_H_ +#define CHROME_BROWSER_COCOA_ABOUT_IPC_CONTROLLER_H_ + +#import <Cocoa/Cocoa.h> + +#include "base/scoped_ptr.h" +#include "ipc/ipc_logging.h" +#include "ipc/ipc_message_utils.h" +#include "third_party/GTM/Foundation/GTMRegex.h" + +// Must be included after IPC_MESSAGE_LOG_ENABLED gets defined +#import "chrome/browser/cocoa/about_ipc_bridge.h" + +#if defined(IPC_MESSAGE_LOG_ENABLED) + +// An objc wrapper for IPC::LogData to allow use of Cocoa bindings. +@interface CocoaLogData : NSObject { + @private + IPC::LogData data_; +} +- (id)initWithLogData:(const IPC::LogData&)data; +@end + + +// A window controller that handles the about:ipc non-modal dialog. +@interface AboutIPCController : NSWindowController { + @private + scoped_ptr<AboutIPCBridge> bridge_; + IBOutlet NSButton* startStopButton_; + IBOutlet NSTableView* tableView_; + IBOutlet NSArrayController* dataController_; + IBOutlet NSTextField* eventCount_; + IBOutlet NSTextField* filteredEventCount_; + IBOutlet NSTextField* userStringTextField1_; + IBOutlet NSTextField* userStringTextField2_; + IBOutlet NSTextField* userStringTextField3_; + // Count of filtered events. + int filteredEventCounter_; + // Cocoa-bound to check boxes for filtering messages. + // Each BOOL allows events that have that name prefix. + // E.g. if set, appCache_ allows events named AppCache*. + // The actual string to match is defined in the xib. + // The userStrings allow a user-specified prefix. + BOOL appCache_; + BOOL view_; + BOOL utilityHost_; + BOOL viewHost_; + BOOL plugin_; + BOOL npObject_; + BOOL devTools_; + BOOL pluginProcessing_; + BOOL userString1_; + BOOL userString2_; + BOOL userString3_; +} + ++ (AboutIPCController*)sharedController; + +- (IBAction)startStop:(id)sender; +- (IBAction)clear:(id)sender; + +// Called from our C++ bridge class. To accomodate multithreaded +// ownership issues, this method ACCEPTS OWNERSHIP of the arg passed +// in. +- (void)log:(CocoaLogData*)data; + +// Update visible state (e.g. Start/Stop button) based on logging run +// state. Does not change state. +- (void)updateVisibleRunState; + +@end + +@interface AboutIPCController(TestingAPI) +- (BOOL)filterOut:(CocoaLogData*)data; +- (void)setDisplayViewMessages:(BOOL)display; +@end + +#endif // IPC_MESSAGE_LOG_ENABLED +#endif // CHROME_BROWSER_COCOA_ABOUT_IPC_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/about_ipc_controller.mm b/chrome/browser/cocoa/about_ipc_controller.mm new file mode 100644 index 0000000..32a9933 --- /dev/null +++ b/chrome/browser/cocoa/about_ipc_controller.mm @@ -0,0 +1,198 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/mac_util.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "base/time.h" +#include "chrome/browser/browser_process.h" +#import "chrome/browser/cocoa/about_ipc_controller.h" + +#if defined(IPC_MESSAGE_LOG_ENABLED) + +@implementation CocoaLogData + +- (id)initWithLogData:(const IPC::LogData&)data { + if ((self = [super init])) { + data_ = data; + // data_.message_name may not have been filled in if it originated + // somewhere other than the browser process. + IPC::Logging::GetMessageText(data_.type, &data_.message_name, NULL, NULL); + } + return self; +} + +- (NSString*)time { + base::Time t = base::Time::FromInternalValue(data_.sent); + base::Time::Exploded exploded; + t.LocalExplode(&exploded); + return [NSString stringWithFormat:@"%02d:%02d:%02d.%03d", + exploded.hour, exploded.minute, + exploded.second, exploded.millisecond]; +} + +- (NSString*)channel { + return base::SysUTF8ToNSString(data_.channel); +} + +- (NSString*)message { + if (data_.message_name == L"") { + int high = data_.type >> 12; + int low = data_.type - (high<<12); + return [NSString stringWithFormat:@"type=(%d,%d) 0x%x,0x%x", + high, low, high, low]; + } + else { + return base::SysWideToNSString(data_.message_name); + } +} + +- (NSString*)flags { + return base::SysWideToNSString(data_.flags); +} + +- (NSString*)dispatch { + base::Time sent = base::Time::FromInternalValue(data_.sent); + int64 delta = (base::Time::FromInternalValue(data_.receive) - + sent).InMilliseconds(); + return [NSString stringWithFormat:@"%d", delta ? (int)delta : 0]; +} + +- (NSString*)process { + base::TimeDelta delta = (base::Time::FromInternalValue(data_.dispatch) - + base::Time::FromInternalValue(data_.receive)); + int64 t = delta.InMilliseconds(); + return [NSString stringWithFormat:@"%d", t ? (int)t : 0]; +} + +- (NSString*)parameters { + return base::SysWideToNSString(data_.params); +} + +@end + +namespace { +AboutIPCController* gSharedController = nil; +} + +@implementation AboutIPCController + ++ (AboutIPCController*)sharedController { + if (gSharedController == nil) + gSharedController = [[AboutIPCController alloc] init]; + return gSharedController; +} + +- (id)init { + NSString* nibpath = [mac_util::MainAppBundle() pathForResource:@"AboutIPC" + ofType:@"nib"]; + if ((self = [super initWithWindowNibPath:nibpath owner:self])) { + // Default to all on + appCache_ = view_ = utilityHost_ = viewHost_ = plugin_ = + npObject_ = devTools_ = pluginProcessing_ = userString1_ = + userString2_ = userString3_ = YES; + } + return self; +} + +- (void)dealloc { + if (gSharedController == self) + gSharedController = nil; + if (g_browser_process) + g_browser_process->SetIPCLoggingEnabled(false); // just in case... + IPC::Logging::current()->SetConsumer(NULL); + [super dealloc]; +} + +- (void)awakeFromNib { + // Running Chrome with the --ipc-logging switch might cause it to + // be enabled before the about:ipc window comes up; accomodate. + [self updateVisibleRunState]; + + // We are now able to display information, so let'er rip. + bridge_.reset(new AboutIPCBridge(self)); + IPC::Logging::current()->SetConsumer(bridge_.get()); +} + +// Delegate callback. Closing the window means there is no more need +// for the me, the controller. +- (void)windowWillClose:(NSNotification*)notification { + [self autorelease]; +} + +- (void)updateVisibleRunState { + if (IPC::Logging::current()->Enabled()) + [startStopButton_ setTitle:@"Stop"]; + else + [startStopButton_ setTitle:@"Start"]; +} + +- (IBAction)startStop:(id)sender { + g_browser_process->SetIPCLoggingEnabled(!IPC::Logging::current()->Enabled()); + [self updateVisibleRunState]; +} + +- (IBAction)clear:(id)sender { + [dataController_ setContent:[NSMutableArray array]]; + [eventCount_ setStringValue:@"0"]; + [filteredEventCount_ setStringValue:@"0"]; + filteredEventCounter_ = 0; +} + +// Return YES if we should filter this out; else NO. +// Just to be clear, [@"any string" hasPrefix:@""] returns NO. +- (BOOL)filterOut:(CocoaLogData*)data { + NSString* name = [data message]; + if ((appCache_) && [name hasPrefix:@"AppCache"]) + return NO; + if ((view_) && [name hasPrefix:@"ViewMsg"]) + return NO; + if ((utilityHost_) && [name hasPrefix:@"UtilityHost"]) + return NO; + if ((viewHost_) && [name hasPrefix:@"ViewHost"]) + return NO; + if ((plugin_) && [name hasPrefix:@"PluginMsg"]) + return NO; + if ((npObject_) && [name hasPrefix:@"NPObject"]) + return NO; + if ((devTools_) && [name hasPrefix:@"DevTools"]) + return NO; + if ((pluginProcessing_) && [name hasPrefix:@"PluginProcessing"]) + return NO; + if ((userString1_) && ([name hasPrefix:[userStringTextField1_ stringValue]])) + return NO; + if ((userString2_) && ([name hasPrefix:[userStringTextField2_ stringValue]])) + return NO; + if ((userString3_) && ([name hasPrefix:[userStringTextField3_ stringValue]])) + return NO; + + // Special case the unknown type. + if ([name hasPrefix:@"type="]) + return NO; + + return YES; // filter out. +} + +- (void)log:(CocoaLogData*)data { + if ([self filterOut:data]) { + [filteredEventCount_ setStringValue:[NSString stringWithFormat:@"%d", + ++filteredEventCounter_]]; + return; + } + [dataController_ addObject:data]; + NSUInteger count = [[dataController_ arrangedObjects] count]; + // Uncomment if you want scroll-to-end behavior... but seems expensive. + // [tableView_ scrollRowToVisible:count-1]; + [eventCount_ setStringValue:[NSString stringWithFormat:@"%d", count]]; +} + +- (void)setDisplayViewMessages:(BOOL)display { + view_ = display; +} + +@end + +#endif // IPC_MESSAGE_LOG_ENABLED + diff --git a/chrome/browser/cocoa/about_ipc_controller_unittest.mm b/chrome/browser/cocoa/about_ipc_controller_unittest.mm new file mode 100644 index 0000000..cff2281 --- /dev/null +++ b/chrome/browser/cocoa/about_ipc_controller_unittest.mm @@ -0,0 +1,51 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import <Cocoa/Cocoa.h> + +#import "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/about_ipc_controller.h" +#include "chrome/browser/cocoa/browser_test_helper.h" +#include "chrome/browser/cocoa/cocoa_test_helper.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +#if defined(IPC_MESSAGE_LOG_ENABLED) + +namespace { + +class AboutIPCControllerTest : public PlatformTest { + CocoaTestHelper cocoa_helper_; // Inits Cocoa, creates window, etc... +}; + +TEST_F(AboutIPCControllerTest, TestFilter) { + scoped_nsobject<AboutIPCController> controller( + [[AboutIPCController alloc] init]); + [controller window]; // force nib load. + IPC::LogData data; + + // Make sure generic names do NOT get filtered. + std::wstring names[] = { L"PluginProcessingIsMyLife", + L"ViewMsgFoo", + L"NPObjectHell" }; + for (unsigned int i = 0; i < arraysize(names); i++) { + data.message_name = names[i]; + scoped_nsobject<CocoaLogData> cdata([[CocoaLogData alloc] + initWithLogData:data]); + EXPECT_FALSE([controller filterOut:cdata.get()]); + } + + // Flip a checkbox, see it filtered, flip back, all is fine. + data.message_name = L"ViewMsgFoo"; + scoped_nsobject<CocoaLogData> cdata([[CocoaLogData alloc] + initWithLogData:data]); + [controller setDisplayViewMessages:NO]; + EXPECT_TRUE([controller filterOut:cdata.get()]); + [controller setDisplayViewMessages:YES]; + EXPECT_FALSE([controller filterOut:cdata.get()]); +} + +} // namespace + +#endif // IPC_MESSAGE_LOG_ENABLED diff --git a/chrome/browser/cocoa/about_ipc_dialog.h b/chrome/browser/cocoa/about_ipc_dialog.h new file mode 100644 index 0000000..e190f7a --- /dev/null +++ b/chrome/browser/cocoa/about_ipc_dialog.h @@ -0,0 +1,23 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_COCOA_ABOUT_IPC_DIALOG_H_ +#define CHROME_BROWSER_COCOA_ABOUT_IPC_DIALOG_H_ + +#include "ipc/ipc_message.h" + +#if defined(IPC_MESSAGE_LOG_ENABLED) + +namespace AboutIPCDialog { +// The dialog is a singleton. If the dialog is already opened, it won't do +// anything, so you can just blindly call this function all you want. +// RunDialog() is Called from chrome/browser/browser_about_handler.cc +// in response to an about:ipc URL. +void RunDialog(); +}; + + +#endif /* IPC_MESSAGE_LOG_ENABLED */ + +#endif /* CHROME_BROWSER_COCOA_ABOUT_IPC_DIALOG_H_ */ diff --git a/chrome/browser/cocoa/about_ipc_dialog.mm b/chrome/browser/cocoa/about_ipc_dialog.mm new file mode 100644 index 0000000..d9b9448f --- /dev/null +++ b/chrome/browser/cocoa/about_ipc_dialog.mm @@ -0,0 +1,21 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/cocoa/about_ipc_dialog.h" +#include "chrome/browser/cocoa/about_ipc_controller.h" + +#if defined(IPC_MESSAGE_LOG_ENABLED) + +namespace AboutIPCDialog { + +void RunDialog() { + // The controller gets deallocated when then window is closed, + // so it is safe to "fire and forget". + AboutIPCController* controller = [AboutIPCController sharedController]; + [[controller window] makeKeyAndOrderFront:controller]; +} + +}; // namespace AboutIPCDialog + +#endif // IPC_MESSAGE_LOG_ENABLED |