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

Side by Side Diff: chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm

Issue 9460045: Add Mac interface for installing bundles of extensions. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controlle r.h" 5 #import "chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controlle r.h"
6 6
7 #include "base/i18n/rtl.h" 7 #include "base/i18n/rtl.h"
8 #include "base/mac/bundle_locations.h" 8 #include "base/mac/bundle_locations.h"
9 #include "base/mac/mac_util.h" 9 #include "base/mac/mac_util.h"
10 #include "base/sys_string_conversions.h" 10 #include "base/sys_string_conversions.h"
11 #include "base/utf_string_conversions.h" 11 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/extensions/bundle_installer.h"
12 #include "chrome/browser/ui/browser.h" 13 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_window.h" 14 #include "chrome/browser/ui/browser_window.h"
14 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h" 15 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
15 #include "chrome/browser/ui/cocoa/browser_window_controller.h" 16 #include "chrome/browser/ui/cocoa/browser_window_controller.h"
16 #include "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h" 17 #include "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
17 #include "chrome/browser/ui/cocoa/hover_close_button.h" 18 #include "chrome/browser/ui/cocoa/hover_close_button.h"
18 #include "chrome/browser/ui/cocoa/info_bubble_view.h" 19 #include "chrome/browser/ui/cocoa/info_bubble_view.h"
19 #include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" 20 #include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
20 #include "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" 21 #include "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
21 #include "chrome/common/chrome_notification_types.h" 22 #include "chrome/common/chrome_notification_types.h"
22 #include "chrome/common/extensions/extension.h" 23 #include "chrome/common/extensions/extension.h"
23 #include "chrome/common/extensions/extension_action.h" 24 #include "chrome/common/extensions/extension_action.h"
24 #include "content/public/browser/notification_details.h" 25 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_registrar.h" 26 #include "content/public/browser/notification_registrar.h"
26 #include "content/public/browser/notification_source.h" 27 #include "content/public/browser/notification_source.h"
27 #include "grit/chromium_strings.h" 28 #include "grit/chromium_strings.h"
28 #include "grit/generated_resources.h" 29 #include "grit/generated_resources.h"
29 #import "skia/ext/skia_utils_mac.h" 30 #import "skia/ext/skia_utils_mac.h"
30 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" 31 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
31 #include "ui/base/l10n/l10n_util.h" 32 #include "ui/base/l10n/l10n_util.h"
32 33
33 using content::BrowserThread; 34 using content::BrowserThread;
35 using extensions::BundleInstaller;
34 36
35 // C++ class that receives EXTENSION_LOADED notifications and proxies them back 37 // C++ class that receives EXTENSION_LOADED notifications and proxies them back
36 // to |controller|. 38 // to |controller|.
37 class ExtensionLoadedNotificationObserver 39 class ExtensionLoadedNotificationObserver
38 : public content::NotificationObserver { 40 : public content::NotificationObserver {
39 public: 41 public:
40 ExtensionLoadedNotificationObserver( 42 ExtensionLoadedNotificationObserver(
41 ExtensionInstalledBubbleController* controller, Profile* profile) 43 ExtensionInstalledBubbleController* controller, Profile* profile)
42 : controller_(controller) { 44 : controller_(controller) {
43 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, 45 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
(...skipping 29 matching lines...) Expand all
73 } 75 }
74 } 76 }
75 77
76 content::NotificationRegistrar registrar_; 78 content::NotificationRegistrar registrar_;
77 ExtensionInstalledBubbleController* controller_; // weak, owns us 79 ExtensionInstalledBubbleController* controller_; // weak, owns us
78 }; 80 };
79 81
80 @implementation ExtensionInstalledBubbleController 82 @implementation ExtensionInstalledBubbleController
81 83
82 @synthesize extension = extension_; 84 @synthesize extension = extension_;
85 @synthesize bundle = bundle_;
83 @synthesize pageActionRemoved = pageActionRemoved_; // Exposed for unit test. 86 @synthesize pageActionRemoved = pageActionRemoved_; // Exposed for unit test.
84 87
85 - (id)initWithParentWindow:(NSWindow*)parentWindow 88 - (id)initWithParentWindow:(NSWindow*)parentWindow
86 extension:(const Extension*)extension 89 extension:(const Extension*)extension
90 bundle:(const BundleInstaller*)bundle
87 browser:(Browser*)browser 91 browser:(Browser*)browser
88 icon:(SkBitmap)icon { 92 icon:(SkBitmap)icon {
89 NSString* nibPath = 93 NSString* nibName = bundle ? @"ExtensionInstalledBubbleBundle" :
90 [base::mac::FrameworkBundle() pathForResource:@"ExtensionInstalledBubble" 94 @"ExtensionInstalledBubble";
91 ofType:@"nib"]; 95 NSString* nibPath = [base::mac::FrameworkBundle() pathForResource:nibName
96 ofType:@"nib"];
92 if ((self = [super initWithWindowNibPath:nibPath owner:self])) { 97 if ((self = [super initWithWindowNibPath:nibPath owner:self])) {
93 DCHECK(parentWindow); 98 DCHECK(parentWindow);
94 parentWindow_ = parentWindow; 99 parentWindow_ = parentWindow;
95 DCHECK(extension);
96 extension_ = extension; 100 extension_ = extension;
101 bundle_ = bundle;
97 DCHECK(browser); 102 DCHECK(browser);
98 browser_ = browser; 103 browser_ = browser;
99 icon_.reset([gfx::SkBitmapToNSImage(icon) retain]); 104 icon_.reset([gfx::SkBitmapToNSImage(icon) retain]);
100 pageActionRemoved_ = NO; 105 pageActionRemoved_ = NO;
101 106
102 if (!extension->omnibox_keyword().empty()) { 107 if (bundle_) {
108 type_ = extension_installed_bubble::kBundle;
109 } else if (!extension->omnibox_keyword().empty()) {
103 type_ = extension_installed_bubble::kOmniboxKeyword; 110 type_ = extension_installed_bubble::kOmniboxKeyword;
104 } else if (extension->browser_action()) { 111 } else if (extension->browser_action()) {
105 type_ = extension_installed_bubble::kBrowserAction; 112 type_ = extension_installed_bubble::kBrowserAction;
106 } else if (extension->page_action() && 113 } else if (extension->page_action() &&
107 !extension->page_action()->default_icon_path().empty()) { 114 !extension->page_action()->default_icon_path().empty()) {
108 type_ = extension_installed_bubble::kPageAction; 115 type_ = extension_installed_bubble::kPageAction;
109 } else { 116 } else {
110 NOTREACHED(); // kGeneric installs handled in the extension_install_ui. 117 NOTREACHED(); // kGeneric installs handled in the extension_install_ui.
111 } 118 }
112 119
113 // Start showing window only after extension has fully loaded. 120 if (type_ == extension_installed_bubble::kBundle) {
114 extensionObserver_.reset(new ExtensionLoadedNotificationObserver( 121 [self showWindow:self];
115 self, browser->profile())); 122 } else {
123 // Start showing window only after extension has fully loaded.
124 extensionObserver_.reset(new ExtensionLoadedNotificationObserver(
125 self, browser->profile()));
126 }
116 127
117 // Watch to see if the parent window closes, and if so, close this one. 128 // Watch to see if the parent window closes, and if so, close this one.
118 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 129 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
119 [center addObserver:self 130 [center addObserver:self
120 selector:@selector(parentWindowWillClose:) 131 selector:@selector(parentWindowWillClose:)
121 name:NSWindowWillCloseNotification 132 name:NSWindowWillCloseNotification
122 object:parentWindow_]; 133 object:parentWindow_];
123 } 134 }
124 return self; 135 return self;
125 } 136 }
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 // would ordinarily only be displayed on a page of the appropriate type. 228 // would ordinarily only be displayed on a page of the appropriate type.
218 // We remove this preview when the extension installed bubble closes. 229 // We remove this preview when the extension installed bubble closes.
219 locationBarView->SetPreviewEnabledPageAction(extension_->page_action(), 230 locationBarView->SetPreviewEnabledPageAction(extension_->page_action(),
220 true); 231 true);
221 232
222 // Find the center of the bottom of the page action icon. 233 // Find the center of the bottom of the page action icon.
223 arrowPoint = 234 arrowPoint =
224 locationBarView->GetPageActionBubblePoint(extension_->page_action()); 235 locationBarView->GetPageActionBubblePoint(extension_->page_action());
225 break; 236 break;
226 } 237 }
238 case extension_installed_bubble::kBundle: {
239 NSView* wrenchButton =
240 [[window->cocoa_controller() toolbarController] wrenchButton];
241 const NSRect bounds = [wrenchButton bounds];
242 NSPoint anchor = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
243 arrowPoint = [wrenchButton convertPoint:anchor toView:nil];
244 break;
245 }
227 default: { 246 default: {
228 NOTREACHED() << "Generic extension type not allowed in install bubble."; 247 NOTREACHED() << "Generic extension type not allowed in install bubble.";
229 } 248 }
230 } 249 }
231 return arrowPoint; 250 return arrowPoint;
232 } 251 }
233 252
234 // We want this to be a child of a browser window. addChildWindow: 253 // We want this to be a child of a browser window. addChildWindow:
235 // (called from this function) will bring the window on-screen; 254 // (called from this function) will bring the window on-screen;
236 // unfortunately, [NSWindowController showWindow:] will also bring it 255 // unfortunately, [NSWindowController showWindow:] will also bring it
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
277 // function is exposed for unit testing. 296 // function is exposed for unit testing.
278 - (NSWindow*)initializeWindow { 297 - (NSWindow*)initializeWindow {
279 NSWindow* window = [self window]; // completes nib load 298 NSWindow* window = [self window]; // completes nib load
280 299
281 if (type_ == extension_installed_bubble::kOmniboxKeyword) { 300 if (type_ == extension_installed_bubble::kOmniboxKeyword) {
282 [infoBubbleView_ setArrowLocation:info_bubble::kTopLeft]; 301 [infoBubbleView_ setArrowLocation:info_bubble::kTopLeft];
283 } else { 302 } else {
284 [infoBubbleView_ setArrowLocation:info_bubble::kTopRight]; 303 [infoBubbleView_ setArrowLocation:info_bubble::kTopRight];
285 } 304 }
286 305
306 if (type_ == extension_installed_bubble::kBundle)
307 return window;
308
287 // Set appropriate icon, resizing if necessary. 309 // Set appropriate icon, resizing if necessary.
288 if ([icon_ size].width > extension_installed_bubble::kIconSize) { 310 if ([icon_ size].width > extension_installed_bubble::kIconSize) {
289 [icon_ setSize:NSMakeSize(extension_installed_bubble::kIconSize, 311 [icon_ setSize:NSMakeSize(extension_installed_bubble::kIconSize,
290 extension_installed_bubble::kIconSize)]; 312 extension_installed_bubble::kIconSize)];
291 } 313 }
292 [iconImage_ setImage:icon_]; 314 [iconImage_ setImage:icon_];
293 [iconImage_ setNeedsDisplay:YES]; 315 [iconImage_ setNeedsDisplay:YES];
294 return window; 316 return window;
295 } 317 }
296 318
297 // Calculate the height of each install message, resizing messages in their 319 // Calculate the height of each install message, resizing messages in their
298 // frames to fit window width. Return the new window height, based on the 320 // frames to fit window width. Return the new window height, based on the
299 // total of all message heights. 321 // total of all message heights.
300 - (int)calculateWindowHeight { 322 - (int)calculateWindowHeight {
301 // Adjust the window height to reflect the sum height of all messages 323 // Adjust the window height to reflect the sum height of all messages
302 // and vertical padding. 324 // and vertical padding.
303 int newWindowHeight = 2 * extension_installed_bubble::kOuterVerticalMargin; 325 int newWindowHeight = 2 * extension_installed_bubble::kOuterVerticalMargin;
304 326
305 // First part of extension installed message. 327 // First part of extension installed message.
306 string16 extension_name = UTF8ToUTF16(extension_->name().c_str()); 328 if (type_ != extension_installed_bubble::kBundle) {
307 base::i18n::AdjustStringForLocaleDirection(&extension_name); 329 string16 extension_name = UTF8ToUTF16(extension_->name().c_str());
308 [extensionInstalledMsg_ setStringValue:l10n_util::GetNSStringF( 330 base::i18n::AdjustStringForLocaleDirection(&extension_name);
309 IDS_EXTENSION_INSTALLED_HEADING, extension_name)]; 331 [extensionInstalledMsg_ setStringValue:l10n_util::GetNSStringF(
310 [GTMUILocalizerAndLayoutTweaker 332 IDS_EXTENSION_INSTALLED_HEADING, extension_name)];
333 [GTMUILocalizerAndLayoutTweaker
311 sizeToFitFixedWidthTextField:extensionInstalledMsg_]; 334 sizeToFitFixedWidthTextField:extensionInstalledMsg_];
312 newWindowHeight += [extensionInstalledMsg_ frame].size.height + 335 newWindowHeight += [extensionInstalledMsg_ frame].size.height +
313 extension_installed_bubble::kInnerVerticalMargin; 336 extension_installed_bubble::kInnerVerticalMargin;
337 }
314 338
315 // If type is page action, include a special message about page actions. 339 // If type is page action, include a special message about page actions.
316 if (type_ == extension_installed_bubble::kPageAction) { 340 if (type_ == extension_installed_bubble::kPageAction) {
317 [extraInfoMsg_ setHidden:NO]; 341 [extraInfoMsg_ setHidden:NO];
318 [[extraInfoMsg_ cell] 342 [[extraInfoMsg_ cell]
319 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; 343 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
320 [GTMUILocalizerAndLayoutTweaker 344 [GTMUILocalizerAndLayoutTweaker
321 sizeToFitFixedWidthTextField:extraInfoMsg_]; 345 sizeToFitFixedWidthTextField:extraInfoMsg_];
322 newWindowHeight += [extraInfoMsg_ frame].size.height + 346 newWindowHeight += [extraInfoMsg_ frame].size.height +
323 extension_installed_bubble::kInnerVerticalMargin; 347 extension_installed_bubble::kInnerVerticalMargin;
324 } 348 }
325 349
326 // If type is omnibox keyword, include a special message about the keyword. 350 // If type is omnibox keyword, include a special message about the keyword.
327 if (type_ == extension_installed_bubble::kOmniboxKeyword) { 351 if (type_ == extension_installed_bubble::kOmniboxKeyword) {
328 [extraInfoMsg_ setStringValue:l10n_util::GetNSStringF( 352 [extraInfoMsg_ setStringValue:l10n_util::GetNSStringF(
329 IDS_EXTENSION_INSTALLED_OMNIBOX_KEYWORD_INFO, 353 IDS_EXTENSION_INSTALLED_OMNIBOX_KEYWORD_INFO,
330 UTF8ToUTF16(extension_->omnibox_keyword()))]; 354 UTF8ToUTF16(extension_->omnibox_keyword()))];
331 [extraInfoMsg_ setHidden:NO]; 355 [extraInfoMsg_ setHidden:NO];
332 [[extraInfoMsg_ cell] 356 [[extraInfoMsg_ cell]
333 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; 357 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
334 [GTMUILocalizerAndLayoutTweaker 358 [GTMUILocalizerAndLayoutTweaker
335 sizeToFitFixedWidthTextField:extraInfoMsg_]; 359 sizeToFitFixedWidthTextField:extraInfoMsg_];
336 newWindowHeight += [extraInfoMsg_ frame].size.height + 360 newWindowHeight += [extraInfoMsg_ frame].size.height +
337 extension_installed_bubble::kInnerVerticalMargin; 361 extension_installed_bubble::kInnerVerticalMargin;
338 } 362 }
339 363
340 // Second part of extension installed message. 364 // If type is bundle, list the extensions that were installed and those that
341 [[extensionInstalledInfoMsg_ cell] 365 // failed.
342 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; 366 if (type_ == extension_installed_bubble::kBundle) {
343 [GTMUILocalizerAndLayoutTweaker 367 NSInteger installedListHeight =
344 sizeToFitFixedWidthTextField:extensionInstalledInfoMsg_]; 368 [self addExtensionList:installedHeadingMsg_
345 newWindowHeight += [extensionInstalledInfoMsg_ frame].size.height; 369 itemsMsg:installedItemsMsg_
370 state:BundleInstaller::Item::STATE_INSTALLED];
371
372 NSInteger failedListHeight =
373 [self addExtensionList:failedHeadingMsg_
374 itemsMsg:failedItemsMsg_
375 state:BundleInstaller::Item::STATE_FAILED];
376
377 newWindowHeight += installedListHeight + failedListHeight;
378
379 // Put some space between the lists if both are present.
380 if (installedListHeight > 0 && failedListHeight > 0)
381 newWindowHeight += extension_installed_bubble::kInnerVerticalMargin;
382
Robert Sesek 2012/03/05 18:30:13 nit: remove blank line
jstritar 2012/03/05 19:12:00 Done.
383 } else {
384 // Second part of extension installed message.
385 [[extensionInstalledInfoMsg_ cell]
386 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
387 [GTMUILocalizerAndLayoutTweaker
388 sizeToFitFixedWidthTextField:extensionInstalledInfoMsg_];
389 newWindowHeight += [extensionInstalledInfoMsg_ frame].size.height;
390 }
346 391
347 return newWindowHeight; 392 return newWindowHeight;
348 } 393 }
349 394
395 - (int)addExtensionList:(NSTextField*)headingMsg
Robert Sesek 2012/03/05 18:30:13 (NSInteger)
jstritar 2012/03/05 19:12:00 Done.
396 itemsMsg:(NSTextField*)itemsMsg
397 state:(BundleInstaller::Item::State)state {
398 string16 heading = bundle_->GetHeadingTextFor(state);
399 bool hidden = heading.empty();
400 [headingMsg setHidden:hidden];
401 [itemsMsg setHidden:hidden];
402 if (hidden)
403 return 0;
404
405 [headingMsg setStringValue:base::SysUTF16ToNSString(heading)];
406 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:headingMsg];
407
408 NSMutableString* joinedItems = [NSMutableString string];
409 BundleInstaller::ItemList items = bundle_->GetItemsWithState(state);
410 for (size_t i = 0; i < items.size(); ++i) {
411 if (i > 0)
412 [joinedItems appendString:@"\n"];
413 [joinedItems appendString:base::SysUTF16ToNSString(
414 items[i].GetNameForDisplay())];
415 }
416
417 [itemsMsg setStringValue:joinedItems];
418 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:itemsMsg];
419
420 return [headingMsg frame].size.height +
421 extension_installed_bubble::kInnerVerticalMargin +
422 [itemsMsg frame].size.height;
423 }
424
350 // Adjust y-position of messages to sit properly in new window height. 425 // Adjust y-position of messages to sit properly in new window height.
351 - (void)setMessageFrames:(int)newWindowHeight { 426 - (void)setMessageFrames:(int)newWindowHeight {
352 // The extension messages will always be shown. 427 if (type_ == extension_installed_bubble::kBundle) {
428 // Layout the messages from the bottom up.
429 NSTextField* msgs[] = { failedItemsMsg_, failedHeadingMsg_,
430 installedItemsMsg_, installedHeadingMsg_ };
431 int offsetFromBottom = 0;
432 BOOL is_first_visible = YES;
Robert Sesek 2012/03/05 18:30:13 naming: isFirstVisible
jstritar 2012/03/05 19:12:00 Done.
433 for (size_t i = 0; i < arraysize(msgs); ++i) {
434 if ([msgs[i] isHidden])
435 continue;
436
437 NSRect frame = [msgs[i] frame];
438 int margin = is_first_visible ?
439 extension_installed_bubble::kOuterVerticalMargin :
440 extension_installed_bubble::kInnerVerticalMargin;
441
442 frame.origin.y = offsetFromBottom + margin;
443 [msgs[i] setFrame:frame];
444 offsetFromBottom += frame.size.height + margin;
445
446 is_first_visible = NO;
447 }
448
449 // Move the close button a bit to vertically align it with the heading.
450 int closeButtonFudge = 1;
451 NSRect frame = [closeButton_ frame];
452 frame.origin.y = newWindowHeight - (frame.size.height + closeButtonFudge +
453 extension_installed_bubble::kOuterVerticalMargin);
454 [closeButton_ setFrame:frame];
455
456 return;
457 }
458
353 NSRect extensionMessageFrame1 = [extensionInstalledMsg_ frame]; 459 NSRect extensionMessageFrame1 = [extensionInstalledMsg_ frame];
354 NSRect extensionMessageFrame2 = [extensionInstalledInfoMsg_ frame]; 460 NSRect extensionMessageFrame2 = [extensionInstalledInfoMsg_ frame];
355 461
356 extensionMessageFrame1.origin.y = newWindowHeight - ( 462 extensionMessageFrame1.origin.y = newWindowHeight - (
357 extensionMessageFrame1.size.height + 463 extensionMessageFrame1.size.height +
358 extension_installed_bubble::kOuterVerticalMargin); 464 extension_installed_bubble::kOuterVerticalMargin);
359 [extensionInstalledMsg_ setFrame:extensionMessageFrame1]; 465 [extensionInstalledMsg_ setFrame:extensionMessageFrame1];
360 if (type_ == extension_installed_bubble::kPageAction || 466 if (type_ == extension_installed_bubble::kPageAction ||
361 type_ == extension_installed_bubble::kOmniboxKeyword) { 467 type_ == extension_installed_bubble::kOmniboxKeyword) {
362 // The extra message is only shown when appropriate. 468 // The extra message is only shown when appropriate.
(...skipping 24 matching lines...) Expand all
387 493
388 - (NSRect)getExtensionInstalledInfoMsgFrame { 494 - (NSRect)getExtensionInstalledInfoMsgFrame {
389 return [extensionInstalledInfoMsg_ frame]; 495 return [extensionInstalledInfoMsg_ frame];
390 } 496 }
391 497
392 - (void)extensionUnloaded:(id)sender { 498 - (void)extensionUnloaded:(id)sender {
393 extension_ = NULL; 499 extension_ = NULL;
394 } 500 }
395 501
396 @end 502 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698