Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 #include "chrome/browser/ui/browser_list.h" | 5 #include "chrome/browser/ui/browser_list.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
| 9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
| 10 #include "build/build_config.h" | 10 #include "build/build_config.h" |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 110 BrowserActivityObserver* activity_observer = NULL; | 110 BrowserActivityObserver* activity_observer = NULL; |
| 111 | 111 |
| 112 // Type used to indicate to match anything. | 112 // Type used to indicate to match anything. |
| 113 const int kMatchAny = 0; | 113 const int kMatchAny = 0; |
| 114 | 114 |
| 115 // See BrowserMatches for details. | 115 // See BrowserMatches for details. |
| 116 const int kMatchOriginalProfile = 1 << 0; | 116 const int kMatchOriginalProfile = 1 << 0; |
| 117 const int kMatchCanSupportWindowFeature = 1 << 1; | 117 const int kMatchCanSupportWindowFeature = 1 << 1; |
| 118 const int kMatchTabbed = 1 << 2; | 118 const int kMatchTabbed = 1 << 2; |
| 119 | 119 |
| 120 static BrowserList::BrowserVector& browsers() { | |
| 121 CR_DEFINE_STATIC_LOCAL(BrowserList::BrowserVector, b, ()); | |
|
Mark Mentovai
2011/11/16 22:54:24
Something better than b? (And b on 126 and o on 13
Nico
2011/11/16 22:59:20
c?
| |
| 122 return b; | |
| 123 } | |
| 124 | |
| 125 static BrowserList::BrowserVector& last_active_browsers() { | |
| 126 CR_DEFINE_STATIC_LOCAL(BrowserList::BrowserVector, b, ()); | |
| 127 return b; | |
| 128 } | |
| 129 | |
| 130 static ObserverList<BrowserList::Observer>& observers() { | |
| 131 CR_DEFINE_STATIC_LOCAL(ObserverList<BrowserList::Observer>, o, ()); | |
| 132 return o; | |
| 133 } | |
| 134 | |
| 120 // Returns true if the specified |browser| matches the specified arguments. | 135 // Returns true if the specified |browser| matches the specified arguments. |
| 121 // |match_types| is a bitmask dictating what parameters to match: | 136 // |match_types| is a bitmask dictating what parameters to match: |
| 122 // . If it contains kMatchOriginalProfile then the original profile of the | 137 // . If it contains kMatchOriginalProfile then the original profile of the |
| 123 // browser must match |profile->GetOriginalProfile()|. This is used to match | 138 // browser must match |profile->GetOriginalProfile()|. This is used to match |
| 124 // incognito windows. | 139 // incognito windows. |
| 125 // . If it contains kMatchCanSupportWindowFeature | 140 // . If it contains kMatchCanSupportWindowFeature |
| 126 // |CanSupportWindowFeature(window_feature)| must return true. | 141 // |CanSupportWindowFeature(window_feature)| must return true. |
| 127 // . If it contains kMatchTabbed, the browser must be a tabbed browser. | 142 // . If it contains kMatchTabbed, the browser must be a tabbed browser. |
| 128 bool BrowserMatches(Browser* browser, | 143 bool BrowserMatches(Browser* browser, |
| 129 Profile* profile, | 144 Profile* profile, |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 244 chromeos::WmIpc::instance()->NotifyAboutSignout(); | 259 chromeos::WmIpc::instance()->NotifyAboutSignout(); |
| 245 notified = true; | 260 notified = true; |
| 246 } | 261 } |
| 247 #endif | 262 #endif |
| 248 } | 263 } |
| 249 | 264 |
| 250 #endif | 265 #endif |
| 251 | 266 |
| 252 } // namespace | 267 } // namespace |
| 253 | 268 |
| 254 BrowserList::BrowserVector BrowserList::browsers_; | |
| 255 ObserverList<BrowserList::Observer> BrowserList::observers_; | |
| 256 | |
| 257 // static | 269 // static |
| 258 void BrowserList::AddBrowser(Browser* browser) { | 270 void BrowserList::AddBrowser(Browser* browser) { |
| 259 DCHECK(browser); | 271 DCHECK(browser); |
| 260 browsers_.push_back(browser); | 272 browsers().push_back(browser); |
| 261 | 273 |
| 262 g_browser_process->AddRefModule(); | 274 g_browser_process->AddRefModule(); |
| 263 | 275 |
| 264 if (!activity_observer) | 276 if (!activity_observer) |
| 265 activity_observer = new BrowserActivityObserver; | 277 activity_observer = new BrowserActivityObserver; |
| 266 | 278 |
| 267 content::NotificationService::current()->Notify( | 279 content::NotificationService::current()->Notify( |
| 268 chrome::NOTIFICATION_BROWSER_OPENED, | 280 chrome::NOTIFICATION_BROWSER_OPENED, |
| 269 content::Source<Browser>(browser), | 281 content::Source<Browser>(browser), |
| 270 content::NotificationService::NoDetails()); | 282 content::NotificationService::NoDetails()); |
| 271 | 283 |
| 272 // Send out notifications after add has occurred. Do some basic checking to | 284 // Send out notifications after add has occurred. Do some basic checking to |
| 273 // try to catch evil observers that change the list from under us. | 285 // try to catch evil observers that change the list from under us. |
| 274 size_t original_count = observers_.size(); | 286 size_t original_count = observers().size(); |
| 275 FOR_EACH_OBSERVER(Observer, observers_, OnBrowserAdded(browser)); | 287 FOR_EACH_OBSERVER(Observer, observers(), OnBrowserAdded(browser)); |
| 276 DCHECK_EQ(original_count, observers_.size()) | 288 DCHECK_EQ(original_count, observers().size()) |
| 277 << "observer list modified during notification"; | 289 << "observer list modified during notification"; |
| 278 } | 290 } |
| 279 | 291 |
| 280 // static | 292 // static |
| 281 void BrowserList::MarkAsCleanShutdown() { | 293 void BrowserList::MarkAsCleanShutdown() { |
| 282 for (const_iterator i = begin(); i != end(); ++i) { | 294 for (const_iterator i = begin(); i != end(); ++i) { |
| 283 (*i)->profile()->MarkAsCleanShutdown(); | 295 (*i)->profile()->MarkAsCleanShutdown(); |
| 284 } | 296 } |
| 285 } | 297 } |
| 286 | 298 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 330 } | 342 } |
| 331 return; | 343 return; |
| 332 } | 344 } |
| 333 // If running the Chrome OS build, but we're not on the device, fall through | 345 // If running the Chrome OS build, but we're not on the device, fall through |
| 334 #endif | 346 #endif |
| 335 AllBrowsersClosedAndAppExiting(); | 347 AllBrowsersClosedAndAppExiting(); |
| 336 } | 348 } |
| 337 | 349 |
| 338 // static | 350 // static |
| 339 void BrowserList::RemoveBrowser(Browser* browser) { | 351 void BrowserList::RemoveBrowser(Browser* browser) { |
| 340 RemoveBrowserFrom(browser, &last_active_browsers_); | 352 RemoveBrowserFrom(browser, &last_active_browsers()); |
| 341 | 353 |
| 342 // Closing all windows does not indicate quitting the application on the Mac, | 354 // Closing all windows does not indicate quitting the application on the Mac, |
| 343 // however, many UI tests rely on this behavior so leave it be for now and | 355 // however, many UI tests rely on this behavior so leave it be for now and |
| 344 // simply ignore the behavior on the Mac outside of unit tests. | 356 // simply ignore the behavior on the Mac outside of unit tests. |
| 345 // TODO(andybons): Fix the UI tests to Do The Right Thing. | 357 // TODO(andybons): Fix the UI tests to Do The Right Thing. |
| 346 bool closing_last_browser = (browsers_.size() == 1); | 358 bool closing_last_browser = (browsers().size() == 1); |
| 347 content::NotificationService::current()->Notify( | 359 content::NotificationService::current()->Notify( |
| 348 chrome::NOTIFICATION_BROWSER_CLOSED, | 360 chrome::NOTIFICATION_BROWSER_CLOSED, |
| 349 content::Source<Browser>(browser), | 361 content::Source<Browser>(browser), |
| 350 content::Details<bool>(&closing_last_browser)); | 362 content::Details<bool>(&closing_last_browser)); |
| 351 | 363 |
| 352 RemoveBrowserFrom(browser, &browsers_); | 364 RemoveBrowserFrom(browser, &browsers()); |
| 353 | 365 |
| 354 // Do some basic checking to try to catch evil observers | 366 // Do some basic checking to try to catch evil observers |
| 355 // that change the list from under us. | 367 // that change the list from under us. |
| 356 size_t original_count = observers_.size(); | 368 size_t original_count = observers().size(); |
| 357 FOR_EACH_OBSERVER(Observer, observers_, OnBrowserRemoved(browser)); | 369 FOR_EACH_OBSERVER(Observer, observers(), OnBrowserRemoved(browser)); |
| 358 DCHECK_EQ(original_count, observers_.size()) | 370 DCHECK_EQ(original_count, observers().size()) |
| 359 << "observer list modified during notification"; | 371 << "observer list modified during notification"; |
| 360 | 372 |
| 361 // If the last Browser object was destroyed, make sure we try to close any | 373 // If the last Browser object was destroyed, make sure we try to close any |
| 362 // remaining dependent windows too. | 374 // remaining dependent windows too. |
| 363 if (browsers_.empty()) { | 375 if (browsers().empty()) { |
| 364 delete activity_observer; | 376 delete activity_observer; |
| 365 activity_observer = NULL; | 377 activity_observer = NULL; |
| 366 } | 378 } |
| 367 | 379 |
| 368 g_browser_process->ReleaseModule(); | 380 g_browser_process->ReleaseModule(); |
| 369 | 381 |
| 370 // If we're exiting, send out the APP_TERMINATING notification to allow other | 382 // If we're exiting, send out the APP_TERMINATING notification to allow other |
| 371 // modules to shut themselves down. | 383 // modules to shut themselves down. |
| 372 if (browsers_.empty() && | 384 if (browsers().empty() && |
| 373 (browser_shutdown::IsTryingToQuit() || | 385 (browser_shutdown::IsTryingToQuit() || |
| 374 g_browser_process->IsShuttingDown())) { | 386 g_browser_process->IsShuttingDown())) { |
| 375 // Last browser has just closed, and this is a user-initiated quit or there | 387 // Last browser has just closed, and this is a user-initiated quit or there |
| 376 // is no module keeping the app alive, so send out our notification. No need | 388 // is no module keeping the app alive, so send out our notification. No need |
| 377 // to call ProfileManager::ShutdownSessionServices() as part of the | 389 // to call ProfileManager::ShutdownSessionServices() as part of the |
| 378 // shutdown, because Browser::WindowClosing() already makes sure that the | 390 // shutdown, because Browser::WindowClosing() already makes sure that the |
| 379 // SessionService is created and notified. | 391 // SessionService is created and notified. |
| 380 NotifyAppTerminating(); | 392 NotifyAppTerminating(); |
| 381 AllBrowsersClosedAndAppExiting(); | 393 AllBrowsersClosedAndAppExiting(); |
| 382 } | 394 } |
| 383 } | 395 } |
| 384 | 396 |
| 385 // static | 397 // static |
| 386 void BrowserList::AddObserver(BrowserList::Observer* observer) { | 398 void BrowserList::AddObserver(BrowserList::Observer* observer) { |
| 387 observers_.AddObserver(observer); | 399 observers().AddObserver(observer); |
| 388 } | 400 } |
| 389 | 401 |
| 390 // static | 402 // static |
| 391 void BrowserList::RemoveObserver(BrowserList::Observer* observer) { | 403 void BrowserList::RemoveObserver(BrowserList::Observer* observer) { |
| 392 observers_.RemoveObserver(observer); | 404 observers().RemoveObserver(observer); |
| 393 } | 405 } |
| 394 | 406 |
| 395 // static | 407 // static |
| 396 void BrowserList::CloseAllBrowsers() { | 408 void BrowserList::CloseAllBrowsers() { |
| 397 bool session_ending = | 409 bool session_ending = |
| 398 browser_shutdown::GetShutdownType() == browser_shutdown::END_SESSION; | 410 browser_shutdown::GetShutdownType() == browser_shutdown::END_SESSION; |
| 399 // Tell everyone that we are shutting down. | 411 // Tell everyone that we are shutting down. |
| 400 browser_shutdown::SetTryingToQuit(true); | 412 browser_shutdown::SetTryingToQuit(true); |
| 401 | 413 |
| 402 // Before we close the browsers shutdown all session services. That way an | 414 // Before we close the browsers shutdown all session services. That way an |
| 403 // exit can restore all browsers open before exiting. | 415 // exit can restore all browsers open before exiting. |
| 404 ProfileManager::ShutdownSessionServices(); | 416 ProfileManager::ShutdownSessionServices(); |
| 405 | 417 |
| 406 // If there are no browsers, send the APP_TERMINATING action here. Otherwise, | 418 // If there are no browsers, send the APP_TERMINATING action here. Otherwise, |
| 407 // it will be sent by RemoveBrowser() when the last browser has closed. | 419 // it will be sent by RemoveBrowser() when the last browser has closed. |
| 408 if (browser_shutdown::ShuttingDownWithoutClosingBrowsers() || | 420 if (browser_shutdown::ShuttingDownWithoutClosingBrowsers() || |
| 409 browsers_.empty()) { | 421 browsers().empty()) { |
| 410 NotifyAndTerminate(true); | 422 NotifyAndTerminate(true); |
| 411 return; | 423 return; |
| 412 } | 424 } |
| 413 | 425 |
| 414 #if defined(OS_CHROMEOS) | 426 #if defined(OS_CHROMEOS) |
| 415 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( | 427 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( |
| 416 "StartedClosingWindows", false); | 428 "StartedClosingWindows", false); |
| 417 #endif | 429 #endif |
| 418 for (BrowserList::const_iterator i = BrowserList::begin(); | 430 for (BrowserList::const_iterator i = BrowserList::begin(); |
| 419 i != BrowserList::end();) { | 431 i != BrowserList::end();) { |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 616 // Although we should have a browser process, if there is none, | 628 // Although we should have a browser process, if there is none, |
| 617 // there is nothing to do. | 629 // there is nothing to do. |
| 618 if (!g_browser_process) return; | 630 if (!g_browser_process) return; |
| 619 | 631 |
| 620 // Allow the app to shutdown again. | 632 // Allow the app to shutdown again. |
| 621 if (!WillKeepAlive()) { | 633 if (!WillKeepAlive()) { |
| 622 g_browser_process->ReleaseModule(); | 634 g_browser_process->ReleaseModule(); |
| 623 // If there are no browsers open and we aren't already shutting down, | 635 // If there are no browsers open and we aren't already shutting down, |
| 624 // initiate a shutdown. Also skips shutdown if this is a unit test | 636 // initiate a shutdown. Also skips shutdown if this is a unit test |
| 625 // (MessageLoop::current() == null). | 637 // (MessageLoop::current() == null). |
| 626 if (browsers_.empty() && !browser_shutdown::IsTryingToQuit() && | 638 if (browsers().empty() && !browser_shutdown::IsTryingToQuit() && |
| 627 MessageLoop::current()) | 639 MessageLoop::current()) |
| 628 CloseAllBrowsers(); | 640 CloseAllBrowsers(); |
| 629 } | 641 } |
| 630 } | 642 } |
| 631 | 643 |
| 632 // static | 644 // static |
| 633 bool BrowserList::WillKeepAlive() { | 645 bool BrowserList::WillKeepAlive() { |
| 634 return keep_alive_count_ > 0; | 646 return keep_alive_count_ > 0; |
| 635 } | 647 } |
| 636 | 648 |
| 637 // static | 649 // static |
| 638 BrowserList::BrowserVector BrowserList::last_active_browsers_; | 650 BrowserList::const_iterator BrowserList::begin() { |
| 651 return browsers().begin(); | |
| 652 } | |
| 653 | |
| 654 // static | |
| 655 BrowserList::const_iterator BrowserList::end() { | |
| 656 return browsers().end(); | |
| 657 } | |
| 658 | |
| 659 // static | |
| 660 bool BrowserList::empty() { | |
| 661 return browsers().empty(); | |
| 662 } | |
| 663 | |
| 664 // static | |
| 665 size_t BrowserList::size() { | |
| 666 return browsers().size(); | |
| 667 } | |
| 639 | 668 |
| 640 // static | 669 // static |
| 641 void BrowserList::SetLastActive(Browser* browser) { | 670 void BrowserList::SetLastActive(Browser* browser) { |
| 642 // If the browser is currently trying to quit, we don't want to set the last | 671 // If the browser is currently trying to quit, we don't want to set the last |
| 643 // active browser because that can alter the last active browser that the user | 672 // active browser because that can alter the last active browser that the user |
| 644 // intended depending on the order in which the windows close. | 673 // intended depending on the order in which the windows close. |
| 645 if (browser_shutdown::IsTryingToQuit()) | 674 if (browser_shutdown::IsTryingToQuit()) |
| 646 return; | 675 return; |
| 647 RemoveBrowserFrom(browser, &last_active_browsers_); | 676 RemoveBrowserFrom(browser, &last_active_browsers()); |
| 648 last_active_browsers_.push_back(browser); | 677 last_active_browsers().push_back(browser); |
| 649 | 678 |
| 650 FOR_EACH_OBSERVER(Observer, observers_, OnBrowserSetLastActive(browser)); | 679 FOR_EACH_OBSERVER(Observer, observers(), OnBrowserSetLastActive(browser)); |
| 651 } | 680 } |
| 652 | 681 |
| 653 // static | 682 // static |
| 654 Browser* BrowserList::GetLastActive() { | 683 Browser* BrowserList::GetLastActive() { |
| 655 if (!last_active_browsers_.empty()) | 684 if (!last_active_browsers().empty()) |
| 656 return *(last_active_browsers_.rbegin()); | 685 return *(last_active_browsers().rbegin()); |
| 657 | 686 |
| 658 return NULL; | 687 return NULL; |
| 659 } | 688 } |
| 660 | 689 |
| 661 // static | 690 // static |
| 662 Browser* BrowserList::GetLastActiveWithProfile(Profile* profile) { | 691 Browser* BrowserList::GetLastActiveWithProfile(Profile* profile) { |
| 663 // We are only interested in last active browsers, so we don't fall back to | 692 // We are only interested in last active browsers, so we don't fall back to |
| 664 // all browsers like FindBrowserWith* do. | 693 // all browsers like FindBrowserWith* do. |
| 665 return FindBrowserMatching( | 694 return FindBrowserMatching( |
| 666 BrowserList::begin_last_active(), BrowserList::end_last_active(), profile, | 695 BrowserList::begin_last_active(), BrowserList::end_last_active(), profile, |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 721 Browser* BrowserList::FindBrowserWithTabContents(TabContents* tab_contents) { | 750 Browser* BrowserList::FindBrowserWithTabContents(TabContents* tab_contents) { |
| 722 DCHECK(tab_contents); | 751 DCHECK(tab_contents); |
| 723 for (TabContentsIterator it; !it.done(); ++it) { | 752 for (TabContentsIterator it; !it.done(); ++it) { |
| 724 if (it->tab_contents() == tab_contents) | 753 if (it->tab_contents() == tab_contents) |
| 725 return it.browser(); | 754 return it.browser(); |
| 726 } | 755 } |
| 727 return NULL; | 756 return NULL; |
| 728 } | 757 } |
| 729 | 758 |
| 730 // static | 759 // static |
| 760 BrowserList::const_reverse_iterator BrowserList::begin_last_active() { | |
| 761 return last_active_browsers().rbegin(); | |
| 762 } | |
| 763 | |
| 764 // static | |
| 765 BrowserList::const_reverse_iterator BrowserList::end_last_active() { | |
| 766 return last_active_browsers().rend(); | |
| 767 } | |
| 768 | |
| 769 // static | |
| 770 size_t BrowserList::GetBrowserCount(Profile* profile) { | |
| 771 size_t result = 0; | |
| 772 for (BrowserList::const_iterator i = BrowserList::begin(); | |
| 773 i != BrowserList::end(); ++i) { | |
| 774 if (BrowserMatches(*i, profile, Browser::FEATURE_NONE, kMatchAny)) { | |
| 775 result++; | |
|
Mark Mentovai
2011/11/16 22:54:24
For consistency with the preincrement two lines ab
Nico
2011/11/16 22:59:20
Done.
| |
| 776 } | |
| 777 } | |
| 778 return result; | |
| 779 } | |
| 780 | |
| 781 // static | |
| 731 size_t BrowserList::GetBrowserCountForType(Profile* profile, | 782 size_t BrowserList::GetBrowserCountForType(Profile* profile, |
| 732 bool match_tabbed) { | 783 bool match_tabbed) { |
| 733 size_t result = 0; | 784 size_t result = 0; |
| 734 for (BrowserList::const_iterator i = BrowserList::begin(); | 785 for (BrowserList::const_iterator i = BrowserList::begin(); |
| 735 i != BrowserList::end(); ++i) { | 786 i != BrowserList::end(); ++i) { |
| 736 if (BrowserMatches(*i, profile, Browser::FEATURE_NONE, | 787 if (BrowserMatches(*i, profile, Browser::FEATURE_NONE, |
| 737 match_tabbed ? kMatchTabbed : kMatchAny)) | 788 match_tabbed ? kMatchTabbed : kMatchAny)) |
| 738 ++result; | 789 ++result; |
| 739 } | 790 } |
| 740 return result; | 791 return result; |
| 741 } | 792 } |
| 742 | |
| 743 // static | |
| 744 size_t BrowserList::GetBrowserCount(Profile* profile) { | |
| 745 size_t result = 0; | |
| 746 for (BrowserList::const_iterator i = BrowserList::begin(); | |
| 747 i != BrowserList::end(); ++i) { | |
| 748 if (BrowserMatches(*i, profile, Browser::FEATURE_NONE, kMatchAny)) { | |
| 749 result++; | |
| 750 } | |
| 751 } | |
| 752 return result; | |
| 753 } | |
| 754 | 793 |
| 755 // static | 794 // static |
| 756 bool BrowserList::IsOffTheRecordSessionActive() { | 795 bool BrowserList::IsOffTheRecordSessionActive() { |
| 757 for (BrowserList::const_iterator i = BrowserList::begin(); | 796 for (BrowserList::const_iterator i = BrowserList::begin(); |
| 758 i != BrowserList::end(); ++i) { | 797 i != BrowserList::end(); ++i) { |
| 759 if ((*i)->profile()->IsOffTheRecord()) | 798 if ((*i)->profile()->IsOffTheRecord()) |
| 760 return true; | 799 return true; |
| 761 } | 800 } |
| 762 return false; | 801 return false; |
| 763 } | 802 } |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 815 // If no more TabContents from Browsers, check the BackgroundPrintingManager. | 854 // If no more TabContents from Browsers, check the BackgroundPrintingManager. |
| 816 while (bg_printing_iterator_ != GetBackgroundPrintingManager()->end()) { | 855 while (bg_printing_iterator_ != GetBackgroundPrintingManager()->end()) { |
| 817 cur_ = *bg_printing_iterator_; | 856 cur_ = *bg_printing_iterator_; |
| 818 CHECK(cur_); | 857 CHECK(cur_); |
| 819 ++bg_printing_iterator_; | 858 ++bg_printing_iterator_; |
| 820 return; | 859 return; |
| 821 } | 860 } |
| 822 // Reached the end - no more TabContents. | 861 // Reached the end - no more TabContents. |
| 823 cur_ = NULL; | 862 cur_ = NULL; |
| 824 } | 863 } |
| OLD | NEW |