summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa
diff options
context:
space:
mode:
authorjrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-28 21:08:04 +0000
committerjrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-28 21:08:04 +0000
commitd55aaa139da94452ebd6812bfa9241ba050978d9 (patch)
treec5fd3ad94b96114b9109fc92610cae3b9688014b /chrome/browser/cocoa
parent50e8299eb26f3a289c35badb7e740eb109981cd1 (diff)
downloadchromium_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.h32
-rw-r--r--chrome/browser/cocoa/about_ipc_bridge.mm21
-rw-r--r--chrome/browser/cocoa/about_ipc_controller.h83
-rw-r--r--chrome/browser/cocoa/about_ipc_controller.mm198
-rw-r--r--chrome/browser/cocoa/about_ipc_controller_unittest.mm51
-rw-r--r--chrome/browser/cocoa/about_ipc_dialog.h23
-rw-r--r--chrome/browser/cocoa/about_ipc_dialog.mm21
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