mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Add support for media device discovery and messaging (fixes issue #2900)
Chromium supports communication with media devices on the local network via the Cast and DIAL protocols. This takes two primary forms: 1. Messaging, where strings representing state information are passed between the client and a dedicated receiver app on the media device. The receiver app communicates directly with an app-specific backend service to retrieve and possibly control media playback. 2. Tab/desktop mirroring, where the media contents are streamed directly from the browser to a generic streaming app on the media device and playback is controlled by the browser. This change adds support for device discovery and messaging (but not mirroring) with functionality exposed via the new CefMediaRouter interface. To test: Navigate to http://tests/media_router in cefclient and follow the on-screen instructions.
This commit is contained in:
491
tests/cefclient/browser/media_router_test.cc
Normal file
491
tests/cefclient/browser/media_router_test.cc
Normal file
@ -0,0 +1,491 @@
|
||||
// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
#include "tests/cefclient/browser/media_router_test.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "include/base/cef_logging.h"
|
||||
#include "include/cef_media_router.h"
|
||||
#include "include/cef_parser.h"
|
||||
#include "tests/cefclient/browser/test_runner.h"
|
||||
|
||||
namespace client {
|
||||
namespace media_router_test {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kTestUrlPath[] = "/media_router";
|
||||
|
||||
// Application-specific error codes.
|
||||
const int kMessageFormatError = 1;
|
||||
const int kRequestFailedError = 2;
|
||||
|
||||
// Message strings.
|
||||
const char kNameKey[] = "name";
|
||||
const char kNameValueSubscribe[] = "subscribe";
|
||||
const char kNameValueCreateRoute[] = "createRoute";
|
||||
const char kNameValueTerminateRoute[] = "terminateRoute";
|
||||
const char kNameValueSendMessage[] = "sendMessage";
|
||||
const char kSourceKey[] = "source_urn";
|
||||
const char kSinkKey[] = "sink_id";
|
||||
const char kRouteKey[] = "route_id";
|
||||
const char kMessageKey[] = "message";
|
||||
const char kSuccessKey[] = "success";
|
||||
const char kPayloadKey[] = "payload";
|
||||
|
||||
// Convert a dictionary value to a JSON string.
|
||||
CefString GetJSON(CefRefPtr<CefDictionaryValue> dictionary) {
|
||||
CefRefPtr<CefValue> value = CefValue::Create();
|
||||
value->SetDictionary(dictionary);
|
||||
return CefWriteJSON(value, JSON_WRITER_DEFAULT);
|
||||
}
|
||||
|
||||
typedef CefMessageRouterBrowserSide::Callback CallbackType;
|
||||
|
||||
void SendSuccess(CefRefPtr<CallbackType> callback,
|
||||
CefRefPtr<CefDictionaryValue> result) {
|
||||
callback->Success(GetJSON(result));
|
||||
}
|
||||
|
||||
void SendFailure(CefRefPtr<CallbackType> callback,
|
||||
int error_code,
|
||||
const std::string& error_message) {
|
||||
callback->Failure(error_code, error_message);
|
||||
}
|
||||
|
||||
// Callback for CefMediaRouter::CreateRoute.
|
||||
class MediaRouteCreateCallback : public CefMediaRouteCreateCallback {
|
||||
public:
|
||||
explicit MediaRouteCreateCallback(CefRefPtr<CallbackType> create_callback)
|
||||
: create_callback_(create_callback) {}
|
||||
|
||||
// CefMediaRouteCreateCallback method:
|
||||
void OnMediaRouteCreateFinished(RouteCreateResult result,
|
||||
const CefString& error,
|
||||
CefRefPtr<CefMediaRoute> route) OVERRIDE {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
if (result == CEF_MRCR_OK) {
|
||||
CefRefPtr<CefDictionaryValue> result = CefDictionaryValue::Create();
|
||||
result->SetString(kRouteKey, route->GetId());
|
||||
SendSuccess(create_callback_, result);
|
||||
} else {
|
||||
SendFailure(create_callback_, kRequestFailedError + result, error);
|
||||
}
|
||||
create_callback_ = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
CefRefPtr<CallbackType> create_callback_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(MediaRouteCreateCallback);
|
||||
DISALLOW_COPY_AND_ASSIGN(MediaRouteCreateCallback);
|
||||
};
|
||||
|
||||
// Observes MediaRouter events. Only accessed on the UI thread.
|
||||
class MediaObserver : public CefMediaObserver {
|
||||
public:
|
||||
typedef std::vector<CefRefPtr<CefMediaRoute>> MediaRouteVector;
|
||||
typedef std::vector<CefRefPtr<CefMediaSink>> MediaSinkVector;
|
||||
|
||||
MediaObserver(CefRefPtr<CefMediaRouter> media_router,
|
||||
CefRefPtr<CallbackType> subscription_callback)
|
||||
: media_router_(media_router),
|
||||
subscription_callback_(subscription_callback) {}
|
||||
|
||||
bool CreateRoute(const std::string& source_urn,
|
||||
const std::string& sink_id,
|
||||
CefRefPtr<CallbackType> callback,
|
||||
std::string& error) {
|
||||
CefRefPtr<CefMediaSource> source = GetSource(source_urn);
|
||||
if (!source) {
|
||||
error = "Invalid source: " + source_urn;
|
||||
return false;
|
||||
}
|
||||
|
||||
CefRefPtr<CefMediaSink> sink = GetSink(sink_id);
|
||||
if (!sink) {
|
||||
error = "Invalid sink: " + sink_id;
|
||||
return false;
|
||||
}
|
||||
|
||||
media_router_->CreateRoute(source, sink,
|
||||
new MediaRouteCreateCallback(callback));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TerminateRoute(const std::string& route_id, std::string& error) {
|
||||
CefRefPtr<CefMediaRoute> route = GetRoute(route_id);
|
||||
if (!route) {
|
||||
error = "Invalid route: " + route_id;
|
||||
return false;
|
||||
}
|
||||
|
||||
route->Terminate();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SendRouteMessage(const std::string& route_id,
|
||||
const std::string& message,
|
||||
std::string& error) {
|
||||
CefRefPtr<CefMediaRoute> route = GetRoute(route_id);
|
||||
if (!route) {
|
||||
error = "Invalid route: " + route_id;
|
||||
return false;
|
||||
}
|
||||
|
||||
route->SendRouteMessage(message.c_str(), message.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
// CefMediaObserver methods:
|
||||
void OnSinks(const MediaSinkVector& sinks) OVERRIDE {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
sink_map_.clear();
|
||||
|
||||
CefRefPtr<CefDictionaryValue> payload = CefDictionaryValue::Create();
|
||||
CefRefPtr<CefListValue> sinks_list = CefListValue::Create();
|
||||
sinks_list->SetSize(sinks.size());
|
||||
|
||||
MediaSinkVector::const_iterator it = sinks.begin();
|
||||
for (size_t idx = 0; it != sinks.end(); ++it, ++idx) {
|
||||
CefRefPtr<CefMediaSink> sink = *it;
|
||||
const std::string& sink_id = sink->GetId();
|
||||
sink_map_.insert(std::make_pair(sink_id, sink));
|
||||
|
||||
CefRefPtr<CefDictionaryValue> sink_dict = CefDictionaryValue::Create();
|
||||
sink_dict->SetString("id", sink_id);
|
||||
sink_dict->SetString("name", sink->GetName());
|
||||
sink_dict->SetString("desc", sink->GetDescription());
|
||||
sink_dict->SetString(
|
||||
"type", sink->IsCastSink() ? "cast"
|
||||
: sink->IsDialSink() ? "dial" : "unknown");
|
||||
sinks_list->SetDictionary(idx, sink_dict);
|
||||
}
|
||||
|
||||
payload->SetList("sinks_list", sinks_list);
|
||||
SendResponse("onSinks", payload);
|
||||
}
|
||||
|
||||
void OnRoutes(const MediaRouteVector& routes) OVERRIDE {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
route_map_.clear();
|
||||
|
||||
CefRefPtr<CefDictionaryValue> payload = CefDictionaryValue::Create();
|
||||
CefRefPtr<CefListValue> routes_list = CefListValue::Create();
|
||||
routes_list->SetSize(routes.size());
|
||||
|
||||
MediaRouteVector::const_iterator it = routes.begin();
|
||||
for (size_t idx = 0; it != routes.end(); ++it, ++idx) {
|
||||
CefRefPtr<CefMediaRoute> route = *it;
|
||||
const std::string& route_id = route->GetId();
|
||||
route_map_.insert(std::make_pair(route_id, route));
|
||||
|
||||
CefRefPtr<CefDictionaryValue> route_dict = CefDictionaryValue::Create();
|
||||
route_dict->SetString("id", route_id);
|
||||
route_dict->SetString(kSourceKey, route->GetSource()->GetId());
|
||||
route_dict->SetString(kSinkKey, route->GetSink()->GetId());
|
||||
routes_list->SetDictionary(idx, route_dict);
|
||||
}
|
||||
|
||||
payload->SetList("routes_list", routes_list);
|
||||
SendResponse("onRoutes", payload);
|
||||
}
|
||||
|
||||
void OnRouteStateChanged(CefRefPtr<CefMediaRoute> route,
|
||||
ConnectionState state) OVERRIDE {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
CefRefPtr<CefDictionaryValue> payload = CefDictionaryValue::Create();
|
||||
payload->SetString(kRouteKey, route->GetId());
|
||||
payload->SetInt("connection_state", state);
|
||||
SendResponse("onRouteStateChanged", payload);
|
||||
}
|
||||
|
||||
void OnRouteMessageReceived(CefRefPtr<CefMediaRoute> route,
|
||||
const void* message,
|
||||
size_t message_size) OVERRIDE {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
std::string message_str(static_cast<const char*>(message), message_size);
|
||||
|
||||
CefRefPtr<CefDictionaryValue> payload = CefDictionaryValue::Create();
|
||||
payload->SetString(kRouteKey, route->GetId());
|
||||
payload->SetString(kMessageKey, message_str);
|
||||
SendResponse("onRouteMessageReceived", payload);
|
||||
}
|
||||
|
||||
private:
|
||||
CefRefPtr<CefMediaSource> GetSource(const std::string& source_urn) {
|
||||
CefRefPtr<CefMediaSource> source = media_router_->GetSource(source_urn);
|
||||
if (!source || !source->IsValid())
|
||||
return NULL;
|
||||
return source;
|
||||
}
|
||||
|
||||
CefRefPtr<CefMediaSink> GetSink(const std::string& sink_id) {
|
||||
SinkMap::const_iterator it = sink_map_.find(sink_id);
|
||||
if (it != sink_map_.end())
|
||||
return it->second;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CefRefPtr<CefMediaRoute> GetRoute(const std::string& route_id) {
|
||||
RouteMap::const_iterator it = route_map_.find(route_id);
|
||||
if (it != route_map_.end())
|
||||
return it->second;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void SendResponse(const std::string& name,
|
||||
CefRefPtr<CefDictionaryValue> payload) {
|
||||
CefRefPtr<CefDictionaryValue> result = CefDictionaryValue::Create();
|
||||
result->SetString(kNameKey, name);
|
||||
result->SetDictionary(kPayloadKey, payload);
|
||||
SendSuccess(subscription_callback_, result);
|
||||
}
|
||||
|
||||
CefRefPtr<CefMediaRouter> media_router_;
|
||||
CefRefPtr<CallbackType> subscription_callback_;
|
||||
|
||||
typedef std::map<std::string, CefRefPtr<CefMediaSink>> SinkMap;
|
||||
SinkMap sink_map_;
|
||||
|
||||
typedef std::map<std::string, CefRefPtr<CefMediaRoute>> RouteMap;
|
||||
RouteMap route_map_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(MediaObserver);
|
||||
DISALLOW_COPY_AND_ASSIGN(MediaObserver);
|
||||
};
|
||||
|
||||
// Handle messages in the browser process. Only accessed on the UI thread.
|
||||
class Handler : public CefMessageRouterBrowserSide::Handler {
|
||||
public:
|
||||
typedef std::vector<std::string> NameVector;
|
||||
|
||||
Handler() { CEF_REQUIRE_UI_THREAD(); }
|
||||
|
||||
virtual ~Handler() {
|
||||
SubscriptionStateMap::iterator it = subscription_state_map_.begin();
|
||||
for (; it != subscription_state_map_.end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Called due to cefQuery execution in media_router.html.
|
||||
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64 query_id,
|
||||
const CefString& request,
|
||||
bool persistent,
|
||||
CefRefPtr<Callback> callback) OVERRIDE {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
// Only handle messages from the test URL.
|
||||
const std::string& url = frame->GetURL();
|
||||
if (!test_runner::IsTestURL(url, kTestUrlPath))
|
||||
return false;
|
||||
|
||||
// Parse |request| as a JSON dictionary.
|
||||
CefRefPtr<CefDictionaryValue> request_dict = ParseJSON(request);
|
||||
if (!request_dict) {
|
||||
SendFailure(callback, kMessageFormatError, "Incorrect message format");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Verify the "name" key.
|
||||
if (!VerifyKey(request_dict, kNameKey, VTYPE_STRING, callback))
|
||||
return true;
|
||||
|
||||
const std::string& message_name = request_dict->GetString(kNameKey);
|
||||
if (message_name == kNameValueSubscribe) {
|
||||
// Subscribe to notifications from the media router.
|
||||
|
||||
if (!persistent) {
|
||||
SendFailure(callback, kMessageFormatError,
|
||||
"Subscriptions must be persistent");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!CreateSubscription(browser, query_id, callback)) {
|
||||
SendFailure(callback, kRequestFailedError,
|
||||
"Browser is already subscribed");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// All other messages require a current subscription.
|
||||
CefRefPtr<MediaObserver> media_observer =
|
||||
GetMediaObserver(browser->GetIdentifier());
|
||||
if (!media_observer) {
|
||||
SendFailure(callback, kRequestFailedError,
|
||||
"Browser is not currently subscribed");
|
||||
}
|
||||
|
||||
if (message_name == kNameValueCreateRoute) {
|
||||
// Create a new route.
|
||||
|
||||
// Verify the "source_urn" key.
|
||||
if (!VerifyKey(request_dict, kSourceKey, VTYPE_STRING, callback))
|
||||
return true;
|
||||
// Verify the "sink_id" key.
|
||||
if (!VerifyKey(request_dict, kSinkKey, VTYPE_STRING, callback))
|
||||
return true;
|
||||
|
||||
const std::string& source_urn = request_dict->GetString(kSourceKey);
|
||||
const std::string& sink_id = request_dict->GetString(kSinkKey);
|
||||
|
||||
// |callback| will be executed once the route is created.
|
||||
std::string error;
|
||||
if (!media_observer->CreateRoute(source_urn, sink_id, callback, error)) {
|
||||
SendFailure(callback, kRequestFailedError, error);
|
||||
}
|
||||
return true;
|
||||
} else if (message_name == kNameValueTerminateRoute) {
|
||||
// Terminate an existing route.
|
||||
|
||||
// Verify the "route" key.
|
||||
if (!VerifyKey(request_dict, kRouteKey, VTYPE_STRING, callback))
|
||||
return true;
|
||||
|
||||
const std::string& route_id = request_dict->GetString(kRouteKey);
|
||||
std::string error;
|
||||
if (!media_observer->TerminateRoute(route_id, error)) {
|
||||
SendFailure(callback, kRequestFailedError, error);
|
||||
} else {
|
||||
SendSuccessACK(callback);
|
||||
}
|
||||
return true;
|
||||
} else if (message_name == kNameValueSendMessage) {
|
||||
// Send a route message.
|
||||
|
||||
// Verify the "route_id" key.
|
||||
if (!VerifyKey(request_dict, kRouteKey, VTYPE_STRING, callback))
|
||||
return true;
|
||||
// Verify the "message" key.
|
||||
if (!VerifyKey(request_dict, kMessageKey, VTYPE_STRING, callback))
|
||||
return true;
|
||||
|
||||
const std::string& route_id = request_dict->GetString(kRouteKey);
|
||||
const std::string& message = request_dict->GetString(kMessageKey);
|
||||
std::string error;
|
||||
if (!media_observer->SendRouteMessage(route_id, message, error)) {
|
||||
SendFailure(callback, kRequestFailedError, error);
|
||||
} else {
|
||||
SendSuccessACK(callback);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnQueryCanceled(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int64 query_id) OVERRIDE {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
RemoveSubscription(browser->GetIdentifier(), query_id);
|
||||
}
|
||||
|
||||
private:
|
||||
static void SendSuccessACK(CefRefPtr<Callback> callback) {
|
||||
CefRefPtr<CefDictionaryValue> result = CefDictionaryValue::Create();
|
||||
result->SetBool(kSuccessKey, true);
|
||||
SendSuccess(callback, result);
|
||||
}
|
||||
|
||||
// Convert a JSON string to a dictionary value.
|
||||
static CefRefPtr<CefDictionaryValue> ParseJSON(const CefString& string) {
|
||||
CefRefPtr<CefValue> value = CefParseJSON(string, JSON_PARSER_RFC);
|
||||
if (value.get() && value->GetType() == VTYPE_DICTIONARY)
|
||||
return value->GetDictionary();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Verify that |key| exists in |dictionary| and has type |value_type|. Fails
|
||||
// |callback| and returns false on failure.
|
||||
static bool VerifyKey(CefRefPtr<CefDictionaryValue> dictionary,
|
||||
const char* key,
|
||||
cef_value_type_t value_type,
|
||||
CefRefPtr<Callback> callback) {
|
||||
if (!dictionary->HasKey(key) || dictionary->GetType(key) != value_type) {
|
||||
SendFailure(
|
||||
callback, kMessageFormatError,
|
||||
"Missing or incorrectly formatted message key: " + std::string(key));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Subscription state associated with a single browser.
|
||||
struct SubscriptionState {
|
||||
int64 query_id;
|
||||
CefRefPtr<MediaObserver> observer;
|
||||
CefRefPtr<CefRegistration> registration;
|
||||
};
|
||||
|
||||
bool CreateSubscription(CefRefPtr<CefBrowser> browser,
|
||||
int64 query_id,
|
||||
CefRefPtr<Callback> callback) {
|
||||
const int browser_id = browser->GetIdentifier();
|
||||
if (subscription_state_map_.find(browser_id) !=
|
||||
subscription_state_map_.end()) {
|
||||
// An subscription already exists for this browser.
|
||||
return false;
|
||||
}
|
||||
|
||||
CefRefPtr<CefMediaRouter> media_router =
|
||||
browser->GetHost()->GetRequestContext()->GetMediaRouter();
|
||||
|
||||
SubscriptionState* state = new SubscriptionState();
|
||||
state->query_id = query_id;
|
||||
state->observer = new MediaObserver(media_router, callback);
|
||||
state->registration = media_router->AddObserver(state->observer);
|
||||
subscription_state_map_.insert(std::make_pair(browser_id, state));
|
||||
|
||||
// Trigger sink and route callbacks.
|
||||
media_router->NotifyCurrentSinks();
|
||||
media_router->NotifyCurrentRoutes();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveSubscription(int browser_id, int64 query_id) {
|
||||
SubscriptionStateMap::iterator it =
|
||||
subscription_state_map_.find(browser_id);
|
||||
if (it != subscription_state_map_.end() &&
|
||||
it->second->query_id == query_id) {
|
||||
delete it->second;
|
||||
subscription_state_map_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
CefRefPtr<MediaObserver> GetMediaObserver(int browser_id) {
|
||||
SubscriptionStateMap::const_iterator it =
|
||||
subscription_state_map_.find(browser_id);
|
||||
if (it != subscription_state_map_.end()) {
|
||||
return it->second->observer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Map of browser ID to SubscriptionState object.
|
||||
typedef std::map<int, SubscriptionState*> SubscriptionStateMap;
|
||||
SubscriptionStateMap subscription_state_map_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Handler);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
|
||||
handlers.insert(new Handler());
|
||||
}
|
||||
|
||||
} // namespace media_router_test
|
||||
} // namespace client
|
20
tests/cefclient/browser/media_router_test.h
Normal file
20
tests/cefclient/browser/media_router_test.h
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2020 The Chromium Embedded Framework 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 CEF_TESTS_CEFCLIENT_BROWSER_MEDIA_ROUTER_TEST_H_
|
||||
#define CEF_TESTS_CEFCLIENT_BROWSER_MEDIA_ROUTER_TEST_H_
|
||||
#pragma once
|
||||
|
||||
#include "tests/cefclient/browser/test_runner.h"
|
||||
|
||||
namespace client {
|
||||
namespace media_router_test {
|
||||
|
||||
// Create message handlers. Called from test_runner.cc.
|
||||
void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers);
|
||||
|
||||
} // namespace media_router_test
|
||||
} // namespace client
|
||||
|
||||
#endif // CEF_TESTS_CEFCLIENT_BROWSER_MEDIA_ROUTER_TEST_H_
|
@ -50,24 +50,25 @@
|
||||
#define IDS_DRM_HTML 1003
|
||||
#define IDS_LOCALSTORAGE_HTML 1004
|
||||
#define IDS_LOGO_PNG 1005
|
||||
#define IDS_MENU_ICON_1X_PNG 1006
|
||||
#define IDS_MENU_ICON_2X_PNG 1007
|
||||
#define IDS_OSRTEST_HTML 1008
|
||||
#define IDS_OTHER_TESTS_HTML 1009
|
||||
#define IDS_PDF_HTML 1010
|
||||
#define IDS_PDF_PDF 1011
|
||||
#define IDS_PERFORMANCE_HTML 1012
|
||||
#define IDS_PERFORMANCE2_HTML 1013
|
||||
#define IDS_PREFERENCES_HTML 1014
|
||||
#define IDS_RESPONSE_FILTER_HTML 1015
|
||||
#define IDS_SERVER_HTML 1016
|
||||
#define IDS_TRANSPARENCY_HTML 1017
|
||||
#define IDS_URLREQUEST_HTML 1018
|
||||
#define IDS_WEBSOCKET_HTML 1019
|
||||
#define IDS_WINDOW_HTML 1020
|
||||
#define IDS_WINDOW_ICON_1X_PNG 1021
|
||||
#define IDS_WINDOW_ICON_2X_PNG 1022
|
||||
#define IDS_XMLHTTPREQUEST_HTML 1023
|
||||
#define IDS_MEDIA_ROUTER_HTML 1006
|
||||
#define IDS_MENU_ICON_1X_PNG 1007
|
||||
#define IDS_MENU_ICON_2X_PNG 1008
|
||||
#define IDS_OSRTEST_HTML 1009
|
||||
#define IDS_OTHER_TESTS_HTML 1010
|
||||
#define IDS_PDF_HTML 1011
|
||||
#define IDS_PDF_PDF 1012
|
||||
#define IDS_PERFORMANCE_HTML 1013
|
||||
#define IDS_PERFORMANCE2_HTML 1014
|
||||
#define IDS_PREFERENCES_HTML 1015
|
||||
#define IDS_RESPONSE_FILTER_HTML 1016
|
||||
#define IDS_SERVER_HTML 1017
|
||||
#define IDS_TRANSPARENCY_HTML 1018
|
||||
#define IDS_URLREQUEST_HTML 1019
|
||||
#define IDS_WEBSOCKET_HTML 1020
|
||||
#define IDS_WINDOW_HTML 1021
|
||||
#define IDS_WINDOW_ICON_1X_PNG 1022
|
||||
#define IDS_WINDOW_ICON_2X_PNG 1023
|
||||
#define IDS_XMLHTTPREQUEST_HTML 1024
|
||||
|
||||
#define IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG 1030
|
||||
#define IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON 1031
|
||||
|
@ -26,8 +26,9 @@ int GetResourceId(const char* resource_name) {
|
||||
IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_HTML},
|
||||
{"extensions/set_page_color/popup.js",
|
||||
IDS_EXTENSIONS_SET_PAGE_COLOR_POPUP_JS},
|
||||
{"logo.png", IDS_LOGO_PNG},
|
||||
{"localstorage.html", IDS_LOCALSTORAGE_HTML},
|
||||
{"logo.png", IDS_LOGO_PNG},
|
||||
{"media_router.html", IDS_MEDIA_ROUTER_HTML},
|
||||
{"menu_icon.1x.png", IDS_MENU_ICON_1X_PNG},
|
||||
{"menu_icon.2x.png", IDS_MENU_ICON_2X_PNG},
|
||||
{"osr_test.html", IDS_OSRTEST_HTML},
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "tests/cefclient/browser/dialog_test.h"
|
||||
#include "tests/cefclient/browser/drm_test.h"
|
||||
#include "tests/cefclient/browser/main_context.h"
|
||||
#include "tests/cefclient/browser/media_router_test.h"
|
||||
#include "tests/cefclient/browser/preferences_test.h"
|
||||
#include "tests/cefclient/browser/resource.h"
|
||||
#include "tests/cefclient/browser/response_filter_test.h"
|
||||
@ -882,6 +883,9 @@ void CreateMessageHandlers(MessageHandlerSet& handlers) {
|
||||
// Create the drm test handlers.
|
||||
drm_test::CreateMessageHandlers(handlers);
|
||||
|
||||
// Create the media router test handlers.
|
||||
media_router_test::CreateMessageHandlers(handlers);
|
||||
|
||||
// Create the preferences test handlers.
|
||||
preferences_test::CreateMessageHandlers(handlers);
|
||||
|
||||
|
Reference in New Issue
Block a user