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

Side by Side Diff: chrome/browser/tab_contents/tab_contents.cc

Issue 4767001: Make TabContents own its infobar delegates.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 1 month 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) 2010 The Chromium Authors. All rights reserved. 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 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/tab_contents/tab_contents.h" 5 #include "chrome/browser/tab_contents/tab_contents.h"
6 6
7 #include <cmath> 7 #include <cmath>
8 8
9 #include "app/l10n_util.h" 9 #include "app/l10n_util.h"
10 #include "app/resource_bundle.h" 10 #include "app/resource_bundle.h"
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after
470 Source<TabContents>(this), 470 Source<TabContents>(this),
471 NotificationService::NoDetails()); 471 NotificationService::NoDetails());
472 472
473 // Notify any lasting InfobarDelegates that have not yet been removed that 473 // Notify any lasting InfobarDelegates that have not yet been removed that
474 // whatever infobar they were handling in this TabContents has closed, 474 // whatever infobar they were handling in this TabContents has closed,
475 // because the TabContents is going away entirely. 475 // because the TabContents is going away entirely.
476 // This must happen after the TAB_CONTENTS_DESTROYED notification as the 476 // This must happen after the TAB_CONTENTS_DESTROYED notification as the
477 // notification may trigger infobars calls that access their delegate. (and 477 // notification may trigger infobars calls that access their delegate. (and
478 // some implementations of InfoBarDelegate do delete themselves on 478 // some implementations of InfoBarDelegate do delete themselves on
479 // InfoBarClosed()). 479 // InfoBarClosed()).
480 for (int i = 0; i < infobar_delegate_count(); ++i) { 480 std::for_each(infobar_delegates_.begin(), infobar_delegates_.end(),
481 InfoBarDelegate* delegate = GetInfoBarDelegateAt(i); 481 std::mem_fun(&InfoBarDelegate::InfoBarClosed));
482 delegate->InfoBarClosed();
483 }
484 infobar_delegates_.clear(); 482 infobar_delegates_.clear();
485 483
486 // TODO(brettw) this should be moved to the view. 484 // TODO(brettw) this should be moved to the view.
487 #if defined(OS_WIN) 485 #if defined(OS_WIN)
488 // If we still have a window handle, destroy it. GetNativeView can return 486 // If we still have a window handle, destroy it. GetNativeView can return
489 // NULL if this contents was part of a window that closed. 487 // NULL if this contents was part of a window that closed.
490 if (GetNativeView()) 488 if (GetNativeView())
491 ::DestroyWindow(GetNativeView()); 489 ::DestroyWindow(GetNativeView());
492 #endif 490 #endif
493 491
(...skipping 565 matching lines...) Expand 10 before | Expand all | Expand 10 after
1059 return true; 1057 return true;
1060 return false; 1058 return false;
1061 } 1059 }
1062 1060
1063 void TabContents::SetFocusToLocationBar(bool select_all) { 1061 void TabContents::SetFocusToLocationBar(bool select_all) {
1064 if (delegate()) 1062 if (delegate())
1065 delegate()->SetFocusToLocationBar(select_all); 1063 delegate()->SetFocusToLocationBar(select_all);
1066 } 1064 }
1067 1065
1068 void TabContents::AddInfoBar(InfoBarDelegate* delegate) { 1066 void TabContents::AddInfoBar(InfoBarDelegate* delegate) {
1067 DCHECK(delegate != NULL);
1069 if (delegate_ && !delegate_->infobars_enabled()) { 1068 if (delegate_ && !delegate_->infobars_enabled()) {
1070 delegate->InfoBarClosed(); 1069 delegate->InfoBarClosed();
1071 return; 1070 return;
1072 } 1071 }
1073 1072
1074 // Look through the existing InfoBarDelegates we have for a match. If we've 1073 // Look through the existing InfoBarDelegates we have for a match. If we've
1075 // already got one that matches, then we don't add the new one. 1074 // already got one that matches, then we don't add the new one.
1076 for (int i = 0; i < infobar_delegate_count(); ++i) { 1075 InfoBarDelegates::iterator i(std::find_if(infobar_delegates_.begin(),
1077 if (GetInfoBarDelegateAt(i)->EqualsDelegate(delegate)) { 1076 infobar_delegates_.end(),
1078 // Tell the new infobar to close so that it can clean itself up. 1077 std::bind2nd(std::mem_fun(&InfoBarDelegate::EqualsDelegate), delegate)));
1079 delegate->InfoBarClosed(); 1078 if (i != infobar_delegates_.end()) {
1080 return; 1079 // Tell the new infobar to close so that it can clean itself up.
1081 } 1080 delegate->InfoBarClosed();
1081 return;
1082 } 1082 }
1083 1083
1084 infobar_delegates_.push_back(delegate); 1084 infobar_delegates_.push_back(delegate);
1085 NotificationService::current()->Notify( 1085 NotificationService::current()->Notify(
1086 NotificationType::TAB_CONTENTS_INFOBAR_ADDED, 1086 NotificationType::TAB_CONTENTS_INFOBAR_ADDED,
1087 Source<TabContents>(this), 1087 Source<TabContents>(this), Details<InfoBarDelegate>(delegate));
1088 Details<InfoBarDelegate>(delegate));
1089 1088
1090 // Add ourselves as an observer for navigations the first time a delegate is 1089 // Add ourselves as an observer for navigations the first time a delegate is
1091 // added. We use this notification to expire InfoBars that need to expire on 1090 // added. We use this notification to expire InfoBars that need to expire on
1092 // page transitions. 1091 // page transitions.
1093 if (infobar_delegates_.size() == 1) { 1092 if (infobar_delegates_.size() == 1) {
1094 registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, 1093 registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
1095 Source<NavigationController>(&controller_)); 1094 Source<NavigationController>(&controller_));
1096 } 1095 }
1097 } 1096 }
1098 1097
1099 void TabContents::RemoveInfoBar(InfoBarDelegate* delegate) { 1098 void TabContents::RemoveInfoBar(InfoBarDelegate* delegate) {
1099 DCHECK(delegate != NULL);
1100 if (delegate_ && !delegate_->infobars_enabled()) { 1100 if (delegate_ && !delegate_->infobars_enabled()) {
1101 DCHECK(infobar_delegates_.empty());
1101 return; 1102 return;
1102 } 1103 }
1103 1104
1104 std::vector<InfoBarDelegate*>::iterator it = 1105 InfoBarDelegates::iterator i(std::find(infobar_delegates_.begin(),
1105 find(infobar_delegates_.begin(), infobar_delegates_.end(), delegate); 1106 infobar_delegates_.end(), delegate));
1106 if (it != infobar_delegates_.end()) { 1107 DCHECK(i != infobar_delegates_.end());
1107 InfoBarDelegate* delegate = *it; 1108 NotificationService::current()->Notify(
1108 NotificationService::current()->Notify( 1109 NotificationType::TAB_CONTENTS_INFOBAR_REMOVED,
1109 NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, 1110 Source<TabContents>(this), Details<InfoBarDelegate>(delegate));
1110 Source<TabContents>(this),
1111 Details<InfoBarDelegate>(delegate));
1112 1111
1113 // Just to be safe, make sure the delegate was not removed by an observer. 1112 // Just to be safe, make sure the delegate was not removed by an observer. If
1114 it = find(infobar_delegates_.begin(), infobar_delegates_.end(), delegate); 1113 // you hit this DCHECK, please comment in http://crbug.com/50428 how you got
1115 if (it != infobar_delegates_.end()) { 1114 // there.
1116 infobar_delegates_.erase(it); 1115 i = std::find(infobar_delegates_.begin(), infobar_delegates_.end(), delegate);
1117 // Remove ourselves as an observer if we are tracking no more InfoBars. 1116 DCHECK(i != infobar_delegates_.end());
1118 if (infobar_delegates_.empty()) { 1117 infobar_delegates_.erase(i);
1119 registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED, 1118
1120 Source<NavigationController>(&controller_)); 1119 // Remove ourselves as an observer if we are tracking no more InfoBars.
1121 } 1120 // NOTE: We must do this before calling InfoBarClosed() below. Otherwise, if
1122 } else { 1121 // that call causes a call to AddInfoBar(), we'll try to register for the
1123 // If you hit this NOTREACHED, please comment in bug 1122 // NAV_ENTRY_COMMITTED notification, which we won't have yet unregistered
1124 // http://crbug.com/50428 how you got there. 1123 // here, and we'll fail the "not already registered" DCHECK in
1125 NOTREACHED(); 1124 // NotificationRegistrar::Add().
1126 } 1125 if (infobar_delegates_.empty()) {
1126 registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED,
1127 Source<NavigationController>(&controller_));
1127 } 1128 }
1129
1130 delegate->InfoBarClosed();
1128 } 1131 }
1129 1132
1130 void TabContents::ReplaceInfoBar(InfoBarDelegate* old_delegate, 1133 void TabContents::ReplaceInfoBar(InfoBarDelegate* old_delegate,
1131 InfoBarDelegate* new_delegate) { 1134 InfoBarDelegate* new_delegate) {
1135 DCHECK(old_delegate != NULL);
1136 DCHECK(new_delegate != NULL);
1132 if (delegate_ && !delegate_->infobars_enabled()) { 1137 if (delegate_ && !delegate_->infobars_enabled()) {
1138 DCHECK(infobar_delegates_.empty());
1133 new_delegate->InfoBarClosed(); 1139 new_delegate->InfoBarClosed();
1134 return; 1140 return;
1135 } 1141 }
1136 1142
1137 std::vector<InfoBarDelegate*>::iterator it = 1143 InfoBarDelegates::iterator i(std::find(infobar_delegates_.begin(),
1138 find(infobar_delegates_.begin(), infobar_delegates_.end(), old_delegate); 1144 infobar_delegates_.end(), old_delegate));
1139 DCHECK(it != infobar_delegates_.end()); 1145 DCHECK(i != infobar_delegates_.end());
1140 1146
1141 // Notify the container about the change of plans. 1147 // Notify the container about the change of plans.
1142 scoped_ptr<std::pair<InfoBarDelegate*, InfoBarDelegate*> > details( 1148 typedef std::pair<InfoBarDelegate*, InfoBarDelegate*> DelegatePair;
1143 new std::pair<InfoBarDelegate*, InfoBarDelegate*>( 1149 scoped_ptr<DelegatePair> delegate_pair(new DelegatePair(old_delegate,
1144 old_delegate, new_delegate)); 1150 new_delegate));
1145 NotificationService::current()->Notify( 1151 NotificationService::current()->Notify(
1146 NotificationType::TAB_CONTENTS_INFOBAR_REPLACED, 1152 NotificationType::TAB_CONTENTS_INFOBAR_REPLACED,
1147 Source<TabContents>(this), 1153 Source<TabContents>(this), Details<DelegatePair>(delegate_pair.get()));
1148 Details<std::pair<InfoBarDelegate*, InfoBarDelegate*> >(details.get()));
1149 1154
1150 // Just to be safe, make sure the delegate was not removed by an observer. 1155 // Just to be safe, make sure the delegate was not removed by an observer. If
1151 it = find(infobar_delegates_.begin(), infobar_delegates_.end(), old_delegate); 1156 // you hit this DCHECK, please comment in http://crbug.com/50428 how you got
1152 if (it != infobar_delegates_.end()) { 1157 // there.
1153 // Remove the old one. 1158 i = std::find(infobar_delegates_.begin(), infobar_delegates_.end(),
1154 infobar_delegates_.erase(it); 1159 old_delegate);
1155 } else { 1160 DCHECK(i != infobar_delegates_.end());
1156 // If you hit this NOTREACHED, please comment in bug 1161 // Remove the old one.
1157 // http://crbug.com/50428 how you got there. 1162 infobar_delegates_.erase(i);
1158 NOTREACHED();
1159 }
1160 1163
1161 // Add the new one. 1164 // Add the new one. We do this before calling old_delegate->InfoBarClosed()
1162 DCHECK(find(infobar_delegates_.begin(), 1165 // for the same reason as why we call that function last in RemoveInfoBar()
1163 infobar_delegates_.end(), new_delegate) == 1166 // above; see comments there.
1164 infobar_delegates_.end()); 1167 DCHECK(std::find(infobar_delegates_.begin(), infobar_delegates_.end(),
1168 new_delegate) == infobar_delegates_.end());
1165 infobar_delegates_.push_back(new_delegate); 1169 infobar_delegates_.push_back(new_delegate);
1170
1171 old_delegate->InfoBarClosed();
1166 } 1172 }
1167 1173
1168 bool TabContents::ShouldShowBookmarkBar() { 1174 bool TabContents::ShouldShowBookmarkBar() {
1169 if (showing_interstitial_page()) 1175 if (showing_interstitial_page())
1170 return false; 1176 return false;
1171 1177
1172 // Do not show bookmarks bar if bookmarks aren't enabled. 1178 // Do not show bookmarks bar if bookmarks aren't enabled.
1173 if (!browser_defaults::bookmarks_enabled) 1179 if (!browser_defaults::bookmarks_enabled)
1174 return false; 1180 return false;
1175 1181
1176 // See GetDOMUIForCurrentState() comment for more info. This case is very 1182 // See GetDOMUIForCurrentState() comment for more info. This case is very
1177 // similar, but for non-first loads, we want to use the committed entry. This 1183 // similar, but for non-first loads, we want to use the committed entry. This
1178 // is so the bookmarks bar disappears at the same time the page does. 1184 // is so the bookmarks bar disappears at the same time the page does.
1179 if (controller_.GetLastCommittedEntry()) { 1185 if (controller_.GetLastCommittedEntry()) {
1180 // Not the first load, always use the committed DOM UI. 1186 // Not the first load, always use the committed DOM UI.
1181 if (render_manager_.dom_ui()) 1187 return (render_manager_.dom_ui() == NULL) ?
1182 return render_manager_.dom_ui()->force_bookmark_bar_visible(); 1188 false : render_manager_.dom_ui()->force_bookmark_bar_visible();
1183 return false; // Default.
1184 } 1189 }
1185 1190
1186 // When it's the first load, we know either the pending one or the committed 1191 // When it's the first load, we know either the pending one or the committed
1187 // one will have the DOM UI in it (see GetDOMUIForCurrentState), and only one 1192 // one will have the DOM UI in it (see GetDOMUIForCurrentState), and only one
1188 // of them will be valid, so we can just check both. 1193 // of them will be valid, so we can just check both.
1189 if (render_manager_.pending_dom_ui()) 1194 if (render_manager_.pending_dom_ui())
1190 return render_manager_.pending_dom_ui()->force_bookmark_bar_visible(); 1195 return render_manager_.pending_dom_ui()->force_bookmark_bar_visible();
1191 if (render_manager_.dom_ui()) 1196 return (render_manager_.dom_ui() == NULL) ?
1192 return render_manager_.dom_ui()->force_bookmark_bar_visible(); 1197 false : render_manager_.dom_ui()->force_bookmark_bar_visible();
1193 return false; // Default.
1194 } 1198 }
1195 1199
1196 void TabContents::ToolbarSizeChanged(bool is_animating) { 1200 void TabContents::ToolbarSizeChanged(bool is_animating) {
1197 TabContentsDelegate* d = delegate(); 1201 TabContentsDelegate* d = delegate();
1198 if (d) 1202 if (d)
1199 d->ToolbarSizeChanged(this, is_animating); 1203 d->ToolbarSizeChanged(this, is_animating);
1200 } 1204 }
1201 1205
1202 bool TabContents::CanDownload(int request_id) { 1206 bool TabContents::CanDownload(int request_id) {
1203 TabContentsDelegate* d = delegate(); 1207 TabContentsDelegate* d = delegate();
1204 if (d) 1208 if (d)
1205 return d->CanDownload(request_id); 1209 return d->CanDownload(request_id);
1206 return true; 1210 return true;
1207 } 1211 }
1208 1212
1209 void TabContents::OnStartDownload(DownloadItem* download) { 1213 void TabContents::OnStartDownload(DownloadItem* download) {
1210 DCHECK(download); 1214 DCHECK(download);
1211 1215
1212 // Download in a constrained popup is shown in the tab that opened it. 1216 // Download in a constrained popup is shown in the tab that opened it.
1213 TabContents* tab_contents = delegate()->GetConstrainingContents(this); 1217 TabContents* tab_contents = delegate()->GetConstrainingContents(this);
1214 1218
1215 if (tab_contents && tab_contents->delegate()) 1219 if (tab_contents && tab_contents->delegate())
1216 tab_contents->delegate()->OnStartDownload(download, this); 1220 tab_contents->delegate()->OnStartDownload(download, this);
1217 } 1221 }
1218 1222
1219 void TabContents::WillClose(ConstrainedWindow* window) { 1223 void TabContents::WillClose(ConstrainedWindow* window) {
1220 ConstrainedWindowList::iterator it = 1224 ConstrainedWindowList::iterator i(
1221 find(child_windows_.begin(), child_windows_.end(), window); 1225 std::find(child_windows_.begin(), child_windows_.end(), window));
1222 bool removed_topmost_window = it == child_windows_.begin(); 1226 bool removed_topmost_window = (i == child_windows_.begin());
1223 if (it != child_windows_.end()) 1227 if (i != child_windows_.end())
1224 child_windows_.erase(it); 1228 child_windows_.erase(i);
1225 if (child_windows_.size() > 0) { 1229 if (child_windows_.empty()) {
1226 if (removed_topmost_window) { 1230 BlockTabContent(false);
1231 } else {
1232 if (removed_topmost_window)
1227 child_windows_[0]->ShowConstrainedWindow(); 1233 child_windows_[0]->ShowConstrainedWindow();
1228 }
1229 BlockTabContent(true); 1234 BlockTabContent(true);
1230 } else {
1231 BlockTabContent(false);
1232 } 1235 }
1233 } 1236 }
1234 1237
1235 void TabContents::WillCloseBlockedContentContainer( 1238 void TabContents::WillCloseBlockedContentContainer(
1236 BlockedContentContainer* container) { 1239 BlockedContentContainer* container) {
1237 DCHECK(blocked_contents_ == container); 1240 DCHECK(blocked_contents_ == container);
1238 blocked_contents_ = NULL; 1241 blocked_contents_ = NULL;
1239 PopupNotificationVisibilityChanged(false); 1242 PopupNotificationVisibilityChanged(false);
1240 } 1243 }
1241 1244
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after
1552 } 1555 }
1553 1556
1554 void TabContents::ExpireInfoBars( 1557 void TabContents::ExpireInfoBars(
1555 const NavigationController::LoadCommittedDetails& details) { 1558 const NavigationController::LoadCommittedDetails& details) {
1556 // Only hide InfoBars when the user has done something that makes the main 1559 // Only hide InfoBars when the user has done something that makes the main
1557 // frame load. We don't want various automatic or subframe navigations making 1560 // frame load. We don't want various automatic or subframe navigations making
1558 // it disappear. 1561 // it disappear.
1559 if (!details.is_user_initiated_main_frame_load()) 1562 if (!details.is_user_initiated_main_frame_load())
1560 return; 1563 return;
1561 1564
1565 // NOTE: It is not safe to change the following code to count upwards or use
1566 // iterators, as the RemvoeInfoBar() call synchronously modifies our delegate
Ben Goodger (Google) 2010/11/12 18:56:43 Remove
1567 // list.
1562 for (int i = infobar_delegate_count() - 1; i >= 0; --i) { 1568 for (int i = infobar_delegate_count() - 1; i >= 0; --i) {
1563 InfoBarDelegate* delegate = GetInfoBarDelegateAt(i); 1569 InfoBarDelegate* delegate = GetInfoBarDelegateAt(i);
1564 if (!delegate) {
1565 // If you hit this NOTREACHED, please comment in bug
1566 // http://crbug.com/50428 how you got there.
1567 NOTREACHED();
1568 continue;
1569 }
1570
1571 if (delegate->ShouldExpire(details)) 1570 if (delegate->ShouldExpire(details))
1572 RemoveInfoBar(delegate); 1571 RemoveInfoBar(delegate);
1573 } 1572 }
1574 } 1573 }
1575 1574
1576 DOMUI* TabContents::GetDOMUIForCurrentState() { 1575 DOMUI* TabContents::GetDOMUIForCurrentState() {
1577 // When there is a pending navigation entry, we want to use the pending DOMUI 1576 // When there is a pending navigation entry, we want to use the pending DOMUI
1578 // that goes along with it to control the basic flags. For example, we want to 1577 // that goes along with it to control the basic flags. For example, we want to
1579 // show the pending URL in the URL bar, so we want the display_url flag to 1578 // show the pending URL in the URL bar, so we want the display_url flag to
1580 // be from the pending entry. 1579 // be from the pending entry.
(...skipping 832 matching lines...) Expand 10 before | Expand all | Expand 10 after
2413 if (rvh != render_view_host()) { 2412 if (rvh != render_view_host()) {
2414 // The pending page's RenderViewHost is gone. 2413 // The pending page's RenderViewHost is gone.
2415 return; 2414 return;
2416 } 2415 }
2417 2416
2418 SetIsLoading(false, NULL); 2417 SetIsLoading(false, NULL);
2419 NotifyDisconnected(); 2418 NotifyDisconnected();
2420 SetIsCrashed(true); 2419 SetIsCrashed(true);
2421 2420
2422 // Remove all infobars. 2421 // Remove all infobars.
2423 for (int i = infobar_delegate_count() - 1; i >=0 ; --i) 2422 while (!infobar_delegates_.empty())
2424 RemoveInfoBar(GetInfoBarDelegateAt(i)); 2423 RemoveInfoBar(infobar_delegates_.front());
2425 2424
2426 // Tell the view that we've crashed so it can prepare the sad tab page. 2425 // Tell the view that we've crashed so it can prepare the sad tab page.
2427 // Only do this if we're not in browser shutdown, so that TabContents 2426 // Only do this if we're not in browser shutdown, so that TabContents
2428 // objects that are not in a browser (e.g., HTML dialogs) and thus are 2427 // objects that are not in a browser (e.g., HTML dialogs) and thus are
2429 // visible do not flash a sad tab page. 2428 // visible do not flash a sad tab page.
2430 if (browser_shutdown::GetShutdownType() == browser_shutdown::NOT_VALID) 2429 if (browser_shutdown::GetShutdownType() == browser_shutdown::NOT_VALID)
2431 view_->OnTabCrashed(); 2430 view_->OnTabCrashed();
2432 2431
2433 // Hide any visible hung renderer warning for this web contents' process. 2432 // Hide any visible hung renderer warning for this web contents' process.
2434 hung_renderer_dialog::HideForTabContents(this); 2433 hung_renderer_dialog::HideForTabContents(this);
(...skipping 898 matching lines...) Expand 10 before | Expand all | Expand 10 after
3333 AddInfoBar(new SavePasswordInfoBarDelegate(this, form_to_save)); 3332 AddInfoBar(new SavePasswordInfoBarDelegate(this, form_to_save));
3334 } 3333 }
3335 3334
3336 Profile* TabContents::GetProfileForPasswordManager() { 3335 Profile* TabContents::GetProfileForPasswordManager() {
3337 return profile(); 3336 return profile();
3338 } 3337 }
3339 3338
3340 bool TabContents::DidLastPageLoadEncounterSSLErrors() { 3339 bool TabContents::DidLastPageLoadEncounterSSLErrors() {
3341 return controller().ssl_manager()->ProcessedSSLErrorFromRequest(); 3340 return controller().ssl_manager()->ProcessedSSLErrorFromRequest();
3342 } 3341 }
OLDNEW
« no previous file with comments | « chrome/browser/tab_contents/tab_contents.h ('k') | chrome/browser/tab_contents/test_tab_contents.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698