// 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 COMPONENTS_WEB_VIEW_FRAME_H_ #define COMPONENTS_WEB_VIEW_FRAME_H_ #include #include #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "components/mus/common/types.h" #include "components/mus/public/cpp/window_observer.h" #include "components/web_view/public/interfaces/frame.mojom.h" #include "mojo/public/cpp/bindings/binding.h" class GURL; namespace web_view { class FrameTest; class FrameTree; class FrameUserData; namespace mojom { class FrameClient; } enum class WindowOwnership { OWNS_WINDOW, DOESNT_OWN_WINDOW, }; // Frame represents an embedding in a frame. Frames own their children. // Frames automatically delete themself if the Window the frame is associated // with is deleted. // // In general each Frame has a Window. When a new Frame is created by a client // there may be a small amount of time where the Window is not yet known // (separate pipes are used for the window and frame, resulting in undefined // message ordering). In this case the window is null and will be set once we // see the window (OnTreeChanged()). // // Each frame has an identifier of the app providing the FrameClient // (|app_id|). This id is used when servicing a request to navigate the frame. // When navigating, if the id of the new app matches that of the existing app, // then it is expected that the new FrameClient will take over rendering to the // existing window. Because of this a new WindowTreeClient is not obtained and // Embed() is not invoked on the Window. The FrameClient can detect this case by // the argument |reuse_existing_view| supplied to OnConnect(). Typically the id // is that of content handler id, but this is left up to the FrameTreeDelegate // to decide. class Frame : public mus::WindowObserver, public mojom::Frame { public: using ClientPropertyMap = std::map>; using FindCallback = mojo::Callback; Frame(FrameTree* tree, mus::Window* window, uint32_t frame_id, uint32_t app_id, WindowOwnership window_ownership, mojom::FrameClient* frame_client, scoped_ptr user_data, const ClientPropertyMap& client_properties); ~Frame() override; void Init(Frame* parent, mus::mojom::WindowTreeClientPtr window_tree_client, mojo::InterfaceRequest frame_request, base::TimeTicks navigation_start_time); // Walks the Window tree starting at |window| going up returning the first // Frame that is associated with |window|. For example, if |window| // has a Frame associated with it, then that is returned. Otherwise // this checks window->parent() and so on. static Frame* FindFirstFrameAncestor(mus::Window* window); FrameTree* tree() { return tree_; } Frame* parent() { return parent_; } const Frame* parent() const { return parent_; } mus::Window* window() { return window_; } const mus::Window* window() const { return window_; } uint32_t id() const { return id_; } uint32_t app_id() const { return app_id_; } bool loading() const { return loading_; } const ClientPropertyMap& client_properties() const { return client_properties_; } // Finds the descendant with the specified id. Frame* FindFrame(uint32_t id) { return const_cast(const_cast(this)->FindFrame(id)); } const Frame* FindFrame(uint32_t id) const; bool HasAncestor(const Frame* frame) const; FrameUserData* user_data() { return user_data_.get(); } const std::vector& children() const { return children_; } // Returns true if this Frame or any child Frame is loading. bool IsLoading() const; // Returns the sum total of loading progress from this Frame and all of its // children, as well as the number of Frames accumulated. double GatherProgress(int* frame_count) const; void Find(int32_t request_id, const mojo::String& search_text, mojom::FindOptionsPtr options, bool wrap_within_frame, const FindCallback& callback); void StopFinding(bool clear_selection); void HighlightFindResults(int32_t request_id, const mojo::String& search_text, mojom::FindOptionsPtr options, bool reset); void StopHighlightingFindResults(); private: friend class FrameTest; friend class FrameTree; // Identifies whether the FrameClient is from the same app or a different // app. enum class ClientType { // The client is either the root frame, or navigating an existing frame // to a different app. EXISTING_FRAME_NEW_APP, // The client is the result of navigating an existing frame in the same // app. EXISTING_FRAME_SAME_APP, // The client is the result of a new frame (not the root). NEW_CHILD_FRAME }; struct FrameUserDataAndBinding; // Initializes the client by sending it the state of the tree. // |data_and_binding| contains the current FrameUserDataAndBinding (if any) // and is destroyed after the connection responds to OnConnect(). // // If |client_type| is SAME_APP we can't destroy the existing client // (and related data) until we get back the ack from OnConnect(). This way // we know the client has completed the switch. If we did not do this it // would be possible for the app to see it's existing Frame connection lost // (and assume the frame is being torn down) before the OnConnect(). void InitClient(ClientType client_type, scoped_ptr data_and_binding, mus::mojom::WindowTreeClientPtr window_tree_client, mojo::InterfaceRequest frame_request, base::TimeTicks navigation_start_time); // Callback from OnConnect(). This does nothing (other than destroying // |data_and_binding|). See InitClient() for details as to why destruction of // |data_and_binding| happens after OnConnect(). static void OnConnectAck( scoped_ptr data_and_binding); // Callback from OnEmbed(). void OnEmbedAck(bool success, mus::ConnectionSpecificId connection_id); // Callback from Frame::OnWillNavigate(). Completes navigation. void OnWillNavigateAck(mojom::FrameClient* frame_client, scoped_ptr user_data, mus::mojom::WindowTreeClientPtr window_tree_client, uint32 app_id, base::TimeTicks navigation_start_time); // Completes a navigation request; swapping the existing FrameClient to the // supplied arguments. void ChangeClient(mojom::FrameClient* frame_client, scoped_ptr user_data, mus::mojom::WindowTreeClientPtr window_tree_client, uint32 app_id, base::TimeTicks navigation_start_time); void SetWindow(mus::Window* window); // Adds this to |frames| and recurses through the children calling the // same function. void BuildFrameTree(std::vector* frames) const; void Add(Frame* node); void Remove(Frame* node); // Starts a new navigation to |request|. The navigation proceeds as long // as there is a Window and once OnWillNavigate() has returned. If there is // no Window the navigation waits until the Window is available. void StartNavigate(mojo::URLRequestPtr request); void OnCanNavigateFrame(const GURL& url, base::TimeTicks navigation_start_time, uint32_t app_id, mojom::FrameClient* frame_client, scoped_ptr user_data, mus::mojom::WindowTreeClientPtr window_tree_client); // Notifies the client and all descendants as appropriate. void NotifyAdded(const Frame* source, const Frame* added_node, uint32_t change_id); void NotifyRemoved(const Frame* source, const Frame* removed_node, uint32_t change_id); void NotifyClientPropertyChanged(const Frame* source, const mojo::String& name, const mojo::Array& value); void NotifyFrameLoadingStateChanged(const Frame* frame, bool loading); void NotifyDispatchFrameLoadEvent(const Frame* frame); // mus::WindowObserver: void OnTreeChanged(const TreeChangeParams& params) override; void OnWindowDestroying(mus::Window* window) override; void OnWindowEmbeddedAppDisconnected(mus::Window* window) override; // mojom::Frame: void PostMessageEventToFrame(uint32_t target_frame_id, mojom::HTMLMessageEventPtr event) override; void LoadingStateChanged(bool loading, double progress) override; void TitleChanged(const mojo::String& title) override; void DidCommitProvisionalLoad() override; void SetClientProperty(const mojo::String& name, mojo::Array value) override; void OnCreatedFrame( mojo::InterfaceRequest frame_request, mojom::FrameClientPtr client, uint32_t frame_id, mojo::Map> client_properties) override; void RequestNavigate(mojom::NavigationTargetType target_type, uint32_t target_frame_id, mojo::URLRequestPtr request) override; void DidNavigateLocally(const mojo::String& url) override; void DispatchLoadEventToParent() override; void OnFindInFrameCountUpdated(int32_t request_id, int32_t count, bool final_update) override; void OnFindInPageSelectionUpdated(int32_t request_id, int32_t active_match_ordinal) override; FrameTree* const tree_; // WARNING: this may be null. See class description for details. mus::Window* window_; // The connection id returned from WindowManager::Embed(). Frames created by // way of OnCreatedFrame() inherit the id from the parent. mus::ConnectionSpecificId embedded_connection_id_; // ID for the frame, which is the same as that of the window. const uint32_t id_; // ID of the app providing the FrameClient and WindowTreeClient. uint32_t app_id_; Frame* parent_; WindowOwnership window_ownership_; std::vector children_; scoped_ptr user_data_; mojom::FrameClient* frame_client_; bool loading_; double progress_; ClientPropertyMap client_properties_; // StartNavigate() stores the request here if the window isn't available at // the time of StartNavigate(). mojo::URLRequestPtr pending_navigate_; scoped_ptr> frame_binding_; // True if waiting on callback from FrameClient::OnWillNavigate(). bool waiting_for_on_will_navigate_ack_; base::WeakPtrFactory embed_weak_ptr_factory_; base::WeakPtrFactory navigate_weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(Frame); }; } // namespace web_view #endif // COMPONENTS_WEB_VIEW_FRAME_H_