Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_MASTER_H_
      6 #define CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_MASTER_H_
      7 #pragma once
      8 
      9 #include "base/file_path.h"
     10 #include "base/gtest_prod_util.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/shared_memory.h"
     13 #include "chrome/common/extensions/user_script.h"
     14 #include "content/browser/browser_thread.h"
     15 #include "content/common/notification_observer.h"
     16 #include "content/common/notification_registrar.h"
     17 
     18 namespace base {
     19 class StringPiece;
     20 }
     21 
     22 class Profile;
     23 
     24 // Manages a segment of shared memory that contains the user scripts the user
     25 // has installed.  Lives on the UI thread.
     26 class UserScriptMaster : public base::RefCountedThreadSafe<UserScriptMaster>,
     27                          public NotificationObserver {
     28  public:
     29   // For testability, the constructor takes the path the scripts live in.
     30   // This is normally a directory inside the profile.
     31   explicit UserScriptMaster(const FilePath& script_dir, Profile* profile);
     32 
     33   // Kicks off a process on the file thread to reload scripts from disk
     34   // into a new chunk of shared memory and notify renderers.
     35   virtual void StartScan();
     36 
     37   // Gets the segment of shared memory for the scripts.
     38   base::SharedMemory* GetSharedMemory() const {
     39     return shared_memory_.get();
     40   }
     41 
     42   // Called by the script reloader when new scripts have been loaded.
     43   void NewScriptsAvailable(base::SharedMemory* handle);
     44 
     45   // Return true if we have any scripts ready.
     46   bool ScriptsReady() const { return shared_memory_.get() != NULL; }
     47 
     48   // Returns the path to the directory user scripts are stored in.
     49   FilePath user_script_dir() const { return user_script_dir_; }
     50 
     51  protected:
     52   friend class base::RefCountedThreadSafe<UserScriptMaster>;
     53 
     54   virtual ~UserScriptMaster();
     55 
     56  private:
     57   FRIEND_TEST_ALL_PREFIXES(UserScriptMasterTest, Parse1);
     58   FRIEND_TEST_ALL_PREFIXES(UserScriptMasterTest, Parse2);
     59   FRIEND_TEST_ALL_PREFIXES(UserScriptMasterTest, Parse3);
     60   FRIEND_TEST_ALL_PREFIXES(UserScriptMasterTest, Parse4);
     61   FRIEND_TEST_ALL_PREFIXES(UserScriptMasterTest, Parse5);
     62   FRIEND_TEST_ALL_PREFIXES(UserScriptMasterTest, Parse6);
     63 
     64  public:
     65   // We reload user scripts on the file thread to prevent blocking the UI.
     66   // ScriptReloader lives on the file thread and does the reload
     67   // work, and then sends a message back to its master with a new SharedMemory*.
     68   // ScriptReloader is the worker that manages running the script scan
     69   // on the file thread. It must be created on, and its public API must only be
     70   // called from, the master's thread.
     71   class ScriptReloader
     72       : public base::RefCountedThreadSafe<UserScriptMaster::ScriptReloader> {
     73    public:
     74     // Parses the includes out of |script| and returns them in |includes|.
     75     static bool ParseMetadataHeader(const base::StringPiece& script_text,
     76                                     UserScript* script);
     77 
     78     static void LoadScriptsFromDirectory(const FilePath& script_dir,
     79                                          UserScriptList* result);
     80 
     81     explicit ScriptReloader(UserScriptMaster* master);
     82 
     83     // Start a scan for scripts.
     84     // Will always send a message to the master upon completion.
     85     void StartScan(const FilePath& script_dir,
     86                    const UserScriptList& external_scripts);
     87 
     88     // The master is going away; don't call it back.
     89     void DisownMaster() {
     90       master_ = NULL;
     91     }
     92 
     93    private:
     94     friend class base::RefCountedThreadSafe<UserScriptMaster::ScriptReloader>;
     95 
     96     ~ScriptReloader() {}
     97 
     98     // Where functions are run:
     99     //    master          file
    100     //   StartScan   ->  RunScan
    101     //                     LoadScriptsFromDirectory()
    102     //                     LoadLoneScripts()
    103     // NotifyMaster  <-  RunScan
    104 
    105     // Runs on the master thread.
    106     // Notify the master that new scripts are available.
    107     void NotifyMaster(base::SharedMemory* memory);
    108 
    109     // Runs on the File thread.
    110     // Scan the specified directory and lone scripts, calling NotifyMaster when
    111     // done. The parameters are intentionally passed by value so their lifetimes
    112     // aren't tied to the caller.
    113     void RunScan(const FilePath script_dir, UserScriptList lone_scripts);
    114 
    115     // A pointer back to our master.
    116     // May be NULL if DisownMaster() is called.
    117     UserScriptMaster* master_;
    118 
    119     // The message loop to call our master back on.
    120     // Expected to always outlive us.
    121     BrowserThread::ID master_thread_id_;
    122 
    123     DISALLOW_COPY_AND_ASSIGN(ScriptReloader);
    124   };
    125 
    126  private:
    127   // NotificationObserver implementation.
    128   virtual void Observe(NotificationType type,
    129                        const NotificationSource& source,
    130                        const NotificationDetails& details);
    131 
    132   // Manages our notification registrations.
    133   NotificationRegistrar registrar_;
    134 
    135   // The directories containing user scripts.
    136   FilePath user_script_dir_;
    137 
    138   // We hang on to our pointer to know if we've already got one running.
    139   scoped_refptr<ScriptReloader> script_reloader_;
    140 
    141   // Contains the scripts that were found the last time scripts were updated.
    142   scoped_ptr<base::SharedMemory> shared_memory_;
    143 
    144   // List of scripts outside of script directories we should also load.
    145   UserScriptList lone_scripts_;
    146 
    147   // If the extensions service has finished loading its initial set of
    148   // extensions.
    149   bool extensions_service_ready_;
    150 
    151   // If the script directory is modified while we're rescanning it, we note
    152   // that we're currently mid-scan and then start over again once the scan
    153   // finishes.  This boolean tracks whether another scan is pending.
    154   bool pending_scan_;
    155 
    156   // The profile for which the scripts managed here are installed.
    157   Profile* profile_;
    158 
    159   DISALLOW_COPY_AND_ASSIGN(UserScriptMaster);
    160 };
    161 
    162 #endif  // CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_MASTER_H_
    163