diff options
Diffstat (limited to 'chrome/browser')
18 files changed, 910 insertions, 159 deletions
diff --git a/chrome/browser/app_modal_dialog.cc b/chrome/browser/app_modal_dialog.cc index 2eea602..e8f306d 100644 --- a/chrome/browser/app_modal_dialog.cc +++ b/chrome/browser/app_modal_dialog.cc @@ -11,7 +11,11 @@ AppModalDialog::AppModalDialog(TabContents* tab_contents, const std::wstring& title) +#if defined(OS_WIN) || defined(OS_LINUX) : dialog_(NULL), +#elif defined(OS_MACOSX) + : +#endif tab_contents_(tab_contents), title_(title), skip_this_dialog_(false) { diff --git a/chrome/browser/app_modal_dialog.h b/chrome/browser/app_modal_dialog.h index 6567db8..92ac494 100644 --- a/chrome/browser/app_modal_dialog.h +++ b/chrome/browser/app_modal_dialog.h @@ -17,12 +17,7 @@ class ModalDialogDelegate; typedef ModalDialogDelegate* NativeDialog; #elif defined(OS_MACOSX) -#if __OBJC__ -@class NSAlert; -#else -class NSAlert; -#endif -typedef NSAlert* NativeDialog; +typedef void* NativeDialog; #elif defined(TOOLKIT_USES_GTK) typedef struct _GtkDialog GtkDialog; typedef struct _GtkWidget GtkWidget; @@ -71,7 +66,7 @@ class AppModalDialog { #endif // Close the dialog if it is showing. - void CloseModalDialog(); + virtual void CloseModalDialog(); // Called by the app modal window queue to activate the window. void ActivateModalDialog(); @@ -97,7 +92,9 @@ class AppModalDialog { virtual NativeDialog CreateNativeDialog() = 0; // A reference to the platform native dialog box. +#if defined(OS_LINUX) || defined(OS_WIN) NativeDialog dialog_; +#endif // Parent tab contents. TabContents* tab_contents_; diff --git a/chrome/browser/app_modal_dialog_mac.mm b/chrome/browser/app_modal_dialog_mac.mm index 2a21c1da..8d23387 100644 --- a/chrome/browser/app_modal_dialog_mac.mm +++ b/chrome/browser/app_modal_dialog_mac.mm @@ -20,8 +20,5 @@ void AppModalDialog::ActivateModalDialog() { } void AppModalDialog::CloseModalDialog() { - NSAlert* alert = dialog_; - DCHECK([alert isKindOfClass:[NSAlert class]]); - [NSApp endSheet:[alert window]]; - dialog_ = nil; + NOTIMPLEMENTED(); } diff --git a/chrome/browser/cocoa/cookie_details_view_controller.h b/chrome/browser/cocoa/cookie_details_view_controller.h new file mode 100644 index 0000000..04b8db7 --- /dev/null +++ b/chrome/browser/cocoa/cookie_details_view_controller.h @@ -0,0 +1,40 @@ +// 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 <Cocoa/Cocoa.h> + +#include "base/cocoa_protocols_mac.h" +#include "base/scoped_nsobject.h" +#include "base/scoped_ptr.h" +#include "net/base/cookie_monster.h" + +@class CocoaCookieTreeNode; + +// Controller for the view that displays the details of a cookie, +// used both in the cookie prompt dialog as well as the +// show cookies preference sheet of content settings preferences. +@interface CookieDetailsViewController : NSViewController { + @private + // Allows direct access to the object controller for + // the displayed cookie information. + IBOutlet NSObjectController* objectController_; +} + +- (id)init; + +// Configures the cookie detail view that is managed by the controller +// to display the information about a single cookie, the information +// for which is explicitly passed in the parameter |content|. +- (void)setContentObject:(id)content; + +// Adjust the size of the view to exactly fix the information text fields +// that are visible inside it. +- (void)shrinkViewToFit; + +// Called by the cookie tree dialog to establish a binding between +// the the detail view's object controller and the tree controller. +// This binding allows the cookie tree to use the detail view unmodified. +- (void)configureBindingsForTreeController:(NSTreeController*)controller; +@end + diff --git a/chrome/browser/cocoa/cookie_details_view_controller.mm b/chrome/browser/cocoa/cookie_details_view_controller.mm new file mode 100644 index 0000000..70da2a5 --- /dev/null +++ b/chrome/browser/cocoa/cookie_details_view_controller.mm @@ -0,0 +1,80 @@ +// 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/cookie_details_view_controller.h" + +#include "app/l10n_util_mac.h" +#include "app/resource_bundle.h" +#import "base/mac_util.h" +#include "base/sys_string_conversions.h" +#import "chrome/browser/cocoa/cookie_tree_node.h" +#import "chrome/browser/cookie_modal_dialog.h" + +namespace { +static const int kMinimalLabelOffsetFromViewBottom = 20; +} + +#pragma mark View Controller + +@implementation CookieDetailsViewController + +- (id)init { + return [super initWithNibName:@"CookieDetailsView" + bundle:mac_util::MainAppBundle()]; +} + +- (void)awakeFromNib { + DCHECK(objectController_); +} + +// Finds and returns the y offset of the lowest-most non-hidden +// text field in the view. This is used to shrink the view +// appropriately so that it just fits its visible content. +- (void)getLowestLabelVerticalPosition:(NSView*)view + lowestLabelPosition:(float&)lowestLabelPosition { + if (![view isHidden]) { + if ([view isKindOfClass:[NSTextField class]]) { + NSRect frame = [view frame]; + if (frame.origin.y < lowestLabelPosition) { + lowestLabelPosition = frame.origin.y; + } + } + for (NSView* subview in [view subviews]) { + [self getLowestLabelVerticalPosition:subview + lowestLabelPosition:lowestLabelPosition]; + } + } +} + +- (void)setContentObject:(id)content { + [objectController_ setValue:content forKey:@"content"]; +} + +- (void)shrinkViewToFit { + // Adjust the information pane to be exactly the right size + // to hold the visible text information fields. + NSView* view = [self view]; + NSRect frame = [view frame]; + float lowestLabelPosition = frame.origin.y + frame.size.height; + [self getLowestLabelVerticalPosition:view + lowestLabelPosition:lowestLabelPosition]; + float verticalDelta = lowestLabelPosition - frame.origin.y - + kMinimalLabelOffsetFromViewBottom; + frame.origin.y += verticalDelta; + frame.size.height -= verticalDelta; + [[self view] setFrame:frame]; +} + +- (void)configureBindingsForTreeController:(NSTreeController*)treeController { + // There seems to be a bug in the binding logic that it's not possible + // to bind to the selection of the tree controller, the bind seems to + // require an additional path segment in the key, thus the use of + // selection.self rather than just selection below. + [objectController_ bind:@"contentObject" + toObject:treeController + withKeyPath:@"selection.self" + options:nil]; +} + +@end diff --git a/chrome/browser/cocoa/cookie_details_view_controller_unittest.mm b/chrome/browser/cocoa/cookie_details_view_controller_unittest.mm new file mode 100644 index 0000000..a1d6a67 --- /dev/null +++ b/chrome/browser/cocoa/cookie_details_view_controller_unittest.mm @@ -0,0 +1,142 @@ +// 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. + +#include "chrome/browser/cocoa/cocoa_test_helper.h" +#include "chrome/browser/cocoa/cookie_details_view_controller.h" +#include "chrome/browser/cookie_modal_dialog.h" + +// Mock apapter that returns dummy values for the details view +@interface MockCookieDetailsViewContentAdapter : NSObject { + @private + // The type of fake cookie data to display + CookiePromptModalDialog::DialogType promptType_; +} + +- (id)initWithType:(CookiePromptModalDialog::DialogType)type; + +// The following methods are all used in the bindings +// inside the cookie detail view. +@property (readonly) BOOL isFolderOrCookieTreeDetails; +@property (readonly) BOOL isLocalStorageTreeDetails; +@property (readonly) BOOL isDatabaseTreeDetails; +@property (readonly) BOOL isDatabasePromptDetails; +@property (readonly) BOOL isLocalStoragePromptDetails; +@property (readonly) NSString* name; +@property (readonly) NSString* content; +@property (readonly) NSString* domain; +@property (readonly) NSString* path; +@property (readonly) NSString* sendFor; +@property (readonly) NSString* created; +@property (readonly) NSString* expires; +@property (readonly) NSString* fileSize; +@property (readonly) NSString* lastModified; +@property (readonly) NSString* databaseDescription; +@property (readonly) NSString* localStorageKey; +@property (readonly) NSString* localStorageValue; + +@end + +@implementation MockCookieDetailsViewContentAdapter + +- (id)initWithType:(CookiePromptModalDialog::DialogType)type { + if ((self = [super init])) { + promptType_ = type; + } + return self; +} + +- (BOOL)isFolderOrCookieTreeDetails { + return promptType_ == CookiePromptModalDialog::DIALOG_TYPE_COOKIE; +} + +- (BOOL)isLocalStorageTreeDetails { + return false; +} + +- (BOOL)isDatabaseTreeDetails { + return false; +} + +- (BOOL) isDatabasePromptDetails { + return promptType_ == CookiePromptModalDialog::DIALOG_TYPE_DATABASE; +} + +- (BOOL) isLocalStoragePromptDetails { + return promptType_ == CookiePromptModalDialog::DIALOG_TYPE_LOCAL_STORAGE; +} + +- (NSString*)name { + return @"dummyName"; +} + +- (NSString*)content { + return @"dummyContent"; +} + +- (NSString*)domain { + return @"dummyDomain"; +} + +- (NSString*)path { + return @"dummyPath"; +} + +- (NSString*)sendFor { + return @"dummySendFor"; +} + +- (NSString*)created { + return @"dummyCreated"; +} + +- (NSString*)expires { + return @"dummyExpires"; +} + +- (NSString*)fileSize { + return @"dummyFileSize"; +} + +- (NSString*)lastModified { + return @"dummyLastModified"; +} + +- (NSString*)databaseDescription { + return @"dummyDatabaseDescription"; +} + +- (NSString*)localStorageKey { + return @"dummyLocalStorageKey"; +} + +- (NSString*)localStorageValue { + return @"dummyLocalStorageValue"; +} + +@end + +namespace { + +class CookieDetailsViewControllerTest : public CocoaTest { +}; + +TEST_F(CookieDetailsViewControllerTest, Create) { + scoped_nsobject<CookieDetailsViewController> detailsViewController; + detailsViewController.reset([[CookieDetailsViewController alloc] init]); +} + +TEST_F(CookieDetailsViewControllerTest, ShrinkToFit) { + scoped_nsobject<CookieDetailsViewController> detailsViewController; + detailsViewController.reset([[CookieDetailsViewController alloc] init]); + scoped_nsobject<MockCookieDetailsViewContentAdapter> mockAdapter; + mockAdapter.reset([[MockCookieDetailsViewContentAdapter alloc] + initWithType:CookiePromptModalDialog::DIALOG_TYPE_DATABASE]); + [detailsViewController.get() setContentObject:mockAdapter.get()]; + NSRect beforeFrame = [[detailsViewController.get() view] frame]; + [detailsViewController.get() shrinkViewToFit]; + NSRect afterFrame = [[detailsViewController.get() view] frame]; + EXPECT_TRUE(afterFrame.size.height < beforeFrame.size.width); +} + +} // namespace diff --git a/chrome/browser/cocoa/cookie_prompt_window_controller.h b/chrome/browser/cocoa/cookie_prompt_window_controller.h new file mode 100644 index 0000000..5276d5c --- /dev/null +++ b/chrome/browser/cocoa/cookie_prompt_window_controller.h @@ -0,0 +1,61 @@ +// 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 <Cocoa/Cocoa.h> + +#include "base/cocoa_protocols_mac.h" +#include "base/scoped_nsobject.h" +#include "base/scoped_ptr.h" + +class CookiePromptModalDialog; +class CookieTreeNode; + +@class CookieDetailsViewController; +@class CocoaCookieTreeNode; + +// This class is the controller for the window displayed +// to the user as a modal dialog prompting them to accept or +// block new cookies and other browser data. +@interface CookiePromptWindowController : NSWindowController { + @private + // Provides access to platform independent information for + // the cookie prompt dialog. + CookiePromptModalDialog* dialog_; // weak; + + // The controller managing the instances of the cookies details view + // embedded in the prompt window. + scoped_nsobject<CookieDetailsViewController> detailsViewController_; + + // The adapter object that supplies the methods expected by + // the cookie details view. + scoped_nsobject<NSObject> selectionAdapterObject_; + + // Outlets to provide quick access to subviews + // in the prompt window. + IBOutlet NSTextField* description_; + IBOutlet NSView* disclosedViewPlaceholder_; + IBOutlet NSButton* disclosureTriangle_; + IBOutlet NSView* disclosureTriangleSuperView_; + IBOutlet NSMatrix* radioGroupMatrix_; + IBOutlet NSButtonCell* rememberChoiceCell_; +} + +// Designated initializer. +- (id)initWithDialog:(CookiePromptModalDialog*)bridge; + +// Performs the modal dialog loop for the cookie prompt dialog +// and processes the result. +- (void)doModalDialog:(void*)context; + +// Handles the toggling of the disclosure triangle +// to reveal cookie data +- (IBAction)disclosureTrianglePressed:(id)sender; + +// Callback for "block" button. +- (IBAction)block:(id)sender; + +// Callback for "accept" button. +- (IBAction)accept:(id)sender; + +@end diff --git a/chrome/browser/cocoa/cookie_prompt_window_controller.mm b/chrome/browser/cocoa/cookie_prompt_window_controller.mm new file mode 100644 index 0000000..5b85397 --- /dev/null +++ b/chrome/browser/cocoa/cookie_prompt_window_controller.mm @@ -0,0 +1,416 @@ +// 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/cookie_prompt_window_controller.h" + +#include <string> +#include <vector> + +#include "app/l10n_util_mac.h" +#include "app/resource_bundle.h" +#import "base/i18n/time_formatting.h" +#import "base/mac_util.h" +#include "base/sys_string_conversions.h" +#import "chrome/browser/cocoa/cookie_details_view_controller.h" +#include "chrome/browser/cocoa/cookie_tree_node.h" +#include "chrome/browser/cookie_modal_dialog.h" +#include "chrome/browser/cookies_tree_model.h" +#include "grit/generated_resources.h" +#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" + +#pragma mark Window Controller + +// This class is an apapter allows the cookie details view to be shared +// by the cookie prompt window and the cookie tree in the cookies and +// other site data window. As instance of the class is set as the +// content object of the object controller for the details view and +// implements the methods expected by bindings inside that view. +@interface CookiePromptSelectionAdapter : NSObject { + @private + // The type of the cookie prompt being displayed, used to + // determine which subview of the details view is visible + CookiePromptModalDialog::DialogType promptType_; + + // The following members are used to hold information used in the + // cookie prompt detailed information for cookies and web databases. + scoped_nsobject<NSString> name_; + scoped_nsobject<NSString> domain_; + scoped_nsobject<NSString> content_; + + // The following members are used to hold information used in the + // cookie prompt detailed information for cookies only. + scoped_nsobject<NSString> path_; + scoped_nsobject<NSString> sendFor_; + scoped_nsobject<NSString> created_; + scoped_nsobject<NSString> expires_; + + // The following members are used to hold information used in the + // cookie prompt detailed information for local storage only. + scoped_nsobject<NSString> localStorageKey_; + scoped_nsobject<NSString> localStorageValue_; +} + +// Creates and returns an instance approriate for displaying information +// about a cookie. +- (id)initWithCookie:(const std::string&)cookie_line + url:(const GURL&)url; + +// Creates and returns an instance approriate for displaying information +// about a local storage. +- (id)initWithLocalStorage:(const std::string&)domain + key:(const string16&)key + value:(const string16&)value; + +// Creates and returns an instance approriate for displaying information +// about a web database. +- (id)initWithDatabase:(const std::string&)domain + name:(const string16&)name; + +// The following methods are all used in the bindings inside the cookie +// detail view. +@property (readonly) BOOL isFolderOrCookieTreeDetails; +@property (readonly) BOOL isLocalStorageTreeDetails; +@property (readonly) BOOL isDatabaseTreeDetails; +@property (readonly) BOOL isDatabasePromptDetails; +@property (readonly) BOOL isLocalStoragePromptDetails; +@property (readonly) NSString* name; +@property (readonly) NSString* content; +@property (readonly) NSString* domain; +@property (readonly) NSString* path; +@property (readonly) NSString* sendFor; +@property (readonly) NSString* created; +@property (readonly) NSString* expires; +@property (readonly) NSString* fileSize; +@property (readonly) NSString* lastModified; +@property (readonly) NSString* databaseDescription; +@property (readonly) NSString* localStorageKey; +@property (readonly) NSString* localStorageValue; + +@end + +@implementation CookiePromptSelectionAdapter + +- (id)initWithCookie:(const std::string&)cookie_line + url:(const GURL&)url { + if ((self = [super init])) { + promptType_ = CookiePromptModalDialog::DIALOG_TYPE_COOKIE; + net::CookieMonster::ParsedCookie pc(cookie_line); + net::CookieMonster::CanonicalCookie cookie(url, pc); + const std::string& domain(pc.HasDomain() ? pc.Domain() : url.host()); + domain_.reset([base::SysUTF8ToNSString(domain) retain]); + name_.reset([base::SysUTF8ToNSString(cookie.Name()) retain]); + content_.reset([base::SysUTF8ToNSString(cookie.Value()) retain]); + path_.reset([base::SysUTF8ToNSString(cookie.Path()) retain]); + + if (cookie.DoesExpire()) { + expires_.reset([base::SysWideToNSString( + base::TimeFormatFriendlyDateAndTime(cookie.ExpiryDate())) + retain]); + } else { + expires_.reset([l10n_util::GetNSStringWithFixup( + IDS_COOKIES_COOKIE_EXPIRES_SESSION) retain]); + } + + created_.reset([base::SysWideToNSString( + base::TimeFormatFriendlyDateAndTime(cookie.CreationDate())) + retain]); + + if (cookie.IsSecure()) { + sendFor_.reset([l10n_util::GetNSStringWithFixup( + IDS_COOKIES_COOKIE_SENDFOR_SECURE) retain]); + } else { + sendFor_.reset([l10n_util::GetNSStringWithFixup( + IDS_COOKIES_COOKIE_SENDFOR_ANY) retain]); + } + } + return self; +} + +- (id)initWithLocalStorage:(const std::string&)domain + key:(const string16&)key + value:(const string16&)value { + if ((self = [super init])) { + promptType_ = CookiePromptModalDialog::DIALOG_TYPE_LOCAL_STORAGE; + domain_.reset([base::SysUTF8ToNSString(domain) retain]); + localStorageKey_.reset([base::SysUTF16ToNSString(key) retain]); + localStorageValue_.reset([base::SysUTF16ToNSString(value) retain]); + } + return self; +} + +- (id)initWithDatabase:(const std::string&)domain + name:(const string16&)name { + if ((self = [super init])) { + promptType_ = CookiePromptModalDialog::DIALOG_TYPE_DATABASE; + name_.reset([base::SysUTF16ToNSString(name) retain]); + domain_.reset([base::SysUTF8ToNSString(domain) retain]); + } + return self; +} + +- (BOOL)isFolderOrCookieTreeDetails { + return promptType_ == CookiePromptModalDialog::DIALOG_TYPE_COOKIE; +} + +- (BOOL)isLocalStorageTreeDetails { + return false; +} + +- (BOOL)isDatabaseTreeDetails { + return false; +} + +- (BOOL) isDatabasePromptDetails { + return promptType_ == CookiePromptModalDialog::DIALOG_TYPE_DATABASE; +} + +- (BOOL) isLocalStoragePromptDetails { + return promptType_ == CookiePromptModalDialog::DIALOG_TYPE_LOCAL_STORAGE; +} + +- (NSString*)name { + return name_; +} + +- (NSString*)content { + return content_; +} + +- (NSString*)domain { + return domain_; +} + +- (NSString*)path { + return path_; +} + +- (NSString*)sendFor { + return sendFor_; +} + +- (NSString*)created { + return created_; +} + +- (NSString*)expires { + return expires_; +} + +- (NSString*)fileSize { + return nil; +} + +- (NSString*)lastModified { + return nil; +} + +- (NSString*)databaseDescription { + return nil; +} + +- (NSString*)localStorageKey { + return localStorageKey_; +} + +- (NSString*)localStorageValue { + return localStorageValue_; +} + +@end + +@implementation CookiePromptWindowController + +- (id)initWithDialog:(CookiePromptModalDialog*)dialog { + NSString* nibpath = [mac_util::MainAppBundle() + pathForResource:@"CookiePrompt" + ofType:@"nib"]; + if ((self = [super initWithWindowNibPath:nibpath owner:self])) { + dialog_ = dialog; + CookiePromptModalDialog::DialogType type(dialog_->dialog_type()); + if (type == CookiePromptModalDialog::DIALOG_TYPE_COOKIE) { + selectionAdapterObject_.reset([[CookiePromptSelectionAdapter alloc] + initWithCookie:dialog_->cookie_line() + url:dialog_->origin()]); + } else if (type == CookiePromptModalDialog::DIALOG_TYPE_LOCAL_STORAGE) { + selectionAdapterObject_.reset([[CookiePromptSelectionAdapter alloc] + initWithLocalStorage:dialog_->origin().host() + key:dialog_->local_storage_key() + value:dialog_->local_storage_value()]); + } else if (type == CookiePromptModalDialog::DIALOG_TYPE_DATABASE) { + selectionAdapterObject_.reset([[CookiePromptSelectionAdapter alloc] + initWithDatabase:dialog_->origin().host() + name:dialog_->database_name()]); + } else { + NOTIMPLEMENTED(); + } + } + return self; +} + +// Ensures that all parameterized localized strings are filled in. +- (void)doLocalizationTweaks { + int descriptionStringId = 0; + switch (dialog_->dialog_type()) { + case CookiePromptModalDialog::DIALOG_TYPE_COOKIE: + descriptionStringId = IDS_COOKIE_ALERT_LABEL; + break; + case CookiePromptModalDialog::DIALOG_TYPE_LOCAL_STORAGE: + descriptionStringId = IDS_DATA_ALERT_LABEL; + break; + case CookiePromptModalDialog::DIALOG_TYPE_DATABASE: + descriptionStringId = IDS_DATA_ALERT_LABEL; + break; + default: + NOTREACHED(); + break; + } + + string16 displayHost = UTF8ToUTF16(dialog_->origin().host()); + NSString* description = l10n_util::GetNSStringF( + descriptionStringId, displayHost); + [description_ setStringValue:description]; + + // Add the host to the "remember for future prompts" radio button. + NSString* allowText = l10n_util::GetNSStringWithFixup( + IDS_COOKIE_ALERT_REMEMBER_RADIO); + NSString* replacedCellText = base::SysUTF16ToNSString( + ReplaceStringPlaceholders(base::SysNSStringToUTF16(allowText), + displayHost, NULL)); + [rememberChoiceCell_ setTitle:replacedCellText]; +} + +// Adjust the vertical layout of the views in the window so that +// they are spaced correctly with their fully localized contents. +- (void)doLayoutTweaks { + // Wrap the description text. + CGFloat sizeDelta = [GTMUILocalizerAndLayoutTweaker + sizeToFitFixedWidthTextField:description_]; + NSRect descriptionFrame = [description_ frame]; + descriptionFrame.origin.y -= sizeDelta; + [description_ setFrame:descriptionFrame]; + + // Wrap the radio buttons to fit if necessary. + [GTMUILocalizerAndLayoutTweaker + wrapRadioGroupForWidth:radioGroupMatrix_]; + sizeDelta += [GTMUILocalizerAndLayoutTweaker + sizeToFitView:radioGroupMatrix_].height; + NSRect radioGroupFrame = [radioGroupMatrix_ frame]; + radioGroupFrame.origin.y -= sizeDelta; + [radioGroupMatrix_ setFrame:radioGroupFrame]; + + // Adjust views location, they may have moved through the + // expansion of the radio buttons and description text. + NSRect disclosureViewFrame = [disclosureTriangleSuperView_ frame]; + disclosureViewFrame.origin.y -= sizeDelta; + [disclosureTriangleSuperView_ setFrame:disclosureViewFrame]; + + // Adjust the final window size by the size of the cookie details + // view, since it will be initially hidden. + NSRect detailsViewRect = [disclosedViewPlaceholder_ frame]; + sizeDelta -= detailsViewRect.size.height; + + // Final resize the window to fit all of the adjustments + NSRect frame = [[self window] frame]; + frame.origin.y -= sizeDelta; + frame.size.height += sizeDelta; + [[self window] setFrame:frame display:NO animate:NO]; +} + +- (void)replaceCookieDetailsView { + detailsViewController_.reset([[CookieDetailsViewController alloc] init]); + + NSRect viewFrameRect = [disclosedViewPlaceholder_ frame]; + [[disclosedViewPlaceholder_ superview] + replaceSubview:disclosedViewPlaceholder_ + with:[detailsViewController_.get() view]]; +} + +- (void)awakeFromNib { + DCHECK(disclosureTriangle_); + DCHECK(radioGroupMatrix_); + DCHECK(disclosedViewPlaceholder_); + DCHECK(disclosureTriangleSuperView_); + + [self doLocalizationTweaks]; + [self doLayoutTweaks]; + [self replaceCookieDetailsView]; + + [detailsViewController_ setContentObject:selectionAdapterObject_.get()]; + [detailsViewController_ shrinkViewToFit]; + + [[detailsViewController_ view] setHidden:YES]; +} + +- (void)windowWillClose:(NSNotification*)notif { + [self autorelease]; +} + +// |contextInfo| is the bridge back to the C++ CookiePromptModalDialog. +- (void)processModalDialogResult:(void*)contextInfo + returnCode:(int)returnCode { + CookiePromptModalDialog* bridge = + reinterpret_cast<CookiePromptModalDialog*>(contextInfo); + bool remember = [radioGroupMatrix_ selectedRow] == 0; + switch (returnCode) { + case NSAlertFirstButtonReturn: { // OK + bool sessionExpire = false; + bridge->AllowSiteData(remember, sessionExpire); + break; + } + case NSAlertSecondButtonReturn: { // Cancel + bridge->BlockSiteData(remember); + break; + } + default: { + NOTREACHED(); + remember = false; + bridge->BlockSiteData(remember); + } + } +} + +- (void)doModalDialog:(void*)context { + NSInteger returnCode = [NSApp runModalForWindow:[self window]]; + [self processModalDialogResult:context returnCode:returnCode]; +} + +- (IBAction)disclosureTrianglePressed:(id)sender { + NSWindow* window = [self window]; + NSRect frame = [[self window] frame]; + CGFloat sizeChange = [[detailsViewController_.get() view] frame].size.height; + switch ([sender state]) { + case NSOnState: + frame.size.height += sizeChange; + frame.origin.y -= sizeChange; + break; + case NSOffState: + frame.size.height -= sizeChange; + frame.origin.y += sizeChange; + break; + default: + NOTREACHED(); + break; + } + if ([sender state] == NSOffState) { + [[detailsViewController_ view] setHidden:YES]; + } + [window setFrame:frame display:YES animate:YES]; + if ([sender state] == NSOnState) { + [[detailsViewController_ view] setHidden:NO]; + } +} + +// Callback for "accept" button. +- (IBAction)accept:(id)sender { + [[self window] close]; + [NSApp stopModalWithCode:NSAlertFirstButtonReturn]; +} + +// Callback for "block" button. +- (IBAction)block:(id)sender { + [[self window] close]; + [NSApp stopModalWithCode:NSAlertSecondButtonReturn]; +} + +@end diff --git a/chrome/browser/cocoa/cookie_prompt_window_controller_unittest.mm b/chrome/browser/cocoa/cookie_prompt_window_controller_unittest.mm new file mode 100644 index 0000000..8c9da0d --- /dev/null +++ b/chrome/browser/cocoa/cookie_prompt_window_controller_unittest.mm @@ -0,0 +1,50 @@ +// 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. + +#include "base/sys_string_conversions.h" +#include "chrome/browser/cocoa/cocoa_test_helper.h" +#include "chrome/browser/cocoa/cookie_prompt_window_controller.h" +#include "chrome/browser/cookie_modal_dialog.h" + +namespace { + +class CookiePromptWindowControllerTest : public CocoaTest { +}; + +TEST_F(CookiePromptWindowControllerTest, CreateForCookie) { + GURL url("http://chromium.org"); + std::string cookieLine( + "PHPSESSID=0123456789abcdef0123456789abcdef; path=/"); + scoped_ptr<CookiePromptModalDialog> dialog( + new CookiePromptModalDialog(NULL, NULL, url, cookieLine, NULL)); + scoped_nsobject<CookiePromptWindowController> controller( + [[CookiePromptWindowController alloc] initWithDialog:dialog.get()]); + EXPECT_TRUE(controller.get()); + EXPECT_TRUE([controller.get() window]); +} + +TEST_F(CookiePromptWindowControllerTest, CreateForDatabase) { + GURL url("http://google.com"); + string16 databaseName(base::SysNSStringToUTF16(@"some database")); + scoped_ptr<CookiePromptModalDialog> dialog( + new CookiePromptModalDialog(NULL, NULL, url, databaseName, NULL)); + scoped_nsobject<CookiePromptWindowController> controller( + [[CookiePromptWindowController alloc] initWithDialog:dialog.get()]); + EXPECT_TRUE(controller.get()); + EXPECT_TRUE([controller.get() window]); +} + +TEST_F(CookiePromptWindowControllerTest, CreateForLocalStorage) { + GURL url("http://chromium.org"); + string16 key(base::SysNSStringToUTF16(@"key")); + string16 value(base::SysNSStringToUTF16(@"value")); + scoped_ptr<CookiePromptModalDialog> dialog( + new CookiePromptModalDialog(NULL, NULL, url, key, value, NULL)); + scoped_nsobject<CookiePromptWindowController> controller( + [[CookiePromptWindowController alloc] initWithDialog:dialog.get()]); + EXPECT_TRUE(controller.get()); + EXPECT_TRUE([controller.get() window]); +} + +} // namespace diff --git a/chrome/browser/cocoa/cookie_tree_node.h b/chrome/browser/cocoa/cookie_tree_node.h index 5101157..66f5851 100644 --- a/chrome/browser/cocoa/cookie_tree_node.h +++ b/chrome/browser/cocoa/cookie_tree_node.h @@ -75,6 +75,12 @@ enum CocoaCookieTreeNodeType { - (NSArray*)children; - (BOOL)isLeaf; +- (BOOL)isFolderOrCookieTreeDetails; +- (BOOL)isLocalStorageTreeDetails; +- (BOOL)isDatabaseTreeDetails; +- (BOOL)isLocalStoragePromptDetails; +- (BOOL)isDatabasePromptDetails; + // Used only by kCocoaCookieTreeNodeTypeCookie. Nil for other types. - (NSString*)name; - (NSString*)content; diff --git a/chrome/browser/cocoa/cookie_tree_node.mm b/chrome/browser/cocoa/cookie_tree_node.mm index 3d9eeaa..376df82 100644 --- a/chrome/browser/cocoa/cookie_tree_node.mm +++ b/chrome/browser/cocoa/cookie_tree_node.mm @@ -121,6 +121,27 @@ [self nodeType], [[self children] count]]; } +- (BOOL)isFolderOrCookieTreeDetails { + return [self nodeType] == kCocoaCookieTreeNodeTypeFolder || + [self nodeType] == kCocoaCookieTreeNodeTypeCookie; +} + +- (BOOL)isDatabaseTreeDetails { + return [self nodeType] == kCocoaCookieTreeNodeTypeDatabaseStorage; +} + +- (BOOL)isLocalStorageTreeDetails { + return [self nodeType] == kCocoaCookieTreeNodeTypeLocalStorage; +} + +- (BOOL)isDatabasePromptDetails { + return false; +} + +- (BOOL)isLocalStoragePromptDetails { + return false; +} + #pragma mark Cookie Accessors - (NSString*)name { @@ -167,4 +188,24 @@ return databaseDescription_.get(); } +#pragma mark Unused Accessors + +// This method is never called for the cookie tree, it is only +// only included because the Cocoa bindings for the shared view +// used to display browser data details always expects the method +// even though it is only used in the cookie prompt window. +- (id)localStorageKey { + NOTIMPLEMENTED(); + return nil; +} + +// This method is never called for the cookie tree, it is only +// only included because the Cocoa bindings for the shared view +// used to display browser data details always expects the method +// even though it is only used in the cookie prompt window. +- (id)localStorageValue { + NOTIMPLEMENTED(); + return nil; +} + @end diff --git a/chrome/browser/cocoa/cookies_window_controller.h b/chrome/browser/cocoa/cookies_window_controller.h index 8f85285..be38fb5 100644 --- a/chrome/browser/cocoa/cookies_window_controller.h +++ b/chrome/browser/cocoa/cookies_window_controller.h @@ -13,6 +13,7 @@ #include "net/base/cookie_monster.h" @class CookiesWindowController; +@class CookieDetailsViewController; class Profile; namespace { @@ -101,13 +102,9 @@ class CookiesTreeModelObserverBridge : public CookiesTreeModel::Observer { IBOutlet NSTreeController* treeController_; IBOutlet NSOutlineView* outlineView_; IBOutlet NSSearchField* searchField_; + IBOutlet NSView* cookieDetailsViewPlaceholder_; - // These views are laid out inside a NSBox and are shown/hidden to detail - // information about the selected node. - IBOutlet NSView* cookieInfo_; - IBOutlet NSView* databaseInfo_; - IBOutlet NSView* localStorageInfo_; - + scoped_nsobject<CookieDetailsViewController> detailsViewController_; Profile* profile_; // weak BrowsingDataDatabaseHelper* databaseHelper_; // weak BrowsingDataLocalStorageHelper* storageHelper_; // weak @@ -146,8 +143,5 @@ class CookiesTreeModelObserverBridge : public CookiesTreeModel::Observer { - (void)clearBrowsingDataNotification:(NSNotification*)notif; - (CookiesTreeModelObserverBridge*)modelObserver; - (NSArray*)icons; -- (NSView*)cookieInfoView; -- (NSView*)localStorageInfoView; -- (NSView*)databaseInfoInfoView; - (void)loadTreeModelFromProfile; @end diff --git a/chrome/browser/cocoa/cookies_window_controller.mm b/chrome/browser/cocoa/cookies_window_controller.mm index e2f53c5..d3b6035 100644 --- a/chrome/browser/cocoa/cookies_window_controller.mm +++ b/chrome/browser/cocoa/cookies_window_controller.mm @@ -14,6 +14,7 @@ #include "base/sys_string_conversions.h" #include "chrome/browser/browsing_data_remover.h" #include "chrome/browser/cocoa/clear_browsing_data_controller.h" +#include "chrome/browser/cocoa/cookie_details_view_controller.h" #include "chrome/browser/profile.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -226,6 +227,17 @@ bool CookiesTreeModelObserverBridge::HasCocoaModel() { - (void)awakeFromNib { DCHECK([self window]); DCHECK_EQ(self, [[self window] delegate]); + + detailsViewController_.reset([[CookieDetailsViewController alloc] init]); + + NSView* detailView = [detailsViewController_.get() view]; + NSRect viewFrameRect = [cookieDetailsViewPlaceholder_ frame]; + [[detailsViewController_.get() view] setFrame:viewFrameRect]; + [[cookieDetailsViewPlaceholder_ superview] + replaceSubview:cookieDetailsViewPlaceholder_ + with:detailView]; + + [detailsViewController_ configureBindingsForTreeController:treeController_]; } - (void)windowWillClose:(NSNotification*)notif { @@ -356,12 +368,6 @@ bool CookiesTreeModelObserverBridge::HasCocoaModel() { if (count != 1U) { DCHECK_LT(count, 1U) << "User was able to select more than 1 cookie node!"; [self setRemoveButtonEnabled:NO]; - - // Make sure that the cookie info pane is shown when there is no selection. - // That's what windows does. - [cookieInfo_ setHidden:NO]; - [localStorageInfo_ setHidden:YES]; - [databaseInfo_ setHidden:YES]; return; } @@ -378,18 +384,6 @@ bool CookiesTreeModelObserverBridge::HasCocoaModel() { } node = [[node children] objectAtIndex:childIndex]; } - - [self setRemoveButtonEnabled:YES]; - CocoaCookieTreeNodeType nodeType = [[selectedObjects lastObject] nodeType]; - bool hideCookieInfoView = nodeType != kCocoaCookieTreeNodeTypeCookie && - nodeType != kCocoaCookieTreeNodeTypeFolder; - bool hideLocaStorageInfoView = - nodeType != kCocoaCookieTreeNodeTypeLocalStorage; - bool hideDatabaseInfoView = - nodeType != kCocoaCookieTreeNodeTypeDatabaseStorage; - [cookieInfo_ setHidden:hideCookieInfoView]; - [localStorageInfo_ setHidden:hideLocaStorageInfoView]; - [databaseInfo_ setHidden:hideDatabaseInfoView]; } #pragma mark Unit Testing @@ -402,18 +396,6 @@ bool CookiesTreeModelObserverBridge::HasCocoaModel() { return icons_.get(); } -- (NSView*)cookieInfoView { - return cookieInfo_; -} - -- (NSView*)localStorageInfoView { - return localStorageInfo_; -} - -- (NSView*)databaseInfoView { - return databaseInfo_; -} - // Re-initializes the |treeModel_|, creates a new observer for it, and re- // builds the |cocoaTreeModel_|. We use this to initialize the controller and // to rebuild after the user clears browsing data. Because the models get diff --git a/chrome/browser/cocoa/cookies_window_controller_unittest.mm b/chrome/browser/cocoa/cookies_window_controller_unittest.mm index b58c584..fb19db3 100644 --- a/chrome/browser/cocoa/cookies_window_controller_unittest.mm +++ b/chrome/browser/cocoa/cookies_window_controller_unittest.mm @@ -480,8 +480,6 @@ TEST_F(CookiesWindowControllerTest, FLAKY_RemoveButtonEnabled) { [[controller treeController] setSelectionIndexPath:indexPath]; [controller outlineViewSelectionDidChange:nil]; EXPECT_TRUE([controller removeButtonEnabled]); - EXPECT_FALSE([[controller cookieInfoView] isHidden]); - EXPECT_TRUE([[controller localStorageInfoView] isHidden]); } { @@ -491,8 +489,6 @@ TEST_F(CookiesWindowControllerTest, FLAKY_RemoveButtonEnabled) { [[controller treeController] setSelectionIndexPath:indexPath]; [controller outlineViewSelectionDidChange:nil]; EXPECT_TRUE([controller removeButtonEnabled]); - EXPECT_FALSE([[controller cookieInfoView] isHidden]); - EXPECT_TRUE([[controller localStorageInfoView] isHidden]); } { @@ -502,8 +498,6 @@ TEST_F(CookiesWindowControllerTest, FLAKY_RemoveButtonEnabled) { [[controller treeController] setSelectionIndexPath:indexPath]; [controller outlineViewSelectionDidChange:nil]; EXPECT_TRUE([controller removeButtonEnabled]); - EXPECT_TRUE([[controller cookieInfoView] isHidden]); - EXPECT_FALSE([[controller localStorageInfoView] isHidden]); } { @@ -527,8 +521,7 @@ TEST_F(CookiesWindowControllerTest, FLAKY_RemoveButtonEnabled) { [controller closeSheet:nil]; } -TEST_F(CookiesWindowControllerTest, UpdateFilter) -{ +TEST_F(CookiesWindowControllerTest, UpdateFilter) { const GURL url = GURL("http://foo.com"); TestingProfile* profile = browser_helper_.profile(); net::CookieMonster* cm = profile->GetCookieMonster(); diff --git a/chrome/browser/cookie_modal_dialog.h b/chrome/browser/cookie_modal_dialog.h index 7a6ae50..997dd7f 100644 --- a/chrome/browser/cookie_modal_dialog.h +++ b/chrome/browser/cookie_modal_dialog.h @@ -13,6 +13,14 @@ #include "chrome/browser/cookie_prompt_modal_dialog_delegate.h" #include "googleurl/src/gurl.h" +#if defined(OS_MACOSX) +#if __OBJC__ +@class NSWindow; +#else +class NSWindow; +#endif +#endif + class HostContentSettingsMap; class PrefService; @@ -58,6 +66,10 @@ class CookiePromptModalDialog : public AppModalDialog { virtual void CancelWindow(); virtual bool IsValid(); +#if defined(OS_MACOSX) + virtual void CloseModalDialog(); +#endif + DialogType dialog_type() const { return dialog_type_; } const GURL& origin() const { return origin_; } const std::string& cookie_line() const { return cookie_line_; } @@ -78,6 +90,11 @@ class CookiePromptModalDialog : public AppModalDialog { #endif private: + +#if defined(OS_MACOSX) + NSWindow* dialog_; +#endif + // Used to verify our request is still necessary and when the response should // persist. scoped_refptr<HostContentSettingsMap> host_content_settings_map_; diff --git a/chrome/browser/cookie_modal_dialog_mac.mm b/chrome/browser/cookie_modal_dialog_mac.mm index e80c2d6..35a54e0 100644 --- a/chrome/browser/cookie_modal_dialog_mac.mm +++ b/chrome/browser/cookie_modal_dialog_mac.mm @@ -3,118 +3,22 @@ // found in the LICENSE file. #include "chrome/browser/cookie_modal_dialog.h" +#import "chrome/browser/cocoa/cookie_prompt_window_controller.h" #import <Cocoa/Cocoa.h> #include "app/l10n_util_mac.h" #import "base/cocoa_protocols_mac.h" +#include "base/mac_util.h" #include "base/scoped_nsobject.h" #include "base/logging.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "grit/generated_resources.h" -// Helper object that will become a real NSWindowController in the future. -@interface CookiePromptModalDialogHelper : NSObject<NSAlertDelegate> { - @private - scoped_nsobject<NSAlert> alert_; - scoped_nsobject<NSMatrix> matrix_; -} - -- (id)initWithBridge:(CookiePromptModalDialog*)bridge; -- (NSAlert*)alert; -- (void)alertDidEnd:(NSAlert*)alert - returnCode:(int)returnCode - contextInfo:(void*)contextInfo; -@end - -@implementation CookiePromptModalDialogHelper - -- (id)initWithBridge:(CookiePromptModalDialog*)bridge { - // The cookie confirmation dialog needs both a radio group and a disclosure - // triangle, so it's too complex to be shown as an NSAlert -- a custom window - // is required. However, that requires small modifications to the parent class - // AppModalDialog, so I'll do that in another CL. - if ((self = [super init])) { - alert_.reset([[NSAlert alloc] init]); - - string16 displayHost = UTF8ToUTF16(bridge->origin().host()); - int descriptionStringId = - bridge->dialog_type() == CookiePromptModalDialog::DIALOG_TYPE_COOKIE ? - IDS_COOKIE_ALERT_LABEL : IDS_DATA_ALERT_LABEL; - NSString* description = l10n_util::GetNSStringF( - descriptionStringId, displayHost); - NSString* allow = - l10n_util::GetNSStringWithFixup(IDS_COOKIE_ALERT_ALLOW_BUTTON); - NSString* block = - l10n_util::GetNSStringWithFixup(IDS_COOKIE_ALERT_BLOCK_BUTTON); - - NSString* remember = l10n_util::GetNSStringF( - IDS_COOKIE_ALERT_REMEMBER_RADIO, displayHost); - NSString* ask = l10n_util::GetNSStringWithFixup(IDS_COOKIE_ALERT_ASK_RADIO); - - scoped_nsobject<NSButtonCell> prototype([[NSButtonCell alloc] init]); - [prototype.get() setButtonType:NSRadioButton]; - matrix_.reset( - [[NSMatrix alloc] initWithFrame:NSZeroRect - mode:NSRadioModeMatrix - prototype:prototype - numberOfRows:2 - numberOfColumns:1]); - NSArray *cellArray = [matrix_.get() cells]; - [[cellArray objectAtIndex:0] setTitle:remember]; - [[cellArray objectAtIndex:1] setTitle:ask]; - [matrix_.get() sizeToFit]; - [alert_.get() setAccessoryView:matrix_.get()]; - - [alert_.get() setMessageText:description]; - [alert_.get() addButtonWithTitle:allow]; - [alert_.get() addButtonWithTitle:block]; - } - return self; -} - -- (NSAlert*)alert { - return alert_.get(); -} - -// |contextInfo| is the bridge back to the C++ CookiePromptModalDialog. -- (void)alertDidEnd:(NSAlert*)alert - returnCode:(int)returnCode - contextInfo:(void*)contextInfo { - CookiePromptModalDialog* bridge = - reinterpret_cast<CookiePromptModalDialog*>(contextInfo); - bool remember = [matrix_.get() selectedRow] == 0; - switch (returnCode) { - case NSAlertFirstButtonReturn: { // OK - bool sessionExpire = false; - bridge->AllowSiteData(remember, sessionExpire); - break; - } - case NSAlertSecondButtonReturn: { // Cancel - bridge->BlockSiteData(remember); - break; - } - case NSRunStoppedResponse: { // Window was closed underneath us - bridge->BlockSiteData(remember); - break; - } - default: { - NOTREACHED(); - remember = false; - bridge->BlockSiteData(remember); - } - } -} -@end - void CookiePromptModalDialog::CreateAndShowDialog() { - scoped_nsobject<CookiePromptModalDialogHelper> helper( - [[CookiePromptModalDialogHelper alloc] initWithBridge:this]); - NSAlert* alert = [helper alert]; - DCHECK(alert); - - NSInteger result = [alert runModal]; - [helper.get() alertDidEnd:alert returnCode:result contextInfo:this]; + scoped_nsobject<CookiePromptWindowController> controller( + [[CookiePromptWindowController alloc] initWithDialog:this]); + [controller.get() doModalDialog:this]; // Other than JavaScriptAppModalDialog, the cross-platform part of this class // does not call |CompleteDialog()|, an explicit call is required. @@ -133,6 +37,10 @@ void CookiePromptModalDialog::CancelWindow() { NOTIMPLEMENTED(); } +void CookiePromptModalDialog::CloseModalDialog() { + dialog_ = nil; +} + // This is only used by the app-modal dialog machinery on windows. NativeDialog CookiePromptModalDialog::CreateNativeDialog() { NOTIMPLEMENTED(); diff --git a/chrome/browser/js_modal_dialog.h b/chrome/browser/js_modal_dialog.h index 51b9c81..65ed1ae 100644 --- a/chrome/browser/js_modal_dialog.h +++ b/chrome/browser/js_modal_dialog.h @@ -13,6 +13,14 @@ #include "chrome/common/notification_registrar.h" #include "net/base/cookie_monster.h" +#if defined(OS_MACOSX) +#if __OBJC__ +@class NSAlert; +#else +class NSAlert; +#endif +#endif + class ExtensionHost; class JavaScriptMessageBoxClient; @@ -56,6 +64,10 @@ class JavaScriptAppModalDialog : public AppModalDialog, return is_before_unload_dialog_; } +#if defined(OS_MACOSX) + virtual void CloseModalDialog(); +#endif + // Callbacks from NativeDialog when the user accepts or cancels the dialog. void OnCancel(); void OnAccept(const std::wstring& prompt_text, bool suppress_js_messages); @@ -75,6 +87,10 @@ class JavaScriptAppModalDialog : public AppModalDialog, // Initializes for notifications to listen. void InitNotifications(); +#if defined(OS_MACOSX) + NSAlert* dialog_; +#endif + NotificationRegistrar registrar_; // An implementation of the client interface to provide supporting methods diff --git a/chrome/browser/js_modal_dialog_mac.mm b/chrome/browser/js_modal_dialog_mac.mm index 44eb86e..3bdc9aa 100644 --- a/chrome/browser/js_modal_dialog_mac.mm +++ b/chrome/browser/js_modal_dialog_mac.mm @@ -172,3 +172,10 @@ NativeDialog JavaScriptAppModalDialog::CreateNativeDialog() { NOTIMPLEMENTED(); return nil; } + +void JavaScriptAppModalDialog::CloseModalDialog() { + NSAlert* alert = dialog_; + DCHECK([alert isKindOfClass:[NSAlert class]]); + [NSApp endSheet:[alert window]]; + dialog_ = nil; +} |