| // Copyright 2016 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef COMPONENTS_UI_DEVTOOLS_DEVTOOLS_SERVER_H_ | |
| #define COMPONENTS_UI_DEVTOOLS_DEVTOOLS_SERVER_H_ | |
| #include <string_view> | |
| #include <vector> | |
| #include "base/compiler_specific.h" | |
| #include "base/files/file_path.h" | |
| #include "base/functional/callback.h" | |
| #include "base/functional/callback_forward.h" | |
| #include "base/memory/raw_ptr.h" | |
| #include "base/memory/weak_ptr.h" | |
| #include "base/sequence_checker.h" | |
| #include "base/thread_annotations.h" | |
| #include "components/ui_devtools/devtools_client.h" | |
| #include "components/ui_devtools/devtools_export.h" | |
| #include "components/ui_devtools/dom.h" | |
| #include "components/ui_devtools/forward.h" | |
| #include "components/ui_devtools/protocol.h" | |
| #include "net/server/http_server_request_info.h" | |
| #include "net/traffic_annotation/network_traffic_annotation.h" | |
| namespace base { | |
| class SingleThreadTaskRunner; | |
| } | |
| namespace ui_devtools { | |
| class TracingAgent; | |
| class UI_DEVTOOLS_EXPORT UiDevToolsServer { | |
| public: | |
| // Network tags to be used for the UI devtools servers. | |
| static const net::NetworkTrafficAnnotationTag kUIDevtoolsServerTag; | |
| UiDevToolsServer(const UiDevToolsServer&) = delete; | |
| UiDevToolsServer& operator=(const UiDevToolsServer&) = delete; | |
| ~UiDevToolsServer(); | |
| // Returns an empty unique_ptr if ui devtools flag isn't enabled or if a | |
| // server instance has already been created. All network activity is performed | |
| // on `io_thread_task_runner`, which must run tasks on a thread with an IO | |
| // message pump. All other work is done on the thread the UiDevToolsServer is | |
| // created on. If `port` is 0, the server will choose an available port. If | |
| // `port` is 0 and `active_port_output_directory` is present, the server will | |
| // write the chosen port to `kUIDevToolsActivePortFileName` on | |
| // `active_port_output_directory`. | |
| static std::unique_ptr<UiDevToolsServer> CreateForViews( | |
| scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner, | |
| int port, | |
| const base::FilePath& active_port_output_directory = base::FilePath()); | |
| // Returns a list of attached UiDevToolsClient name + URL | |
| using NameUrlPair = std::pair<std::string, std::string>; | |
| static std::vector<NameUrlPair> GetClientNamesAndUrls(); | |
| // Returns true if UI Devtools is enabled by the given commandline switch. | |
| static bool IsUiDevToolsEnabled(const char* enable_devtools_flag); | |
| // Returns the port number specified by a command line flag. If a number is | |
| // not specified as a command line argument, returns the |default_port|. | |
| static int GetUiDevToolsPort(const char* enable_devtools_flag, | |
| int default_port); | |
| void AttachClient(std::unique_ptr<UiDevToolsClient> client); | |
| void SendOverWebSocket(int connection_id, std::string_view message); | |
| int port() const { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_); | |
| return port_; | |
| } | |
| TracingAgent* tracing_agent() { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_); | |
| return tracing_agent_; | |
| } | |
| void set_tracing_agent(TracingAgent* agent) { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_); | |
| tracing_agent_ = agent; | |
| } | |
| // Sets the callback which will be invoked when the session is closed. | |
| // Marks as a const function so it can be called after the server is set up | |
| // and used as a constant instance. | |
| void SetOnSessionEnded(base::OnceClosure callback) const; | |
| // Sets a callback that tests can use to wait for the server to be ready to | |
| // accept connections. | |
| void SetOnSocketConnectedForTesting(base::OnceClosure on_socket_connected); | |
| // Sets a callback that tests can use to wait for a client to connect. | |
| void SetOnClientConnectedForTesting(base::OnceClosure on_client_connected); | |
| // Allows calling OnWebSocketRequest() with unexpected connection IDs for | |
| // tests, bypassing the HttpServer. | |
| void OnWebSocketRequestForTesting(int connection_id, | |
| net::HttpServerRequestInfo info); | |
| private: | |
| // Class that owns the ServerSocket and, after creation on the main thread, | |
| // may only subsequently be used on the IO thread. The class is needed because | |
| // HttpServers must live on an IO thread. This class contains all state that's | |
| // read or written on the IO thread, to avoid UAF's on teardown. When the | |
| // UiDevToolsServer server is destroyed, a task is posted to destroy the | |
| // IOThreadData, but that task could still be run after the UiDevToolsServer | |
| // is fully torn down. | |
| // | |
| // Because of this pattern, it's always safe to post messages to the IO thread | |
| // to dereference the IOThreadData from the UiDevToolsServer assuming this | |
| // class will still be valid, but tasks it posts back to the main thread to | |
| // dereference the UiDevToolsServer must use weak pointers. | |
| class IOThreadData; | |
| UiDevToolsServer( | |
| scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner, | |
| int port, | |
| const net::NetworkTrafficAnnotationTag tag, | |
| const base::FilePath& active_port_output_directory); | |
| // Invoked on the IO thread, initializes `server_` and starts listening for | |
| // connections on `port`. | |
| void MakeServer(int port); | |
| // Invoked on the main thread. Called by MakeServer via a post task. | |
| void ServerCreated(int port); | |
| // These mirror the corresponding HttpServer methods. | |
| void OnWebSocketRequest(int connection_id, net::HttpServerRequestInfo info); | |
| void OnWebSocketMessage(int connection_id, std::string data); | |
| void OnClose(int connection_id); | |
| using ClientsList = std::vector<std::unique_ptr<UiDevToolsClient>>; | |
| using ConnectionsMap = | |
| std::map<uint32_t, raw_ptr<UiDevToolsClient, CtnExperimental>>; | |
| ClientsList clients_ GUARDED_BY_CONTEXT(main_sequence_); | |
| ConnectionsMap connections_ GUARDED_BY_CONTEXT(main_sequence_); | |
| const scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_; | |
| // The port the devtools server listens on. | |
| int port_ GUARDED_BY_CONTEXT(main_sequence_); | |
| raw_ptr<TracingAgent> tracing_agent_ GUARDED_BY_CONTEXT(main_sequence_) = | |
| nullptr; | |
| // Invoked when the server doesn't have any live connections. | |
| mutable base::OnceClosure on_session_ended_ | |
| GUARDED_BY_CONTEXT(main_sequence_); | |
| // Set once the server has been started. | |
| bool connected_ GUARDED_BY_CONTEXT(main_sequence_) = false; | |
| // Invoked once the server has been started. | |
| base::OnceClosure on_socket_connected_ GUARDED_BY_CONTEXT(main_sequence_); | |
| // Invoked when a client connects. | |
| base::OnceClosure on_client_connected_ GUARDED_BY_CONTEXT(main_sequence_); | |
| // The server (owned by Chrome for now) | |
| static UiDevToolsServer* devtools_server_; | |
| std::unique_ptr<IOThreadData> io_thread_data_; | |
| SEQUENCE_CHECKER(main_sequence_); | |
| base::WeakPtrFactory<UiDevToolsServer> weak_ptr_factory_{this}; | |
| }; | |
| } // namespace ui_devtools | |
| #endif // COMPONENTS_UI_DEVTOOLS_DEVTOOLS_SERVER_H_ |