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
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/views/collected_cookies_views.h"
6
7#include "base/prefs/pref_service.h"
8#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
9#include "chrome/browser/browsing_data/browsing_data_channel_id_helper.h"
10#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
11#include "chrome/browser/browsing_data/browsing_data_database_helper.h"
12#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
13#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
14#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
15#include "chrome/browser/browsing_data/cookies_tree_model.h"
16#include "chrome/browser/chrome_notification_types.h"
17#include "chrome/browser/content_settings/cookie_settings.h"
18#include "chrome/browser/content_settings/local_shared_objects_container.h"
19#include "chrome/browser/content_settings/tab_specific_content_settings.h"
20#include "chrome/browser/infobars/infobar_service.h"
21#include "chrome/browser/profiles/profile.h"
22#include "chrome/browser/ui/collected_cookies_infobar_delegate.h"
23#include "chrome/browser/ui/views/constrained_window_views.h"
24#include "chrome/browser/ui/views/cookie_info_view.h"
25#include "chrome/common/pref_names.h"
26#include "chrome/grit/generated_resources.h"
27#include "chrome/grit/locale_settings.h"
28#include "content/public/browser/notification_details.h"
29#include "content/public/browser/notification_source.h"
30#include "content/public/browser/web_contents.h"
31#include "grit/theme_resources.h"
32#include "net/cookies/canonical_cookie.h"
33#include "ui/base/l10n/l10n_util.h"
34#include "ui/base/resource/resource_bundle.h"
35#include "ui/gfx/color_utils.h"
36#include "ui/views/border.h"
37#include "ui/views/controls/button/label_button.h"
38#include "ui/views/controls/image_view.h"
39#include "ui/views/controls/label.h"
40#include "ui/views/controls/scroll_view.h"
41#include "ui/views/controls/tabbed_pane/tabbed_pane.h"
42#include "ui/views/controls/tree/tree_view.h"
43#include "ui/views/layout/box_layout.h"
44#include "ui/views/layout/grid_layout.h"
45#include "ui/views/layout/layout_constants.h"
46#include "ui/views/widget/widget.h"
47
48namespace chrome {
49
50// Declared in browser_dialogs.h so others don't have to depend on our header.
51void ShowCollectedCookiesDialog(content::WebContents* web_contents) {
52  // Deletes itself on close.
53  new CollectedCookiesViews(web_contents);
54}
55
56}  // namespace chrome
57
58namespace {
59
60// Spacing between the infobar frame and its contents.
61const int kInfobarVerticalPadding = 3;
62const int kInfobarHorizontalPadding = 8;
63
64// Width of the infobar frame.
65const int kInfobarBorderSize = 1;
66
67// Dimensions of the tree views.
68const int kTreeViewWidth = 400;
69const int kTreeViewHeight = 125;
70
71// The color of the border around the cookies tree view.
72const SkColor kCookiesBorderColor = SkColorSetRGB(0xC8, 0xC8, 0xC8);
73
74// Spacing constants used with the new dialog style.
75const int kTabbedPaneTopPadding = 14;
76const int kLabelBottomPadding = 17;
77const int kCookieInfoBottomPadding = 4;
78const int kVPanelPadding = 15;
79
80}  // namespace
81
82// A custom view that conditionally displays an infobar.
83class InfobarView : public views::View {
84 public:
85  InfobarView() {
86    content_ = new views::View;
87    SkColor border_color = SK_ColorGRAY;
88    content_->SetBorder(
89        views::Border::CreateSolidBorder(kInfobarBorderSize, border_color));
90
91    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
92    info_image_ = new views::ImageView();
93    info_image_->SetImage(rb.GetImageSkiaNamed(IDR_INFO));
94    label_ = new views::Label();
95  }
96  virtual ~InfobarView() {}
97
98  // Update the visibility of the infobar. If |is_visible| is true, a rule for
99  // |setting| on |domain_name| was created.
100  void UpdateVisibility(bool is_visible,
101                        ContentSetting setting,
102                        const base::string16& domain_name) {
103    if (!is_visible) {
104      SetVisible(false);
105      return;
106    }
107
108    base::string16 label;
109    switch (setting) {
110      case CONTENT_SETTING_BLOCK:
111        label = l10n_util::GetStringFUTF16(
112            IDS_COLLECTED_COOKIES_BLOCK_RULE_CREATED, domain_name);
113        break;
114
115      case CONTENT_SETTING_ALLOW:
116        label = l10n_util::GetStringFUTF16(
117            IDS_COLLECTED_COOKIES_ALLOW_RULE_CREATED, domain_name);
118        break;
119
120      case CONTENT_SETTING_SESSION_ONLY:
121        label = l10n_util::GetStringFUTF16(
122            IDS_COLLECTED_COOKIES_SESSION_RULE_CREATED, domain_name);
123        break;
124
125      default:
126        NOTREACHED();
127    }
128    label_->SetText(label);
129    content_->Layout();
130    SetVisible(true);
131  }
132
133 private:
134  // Initialize contents and layout.
135  void Init() {
136    AddChildView(content_);
137    content_->SetLayoutManager(
138        new views::BoxLayout(views::BoxLayout::kHorizontal,
139                             kInfobarHorizontalPadding,
140                             kInfobarVerticalPadding,
141                             views::kRelatedControlSmallHorizontalSpacing));
142    content_->AddChildView(info_image_);
143    content_->AddChildView(label_);
144    UpdateVisibility(false, CONTENT_SETTING_BLOCK, base::string16());
145  }
146
147  // views::View overrides.
148  virtual gfx::Size GetPreferredSize() const OVERRIDE {
149    if (!visible())
150      return gfx::Size();
151
152    // Add space around the banner.
153    gfx::Size size(content_->GetPreferredSize());
154    size.Enlarge(0, 2 * views::kRelatedControlVerticalSpacing);
155    return size;
156  }
157
158  virtual void Layout() OVERRIDE {
159    content_->SetBounds(
160        0, views::kRelatedControlVerticalSpacing,
161        width(), height() - views::kRelatedControlVerticalSpacing);
162  }
163
164  virtual void ViewHierarchyChanged(
165      const ViewHierarchyChangedDetails& details) OVERRIDE {
166    if (details.is_add && details.child == this)
167      Init();
168  }
169
170  // Holds the info icon image and text label and renders the border.
171  views::View* content_;
172  // Info icon image.
173  views::ImageView* info_image_;
174  // The label responsible for rendering the text.
175  views::Label* label_;
176
177  DISALLOW_COPY_AND_ASSIGN(InfobarView);
178};
179
180///////////////////////////////////////////////////////////////////////////////
181// CollectedCookiesViews, public:
182
183CollectedCookiesViews::CollectedCookiesViews(content::WebContents* web_contents)
184    : web_contents_(web_contents),
185      allowed_label_(NULL),
186      blocked_label_(NULL),
187      allowed_cookies_tree_(NULL),
188      blocked_cookies_tree_(NULL),
189      block_allowed_button_(NULL),
190      delete_allowed_button_(NULL),
191      allow_blocked_button_(NULL),
192      for_session_blocked_button_(NULL),
193      cookie_info_view_(NULL),
194      infobar_(NULL),
195      status_changed_(false) {
196  TabSpecificContentSettings* content_settings =
197      TabSpecificContentSettings::FromWebContents(web_contents);
198  registrar_.Add(this, chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN,
199                 content::Source<TabSpecificContentSettings>(content_settings));
200  ShowWebModalDialogViews(this, web_contents);
201}
202
203///////////////////////////////////////////////////////////////////////////////
204// CollectedCookiesViews, views::DialogDelegate implementation:
205
206base::string16 CollectedCookiesViews::GetWindowTitle() const {
207  return l10n_util::GetStringUTF16(IDS_COLLECTED_COOKIES_DIALOG_TITLE);
208}
209
210int CollectedCookiesViews::GetDialogButtons() const {
211  return ui::DIALOG_BUTTON_CANCEL;
212}
213
214base::string16 CollectedCookiesViews::GetDialogButtonLabel(
215    ui::DialogButton button) const {
216  return l10n_util::GetStringUTF16(IDS_CLOSE);
217}
218
219bool CollectedCookiesViews::Cancel() {
220  if (status_changed_) {
221    CollectedCookiesInfoBarDelegate::Create(
222        InfoBarService::FromWebContents(web_contents_));
223  }
224  return true;
225}
226
227ui::ModalType CollectedCookiesViews::GetModalType() const {
228  return ui::MODAL_TYPE_CHILD;
229}
230
231///////////////////////////////////////////////////////////////////////////////
232// CollectedCookiesViews, views::ButtonListener implementation:
233
234void CollectedCookiesViews::ButtonPressed(views::Button* sender,
235                                          const ui::Event& event) {
236  if (sender == block_allowed_button_) {
237    AddContentException(allowed_cookies_tree_, CONTENT_SETTING_BLOCK);
238  } else if (sender == delete_allowed_button_) {
239    allowed_cookies_tree_model_->DeleteCookieNode(
240        static_cast<CookieTreeNode*>(allowed_cookies_tree_->GetSelectedNode()));
241  } else if (sender == allow_blocked_button_) {
242    AddContentException(blocked_cookies_tree_, CONTENT_SETTING_ALLOW);
243  } else if (sender == for_session_blocked_button_) {
244    AddContentException(blocked_cookies_tree_, CONTENT_SETTING_SESSION_ONLY);
245  }
246}
247
248///////////////////////////////////////////////////////////////////////////////
249// CollectedCookiesViews, views::TabbedPaneListener implementation:
250
251void CollectedCookiesViews::TabSelectedAt(int index) {
252  EnableControls();
253  ShowCookieInfo();
254}
255
256///////////////////////////////////////////////////////////////////////////////
257// CollectedCookiesViews, views::TreeViewController implementation:
258
259void CollectedCookiesViews::OnTreeViewSelectionChanged(
260    views::TreeView* tree_view) {
261  EnableControls();
262  ShowCookieInfo();
263}
264
265///////////////////////////////////////////////////////////////////////////////
266// CollectedCookiesViews, views::View overrides:
267
268gfx::Size CollectedCookiesViews::GetMinimumSize() const {
269  // Allow UpdateWebContentsModalDialogPosition to clamp the dialog width.
270  return gfx::Size(0, View::GetMinimumSize().height());
271}
272
273void CollectedCookiesViews::ViewHierarchyChanged(
274    const ViewHierarchyChangedDetails& details) {
275  if (details.is_add && details.child == this)
276    Init();
277}
278
279////////////////////////////////////////////////////////////////////////////////
280// CollectedCookiesViews, private:
281
282CollectedCookiesViews::~CollectedCookiesViews() {
283  allowed_cookies_tree_->SetModel(NULL);
284  blocked_cookies_tree_->SetModel(NULL);
285}
286
287void CollectedCookiesViews::Init() {
288  using views::GridLayout;
289
290  GridLayout* layout = GridLayout::CreatePanel(this);
291  SetLayoutManager(layout);
292
293  const int single_column_layout_id = 0;
294  views::ColumnSet* column_set = layout->AddColumnSet(single_column_layout_id);
295  column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
296                        GridLayout::USE_PREF, 0, 0);
297
298  layout->StartRow(0, single_column_layout_id);
299  views::TabbedPane* tabbed_pane = new views::TabbedPane();
300  layout->SetInsets(gfx::Insets(kTabbedPaneTopPadding, 0, 0, 0));
301
302  layout->AddView(tabbed_pane);
303  // NOTE: Panes must be added after |tabbed_pane| has been added to its parent.
304  base::string16 label_allowed = l10n_util::GetStringUTF16(
305      IDS_COLLECTED_COOKIES_ALLOWED_COOKIES_TAB_LABEL);
306  base::string16 label_blocked = l10n_util::GetStringUTF16(
307      IDS_COLLECTED_COOKIES_BLOCKED_COOKIES_TAB_LABEL);
308  tabbed_pane->AddTab(label_allowed, CreateAllowedPane());
309  tabbed_pane->AddTab(label_blocked, CreateBlockedPane());
310  tabbed_pane->SelectTabAt(0);
311  tabbed_pane->set_listener(this);
312  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
313
314  layout->StartRow(0, single_column_layout_id);
315  cookie_info_view_ = new CookieInfoView();
316  layout->AddView(cookie_info_view_);
317  layout->AddPaddingRow(0, kCookieInfoBottomPadding);
318
319  layout->StartRow(0, single_column_layout_id);
320  infobar_ = new InfobarView();
321  layout->AddView(infobar_);
322
323  EnableControls();
324  ShowCookieInfo();
325}
326
327views::View* CollectedCookiesViews::CreateAllowedPane() {
328  TabSpecificContentSettings* content_settings =
329      TabSpecificContentSettings::FromWebContents(web_contents_);
330
331  // Create the controls that go into the pane.
332  allowed_label_ = new views::Label(l10n_util::GetStringUTF16(
333      IDS_COLLECTED_COOKIES_ALLOWED_COOKIES_LABEL));
334
335  const LocalSharedObjectsContainer& allowed_data =
336      content_settings->allowed_local_shared_objects();
337  allowed_cookies_tree_model_ = allowed_data.CreateCookiesTreeModel();
338  allowed_cookies_tree_ = new views::TreeView();
339  allowed_cookies_tree_->SetModel(allowed_cookies_tree_model_.get());
340  allowed_cookies_tree_->SetRootShown(false);
341  allowed_cookies_tree_->SetEditable(false);
342  allowed_cookies_tree_->set_auto_expand_children(true);
343  allowed_cookies_tree_->SetController(this);
344
345  block_allowed_button_ = new views::LabelButton(this,
346      l10n_util::GetStringUTF16(IDS_COLLECTED_COOKIES_BLOCK_BUTTON));
347  block_allowed_button_->SetStyle(views::Button::STYLE_BUTTON);
348
349  delete_allowed_button_ = new views::LabelButton(this,
350      l10n_util::GetStringUTF16(IDS_COOKIES_REMOVE_LABEL));
351  delete_allowed_button_->SetStyle(views::Button::STYLE_BUTTON);
352
353  // Create the view that holds all the controls together.  This will be the
354  // pane added to the tabbed pane.
355  using views::GridLayout;
356
357  views::View* pane = new views::View();
358  GridLayout* layout = GridLayout::CreatePanel(pane);
359  layout->SetInsets(kVPanelPadding, views::kButtonHEdgeMarginNew,
360                    kVPanelPadding, views::kButtonHEdgeMarginNew);
361  pane->SetLayoutManager(layout);
362
363  const int single_column_layout_id = 0;
364  views::ColumnSet* column_set = layout->AddColumnSet(single_column_layout_id);
365  column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
366                        GridLayout::USE_PREF, 0, 0);
367
368  const int three_columns_layout_id = 1;
369  column_set = layout->AddColumnSet(three_columns_layout_id);
370  column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
371                        GridLayout::USE_PREF, 0, 0);
372  column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
373  column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
374                        GridLayout::USE_PREF, 0, 0);
375
376  layout->StartRow(0, single_column_layout_id);
377  layout->AddView(allowed_label_);
378  layout->AddPaddingRow(0, kLabelBottomPadding);
379
380  layout->StartRow(1, single_column_layout_id);
381  layout->AddView(CreateScrollView(allowed_cookies_tree_), 1, 1,
382                  GridLayout::FILL, GridLayout::FILL, kTreeViewWidth,
383                  kTreeViewHeight);
384  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
385
386  layout->StartRow(0, three_columns_layout_id);
387  layout->AddView(block_allowed_button_);
388  layout->AddView(delete_allowed_button_);
389
390  return pane;
391}
392
393views::View* CollectedCookiesViews::CreateBlockedPane() {
394  TabSpecificContentSettings* content_settings =
395      TabSpecificContentSettings::FromWebContents(web_contents_);
396
397  Profile* profile =
398      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
399  PrefService* prefs = profile->GetPrefs();
400
401  // Create the controls that go into the pane.
402  blocked_label_ = new views::Label(
403      l10n_util::GetStringUTF16(
404          prefs->GetBoolean(prefs::kBlockThirdPartyCookies) ?
405              IDS_COLLECTED_COOKIES_BLOCKED_THIRD_PARTY_BLOCKING_ENABLED :
406              IDS_COLLECTED_COOKIES_BLOCKED_COOKIES_LABEL));
407  blocked_label_->SetMultiLine(true);
408  blocked_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
409  const LocalSharedObjectsContainer& blocked_data =
410      content_settings->blocked_local_shared_objects();
411  blocked_cookies_tree_model_ = blocked_data.CreateCookiesTreeModel();
412  blocked_cookies_tree_ = new views::TreeView();
413  blocked_cookies_tree_->SetModel(blocked_cookies_tree_model_.get());
414  blocked_cookies_tree_->SetRootShown(false);
415  blocked_cookies_tree_->SetEditable(false);
416  blocked_cookies_tree_->set_auto_expand_children(true);
417  blocked_cookies_tree_->SetController(this);
418
419  allow_blocked_button_ = new views::LabelButton(this,
420      l10n_util::GetStringUTF16(IDS_COLLECTED_COOKIES_ALLOW_BUTTON));
421  allow_blocked_button_->SetStyle(views::Button::STYLE_BUTTON);
422  for_session_blocked_button_ = new views::LabelButton(this,
423      l10n_util::GetStringUTF16(IDS_COLLECTED_COOKIES_SESSION_ONLY_BUTTON));
424  for_session_blocked_button_->SetStyle(views::Button::STYLE_BUTTON);
425
426  // Create the view that holds all the controls together.  This will be the
427  // pane added to the tabbed pane.
428  using views::GridLayout;
429
430  views::View* pane = new views::View();
431  GridLayout* layout = GridLayout::CreatePanel(pane);
432  layout->SetInsets(kVPanelPadding, views::kButtonHEdgeMarginNew,
433                    kVPanelPadding, views::kButtonHEdgeMarginNew);
434  pane->SetLayoutManager(layout);
435
436  const int single_column_layout_id = 0;
437  views::ColumnSet* column_set = layout->AddColumnSet(single_column_layout_id);
438  column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
439                        GridLayout::USE_PREF, 0, 0);
440
441  const int three_columns_layout_id = 1;
442  column_set = layout->AddColumnSet(three_columns_layout_id);
443  column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
444                        GridLayout::USE_PREF, 0, 0);
445  column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
446  column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
447                        GridLayout::USE_PREF, 0, 0);
448
449  layout->StartRow(0, single_column_layout_id);
450  layout->AddView(blocked_label_, 1, 1, GridLayout::FILL, GridLayout::FILL);
451  layout->AddPaddingRow(0, kLabelBottomPadding);
452
453  layout->StartRow(1, single_column_layout_id);
454  layout->AddView(
455      CreateScrollView(blocked_cookies_tree_), 1, 1,
456      GridLayout::FILL, GridLayout::FILL, kTreeViewWidth, kTreeViewHeight);
457  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
458
459  layout->StartRow(0, three_columns_layout_id);
460  layout->AddView(allow_blocked_button_);
461  layout->AddView(for_session_blocked_button_);
462
463  return pane;
464}
465
466views::View* CollectedCookiesViews::CreateScrollView(views::TreeView* pane) {
467  views::ScrollView* scroll_view = new views::ScrollView();
468  scroll_view->SetContents(pane);
469  scroll_view->SetBorder(
470      views::Border::CreateSolidBorder(1, kCookiesBorderColor));
471  return scroll_view;
472}
473
474void CollectedCookiesViews::EnableControls() {
475  bool enable_allowed_buttons = false;
476  ui::TreeModelNode* node = allowed_cookies_tree_->GetSelectedNode();
477  if (node) {
478    CookieTreeNode* cookie_node = static_cast<CookieTreeNode*>(node);
479    if (cookie_node->GetDetailedInfo().node_type ==
480        CookieTreeNode::DetailedInfo::TYPE_HOST) {
481      enable_allowed_buttons = static_cast<CookieTreeHostNode*>(
482          cookie_node)->CanCreateContentException();
483    }
484  }
485  block_allowed_button_->SetEnabled(enable_allowed_buttons);
486  delete_allowed_button_->SetEnabled(node != NULL);
487
488  bool enable_blocked_buttons = false;
489  node = blocked_cookies_tree_->GetSelectedNode();
490  if (node) {
491    CookieTreeNode* cookie_node = static_cast<CookieTreeNode*>(node);
492    if (cookie_node->GetDetailedInfo().node_type ==
493        CookieTreeNode::DetailedInfo::TYPE_HOST) {
494      enable_blocked_buttons = static_cast<CookieTreeHostNode*>(
495          cookie_node)->CanCreateContentException();
496    }
497  }
498  allow_blocked_button_->SetEnabled(enable_blocked_buttons);
499  for_session_blocked_button_->SetEnabled(enable_blocked_buttons);
500}
501
502void CollectedCookiesViews::ShowCookieInfo() {
503  ui::TreeModelNode* node = allowed_cookies_tree_->GetSelectedNode();
504  if (!node)
505    node = blocked_cookies_tree_->GetSelectedNode();
506
507  if (node) {
508    CookieTreeNode* cookie_node = static_cast<CookieTreeNode*>(node);
509    const CookieTreeNode::DetailedInfo detailed_info =
510        cookie_node->GetDetailedInfo();
511
512    if (detailed_info.node_type == CookieTreeNode::DetailedInfo::TYPE_COOKIE) {
513      cookie_info_view_->SetCookie(detailed_info.cookie->Domain(),
514                                   *detailed_info.cookie);
515    } else {
516      cookie_info_view_->ClearCookieDisplay();
517    }
518  } else {
519    cookie_info_view_->ClearCookieDisplay();
520  }
521}
522
523void CollectedCookiesViews::AddContentException(views::TreeView* tree_view,
524                                                ContentSetting setting) {
525  CookieTreeHostNode* host_node =
526      static_cast<CookieTreeHostNode*>(tree_view->GetSelectedNode());
527  Profile* profile =
528      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
529  host_node->CreateContentException(
530      CookieSettings::Factory::GetForProfile(profile).get(), setting);
531  infobar_->UpdateVisibility(true, setting, host_node->GetTitle());
532  status_changed_ = true;
533}
534
535///////////////////////////////////////////////////////////////////////////////
536// CollectedCookiesViews, content::NotificationObserver implementation:
537
538void CollectedCookiesViews::Observe(
539    int type,
540    const content::NotificationSource& source,
541    const content::NotificationDetails& details) {
542  DCHECK_EQ(chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN, type);
543  GetWidget()->Close();
544}
545