15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/extensions/permissions/chrome_permission_message_provider.h"
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/stl_util.h"
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/strings/stringprintf.h"
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/grit/generated_resources.h"
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "extensions/common/extensions_client.h"
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/permissions/permission_message.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/permissions/permission_message_util.h"
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/common/permissions/permission_set.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/url_pattern.h"
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/url_pattern_set.h"
16f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "grit/extensions_strings.h"
17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "url/gurl.h"
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
20f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)namespace extensions {
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace {
23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)typedef std::set<PermissionMessage> PermissionMsgSet;
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)template<typename T>
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)typename T::iterator FindMessageByID(T& messages, int id) {
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (typename T::iterator it = messages.begin();
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       it != messages.end(); ++it) {
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (it->id() == id)
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return it;
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return messages.end();
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)template<typename T>
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)typename T::const_iterator FindMessageByID(const T& messages, int id) {
38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (typename T::const_iterator it = messages.begin();
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       it != messages.end(); ++it) {
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (it->id() == id)
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return it;
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return messages.end();
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)template<typename T>
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SuppressMessage(T& messages,
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     int suppressing_message,
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     int suppressed_message) {
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  typename T::iterator suppressed = FindMessageByID(messages,
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                                    suppressed_message);
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (suppressed != messages.end() &&
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FindMessageByID(messages, suppressing_message) != messages.end()) {
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    messages.erase(suppressed);
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool ContainsMessages(const PermissionMessages& messages,
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      int first_message,
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      int second_message) {
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return FindMessageByID(messages, first_message) != messages.end() &&
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)         FindMessageByID(messages, second_message) != messages.end();
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool ContainsMessages(const PermissionMessages& messages,
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      int first_message,
67f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                      int second_message,
68f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                      int third_message) {
69f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return ContainsMessages(messages, first_message, second_message) &&
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         FindMessageByID(messages, third_message) != messages.end();
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}  // namespace
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
75f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ChromePermissionMessageProvider::ChromePermissionMessageProvider() {
76f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)ChromePermissionMessageProvider::~ChromePermissionMessageProvider() {
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)PermissionMessages ChromePermissionMessageProvider::GetPermissionMessages(
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const PermissionSet* permissions,
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    Manifest::Type extension_type) const {
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  PermissionMessages messages;
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (permissions->HasEffectiveFullAccess()) {
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    messages.push_back(PermissionMessage(
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        PermissionMessage::kFullAccess,
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS)));
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return messages;
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
92f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Some warnings are more generic and/or powerful and superseed other
93f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // warnings. In that case, the first message suppresses the second one.
94f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  std::multimap<PermissionMessage::ID, PermissionMessage::ID> kSuppressList;
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  kSuppressList.insert(
96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      {PermissionMessage::kBluetooth, PermissionMessage::kBluetoothDevices});
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  kSuppressList.insert(
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      {PermissionMessage::kBookmarks, PermissionMessage::kOverrideBookmarksUI});
99f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // History already allows reading favicons.
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  kSuppressList.insert(
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      {PermissionMessage::kBrowsingHistory, PermissionMessage::kFavicon});
102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // History already allows tabs access.
103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  kSuppressList.insert(
104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      {PermissionMessage::kBrowsingHistory, PermissionMessage::kTabs});
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // History already allows access the list of most frequently visited sites.
106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  kSuppressList.insert(
107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      {PermissionMessage::kBrowsingHistory, PermissionMessage::kTopSites});
108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // A special hack: If kFileSystemWriteDirectory would be displayed, hide
109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // kFileSystemDirectory as the write directory message implies it.
110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // TODO(sammc): Remove this. See http://crbug.com/284849.
111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  kSuppressList.insert({PermissionMessage::kFileSystemWriteDirectory,
112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                        PermissionMessage::kFileSystemDirectory});
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Full access already allows DeclarativeWebRequest.
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  kSuppressList.insert({PermissionMessage::kHostsAll,
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        PermissionMessage::kDeclarativeWebRequest});
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Full access implies reading the list of most frequently visited sites.
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  kSuppressList.insert(
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      {PermissionMessage::kHostsAll, PermissionMessage::kTopSites});
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Full access already covers tabs access.
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  kSuppressList.insert(
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      {PermissionMessage::kHostsAll, PermissionMessage::kTabs});
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Tabs already allows reading favicons.
1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  kSuppressList.insert({PermissionMessage::kTabs, PermissionMessage::kFavicon});
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Tabs already allows reading the list of most frequently visited sites.
1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  kSuppressList.insert(
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      {PermissionMessage::kTabs, PermissionMessage::kTopSites});
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PermissionMsgSet host_msgs =
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      GetHostPermissionMessages(permissions, extension_type);
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PermissionMsgSet api_msgs = GetAPIPermissionMessages(permissions);
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PermissionMsgSet manifest_permission_msgs =
132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      GetManifestPermissionMessages(permissions);
133f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  messages.insert(messages.end(), host_msgs.begin(), host_msgs.end());
134f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  messages.insert(messages.end(), api_msgs.begin(), api_msgs.end());
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  messages.insert(messages.end(), manifest_permission_msgs.begin(),
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                  manifest_permission_msgs.end());
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (std::multimap<PermissionMessage::ID,
139                     PermissionMessage::ID>::const_iterator it =
140           kSuppressList.begin();
141       it != kSuppressList.end();
142       ++it) {
143    SuppressMessage(messages, it->first, it->second);
144  }
145
146  return messages;
147}
148
149std::vector<base::string16> ChromePermissionMessageProvider::GetWarningMessages(
150    const PermissionSet* permissions,
151    Manifest::Type extension_type) const {
152  std::vector<base::string16> message_strings;
153  PermissionMessages messages =
154      GetPermissionMessages(permissions, extension_type);
155
156  for (PermissionMessages::const_iterator i = messages.begin();
157       i != messages.end(); ++i) {
158    int id = i->id();
159    // Access to users' devices should provide a single warning message
160    // specifying the transport method used; USB, serial and/or Bluetooth.
161    if (id == PermissionMessage::kBluetooth ||
162        id == PermissionMessage::kSerial ||
163        id == PermissionMessage::kUsb) {
164      if (ContainsMessages(messages,
165                           PermissionMessage::kBluetooth,
166                           PermissionMessage::kSerial,
167                           PermissionMessage::kUsb)) {
168        if (id == PermissionMessage::kBluetooth) {
169          message_strings.push_back(l10n_util::GetStringUTF16(
170              IDS_EXTENSION_PROMPT_WARNING_ALL_DEVICES));
171        }
172        continue;
173      }
174      if (ContainsMessages(messages,
175                           PermissionMessage::kBluetooth,
176                           PermissionMessage::kUsb)) {
177        if (id == PermissionMessage::kBluetooth) {
178          message_strings.push_back(l10n_util::GetStringUTF16(
179              IDS_EXTENSION_PROMPT_WARNING_USB_BLUETOOTH));
180        }
181        continue;
182      }
183      if (ContainsMessages(messages,
184                           PermissionMessage::kSerial,
185                           PermissionMessage::kUsb)) {
186        if (id == PermissionMessage::kSerial) {
187          message_strings.push_back(l10n_util::GetStringUTF16(
188              IDS_EXTENSION_PROMPT_WARNING_USB_SERIAL));
189        }
190        continue;
191      }
192      if (ContainsMessages(messages,
193                           PermissionMessage::kBluetooth,
194                           PermissionMessage::kSerial)) {
195        if (id == PermissionMessage::kBluetooth) {
196          message_strings.push_back(l10n_util::GetStringUTF16(
197              IDS_EXTENSION_PROMPT_WARNING_BLUETOOTH_SERIAL));
198        }
199        continue;
200      }
201    }
202    if (id == PermissionMessage::kAccessibilityFeaturesModify ||
203        id == PermissionMessage::kAccessibilityFeaturesRead) {
204      if (ContainsMessages(messages,
205                           PermissionMessage::kAccessibilityFeaturesModify,
206                           PermissionMessage::kAccessibilityFeaturesRead)) {
207        if (id == PermissionMessage::kAccessibilityFeaturesModify) {
208          message_strings.push_back(l10n_util::GetStringUTF16(
209              IDS_EXTENSION_PROMPT_WARNING_ACCESSIBILITY_FEATURES_READ_MODIFY));
210        }
211        continue;
212      }
213    }
214    if (id == PermissionMessage::kAudioCapture ||
215        id == PermissionMessage::kVideoCapture) {
216      if (ContainsMessages(messages,
217                           PermissionMessage::kAudioCapture,
218                           PermissionMessage::kVideoCapture)) {
219        if (id == PermissionMessage::kAudioCapture) {
220          message_strings.push_back(l10n_util::GetStringUTF16(
221              IDS_EXTENSION_PROMPT_WARNING_AUDIO_AND_VIDEO_CAPTURE));
222        }
223        continue;
224      }
225    }
226    if (id == PermissionMessage::kMediaGalleriesAllGalleriesCopyTo ||
227        id == PermissionMessage::kMediaGalleriesAllGalleriesDelete ||
228        id == PermissionMessage::kMediaGalleriesAllGalleriesRead) {
229      if (ContainsMessages(
230              messages,
231              PermissionMessage::kMediaGalleriesAllGalleriesCopyTo,
232              PermissionMessage::kMediaGalleriesAllGalleriesDelete,
233              PermissionMessage::kMediaGalleriesAllGalleriesRead)) {
234        if (id == PermissionMessage::kMediaGalleriesAllGalleriesCopyTo) {
235          message_strings.push_back(l10n_util::GetStringUTF16(
236              IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_WRITE_DELETE));
237        }
238        continue;
239      }
240      if (ContainsMessages(
241              messages,
242              PermissionMessage::kMediaGalleriesAllGalleriesCopyTo,
243              PermissionMessage::kMediaGalleriesAllGalleriesRead)) {
244        if (id == PermissionMessage::kMediaGalleriesAllGalleriesCopyTo) {
245          message_strings.push_back(l10n_util::GetStringUTF16(
246              IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_WRITE));
247        }
248        continue;
249      }
250      if (ContainsMessages(
251              messages,
252              PermissionMessage::kMediaGalleriesAllGalleriesDelete,
253              PermissionMessage::kMediaGalleriesAllGalleriesRead)) {
254        if (id == PermissionMessage::kMediaGalleriesAllGalleriesDelete) {
255          message_strings.push_back(l10n_util::GetStringUTF16(
256              IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_READ_DELETE));
257        }
258        continue;
259      }
260    }
261    if (permissions->HasAPIPermission(APIPermission::kSessions) &&
262        id == PermissionMessage::kTabs) {
263      message_strings.push_back(l10n_util::GetStringUTF16(
264          IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ_AND_SESSIONS));
265      continue;
266    }
267    if (permissions->HasAPIPermission(APIPermission::kSessions) &&
268        id == PermissionMessage::kBrowsingHistory) {
269      message_strings.push_back(l10n_util::GetStringUTF16(
270          IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE_AND_SESSIONS));
271      continue;
272    }
273
274    message_strings.push_back(i->message());
275  }
276
277  return message_strings;
278}
279
280std::vector<base::string16>
281ChromePermissionMessageProvider::GetWarningMessagesDetails(
282    const PermissionSet* permissions,
283    Manifest::Type extension_type) const {
284  std::vector<base::string16> message_strings;
285  PermissionMessages messages =
286      GetPermissionMessages(permissions, extension_type);
287
288  for (PermissionMessages::const_iterator i = messages.begin();
289       i != messages.end(); ++i)
290    message_strings.push_back(i->details());
291
292  return message_strings;
293}
294
295bool ChromePermissionMessageProvider::IsPrivilegeIncrease(
296    const PermissionSet* old_permissions,
297    const PermissionSet* new_permissions,
298    Manifest::Type extension_type) const {
299  // Things can't get worse than native code access.
300  if (old_permissions->HasEffectiveFullAccess())
301    return false;
302
303  // Otherwise, it's a privilege increase if the new one has full access.
304  if (new_permissions->HasEffectiveFullAccess())
305    return true;
306
307  if (IsHostPrivilegeIncrease(old_permissions, new_permissions, extension_type))
308    return true;
309
310  if (IsAPIPrivilegeIncrease(old_permissions, new_permissions))
311    return true;
312
313  if (IsManifestPermissionPrivilegeIncrease(old_permissions, new_permissions))
314    return true;
315
316  return false;
317}
318
319std::set<PermissionMessage>
320ChromePermissionMessageProvider::GetAPIPermissionMessages(
321    const PermissionSet* permissions) const {
322  PermissionMsgSet messages;
323  for (APIPermissionSet::const_iterator permission_it =
324           permissions->apis().begin();
325       permission_it != permissions->apis().end(); ++permission_it) {
326    if (permission_it->HasMessages()) {
327      PermissionMessages new_messages = permission_it->GetMessages();
328      messages.insert(new_messages.begin(), new_messages.end());
329    }
330  }
331
332  // A special hack: The warning message for declarativeWebRequest
333  // permissions speaks about blocking parts of pages, which is a
334  // subset of what the "<all_urls>" access allows. Therefore we
335  // display only the "<all_urls>" warning message if both permissions
336  // are required.
337  if (permissions->ShouldWarnAllHosts()) {
338    messages.erase(
339        PermissionMessage(
340            PermissionMessage::kDeclarativeWebRequest, base::string16()));
341  }
342  return messages;
343}
344
345std::set<PermissionMessage>
346ChromePermissionMessageProvider::GetManifestPermissionMessages(
347    const PermissionSet* permissions) const {
348  PermissionMsgSet messages;
349  for (ManifestPermissionSet::const_iterator permission_it =
350           permissions->manifest_permissions().begin();
351      permission_it != permissions->manifest_permissions().end();
352      ++permission_it) {
353    if (permission_it->HasMessages()) {
354      PermissionMessages new_messages = permission_it->GetMessages();
355      messages.insert(new_messages.begin(), new_messages.end());
356    }
357  }
358  return messages;
359}
360
361std::set<PermissionMessage>
362ChromePermissionMessageProvider::GetHostPermissionMessages(
363    const PermissionSet* permissions,
364    Manifest::Type extension_type) const {
365  PermissionMsgSet messages;
366  // Since platform apps always use isolated storage, they can't (silently)
367  // access user data on other domains, so there's no need to prompt.
368  // Note: this must remain consistent with IsHostPrivilegeIncrease.
369  // See crbug.com/255229.
370  if (extension_type == Manifest::TYPE_PLATFORM_APP)
371    return messages;
372
373  if (permissions->ShouldWarnAllHosts()) {
374    messages.insert(PermissionMessage(
375        PermissionMessage::kHostsAll,
376        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS)));
377  } else {
378    URLPatternSet regular_hosts;
379    ExtensionsClient::Get()->FilterHostPermissions(
380        permissions->effective_hosts(), &regular_hosts, &messages);
381
382    std::set<std::string> hosts =
383        permission_message_util::GetDistinctHosts(regular_hosts, true, true);
384    if (!hosts.empty())
385      messages.insert(permission_message_util::CreateFromHostList(hosts));
386  }
387  return messages;
388}
389
390bool ChromePermissionMessageProvider::IsAPIPrivilegeIncrease(
391    const PermissionSet* old_permissions,
392    const PermissionSet* new_permissions) const {
393  if (new_permissions == NULL)
394    return false;
395
396  PermissionMsgSet old_warnings = GetAPIPermissionMessages(old_permissions);
397  PermissionMsgSet new_warnings = GetAPIPermissionMessages(new_permissions);
398  PermissionMsgSet delta_warnings =
399      base::STLSetDifference<PermissionMsgSet>(new_warnings, old_warnings);
400
401  // A special hack: kFileSystemWriteDirectory implies kFileSystemDirectory.
402  // TODO(sammc): Remove this. See http://crbug.com/284849.
403  if (old_warnings.find(PermissionMessage(
404          PermissionMessage::kFileSystemWriteDirectory, base::string16())) !=
405      old_warnings.end()) {
406    delta_warnings.erase(
407        PermissionMessage(PermissionMessage::kFileSystemDirectory,
408                          base::string16()));
409  }
410
411  // It is a privilege increase if there are additional warnings present.
412  return !delta_warnings.empty();
413}
414
415bool ChromePermissionMessageProvider::IsManifestPermissionPrivilegeIncrease(
416    const PermissionSet* old_permissions,
417    const PermissionSet* new_permissions) const {
418  if (new_permissions == NULL)
419    return false;
420
421  PermissionMsgSet old_warnings =
422      GetManifestPermissionMessages(old_permissions);
423  PermissionMsgSet new_warnings =
424      GetManifestPermissionMessages(new_permissions);
425  PermissionMsgSet delta_warnings =
426      base::STLSetDifference<PermissionMsgSet>(new_warnings, old_warnings);
427
428  // It is a privilege increase if there are additional warnings present.
429  return !delta_warnings.empty();
430}
431
432bool ChromePermissionMessageProvider::IsHostPrivilegeIncrease(
433    const PermissionSet* old_permissions,
434    const PermissionSet* new_permissions,
435    Manifest::Type extension_type) const {
436  // Platform apps host permission changes do not count as privilege increases.
437  // Note: this must remain consistent with GetHostPermissionMessages.
438  if (extension_type == Manifest::TYPE_PLATFORM_APP)
439    return false;
440
441  // If the old permission set can access any host, then it can't be elevated.
442  if (old_permissions->HasEffectiveAccessToAllHosts())
443    return false;
444
445  // Likewise, if the new permission set has full host access, then it must be
446  // a privilege increase.
447  if (new_permissions->HasEffectiveAccessToAllHosts())
448    return true;
449
450  const URLPatternSet& old_list = old_permissions->effective_hosts();
451  const URLPatternSet& new_list = new_permissions->effective_hosts();
452
453  // TODO(jstritar): This is overly conservative with respect to subdomains.
454  // For example, going from *.google.com to www.google.com will be
455  // considered an elevation, even though it is not (http://crbug.com/65337).
456  std::set<std::string> new_hosts_set(
457      permission_message_util::GetDistinctHosts(new_list, false, false));
458  std::set<std::string> old_hosts_set(
459      permission_message_util::GetDistinctHosts(old_list, false, false));
460  std::set<std::string> new_hosts_only =
461      base::STLSetDifference<std::set<std::string> >(new_hosts_set,
462                                                     old_hosts_set);
463
464  return !new_hosts_only.empty();
465}
466
467}  // namespace extensions
468