summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/hung_renderer_controller.mm
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/cocoa/hung_renderer_controller.mm')
-rw-r--r--chrome/browser/cocoa/hung_renderer_controller.mm203
1 files changed, 203 insertions, 0 deletions
diff --git a/chrome/browser/cocoa/hung_renderer_controller.mm b/chrome/browser/cocoa/hung_renderer_controller.mm
new file mode 100644
index 0000000..74ddff5
--- /dev/null
+++ b/chrome/browser/cocoa/hung_renderer_controller.mm
@@ -0,0 +1,203 @@
+// Copyright (c) 2010 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 "chrome/browser/cocoa/hung_renderer_controller.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "app/resource_bundle.h"
+#include "app/l10n_util_mac.h"
+#include "base/mac_util.h"
+#include "base/process_util.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/hung_renderer_dialog.h"
+#import "chrome/browser/cocoa/multi_key_equivalent_button.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/logging_chrome.h"
+#include "chrome/common/result_codes.h"
+#include "grit/chromium_strings.h"
+#include "grit/app_resources.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "skia/ext/skia_utils_mac.h"
+#include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
+
+namespace {
+// We only support showing one of these at a time per app. The
+// controller owns itself and is released when its window is closed.
+HungRendererController* g_instance = NULL;
+} // end namespace
+
+@implementation HungRendererController
+
+- (id)initWithWindowNibName:(NSString*)nibName {
+ NSString* nibpath = [mac_util::MainAppBundle() pathForResource:nibName
+ ofType:@"nib"];
+ self = [super initWithWindowNibPath:nibpath owner:self];
+ if (self) {
+ [tableView_ setDataSource:self];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ DCHECK(!g_instance);
+ [tableView_ setDataSource:nil];
+ [super dealloc];
+}
+
+- (void)awakeFromNib {
+ // Load in the image
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ NSImage* backgroundImage = rb.GetNSImageNamed(IDR_FROZEN_TAB_ICON);
+ DCHECK(backgroundImage);
+ [imageView_ setImage:backgroundImage];
+
+ // Make the message fit.
+ CGFloat messageShift =
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:messageView_];
+
+ // Move the graphic up to be top even with the message.
+ NSRect graphicFrame = [imageView_ frame];
+ graphicFrame.origin.y += messageShift;
+ [imageView_ setFrame:graphicFrame];
+
+ // Make the window taller to fit everything.
+ NSSize windowDelta = NSMakeSize(0, messageShift);
+ [GTMUILocalizerAndLayoutTweaker
+ resizeWindowWithoutAutoResizingSubViews:[self window]
+ delta:windowDelta];
+
+ // Make the "wait" button respond to additional keys. By setting this to
+ // @"\e", it will respond to both Esc and Command-. (period).
+ KeyEquivalentAndModifierMask key;
+ key.charCode = @"\e";
+ [waitButton_ addKeyEquivalent:key];
+}
+
+- (IBAction)kill:(id)sender {
+ if (hungContents_)
+ base::KillProcess(hungContents_->GetRenderProcessHost()->GetHandle(),
+ ResultCodes::HUNG, false);
+ // Cannot call performClose:, because the close button is disabled.
+ [self close];
+}
+
+- (IBAction)wait:(id)sender {
+ if (hungContents_ && hungContents_->render_view_host())
+ hungContents_->render_view_host()->RestartHangMonitorTimeout();
+ // Cannot call performClose:, because the close button is disabled.
+ [self close];
+}
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView {
+ return [hungTitles_ count];
+}
+
+- (id)tableView:(NSTableView*)aTableView
+ objectValueForTableColumn:(NSTableColumn*)column
+ row:(NSInteger)rowIndex {
+ return [NSNumber numberWithInt:NSOffState];
+}
+
+- (NSCell*)tableView:(NSTableView*)tableView
+ dataCellForTableColumn:(NSTableColumn*)tableColumn
+ row:(NSInteger)rowIndex {
+ NSCell* cell = [tableColumn dataCellForRow:rowIndex];
+
+ if ([[tableColumn identifier] isEqualToString:@"title"]) {
+ DCHECK([cell isKindOfClass:[NSButtonCell class]]);
+ NSButtonCell* buttonCell = static_cast<NSButtonCell*>(cell);
+ [buttonCell setTitle:[hungTitles_ objectAtIndex:rowIndex]];
+ [buttonCell setImage:[hungFavicons_ objectAtIndex:rowIndex]];
+ [buttonCell setRefusesFirstResponder:YES]; // Don't push in like a button.
+ [buttonCell setHighlightsBy:NSNoCellMask];
+ }
+ return cell;
+}
+
+- (void)windowWillClose:(NSNotification*)notification {
+ // We have to reset g_instance before autoreleasing the window,
+ // because we want to avoid reusing the same dialog if someone calls
+ // hung_renderer_dialog::ShowForTabContents() between the autorelease
+ // call and the actual dealloc.
+ g_instance = nil;
+
+ [self autorelease];
+}
+
+- (void)showForTabContents:(TabContents*)contents {
+ DCHECK(contents);
+ hungContents_ = contents;
+ scoped_nsobject<NSMutableArray> titles([[NSMutableArray alloc] init]);
+ scoped_nsobject<NSMutableArray> favicons([[NSMutableArray alloc] init]);
+ for (TabContentsIterator it; !it.done(); ++it) {
+ if (it->GetRenderProcessHost() == hungContents_->GetRenderProcessHost()) {
+ string16 title = (*it)->GetTitle();
+ if (title.empty())
+ title = TabContents::GetDefaultTitle();
+ [titles addObject:base::SysUTF16ToNSString(title)];
+
+ // TabContents can return a null SkBitmap if it has no favicon. If this
+ // happens, use the default favicon.
+ const SkBitmap& bitmap = it->GetFavIcon();
+ if (!bitmap.isNull()) {
+ [favicons addObject:gfx::SkBitmapToNSImage(bitmap)];
+ } else {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ [favicons addObject:rb.GetNSImageNamed(IDR_DEFAULT_FAVICON)];
+ }
+ }
+ }
+ hungTitles_.reset([titles copy]);
+ hungFavicons_.reset([favicons copy]);
+ [tableView_ reloadData];
+
+ [[self window] center];
+ [self showWindow:self];
+}
+
+- (void)endForTabContents:(TabContents*)contents {
+ DCHECK(contents);
+ DCHECK(hungContents_);
+ if (hungContents_ && hungContents_->GetRenderProcessHost() ==
+ contents->GetRenderProcessHost()) {
+ // Cannot call performClose:, because the close button is disabled.
+ [self close];
+ }
+}
+
+@end
+
+@implementation HungRendererController (JustForTesting)
+- (NSButton*)killButton {
+ return killButton_;
+}
+
+- (MultiKeyEquivalentButton*)waitButton {
+ return waitButton_;
+}
+@end
+
+namespace hung_renderer_dialog {
+
+void ShowForTabContents(TabContents* contents) {
+ if (!logging::DialogsAreSuppressed()) {
+ if (!g_instance)
+ g_instance = [[HungRendererController alloc]
+ initWithWindowNibName:@"HungRendererDialog"];
+ [g_instance showForTabContents:contents];
+ }
+}
+
+// static
+void HideForTabContents(TabContents* contents) {
+ if (!logging::DialogsAreSuppressed() && g_instance)
+ [g_instance endForTabContents:contents];
+}
+
+} // namespace hung_renderer_dialog