15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef CHROME_BROWSER_EXTENSIONS_WEBSTORE_STANDALONE_INSTALLER_H_ 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define CHROME_BROWSER_EXTENSIONS_WEBSTORE_STANDALONE_INSTALLER_H_ 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/callback.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/ref_counted.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_install_prompt.h" 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/webstore_data_fetcher_delegate.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/webstore_install_helper.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/webstore_installer.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher_delegate.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class GURL; 212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace base { 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class DictionaryValue; 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net { 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class URLFetcher; 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions { 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Extension; 322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class WebstoreDataFetcher; 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// A a purely abstract base for concrete classes implementing various types of 352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// standalone installs: 362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 1) Downloads and parses metadata from the webstore. 372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 2) Optionally shows an install dialog. 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 3) Starts download once the user confirms (if confirmation was requested). 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 4) Optionally shows a post-install UI. 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Follows the Template Method pattern. Implementing subclasses must override 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the primitive hooks in the corresponding section below. 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class WebstoreStandaloneInstaller 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : public base::RefCountedThreadSafe<WebstoreStandaloneInstaller>, 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public ExtensionInstallPrompt::Delegate, 462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public WebstoreDataFetcherDelegate, 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public WebstoreInstaller::Delegate, 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public WebstoreInstallHelper::Delegate { 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // A callback for when the install process completes, successfully or not. If 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // there was a failure, |success| will be false and |error| may contain a 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // developer-readable error message about why it failed. 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typedef base::Callback<void(bool success, const std::string& error)> Callback; 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) WebstoreStandaloneInstaller(const std::string& webstore_item_id, 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Profile* profile, 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const Callback& callback); 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void BeginInstall(); 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) protected: 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual ~WebstoreStandaloneInstaller(); 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void AbortInstall(); 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void CompleteInstall(const std::string& error); 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Template Method's hooks to be implemented by subclasses. 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Called at certain check points of the workflow to decide whether it makes 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // sense to proceed with installation. A requestor can be a website that 702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // initiated an inline installation, or a command line option. 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual bool CheckRequestorAlive() const = 0; 722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Requestor's URL, if any. Should be an empty GURL if URL is meaningless 742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // (e.g. for a command line option). 752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual const GURL& GetRequestorURL() const = 0; 762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Should a new tab be opened after installation to show the newly installed 782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // extension's icon? 792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual bool ShouldShowPostInstallUI() const = 0; 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Should pop up an "App installed" bubble after installation? 822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual bool ShouldShowAppInstalledBubble() const = 0; 832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // In the very least this should return a dummy WebContents (required 852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // by some calls even when no prompt or other UI is shown). A non-dummy 862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // WebContents is required if the prompt returned by CreateInstallPromt() 872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // contains a navigable link(s). Returned WebContents should correspond 882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // to |profile| passed into the constructor. 892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual content::WebContents* GetWebContents() const = 0; 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Should return an installation prompt with desired properties or NULL if 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // no prompt should be shown. 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual scoped_ptr<ExtensionInstallPrompt::Prompt> 942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CreateInstallPrompt() const = 0; 952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Perform all necessary checks to make sure inline install is permitted, 972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // e.g. in the extension's properties in the store. The implementation may 982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // choose to ignore such properties. 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual bool CheckInlineInstallPermitted( 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::DictionaryValue& webstore_data, 1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::string* error) const = 0; 1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Perform all necessary checks to make sure that requestor is allowed to 1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // initiate this install (e.g. that the requestor's URL matches the verified 1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // author's site specified in the extension's properties in the store). 1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual bool CheckRequestorPermitted( 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::DictionaryValue& webstore_data, 1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::string* error) const = 0; 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Returns an install UI to be shown. By default, this returns an install UI 111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // that is a transient child of the host window for GetWebContents(). 112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch virtual scoped_ptr<ExtensionInstallPrompt> CreateInstallUI(); 113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Accessors to be used by subclasses. 115a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) bool show_user_count() const { return show_user_count_; } 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const std::string& localized_user_count() const { 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return localized_user_count_; 1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) double average_rating() const { return average_rating_; } 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int rating_count() const { return rating_count_; } 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) friend class base::RefCountedThreadSafe<WebstoreStandaloneInstaller>; 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FRIEND_TEST_ALL_PREFIXES(WebstoreStandaloneInstallerTest, DomainVerification); 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Several delegate/client interface implementations follow. The normal flow 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // (for successful installs) is: 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1. BeginInstall: starts the fetch of data from the webstore 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 2. OnURLFetchComplete: starts the parsing of data from the webstore 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 3. OnWebstoreResponseParseSuccess: starts the parsing of the manifest and 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // fetching of icon data. 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 4. OnWebstoreParseSuccess: shows the install UI 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 5. InstallUIProceed: initiates the .crx download/install 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // All flows (whether successful or not) end up in CompleteInstall, which 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // informs our delegate of success/failure. 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // WebstoreDataFetcherDelegate interface implementation. 1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual void OnWebstoreRequestFailure() OVERRIDE; 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual void OnWebstoreResponseParseSuccess( 1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::DictionaryValue* webstore_data) OVERRIDE; 1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) virtual void OnWebstoreResponseParseFailure( 1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const std::string& error) OVERRIDE; 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // WebstoreInstallHelper::Delegate interface implementation. 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void OnWebstoreParseSuccess( 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& id, 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const SkBitmap& icon, 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::DictionaryValue* parsed_manifest) OVERRIDE; 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void OnWebstoreParseFailure( 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& id, 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InstallHelperResultCode result_code, 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& error_message) OVERRIDE; 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // ExtensionInstallPrompt::Delegate interface implementation. 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void InstallUIProceed() OVERRIDE; 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void InstallUIAbort(bool user_initiated) OVERRIDE; 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // WebstoreInstaller::Delegate interface implementation. 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void OnExtensionInstallSuccess(const std::string& id) OVERRIDE; 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void OnExtensionInstallFailure( 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& id, 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& error, 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WebstoreInstaller::FailureReason reason) OVERRIDE; 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 169eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch void ShowInstallUI(); 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Input configuration. 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string id_; 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Callback callback_; 1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Profile* profile_; 1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Installation dialog and its underlying prompt. 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<ExtensionInstallPrompt> install_ui_; 1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) scoped_ptr<ExtensionInstallPrompt::Prompt> install_prompt_; 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // For fetching webstore JSON data. 1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) scoped_ptr<WebstoreDataFetcher> webstore_data_fetcher_; 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Extracted from the webstore JSON data response. 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string localized_name_; 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string localized_description_; 186a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) bool show_user_count_; 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string localized_user_count_; 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) double average_rating_; 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int rating_count_; 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<DictionaryValue> webstore_data_; 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<DictionaryValue> manifest_; 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SkBitmap icon_; 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 194eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Created by ShowInstallUI() when a prompt is shown (if 1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // the implementor returns a non-NULL in CreateInstallPrompt()). 1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) scoped_refptr<Extension> localized_extension_for_display_; 1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DISALLOW_IMPLICIT_CONSTRUCTORS(WebstoreStandaloneInstaller); 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace extensions 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif // CHROME_BROWSER_EXTENSIONS_WEBSTORE_STANDALONE_INSTALLER_H_ 204