Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: chrome/browser/ui/cocoa/options/cookies_window_controller.mm

Issue 6621076: [Mac] Remove native/Cocoa preferences. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase switch removal Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "chrome/browser/ui/cocoa/options/cookies_window_controller.h"
6
7 #include <queue>
8 #include <vector>
9
10 #import "base/mac/mac_util.h"
11 #include "base/sys_string_conversions.h"
12 #include "chrome/browser/browsing_data_remover.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/cocoa/clear_browsing_data_controller.h"
15 #include "chrome/browser/ui/cocoa/content_settings/cookie_details_view_controlle r.h"
16 #include "grit/generated_resources.h"
17 #include "grit/theme_resources.h"
18 #include "skia/ext/skia_utils_mac.h"
19 #include "third_party/apple/ImageAndTextCell.h"
20 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "ui/base/l10n/l10n_util_mac.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/gfx/image.h"
24
25 // Key path used for notifying KVO.
26 static NSString* const kCocoaTreeModel = @"cocoaTreeModel";
27
28 CookiesTreeModelObserverBridge::CookiesTreeModelObserverBridge(
29 CookiesWindowController* controller)
30 : window_controller_(controller),
31 batch_update_(false) {
32 }
33
34 // Notification that nodes were added to the specified parent.
35 void CookiesTreeModelObserverBridge::TreeNodesAdded(ui::TreeModel* model,
36 ui::TreeModelNode* parent,
37 int start,
38 int count) {
39 // We're in for a major rebuild. Ignore this request.
40 if (batch_update_ || !HasCocoaModel())
41 return;
42
43 CocoaCookieTreeNode* cocoa_parent = FindCocoaNode(parent, nil);
44 NSMutableArray* cocoa_children = [cocoa_parent mutableChildren];
45
46 [window_controller_ willChangeValueForKey:kCocoaTreeModel];
47 CookieTreeNode* cookie_parent = static_cast<CookieTreeNode*>(parent);
48 for (int i = 0; i < count; ++i) {
49 CookieTreeNode* cookie_child = cookie_parent->GetChild(start + i);
50 CocoaCookieTreeNode* new_child = CocoaNodeFromTreeNode(cookie_child);
51 [cocoa_children addObject:new_child];
52 }
53 [window_controller_ didChangeValueForKey:kCocoaTreeModel];
54 }
55
56 // Notification that nodes were removed from the specified parent.
57 void CookiesTreeModelObserverBridge::TreeNodesRemoved(ui::TreeModel* model,
58 ui::TreeModelNode* parent,
59 int start,
60 int count) {
61 // We're in for a major rebuild. Ignore this request.
62 if (batch_update_ || !HasCocoaModel())
63 return;
64
65 CocoaCookieTreeNode* cocoa_parent = FindCocoaNode(parent, nil);
66 [window_controller_ willChangeValueForKey:kCocoaTreeModel];
67 NSMutableArray* cocoa_children = [cocoa_parent mutableChildren];
68 for (int i = start + count - 1; i >= start; --i) {
69 [cocoa_children removeObjectAtIndex:i];
70 }
71 [window_controller_ didChangeValueForKey:kCocoaTreeModel];
72 }
73
74 // Notification that the contents of a node has changed.
75 void CookiesTreeModelObserverBridge::TreeNodeChanged(ui::TreeModel* model,
76 ui::TreeModelNode* node) {
77 // If we don't have a Cocoa model, only let the root node change.
78 if (batch_update_ || (!HasCocoaModel() && model->GetRoot() != node))
79 return;
80
81 if (HasCocoaModel()) {
82 // We still have a Cocoa model, so just rebuild the node.
83 [window_controller_ willChangeValueForKey:kCocoaTreeModel];
84 CocoaCookieTreeNode* changed_node = FindCocoaNode(node, nil);
85 [changed_node rebuild];
86 [window_controller_ didChangeValueForKey:kCocoaTreeModel];
87 } else {
88 // Full rebuild.
89 [window_controller_ setCocoaTreeModel:CocoaNodeFromTreeNode(node)];
90 }
91 }
92
93 void CookiesTreeModelObserverBridge::TreeModelBeginBatch(
94 CookiesTreeModel* model) {
95 batch_update_ = true;
96 }
97
98 void CookiesTreeModelObserverBridge::TreeModelEndBatch(
99 CookiesTreeModel* model) {
100 DCHECK(batch_update_);
101 CocoaCookieTreeNode* root = CocoaNodeFromTreeNode(model->GetRoot());
102 [window_controller_ setCocoaTreeModel:root];
103 batch_update_ = false;
104 }
105
106 void CookiesTreeModelObserverBridge::InvalidateCocoaModel() {
107 [[[window_controller_ cocoaTreeModel] mutableChildren] removeAllObjects];
108 }
109
110 CocoaCookieTreeNode* CookiesTreeModelObserverBridge::CocoaNodeFromTreeNode(
111 ui::TreeModelNode* node) {
112 CookieTreeNode* cookie_node = static_cast<CookieTreeNode*>(node);
113 return [[[CocoaCookieTreeNode alloc] initWithNode:cookie_node] autorelease];
114 }
115
116 // Does breadth-first search on the tree to find |node|. This method is most
117 // commonly used to find origin/folder nodes, which are at the first level off
118 // the root (hence breadth-first search).
119 CocoaCookieTreeNode* CookiesTreeModelObserverBridge::FindCocoaNode(
120 ui::TreeModelNode* target, CocoaCookieTreeNode* start) {
121 if (!start) {
122 start = [window_controller_ cocoaTreeModel];
123 }
124 if ([start treeNode] == target) {
125 return start;
126 }
127
128 // Enqueue the root node of the search (sub-)tree.
129 std::queue<CocoaCookieTreeNode*> horizon;
130 horizon.push(start);
131
132 // Loop until we've looked at every node or we found the target.
133 while (!horizon.empty()) {
134 // Dequeue the item at the front.
135 CocoaCookieTreeNode* node = horizon.front();
136 horizon.pop();
137
138 // If this is the droid we're looking for, report it.
139 if ([node treeNode] == target)
140 return node;
141
142 // "Move along, move along." by adding all child nodes to the queue.
143 if (![node isLeaf]) {
144 NSArray* children = [node children];
145 for (CocoaCookieTreeNode* child in children) {
146 horizon.push(child);
147 }
148 }
149 }
150
151 return nil; // We couldn't find the node.
152 }
153
154 // Returns whether or not the Cocoa tree model is built.
155 bool CookiesTreeModelObserverBridge::HasCocoaModel() {
156 return ([[[window_controller_ cocoaTreeModel] children] count] > 0U);
157 }
158
159 #pragma mark Window Controller
160
161 @implementation CookiesWindowController
162
163 @synthesize removeButtonEnabled = removeButtonEnabled_;
164 @synthesize treeController = treeController_;
165
166 - (id)initWithProfile:(Profile*)profile
167 databaseHelper:(BrowsingDataDatabaseHelper*)databaseHelper
168 storageHelper:(BrowsingDataLocalStorageHelper*)storageHelper
169 appcacheHelper:(BrowsingDataAppCacheHelper*)appcacheHelper
170 indexedDBHelper:(BrowsingDataIndexedDBHelper*)indexedDBHelper {
171 DCHECK(profile);
172 NSString* nibpath = [base::mac::MainAppBundle() pathForResource:@"Cookies"
173 ofType:@"nib"];
174 if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
175 profile_ = profile;
176 databaseHelper_ = databaseHelper;
177 storageHelper_ = storageHelper;
178 appcacheHelper_ = appcacheHelper;
179 indexedDBHelper_ = indexedDBHelper;
180
181 [self loadTreeModelFromProfile];
182
183 // Register for Clear Browsing Data controller so we update appropriately.
184 ClearBrowsingDataController* clearingController =
185 [ClearBrowsingDataController controllerForProfile:profile_];
186 if (clearingController) {
187 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
188 [center addObserver:self
189 selector:@selector(clearBrowsingDataNotification:)
190 name:kClearBrowsingDataControllerDidDelete
191 object:clearingController];
192 }
193 }
194 return self;
195 }
196
197 - (void)dealloc {
198 [[NSNotificationCenter defaultCenter] removeObserver:self];
199 [super dealloc];
200 }
201
202 - (void)awakeFromNib {
203 DCHECK([self window]);
204 DCHECK_EQ(self, [[self window] delegate]);
205
206 detailsViewController_.reset([[CookieDetailsViewController alloc] init]);
207
208 NSView* detailView = [detailsViewController_.get() view];
209 NSRect viewFrameRect = [cookieDetailsViewPlaceholder_ frame];
210 [[detailsViewController_.get() view] setFrame:viewFrameRect];
211 [[cookieDetailsViewPlaceholder_ superview]
212 replaceSubview:cookieDetailsViewPlaceholder_
213 with:detailView];
214
215 [detailsViewController_ configureBindingsForTreeController:treeController_];
216 }
217
218 - (void)windowWillClose:(NSNotification*)notif {
219 [searchField_ setTarget:nil];
220 [outlineView_ setDelegate:nil];
221 [self autorelease];
222 }
223
224 - (void)attachSheetTo:(NSWindow*)window {
225 [NSApp beginSheet:[self window]
226 modalForWindow:window
227 modalDelegate:self
228 didEndSelector:@selector(sheetEndSheet:returnCode:contextInfo:)
229 contextInfo:nil];
230 }
231
232 - (void)sheetEndSheet:(NSWindow*)sheet
233 returnCode:(NSInteger)returnCode
234 contextInfo:(void*)context {
235 [sheet close];
236 [sheet orderOut:self];
237 }
238
239 - (IBAction)updateFilter:(id)sender {
240 DCHECK([sender isKindOfClass:[NSSearchField class]]);
241 NSString* string = [sender stringValue];
242 // Invalidate the model here because all the nodes are going to be removed
243 // in UpdateSearchResults(). This could lead to there temporarily being
244 // invalid pointers in the Cocoa model.
245 modelObserver_->InvalidateCocoaModel();
246 treeModel_->UpdateSearchResults(base::SysNSStringToWide(string));
247 }
248
249 - (IBAction)deleteCookie:(id)sender {
250 DCHECK_EQ(1U, [[treeController_ selectedObjects] count]);
251 [self deleteNodeAtIndexPath:[treeController_ selectionIndexPath]];
252 }
253
254 // This will delete the Cocoa model node as well as the backing model object at
255 // the specified index path in the Cocoa model. If the node that was deleted
256 // was the sole child of the parent node, this will be called recursively to
257 // delete empty parents.
258 - (void)deleteNodeAtIndexPath:(NSIndexPath*)path {
259 NSTreeNode* treeNode =
260 [[treeController_ arrangedObjects] descendantNodeAtIndexPath:path];
261 if (!treeNode)
262 return;
263
264 CocoaCookieTreeNode* node = [treeNode representedObject];
265 CookieTreeNode* cookie = static_cast<CookieTreeNode*>([node treeNode]);
266 treeModel_->DeleteCookieNode(cookie);
267 // If there is a next cookie, this will select it because items will slide
268 // up. If there is no next cookie, this is a no-op.
269 [treeController_ setSelectionIndexPath:path];
270 // If the above setting of the selection was in fact a no-op, find the next
271 // node to select.
272 if (![[treeController_ selectedObjects] count]) {
273 NSUInteger lastIndex = [path indexAtPosition:[path length] - 1];
274 if (lastIndex != 0) {
275 // If there any nodes remaining, select the node that is in the list
276 // before this one.
277 path = [path indexPathByRemovingLastIndex];
278 path = [path indexPathByAddingIndex:lastIndex - 1];
279 [treeController_ setSelectionIndexPath:path];
280 }
281 }
282 }
283
284 - (IBAction)deleteAllCookies:(id)sender {
285 // Preemptively delete all cookies in the Cocoa model.
286 modelObserver_->InvalidateCocoaModel();
287 treeModel_->DeleteAllStoredObjects();
288 }
289
290 - (IBAction)closeSheet:(id)sender {
291 [NSApp endSheet:[self window]];
292 }
293
294 - (void)clearBrowsingDataNotification:(NSNotification*)notif {
295 NSNumber* removeMask =
296 [[notif userInfo] objectForKey:kClearBrowsingDataControllerRemoveMask];
297 if ([removeMask intValue] & BrowsingDataRemover::REMOVE_COOKIES) {
298 [self loadTreeModelFromProfile];
299 }
300 }
301
302 // Override keyDown on the controller (which is the first responder) to allow
303 // both backspace and delete to be captured by the Remove button.
304 - (void)keyDown:(NSEvent*)theEvent {
305 NSString* keys = [theEvent characters];
306 if ([keys length]) {
307 unichar key = [keys characterAtIndex:0];
308 // The button has a key equivalent of backspace, so examine this event for
309 // forward delete.
310 if ((key == NSDeleteCharacter || key == NSDeleteFunctionKey) &&
311 [self removeButtonEnabled]) {
312 [removeButton_ performClick:self];
313 return;
314 }
315 }
316 [super keyDown:theEvent];
317 }
318
319 #pragma mark Getters and Setters
320
321 - (CocoaCookieTreeNode*)cocoaTreeModel {
322 return cocoaTreeModel_.get();
323 }
324 - (void)setCocoaTreeModel:(CocoaCookieTreeNode*)model {
325 cocoaTreeModel_.reset([model retain]);
326 }
327
328 - (CookiesTreeModel*)treeModel {
329 return treeModel_.get();
330 }
331
332 #pragma mark Outline View Delegate
333
334 - (void)outlineView:(NSOutlineView*)outlineView
335 willDisplayCell:(id)cell
336 forTableColumn:(NSTableColumn*)tableColumn
337 item:(id)item {
338 CocoaCookieTreeNode* node = [item representedObject];
339 int index = treeModel_->GetIconIndex([node treeNode]);
340 NSImage* icon = nil;
341 if (index >= 0)
342 icon = [icons_ objectAtIndex:index];
343 else
344 icon = [icons_ lastObject];
345 [(ImageAndTextCell*)cell setImage:icon];
346 }
347
348 - (void)outlineViewItemDidExpand:(NSNotification*)notif {
349 NSTreeNode* item = [[notif userInfo] objectForKey:@"NSObject"];
350 CocoaCookieTreeNode* node = [item representedObject];
351 NSArray* children = [node children];
352 if ([children count] == 1U) {
353 // The node that will expand has one child. Do the user a favor and expand
354 // that node (saving her a click) if it is non-leaf.
355 CocoaCookieTreeNode* child = [children lastObject];
356 if (![child isLeaf]) {
357 NSOutlineView* outlineView = [notif object];
358 // Tell the OutlineView to expand the NSTreeNode, not the model object.
359 children = [item childNodes];
360 DCHECK_EQ([children count], 1U);
361 [outlineView expandItem:[children lastObject]];
362 // Select the first node in that child set.
363 NSTreeNode* folderChild = [children lastObject];
364 if ([[folderChild childNodes] count] > 0) {
365 NSTreeNode* firstCookieChild =
366 [[folderChild childNodes] objectAtIndex:0];
367 [treeController_ setSelectionIndexPath:[firstCookieChild indexPath]];
368 }
369 }
370 }
371 }
372
373 - (void)outlineViewSelectionDidChange:(NSNotification*)notif {
374 // Multi-selection should be disabled in the UI, but for sanity, double-check
375 // that they can't do it here.
376 NSArray* selectedObjects = [treeController_ selectedObjects];
377 NSUInteger count = [selectedObjects count];
378 if (count != 1U) {
379 DCHECK_LT(count, 1U) << "User was able to select more than 1 cookie node!";
380 [self setRemoveButtonEnabled:NO];
381 return;
382 }
383
384 // Go through the selection's indexPath and make sure that the node that is
385 // being referenced actually exists in the Cocoa model.
386 NSIndexPath* selection = [treeController_ selectionIndexPath];
387 NSUInteger length = [selection length];
388 CocoaCookieTreeNode* node = [self cocoaTreeModel];
389 for (NSUInteger i = 0; i < length; ++i) {
390 NSUInteger childIndex = [selection indexAtPosition:i];
391 if (childIndex >= [[node children] count]) {
392 [self setRemoveButtonEnabled:NO];
393 return;
394 }
395 node = [[node children] objectAtIndex:childIndex];
396 }
397
398 // If there is a valid selection, make sure that the remove
399 // button is enabled.
400 [self setRemoveButtonEnabled:YES];
401 }
402
403 #pragma mark Unit Testing
404
405 - (CookiesTreeModelObserverBridge*)modelObserver {
406 return modelObserver_.get();
407 }
408
409 - (NSArray*)icons {
410 return icons_.get();
411 }
412
413 // Re-initializes the |treeModel_|, creates a new observer for it, and re-
414 // builds the |cocoaTreeModel_|. We use this to initialize the controller and
415 // to rebuild after the user clears browsing data. Because the models get
416 // clobbered, we rebuild the icon cache for safety (though they do not change).
417 - (void)loadTreeModelFromProfile {
418 treeModel_.reset(new CookiesTreeModel(
419 profile_->GetRequestContext()->GetCookieStore()->GetCookieMonster(),
420 databaseHelper_,
421 storageHelper_,
422 NULL,
423 appcacheHelper_,
424 indexedDBHelper_));
425 modelObserver_.reset(new CookiesTreeModelObserverBridge(self));
426 treeModel_->AddCookiesTreeObserver(modelObserver_.get());
427
428 // Convert the model's icons from Skia to Cocoa.
429 std::vector<SkBitmap> skiaIcons;
430 treeModel_->GetIcons(&skiaIcons);
431 icons_.reset([[NSMutableArray alloc] init]);
432 for (std::vector<SkBitmap>::iterator it = skiaIcons.begin();
433 it != skiaIcons.end(); ++it) {
434 [icons_ addObject:gfx::SkBitmapToNSImage(*it)];
435 }
436
437 // Default icon will be the last item in the array.
438 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
439 // TODO(rsesek): Rename this resource now that it's in multiple places.
440 [icons_ addObject:rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER)];
441
442 // Create the Cocoa model.
443 CookieTreeNode* root = static_cast<CookieTreeNode*>(treeModel_->GetRoot());
444 scoped_nsobject<CocoaCookieTreeNode> model(
445 [[CocoaCookieTreeNode alloc] initWithNode:root]);
446 [self setCocoaTreeModel:model.get()]; // Takes ownership.
447 }
448
449 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698