// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef EXTENSIONS_BROWSER_EXTENSION_API_FRAME_ID_MAP_H_ #define EXTENSIONS_BROWSER_EXTENSION_API_FRAME_ID_MAP_H_ #include #include #include "base/callback.h" #include "base/lazy_instance.h" #include "base/macros.h" #include "base/synchronization/lock.h" namespace content { class NavigationHandle; class RenderFrameHost; class WebContents; } // namespace content namespace extensions { // Extension frame IDs are exposed through the chrome.* APIs and have the // following characteristics: // - The top-level frame has ID 0. // - Any child frame has a positive ID. // - A non-existant frame has ID -1. // - They are only guaranteed to be unique within a tab. // - The ID does not change during the frame's lifetime and is not re-used after // the frame is removed. The frame may change its current RenderFrameHost over // time, so multiple RenderFrameHosts may map to the same extension frame ID. // // This class provides a mapping from a (render_process_id, frame_routing_id) // pair to a FrameData struct, which includes the extension's frame id (as // described above), the parent frame id, and the tab id (the latter can be // invalid if it's not in a tab). // // Unless stated otherwise, the methods can only be called on the UI thread. // // The non-static methods of this class use an internal cache. This cache is // used to minimize IO->UI->IO round-trips of GetFrameIdOnIO. If the cost of // attaching FrameTreeNode IDs to requests is negligible (crbug.com/524228), // then we can remove all key caching and remove the cache from this class. // TODO(robwu): Keep an eye on crbug.com/524228 and act upon the outcome. // TODO(devlin): Also keep an eye on FrameIOData to see if that could help. class ExtensionApiFrameIdMap { public: // The data for a RenderFrame. Every RenderFrameIdKey maps to a FrameData. struct FrameData { FrameData(); FrameData(int frame_id, int parent_frame_id, int tab_id); // The extension API frame ID of the frame. int frame_id; // The extension API frame ID of the parent of the frame. int parent_frame_id; // The id of the tab that the frame is in, or -1 if the frame isn't in a // tab. int tab_id; }; using FrameDataCallback = base::Callback; // An invalid extension API frame ID. static const int kInvalidFrameId; // Extension API frame ID of the top-level frame. static const int kTopFrameId; static ExtensionApiFrameIdMap* Get(); // Get the extension API frame ID for |rfh|. static int GetFrameId(content::RenderFrameHost* rfh); // Get the extension API frame ID for |navigation_handle|. static int GetFrameId(content::NavigationHandle* navigation_handle); // Get the extension API frame ID for the parent of |rfh|. static int GetParentFrameId(content::RenderFrameHost* rfh); // Get the extension API frame ID for the parent of |navigation_handle|. static int GetParentFrameId(content::NavigationHandle* navigation_handle); // Find the current RenderFrameHost for a given WebContents and extension // frame ID. // Returns nullptr if not found. static content::RenderFrameHost* GetRenderFrameHostById( content::WebContents* web_contents, int frame_id); // Runs |callback| with the result that is equivalent to calling GetFrameId() // on the UI thread. Thread hopping is minimized if possible. Callbacks for // the same |render_process_id| and |frame_routing_id| are guaranteed to be // run in order. The order of other callbacks is undefined. void GetFrameDataOnIO(int render_process_id, int frame_routing_id, const FrameDataCallback& callback); // Attempts to populate |frame_data_out| with the FrameData for the specified // frame, but only does so if the data is already cached. Returns true if // cached frame data was found. bool GetCachedFrameDataOnIO(int render_process_id, int frame_routing_id, FrameData* frame_data_out); // Looks up the frame ID and stores it in the map. This method should be // called as early as possible, e.g. in a // WebContentsObserver::RenderFrameCreated notification. void CacheFrameData(content::RenderFrameHost* rfh); // Removes the frame ID mapping for a given frame. This method can be called // at any time, but it is typically called when a frame is destroyed. // If this method is not called, the cached mapping for the frame is retained // forever. void RemoveFrameData(content::RenderFrameHost* rfh); protected: friend struct base::DefaultLazyInstanceTraits; // A set of identifiers that uniquely identifies a RenderFrame. struct RenderFrameIdKey { RenderFrameIdKey(); RenderFrameIdKey(int render_process_id, int frame_routing_id); // The process ID of the renderer that contains the RenderFrame. int render_process_id; // The routing ID of the RenderFrame. int frame_routing_id; bool operator<(const RenderFrameIdKey& other) const; bool operator==(const RenderFrameIdKey& other) const; }; struct FrameDataCallbacks { FrameDataCallbacks(); FrameDataCallbacks(const FrameDataCallbacks& other); ~FrameDataCallbacks(); // This is a std::list so that iterators are not invalidated when the list // is modified during an iteration. std::list callbacks; // To avoid re-entrant processing of callbacks. bool is_iterating; }; using FrameDataMap = std::map; using FrameDataCallbacksMap = std::map; ExtensionApiFrameIdMap(); ~ExtensionApiFrameIdMap(); // Determines the value to be stored in |frame_id_map_| for a given key. This // method is only called when |key| is not in |frame_id_map_|. // virtual for testing. virtual FrameData KeyToValue(const RenderFrameIdKey& key) const; // Looks up the data for the given |key| and adds it to the |frame_data_map_|. // |for_lookup| indicates whether this is for a pending lookup (as opposed to // preemptively caching the frame data). FrameData LookupFrameDataOnUI(const RenderFrameIdKey& key, bool for_lookup); // Called as soon as the frame ID is found for the given |key|, and runs all // queued callbacks with |cached_frame_id_pair|. void ReceivedFrameDataOnIO(const RenderFrameIdKey& key, const FrameData& cached_frame_id_pair); // Implementation of CacheFrameId(RenderFrameHost), separated for testing. void CacheFrameData(const RenderFrameIdKey& key); // Implementation of RemoveFrameId(RenderFrameHost), separated for testing. void RemoveFrameData(const RenderFrameIdKey& key); // Queued callbacks for use on the IO thread. FrameDataCallbacksMap callbacks_map_; // This map is only modified on the UI thread and is used to minimize the // number of thread hops on the IO thread. FrameDataMap frame_data_map_; // This lock protects |frame_id_map_| from being concurrently written on the // UI thread and read on the IO thread. base::Lock frame_data_map_lock_; DISALLOW_COPY_AND_ASSIGN(ExtensionApiFrameIdMap); }; } // namespace extensions #endif // EXTENSIONS_BROWSER_EXTENSION_API_FRAME_ID_MAP_H_