summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorbauerb@chromium.org <bauerb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-15 15:38:44 +0000
committerbauerb@chromium.org <bauerb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-15 15:38:44 +0000
commit0cd07f946c353e33d0b9474d383ab06612e40bd8 (patch)
treec3b05021da46c9f489d5b359f53a3e3f36f02ae7 /chrome/browser
parente19f6b1bb2e8d09ae7471f10d56c834a250182c5 (diff)
downloadchromium_src-0cd07f946c353e33d0b9474d383ab06612e40bd8.zip
chromium_src-0cd07f946c353e33d0b9474d383ab06612e40bd8.tar.gz
chromium_src-0cd07f946c353e33d0b9474d383ab06612e40bd8.tar.bz2
[Mac] Add per-plugin exceptions to content settings.
Screenshot: http://www.dropmocks.com/mXMd I'm adding a subclass of NSArrayController, TableModelArrayController, that binds to a RemoveRowsTableModel that can use groups and displays them using group rows in an NSTableView. This cleans up SimpleContentExceptionsWindowController a lot, and the class could also be used for other table models that use groups (keyword editor and autofill). XIB changes: In SimpleContentExceptionsWindow.xib, bind table view to TableModelArrayController instead of using the dataSource outlet. Buttons call actions on TableModelArrayController, and table view delegate also points to it. BUG=39252 TEST=SimpleContentExceptionsWindowControllerTest.*:TableModelArrayControllerTest.*:PluginExceptionsTableModelTest.* Review URL: http://codereview.chromium.org/3327016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@59501 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/cocoa/content_settings_dialog_controller.mm17
-rw-r--r--chrome/browser/cocoa/simple_content_exceptions_window_controller.h12
-rw-r--r--chrome/browser/cocoa/simple_content_exceptions_window_controller.mm111
-rw-r--r--chrome/browser/cocoa/simple_content_exceptions_window_controller_unittest.mm44
-rw-r--r--chrome/browser/cocoa/table_model_array_controller.h54
-rw-r--r--chrome/browser/cocoa/table_model_array_controller.mm245
-rw-r--r--chrome/browser/cocoa/table_model_array_controller_unittest.mm168
-rw-r--r--chrome/browser/mock_plugin_exceptions_table_model.cc17
-rw-r--r--chrome/browser/mock_plugin_exceptions_table_model.h29
-rw-r--r--chrome/browser/plugin_exceptions_table_model.cc15
-rw-r--r--chrome/browser/plugin_exceptions_table_model_unittest.cc38
11 files changed, 593 insertions, 157 deletions
diff --git a/chrome/browser/cocoa/content_settings_dialog_controller.mm b/chrome/browser/cocoa/content_settings_dialog_controller.mm
index 54112e9..fdfa5a4 100644
--- a/chrome/browser/cocoa/content_settings_dialog_controller.mm
+++ b/chrome/browser/cocoa/content_settings_dialog_controller.mm
@@ -21,6 +21,7 @@
#import "chrome/browser/host_content_settings_map.h"
#import "chrome/browser/notifications/desktop_notification_service.h"
#import "chrome/browser/notifications/notification_exceptions_table_model.h"
+#include "chrome/browser/plugin_exceptions_table_model.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/chrome_switches.h"
@@ -344,7 +345,21 @@ class PrefObserverDisabler {
}
- (IBAction)showPluginsExceptions:(id)sender {
- [self showExceptionsForType:CONTENT_SETTINGS_TYPE_PLUGINS];
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableResourceContentSettings)) {
+ HostContentSettingsMap* settingsMap = profile_->GetHostContentSettingsMap();
+ HostContentSettingsMap* offTheRecordSettingsMap =
+ profile_->HasOffTheRecordProfile() ?
+ profile_->GetOffTheRecordProfile()->GetHostContentSettingsMap() :
+ NULL;
+ PluginExceptionsTableModel* model =
+ new PluginExceptionsTableModel(settingsMap, offTheRecordSettingsMap);
+ model->LoadSettings();
+ [[SimpleContentExceptionsWindowController controllerWithTableModel:model]
+ attachSheetTo:[self window]];
+ } else {
+ [self showExceptionsForType:CONTENT_SETTINGS_TYPE_PLUGINS];
+ }
}
- (IBAction)showPopupsExceptions:(id)sender {
diff --git a/chrome/browser/cocoa/simple_content_exceptions_window_controller.h b/chrome/browser/cocoa/simple_content_exceptions_window_controller.h
index aa1cfcf..0e29f68 100644
--- a/chrome/browser/cocoa/simple_content_exceptions_window_controller.h
+++ b/chrome/browser/cocoa/simple_content_exceptions_window_controller.h
@@ -6,26 +6,25 @@
#include "base/cocoa_protocols_mac.h"
#include "base/scoped_ptr.h"
+#import "chrome/browser/cocoa/table_model_array_controller.h"
#include "chrome/browser/remove_rows_table_model.h"
class RemoveRowsObserverBridge;
// Controller for the geolocation exception dialog.
@interface SimpleContentExceptionsWindowController : NSWindowController
- <NSWindowDelegate,
- NSTableViewDataSource,
- NSTableViewDelegate> {
+ <NSWindowDelegate> {
@private
IBOutlet NSTableView* tableView_;
IBOutlet NSButton* removeButton_;
IBOutlet NSButton* removeAllButton_;
IBOutlet NSButton* doneButton_;
+ IBOutlet TableModelArrayController* arrayController_;
scoped_ptr<RemoveRowsTableModel> model_;
- scoped_ptr<RemoveRowsObserverBridge> tableObserver_;
}
-// Shows or makes frontmost the geolocation exceptions window.
+// Shows or makes frontmost the exceptions window.
// Changes made by the user in the window are persisted in |model|.
// Takes ownership of |model|.
+ (id)controllerWithTableModel:(RemoveRowsTableModel*)model;
@@ -36,7 +35,4 @@ class RemoveRowsObserverBridge;
- (void)attachSheetTo:(NSWindow*)window;
- (IBAction)closeSheet:(id)sender;
-- (IBAction)removeRow:(id)sender;
-- (IBAction)removeAll:(id)sender;
-
@end
diff --git a/chrome/browser/cocoa/simple_content_exceptions_window_controller.mm b/chrome/browser/cocoa/simple_content_exceptions_window_controller.mm
index 7be52a9..a054b54 100644
--- a/chrome/browser/cocoa/simple_content_exceptions_window_controller.mm
+++ b/chrome/browser/cocoa/simple_content_exceptions_window_controller.mm
@@ -4,8 +4,6 @@
#import "chrome/browser/cocoa/simple_content_exceptions_window_controller.h"
-#include <set>
-
#include "app/l10n_util_mac.h"
#include "app/table_model_observer.h"
#import "base/mac_util.h"
@@ -16,35 +14,8 @@
@interface SimpleContentExceptionsWindowController (Private)
- (id)initWithTableModel:(RemoveRowsTableModel*)model;
-- (void)selectedRows:(RemoveRowsTableModel::Rows*)rows;
-- (void)adjustEditingButtons;
-- (void)modelDidChange;
@end
-// Observer for a RemoveRowsTableModel.
-class RemoveRowsObserverBridge : public TableModelObserver {
- public:
- RemoveRowsObserverBridge(SimpleContentExceptionsWindowController* controller)
- : controller_(controller) {}
- virtual ~RemoveRowsObserverBridge() {}
-
- virtual void OnModelChanged() {
- [controller_ modelDidChange];
- }
- virtual void OnItemsChanged(int start, int length) {
- [controller_ modelDidChange];
- }
- virtual void OnItemsAdded(int start, int length) {
- [controller_ modelDidChange];
- }
- virtual void OnItemsRemoved(int start, int length) {
- [controller_ modelDidChange];
- }
-
- private:
- SimpleContentExceptionsWindowController* controller_; // weak
-};
-
namespace {
const CGFloat kButtonBarHeight = 35.0;
@@ -69,8 +40,6 @@ SimpleContentExceptionsWindowController* g_exceptionWindow = nil;
ofType:@"nib"];
if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
model_.reset(model);
- tableObserver_.reset(new RemoveRowsObserverBridge(self));
- model_->SetObserver(tableObserver_.get());
// TODO(thakis): autoremember window rect.
// TODO(thakis): sorting support.
@@ -82,15 +51,19 @@ SimpleContentExceptionsWindowController* g_exceptionWindow = nil;
DCHECK([self window]);
DCHECK_EQ(self, [[self window] delegate]);
DCHECK(tableView_);
- DCHECK_EQ(self, [tableView_ dataSource]);
- DCHECK_EQ(self, [tableView_ delegate]);
+ DCHECK(arrayController_);
CGFloat minWidth = [[removeButton_ superview] bounds].size.width +
[[doneButton_ superview] bounds].size.width;
[[self window] setMinSize:NSMakeSize(minWidth,
[[self window] minSize].height)];
-
- [self adjustEditingButtons];
+ NSDictionary* columns = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInt:IDS_EXCEPTIONS_HOSTNAME_HEADER], @"hostname",
+ [NSNumber numberWithInt:IDS_EXCEPTIONS_ACTION_HEADER], @"action",
+ nil];
+ [arrayController_ bindToTableModel:model_.get()
+ withColumns:columns
+ groupTitleColumn:@"hostname"];
}
- (void)setMinWidth:(CGFloat)minWidth {
@@ -104,9 +77,6 @@ SimpleContentExceptionsWindowController* g_exceptionWindow = nil;
}
- (void)windowWillClose:(NSNotification*)notification {
- // Without this, some of the unit tests fail on 10.6:
- [tableView_ setDataSource:nil];
-
g_exceptionWindow = nil;
[self autorelease];
}
@@ -124,7 +94,7 @@ SimpleContentExceptionsWindowController* g_exceptionWindow = nil;
case NSDeleteFunctionKey:
// Delete deletes.
if ([[tableView_ selectedRowIndexes] count] > 0)
- [self removeRow:self];
+ [arrayController_ remove:event];
return;
}
}
@@ -150,68 +120,5 @@ SimpleContentExceptionsWindowController* g_exceptionWindow = nil;
[NSApp endSheet:[self window]];
}
-- (IBAction)removeRow:(id)sender {
- RemoveRowsTableModel::Rows rows;
- [self selectedRows:&rows];
- model_->RemoveRows(rows);
-}
-
-- (IBAction)removeAll:(id)sender {
- model_->RemoveAll();
-}
-
-// Table View Data Source -----------------------------------------------------
-
-- (NSInteger)numberOfRowsInTableView:(NSTableView*)table {
- return model_->RowCount();
-}
-
-- (id)tableView:(NSTableView*)tv
- objectValueForTableColumn:(NSTableColumn*)tableColumn
- row:(NSInteger)row {
- NSObject* result = nil;
- NSString* identifier = [tableColumn identifier];
- if ([identifier isEqualToString:@"hostname"]) {
- std::wstring host = model_->GetText(row, IDS_EXCEPTIONS_HOSTNAME_HEADER);
- result = base::SysWideToNSString(host);
- } else if ([identifier isEqualToString:@"action"]) {
- std::wstring action = model_->GetText(row, IDS_EXCEPTIONS_ACTION_HEADER);
- result = base::SysWideToNSString(action);
- } else {
- NOTREACHED();
- }
- return result;
-}
-
-// Table View Delegate --------------------------------------------------------
-
-// When the selection in the table view changes, we need to adjust buttons.
-- (void)tableViewSelectionDidChange:(NSNotification*)notification {
- [self adjustEditingButtons];
-}
-
-// Private --------------------------------------------------------------------
-
-// Returns the selected rows.
-- (void)selectedRows:(RemoveRowsTableModel::Rows*)rows {
- NSIndexSet* selection = [tableView_ selectedRowIndexes];
- for (NSUInteger index = [selection lastIndex]; index != NSNotFound;
- index = [selection indexLessThanIndex:index])
- rows->insert(index);
-}
-
-// This method appropriately sets the enabled states on the table's editing
-// buttons.
-- (void)adjustEditingButtons {
- RemoveRowsTableModel::Rows rows;
- [self selectedRows:&rows];
- [removeButton_ setEnabled:model_->CanRemoveRows(rows)];
- [removeAllButton_ setEnabled:([tableView_ numberOfRows] > 0)];
-}
-
-- (void)modelDidChange {
- [tableView_ reloadData];
- [self adjustEditingButtons];
-}
@end
diff --git a/chrome/browser/cocoa/simple_content_exceptions_window_controller_unittest.mm b/chrome/browser/cocoa/simple_content_exceptions_window_controller_unittest.mm
index 34e96a5..05b025c 100644
--- a/chrome/browser/cocoa/simple_content_exceptions_window_controller_unittest.mm
+++ b/chrome/browser/cocoa/simple_content_exceptions_window_controller_unittest.mm
@@ -11,9 +11,26 @@
#include "chrome/browser/cocoa/browser_test_helper.h"
#include "chrome/browser/cocoa/cocoa_test_helper.h"
#include "chrome/browser/geolocation/geolocation_exceptions_table_model.h"
+#include "chrome/browser/host_content_settings_map.h"
+#include "chrome/browser/plugin_exceptions_table_model.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
+@interface SimpleContentExceptionsWindowController (Testing)
+
+@property(readonly, nonatomic) TableModelArrayController* arrayController;
+
+@end
+
+@implementation SimpleContentExceptionsWindowController (Testing)
+
+- (TableModelArrayController*)arrayController {
+ return arrayController_;
+}
+
+@end
+
+
namespace {
class SimpleContentExceptionsWindowControllerTest : public CocoaTest {
@@ -21,12 +38,13 @@ class SimpleContentExceptionsWindowControllerTest : public CocoaTest {
virtual void SetUp() {
CocoaTest::SetUp();
TestingProfile* profile = browser_helper_.profile();
- settingsMap_ = new GeolocationContentSettingsMap(profile);
+ geolocation_settings_ = new GeolocationContentSettingsMap(profile);
+ content_settings_ = new HostContentSettingsMap(profile);
}
SimpleContentExceptionsWindowController* GetController() {
- GeolocationExceptionsTableModel* model = // Freed by window controller.
- new GeolocationExceptionsTableModel(settingsMap_.get());
+ GeolocationExceptionsTableModel* model = // Freed by window controller.
+ new GeolocationExceptionsTableModel(geolocation_settings_.get());
id controller = [SimpleContentExceptionsWindowController
controllerWithTableModel:model];
[controller showWindow:nil];
@@ -34,17 +52,27 @@ class SimpleContentExceptionsWindowControllerTest : public CocoaTest {
}
void ClickRemoveAll(SimpleContentExceptionsWindowController* controller) {
- [controller removeAll:nil];
+ [controller.arrayController removeAll:nil];
}
protected:
BrowserTestHelper browser_helper_;
- scoped_refptr<GeolocationContentSettingsMap> settingsMap_;
+ scoped_refptr<GeolocationContentSettingsMap> geolocation_settings_;
+ scoped_refptr<HostContentSettingsMap> content_settings_;
};
TEST_F(SimpleContentExceptionsWindowControllerTest, Construction) {
GeolocationExceptionsTableModel* model = // Freed by window controller.
- new GeolocationExceptionsTableModel(settingsMap_.get());
+ new GeolocationExceptionsTableModel(geolocation_settings_.get());
+ SimpleContentExceptionsWindowController* controller =
+ [SimpleContentExceptionsWindowController controllerWithTableModel:model];
+ [controller showWindow:nil];
+ [controller close]; // Should autorelease.
+}
+
+TEST_F(SimpleContentExceptionsWindowControllerTest, ShowPluginExceptions) {
+ PluginExceptionsTableModel* model = // Freed by window controller.
+ new PluginExceptionsTableModel(content_settings_.get(), NULL);
SimpleContentExceptionsWindowController* controller =
[SimpleContentExceptionsWindowController controllerWithTableModel:model];
[controller showWindow:nil];
@@ -52,7 +80,7 @@ TEST_F(SimpleContentExceptionsWindowControllerTest, Construction) {
}
TEST_F(SimpleContentExceptionsWindowControllerTest, AddExistingEditAdd) {
- settingsMap_->SetContentSetting(
+ geolocation_settings_->SetContentSetting(
GURL("http://myhost"), GURL(), CONTENT_SETTING_BLOCK);
SimpleContentExceptionsWindowController* controller = GetController();
@@ -60,7 +88,7 @@ TEST_F(SimpleContentExceptionsWindowControllerTest, AddExistingEditAdd) {
[controller close];
- EXPECT_EQ(0u, settingsMap_->GetAllOriginsSettings().size());
+ EXPECT_EQ(0u, geolocation_settings_->GetAllOriginsSettings().size());
}
} // namespace
diff --git a/chrome/browser/cocoa/table_model_array_controller.h b/chrome/browser/cocoa/table_model_array_controller.h
new file mode 100644
index 0000000..a3579868
--- /dev/null
+++ b/chrome/browser/cocoa/table_model_array_controller.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef CHROME_BROWSER_COCOA_TABLE_MODEL_ARRAY_CONTROLLER_H_
+#define CHROME_BROWSER_COCOA_TABLE_MODEL_ARRAY_CONTROLLER_H_
+#pragma once
+
+#import <Cocoa/Cocoa.h>
+
+#include "app/table_model_observer.h"
+#include "base/cocoa_protocols_mac.h"
+#include "base/scoped_nsobject.h"
+#include "base/scoped_ptr.h"
+
+class RemoveRowsObserverBridge;
+class RemoveRowsTableModel;
+@class TableModelArrayController;
+
+// This class functions as an adapter from a RemoveRowsTableModel to a Cocoa
+// NSArrayController, to be used with bindings.
+// It maps the CanRemoveRows method to its canRemove property, and exposes
+// RemoveRows and RemoveAll as actions (remove: and removeAll:).
+// If the table model has groups, these are inserted into the list of arranged
+// objects as group rows.
+// The designated initializer is the same as for NSArrayController,
+// initWithContent:, but usually this class is instantiated from a nib file.
+// Clicking on a group row selects all rows belonging to that group, like it
+// does in a Windows table_view.
+// In order to show group rows, this class must be the delegate of the
+// NSTableView.
+@interface TableModelArrayController : NSArrayController<NSTableViewDelegate> {
+ @private
+ RemoveRowsTableModel* model_; // weak
+ scoped_ptr<RemoveRowsObserverBridge> tableObserver_;
+ scoped_nsobject<NSDictionary> columns_;
+ scoped_nsobject<NSString> groupTitle_;
+}
+
+// Bind this controller to the given model.
+// |columns| is a dictionary mapping table column bindings to NSNumbers
+// containing the column identifier in the TableModel.
+// |groupTitleColumn| is the column in the table that should display the group
+// title for a group row, usually the first column. If the model doesn't have
+// groups, it can be nil.
+- (void)bindToTableModel:(RemoveRowsTableModel*)model
+ withColumns:(NSDictionary*)columns
+ groupTitleColumn:(NSString*)groupTitleColumn;
+
+- (IBAction)removeAll:(id)sender;
+
+@end
+
+#endif // CHROME_BROWSER_COCOA_TABLE_MODEL_ARRAY_CONTROLLER_H_
diff --git a/chrome/browser/cocoa/table_model_array_controller.mm b/chrome/browser/cocoa/table_model_array_controller.mm
new file mode 100644
index 0000000..e88e6e1
--- /dev/null
+++ b/chrome/browser/cocoa/table_model_array_controller.mm
@@ -0,0 +1,245 @@
+// 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/table_model_array_controller.h"
+
+#include "app/table_model.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/remove_rows_table_model.h"
+
+@interface TableModelArrayController (PrivateMethods)
+
+- (NSUInteger)offsetForGroupID:(int)groupID;
+- (NSUInteger)offsetForGroupID:(int)groupID startingOffset:(NSUInteger)offset;
+- (NSIndexSet*)controllerRowsForModelRowsInRange:(NSRange)range;
+- (void)setModelRows:(RemoveRowsTableModel::Rows*)modelRows
+ fromControllerRows:(NSIndexSet*)rows;
+- (void)modelDidChange;
+- (void)modelDidAddItemsInRange:(NSRange)range;
+- (void)modelDidRemoveItemsInRange:(NSRange)range;
+- (NSDictionary*)columnValuesForRow:(NSInteger)row;
+
+@end
+
+// Observer for a RemoveRowsTableModel.
+class RemoveRowsObserverBridge : public TableModelObserver {
+ public:
+ RemoveRowsObserverBridge(TableModelArrayController* controller)
+ : controller_(controller) {}
+ virtual ~RemoveRowsObserverBridge() {}
+
+ // TableModelObserver methods
+ virtual void OnModelChanged();
+ virtual void OnItemsChanged(int start, int length);
+ virtual void OnItemsAdded(int start, int length);
+ virtual void OnItemsRemoved(int start, int length);
+
+ private:
+ TableModelArrayController* controller_; // weak
+};
+
+void RemoveRowsObserverBridge::OnModelChanged() {
+ [controller_ modelDidChange];
+}
+
+void RemoveRowsObserverBridge::OnItemsChanged(int start, int length) {
+ OnItemsRemoved(start, length);
+ OnItemsAdded(start, length);
+}
+
+void RemoveRowsObserverBridge::OnItemsAdded(int start, int length) {
+ [controller_ modelDidAddItemsInRange:NSMakeRange(start, length)];
+}
+
+void RemoveRowsObserverBridge::OnItemsRemoved(int start, int length) {
+ [controller_ modelDidRemoveItemsInRange:NSMakeRange(start, length)];
+}
+
+@implementation TableModelArrayController
+
+static NSString* const kIsGroupRow = @"_is_group_row";
+static NSString* const kGroupID = @"_group_id";
+
+- (void)bindToTableModel:(RemoveRowsTableModel*)model
+ withColumns:(NSDictionary*)columns
+ groupTitleColumn:(NSString*)groupTitleColumn {
+ model_ = model;
+ tableObserver_.reset(new RemoveRowsObserverBridge(self));
+ columns_.reset([columns copy]);
+ groupTitle_.reset([groupTitleColumn copy]);
+ model_->SetObserver(tableObserver_.get());
+ [self modelDidChange];
+}
+
+- (void)modelDidChange {
+ NSIndexSet* indexes = [NSIndexSet indexSetWithIndexesInRange:
+ NSMakeRange(0, [[self arrangedObjects] count])];
+ [self removeObjectsAtArrangedObjectIndexes:indexes];
+ if (model_->HasGroups()) {
+ const TableModel::Groups& groups = model_->GetGroups();
+ DCHECK(groupTitle_.get());
+ for (TableModel::Groups::const_iterator it = groups.begin();
+ it != groups.end(); ++it) {
+ NSDictionary* group = [NSDictionary dictionaryWithObjectsAndKeys:
+ base::SysWideToNSString(it->title), groupTitle_.get(),
+ [NSNumber numberWithBool:YES], kIsGroupRow,
+ nil];
+ [self addObject:group];
+ }
+ }
+ [self modelDidAddItemsInRange:NSMakeRange(0, model_->RowCount())];
+}
+
+- (NSUInteger)offsetForGroupID:(int)groupID startingOffset:(NSUInteger)offset {
+ const TableModel::Groups& groups = model_->GetGroups();
+ DCHECK_GT(offset, 0u);
+ for (NSUInteger i = offset - 1; i < groups.size(); ++i) {
+ if (groups[i].id == groupID)
+ return i + 1;
+ }
+ NOTREACHED();
+ return NSNotFound;
+}
+
+- (NSUInteger)offsetForGroupID:(int)groupID {
+ return [self offsetForGroupID:groupID startingOffset:1];
+}
+
+- (int)groupIDForControllerRow:(NSUInteger)row {
+ NSDictionary* values = [[self arrangedObjects] objectAtIndex:row];
+ return [[values objectForKey:kGroupID] intValue];
+}
+
+- (void)setModelRows:(RemoveRowsTableModel::Rows*)modelRows
+ fromControllerRows:(NSIndexSet*)rows {
+ if ([rows count] == 0)
+ return;
+
+ if (!model_->HasGroups()) {
+ for (NSUInteger i = [rows firstIndex];
+ i != NSNotFound;
+ i = [rows indexGreaterThanIndex:i]) {
+ modelRows->insert(i);
+ }
+ return;
+ }
+
+ NSUInteger offset = 1;
+ for (NSUInteger i = [rows firstIndex];
+ i != NSNotFound;
+ i = [rows indexGreaterThanIndex:i]) {
+ int group = [self groupIDForControllerRow:i];
+ offset = [self offsetForGroupID:group startingOffset:offset];
+ modelRows->insert(i - offset);
+ }
+}
+
+- (NSIndexSet*)controllerRowsForModelRowsInRange:(NSRange)range {
+ if (!model_->HasGroups())
+ return [NSIndexSet indexSetWithIndexesInRange:range];
+ NSMutableIndexSet* indexes = [NSMutableIndexSet indexSet];
+ NSUInteger offset = 1;
+ for (NSUInteger i = range.location; i < NSMaxRange(range); ++i) {
+ int group = model_->GetGroupID(i);
+ offset = [self offsetForGroupID:group startingOffset:offset];
+ [indexes addIndex:i + offset];
+ }
+ return indexes;
+}
+
+- (void)modelDidAddItemsInRange:(NSRange)range {
+ NSMutableArray* rows = [NSMutableArray arrayWithCapacity:range.length];
+ for (NSUInteger i=range.location; i<NSMaxRange(range); ++i)
+ [rows addObject:[self columnValuesForRow:i]];
+ [self insertObjects:rows
+ atArrangedObjectIndexes:[self controllerRowsForModelRowsInRange:range]];
+}
+
+- (void)modelDidRemoveItemsInRange:(NSRange)range {
+ NSMutableIndexSet* indexes =
+ [NSMutableIndexSet indexSetWithIndexesInRange:range];
+ if (model_->HasGroups()) {
+ // When this method is called, the model has already removed items, so
+ // accessing items in the model from |range.location| on may not be possible
+ // anymore. Therefore we use the item right before that, if it exists.
+ NSUInteger offset = 0;
+ if (range.location > 0) {
+ int last_group = model_->GetGroupID(range.location - 1);
+ offset = [self offsetForGroupID:last_group];
+ }
+ [indexes shiftIndexesStartingAtIndex:0 by:offset];
+ for (NSUInteger row = range.location + offset;
+ row < NSMaxRange(range) + offset;
+ ++row) {
+ if ([self tableView:nil isGroupRow:row]) {
+ // Skip over group rows.
+ [indexes shiftIndexesStartingAtIndex:row by:1];
+ offset++;
+ }
+ }
+ }
+ [self removeObjectsAtArrangedObjectIndexes:indexes];
+}
+
+- (NSDictionary*)columnValuesForRow:(NSInteger)row {
+ NSMutableDictionary* dict = [NSMutableDictionary dictionary];
+ if (model_->HasGroups()) {
+ [dict setObject:[NSNumber numberWithInt:model_->GetGroupID(row)]
+ forKey:kGroupID];
+ }
+ for (NSString* identifier in columns_.get()) {
+ int column_id = [[columns_ objectForKey:identifier] intValue];
+ std::wstring text = model_->GetText(row, column_id);
+ [dict setObject:base::SysWideToNSString(text) forKey:identifier];
+ }
+ return dict;
+}
+
+// Overridden from NSArrayController -----------------------------------------
+
+- (BOOL)canRemove {
+ if (!model_)
+ return NO;
+ RemoveRowsTableModel::Rows rows;
+ [self setModelRows:&rows fromControllerRows:[self selectionIndexes]];
+ return model_->CanRemoveRows(rows);
+}
+
+- (IBAction)remove:(id)sender {
+ RemoveRowsTableModel::Rows rows;
+ [self setModelRows:&rows fromControllerRows:[self selectionIndexes]];
+ model_->RemoveRows(rows);
+}
+
+// Table View Delegate --------------------------------------------------------
+
+- (BOOL)tableView:(NSTableView*)tv isGroupRow:(NSInteger)row {
+ NSDictionary* values = [[self arrangedObjects] objectAtIndex:row];
+ return [[values objectForKey:kIsGroupRow] boolValue];
+}
+
+- (NSIndexSet*)tableView:(NSTableView*)tableView
+ selectionIndexesForProposedSelection:(NSIndexSet*)proposedIndexes {
+ NSMutableIndexSet* indexes = [proposedIndexes mutableCopy];
+ for (NSUInteger i = [proposedIndexes firstIndex];
+ i != NSNotFound;
+ i = [proposedIndexes indexGreaterThanIndex:i]) {
+ if ([self tableView:tableView isGroupRow:i]) {
+ [indexes removeIndex:i];
+ NSUInteger row = i + 1;
+ while (row < [[self arrangedObjects] count] &&
+ ![self tableView:tableView isGroupRow:row])
+ [indexes addIndex:row++];
+ }
+ }
+ return indexes;
+}
+
+// Actions --------------------------------------------------------------------
+
+- (IBAction)removeAll:(id)sender {
+ model_->RemoveAll();
+}
+
+@end
diff --git a/chrome/browser/cocoa/table_model_array_controller_unittest.mm b/chrome/browser/cocoa/table_model_array_controller_unittest.mm
new file mode 100644
index 0000000..4f32358
--- /dev/null
+++ b/chrome/browser/cocoa/table_model_array_controller_unittest.mm
@@ -0,0 +1,168 @@
+// 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/table_model_array_controller.h"
+
+#include "base/auto_reset.h"
+#include "base/command_line.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/cocoa/browser_test_helper.h"
+#import "chrome/browser/cocoa/cocoa_test_helper.h"
+#include "chrome/browser/mock_plugin_exceptions_table_model.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/testing_profile.h"
+#include "grit/generated_resources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+
+namespace {
+
+class TableModelArrayControllerTest : public CocoaTest {
+ public:
+ TableModelArrayControllerTest()
+ : command_line_(CommandLine::ForCurrentProcess(),
+ *CommandLine::ForCurrentProcess()) {}
+
+ virtual void SetUp() {
+ CocoaTest::SetUp();
+
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableResourceContentSettings);
+
+ TestingProfile* profile = browser_helper_.profile();
+ HostContentSettingsMap* map = profile->GetHostContentSettingsMap();
+
+ HostContentSettingsMap::Pattern example_com("[*.]example.com");
+ HostContentSettingsMap::Pattern moose_org("[*.]moose.org");
+ map->SetContentSetting(example_com,
+ CONTENT_SETTINGS_TYPE_PLUGINS,
+ "foo",
+ CONTENT_SETTING_ALLOW);
+ map->SetContentSetting(moose_org,
+ CONTENT_SETTINGS_TYPE_PLUGINS,
+ "bar",
+ CONTENT_SETTING_BLOCK);
+ map->SetContentSetting(example_com,
+ CONTENT_SETTINGS_TYPE_PLUGINS,
+ "bar",
+ CONTENT_SETTING_ALLOW);
+
+ model_.reset(new MockPluginExceptionsTableModel(map, NULL));
+
+ std::vector<WebPluginInfo> plugins;
+ WebPluginInfo foo_plugin;
+ foo_plugin.path = FilePath(FILE_PATH_LITERAL("foo"));
+ foo_plugin.name = ASCIIToUTF16("FooPlugin");
+ foo_plugin.enabled = true;
+ plugins.push_back(foo_plugin);
+ WebPluginInfo bar_plugin;
+ bar_plugin.path = FilePath(FILE_PATH_LITERAL("bar"));
+ bar_plugin.name = ASCIIToUTF16("BarPlugin");
+ bar_plugin.enabled = true;
+ plugins.push_back(bar_plugin);
+ WebPluginInfo blurp_plugin;
+ blurp_plugin.path = FilePath(FILE_PATH_LITERAL("blurp"));
+ blurp_plugin.name = ASCIIToUTF16("BlurpPlugin");
+ blurp_plugin.enabled = true;
+ plugins.push_back(blurp_plugin);
+
+ model_->set_plugins(plugins);
+ model_->LoadSettings();
+
+ id content = [NSMutableArray array];
+ controller_.reset(
+ [[TableModelArrayController alloc] initWithContent:content]);
+ NSDictionary* columns = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInt:IDS_EXCEPTIONS_HOSTNAME_HEADER], @"title",
+ [NSNumber numberWithInt:IDS_EXCEPTIONS_ACTION_HEADER], @"action",
+ nil];
+ [controller_.get() bindToTableModel:model_.get()
+ withColumns:columns
+ groupTitleColumn:@"title"];
+ }
+
+ protected:
+ BrowserTestHelper browser_helper_;
+ scoped_ptr<MockPluginExceptionsTableModel> model_;
+ scoped_nsobject<TableModelArrayController> controller_;
+
+ private:
+ AutoReset<CommandLine> command_line_;
+};
+
+TEST_F(TableModelArrayControllerTest, CheckTitles) {
+ NSArray* titles = [[controller_.get() arrangedObjects] valueForKey:@"title"];
+ EXPECT_NSEQ(@"(\n"
+ @" FooPlugin,\n"
+ @" \"[*.]example.com\",\n"
+ @" BarPlugin,\n"
+ @" \"[*.]example.com\",\n"
+ @" \"[*.]moose.org\"\n"
+ @")",
+ [titles description]);
+}
+
+TEST_F(TableModelArrayControllerTest, RemoveRows) {
+ NSArrayController* controller = controller_.get();
+ [controller setSelectionIndex:1];
+ [controller remove:nil];
+ NSArray* titles = [[controller arrangedObjects] valueForKey:@"title"];
+ EXPECT_NSEQ(@"(\n"
+ @" BarPlugin,\n"
+ @" \"[*.]example.com\",\n"
+ @" \"[*.]moose.org\"\n"
+ @")",
+ [titles description]);
+
+ [controller setSelectionIndex:2];
+ [controller remove:nil];
+ titles = [[controller arrangedObjects] valueForKey:@"title"];
+ EXPECT_NSEQ(@"(\n"
+ @" BarPlugin,\n"
+ @" \"[*.]example.com\"\n"
+ @")",
+ [titles description]);
+}
+
+TEST_F(TableModelArrayControllerTest, RemoveAll) {
+ [controller_.get() removeAll:nil];
+ EXPECT_EQ(0u, [[controller_.get() arrangedObjects] count]);
+}
+
+TEST_F(TableModelArrayControllerTest, AddException) {
+ TestingProfile* profile = browser_helper_.profile();
+ HostContentSettingsMap* map = profile->GetHostContentSettingsMap();
+ HostContentSettingsMap::Pattern example_com("[*.]example.com");
+ map->SetContentSetting(example_com,
+ CONTENT_SETTINGS_TYPE_PLUGINS,
+ "blurp",
+ CONTENT_SETTING_BLOCK);
+
+ NSArrayController* controller = controller_.get();
+ NSArray* titles = [[controller arrangedObjects] valueForKey:@"title"];
+ EXPECT_NSEQ(@"(\n"
+ @" FooPlugin,\n"
+ @" \"[*.]example.com\",\n"
+ @" BarPlugin,\n"
+ @" \"[*.]example.com\",\n"
+ @" \"[*.]moose.org\",\n"
+ @" BlurpPlugin,\n"
+ @" \"[*.]example.com\"\n"
+ @")",
+ [titles description]);
+ NSMutableIndexSet* indexes = [NSMutableIndexSet indexSetWithIndex:1];
+ [indexes addIndex:6];
+ [controller setSelectionIndexes:indexes];
+ [controller remove:nil];
+ titles = [[controller arrangedObjects] valueForKey:@"title"];
+ EXPECT_NSEQ(@"(\n"
+ @" BarPlugin,\n"
+ @" \"[*.]example.com\",\n"
+ @" \"[*.]moose.org\"\n"
+ @")",
+ [titles description]);
+}
+
+} // namespace
diff --git a/chrome/browser/mock_plugin_exceptions_table_model.cc b/chrome/browser/mock_plugin_exceptions_table_model.cc
new file mode 100644
index 0000000..d74dfa0
--- /dev/null
+++ b/chrome/browser/mock_plugin_exceptions_table_model.cc
@@ -0,0 +1,17 @@
+// 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/mock_plugin_exceptions_table_model.h"
+
+#include "webkit/glue/plugins/webplugininfo.h"
+
+void MockPluginExceptionsTableModel::set_plugins(
+ const std::vector<WebPluginInfo>& plugins) {
+ plugins_ = plugins;
+}
+
+void MockPluginExceptionsTableModel::GetPlugins(
+ std::vector<WebPluginInfo>* plugins) {
+ *plugins = plugins_;
+}
diff --git a/chrome/browser/mock_plugin_exceptions_table_model.h b/chrome/browser/mock_plugin_exceptions_table_model.h
new file mode 100644
index 0000000..4cedcd3
--- /dev/null
+++ b/chrome/browser/mock_plugin_exceptions_table_model.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef CHROME_BROWSER_MOCK_PLUGIN_EXCEPTIONS_TABLE_MODEL_H_
+#define CHROME_BROWSER_MOCK_PLUGIN_EXCEPTIONS_TABLE_MODEL_H_
+#pragma once
+
+#include <vector>
+
+#include "chrome/browser/plugin_exceptions_table_model.h"
+
+class MockPluginExceptionsTableModel : public PluginExceptionsTableModel {
+ public:
+ MockPluginExceptionsTableModel(HostContentSettingsMap* map,
+ HostContentSettingsMap* otr_map)
+ : PluginExceptionsTableModel(map, otr_map) {}
+ virtual ~MockPluginExceptionsTableModel() {}
+
+ void set_plugins(const std::vector<WebPluginInfo>& plugins);
+
+ protected:
+ virtual void GetPlugins(std::vector<WebPluginInfo>* plugins);
+
+ private:
+ std::vector<WebPluginInfo> plugins_;
+};
+
+#endif // CHROME_BROWSER_MOCK_PLUGIN_EXCEPTIONS_TABLE_MODEL_H_
diff --git a/chrome/browser/plugin_exceptions_table_model.cc b/chrome/browser/plugin_exceptions_table_model.cc
index f610432..18d76a6 100644
--- a/chrome/browser/plugin_exceptions_table_model.cc
+++ b/chrome/browser/plugin_exceptions_table_model.cc
@@ -34,6 +34,7 @@ void PluginExceptionsTableModel::RemoveRows(const Rows& rows) {
// Iterate in reverse over the rows to get the indexes right.
for (Rows::const_reverse_iterator it = rows.rbegin();
it != rows.rend(); ++it) {
+ DCHECK_LT(*it, settings_.size());
SettingsEntry& entry = settings_[*it];
HostContentSettingsMap* map = entry.is_otr ? otr_map_ : map_;
map->SetContentSetting(entry.pattern,
@@ -59,16 +60,14 @@ void PluginExceptionsTableModel::RemoveRows(const Rows& rows) {
}
void PluginExceptionsTableModel::RemoveAll() {
- int old_row_count = RowCount();
- {
- AutoReset<bool> tmp(&updates_disabled_, true);
- map_->ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PLUGINS);
- if (otr_map_)
- otr_map_->ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PLUGINS);
- }
+ AutoReset<bool> tmp(&updates_disabled_, true);
+ map_->ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PLUGINS);
+ if (otr_map_)
+ otr_map_->ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PLUGINS);
+
ClearSettings();
if (observer_)
- observer_->OnItemsRemoved(0, old_row_count);
+ observer_->OnModelChanged();
}
int PluginExceptionsTableModel::RowCount() {
diff --git a/chrome/browser/plugin_exceptions_table_model_unittest.cc b/chrome/browser/plugin_exceptions_table_model_unittest.cc
index 7720287..69db0a1 100644
--- a/chrome/browser/plugin_exceptions_table_model_unittest.cc
+++ b/chrome/browser/plugin_exceptions_table_model_unittest.cc
@@ -3,9 +3,10 @@
// found in the LICENSE file.
#include "app/table_model_observer.h"
+#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/utf_string_conversions.h"
-#include "chrome/browser/plugin_exceptions_table_model.h"
+#include "chrome/browser/mock_plugin_exceptions_table_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/testing_pref_service.h"
@@ -26,31 +27,12 @@ class MockTableModelObserver : public TableModelObserver {
MOCK_METHOD2(OnItemsRemoved, void(int start, int length));
};
-class TestingPluginExceptionsTableModel : public PluginExceptionsTableModel {
- public:
- TestingPluginExceptionsTableModel(HostContentSettingsMap* map,
- HostContentSettingsMap* otr_map)
- : PluginExceptionsTableModel(map, otr_map) {}
- virtual ~TestingPluginExceptionsTableModel() {}
-
- void set_plugins(const std::vector<WebPluginInfo>& plugins) {
- plugins_ = plugins;
- }
-
- protected:
- virtual void GetPlugins(std::vector<WebPluginInfo>* plugins) {
- *plugins = plugins_;
- }
-
- private:
- std::vector<WebPluginInfo> plugins_;
-};
-
class PluginExceptionsTableModelTest : public testing::Test {
public:
PluginExceptionsTableModelTest()
: ui_thread_(ChromeThread::UI, &message_loop_),
- command_line_(*CommandLine::ForCurrentProcess()) {}
+ command_line_(CommandLine::ForCurrentProcess(),
+ *CommandLine::ForCurrentProcess()) {}
virtual void SetUp() {
CommandLine::ForCurrentProcess()->AppendSwitch(
@@ -75,7 +57,7 @@ class PluginExceptionsTableModelTest : public testing::Test {
"bar",
CONTENT_SETTING_ALLOW);
- table_model_.reset(new TestingPluginExceptionsTableModel(map, NULL));
+ table_model_.reset(new MockPluginExceptionsTableModel(map, NULL));
std::vector<WebPluginInfo> plugins;
WebPluginInfo foo_plugin;
@@ -93,10 +75,6 @@ class PluginExceptionsTableModelTest : public testing::Test {
table_model_->ReloadSettings();
}
- virtual void TearDown() {
- *CommandLine::ForCurrentProcess() = command_line_;
- }
-
protected:
void CheckInvariants() {
typedef std::deque<PluginExceptionsTableModel::SettingsEntry> Entries;
@@ -136,10 +114,10 @@ class PluginExceptionsTableModelTest : public testing::Test {
ChromeThread ui_thread_;
scoped_ptr<TestingProfile> profile_;
- scoped_ptr<TestingPluginExceptionsTableModel> table_model_;
+ scoped_ptr<MockPluginExceptionsTableModel> table_model_;
private:
- CommandLine command_line_;
+ AutoReset<CommandLine> command_line_;
};
TEST_F(PluginExceptionsTableModelTest, Basic) {
@@ -179,7 +157,7 @@ TEST_F(PluginExceptionsTableModelTest, RemoveAllRows) {
MockTableModelObserver observer;
table_model_->SetObserver(&observer);
- EXPECT_CALL(observer, OnItemsRemoved(0, 3));
+ EXPECT_CALL(observer, OnModelChanged());
table_model_->RemoveAll();
EXPECT_EQ(0, table_model_->RowCount());
EXPECT_EQ(0, static_cast<int>(table_model_->GetGroups().size()));