blob: e81ec17f9fb8f7d8f6bfb44a9c862d22ae49564c [file] [log] [blame]
// Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_DIAL_DIAL_API_H_
#define CHROME_BROWSER_EXTENSIONS_API_DIAL_DIAL_API_H_
#include <memory>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "chrome/browser/media/router/discovery/dial/dial_device_data.h"
#include "chrome/browser/media/router/discovery/dial/dial_registry.h"
#include "chrome/common/extensions/api/dial.h"
#include "components/keyed_service/core/refcounted_keyed_service.h"
#include "extensions/browser/api/async_api_function.h"
#include "extensions/browser/event_router.h"
#include "net/url_request/url_request_context_getter.h"
namespace media_router {
class DeviceDescriptionFetcher;
} // namespace media_router
namespace extensions {
class DialFetchDeviceDescriptionFunction;
// Dial API which is a ref-counted KeyedService that manages
// the DIAL registry. It takes care of creating the registry on the IO thread
// and is an observer of the registry. It makes sure devices events are sent out
// to extension listeners on the right thread.
//
// TODO(mfoltz): This should probably inherit from BrowserContextKeyedAPI
// instead; ShutdownOnUIThread below is a no-op, which is the whole point of
// RefcountedKeyedService.
//
// TODO(mfoltz): The threading model for this API needs to be rethought. At a
// minimum, DialRegistry should move to the UI thread to avoid extra thread hops
// here. This would also allow a straightforward GetDeviceList implementation
// (crbug.com/576817), cleanup some long-tail crashes (crbug.com/640011) that
// are likely due to lifetime issues, and simplify unit tests
// (crbug.com/661457).
//
// Also, DialRegistry should be an interface that can be mocked and injected for
// tests; this would allow us to remove code that injects test data into the
// real DialRegsitry.
class DialAPI : public RefcountedKeyedService,
public EventRouter::Observer,
public media_router::DialRegistry::Observer {
public:
explicit DialAPI(Profile* profile);
// The DialRegistry for the API. This must always be used only from the IO
// thread.
media_router::DialRegistry* dial_registry();
// Called by the DialRegistry on the IO thread so that the DialAPI dispatches
// the event to listeners on the UI thread.
void SendEventOnUIThread(
const media_router::DialRegistry::DeviceList& devices);
void SendErrorOnUIThread(
const media_router::DialRegistry::DialErrorCode type);
// Sets test device data.
void SetDeviceForTest(
const media_router::DialDeviceData& device_data,
const media_router::DialDeviceDescriptionData& device_description);
private:
~DialAPI() override;
friend class DialFetchDeviceDescriptionFunction;
// RefcountedKeyedService:
void ShutdownOnUIThread() override;
// EventRouter::Observer:
void OnListenerAdded(const EventListenerInfo& details) override;
void OnListenerRemoved(const EventListenerInfo& details) override;
// DialRegistry::Observer:
void OnDialDeviceEvent(
const media_router::DialRegistry::DeviceList& devices) override;
void OnDialError(media_router::DialRegistry::DialErrorCode type) override;
// Methods to notify the DialRegistry on the correct thread of new/removed
// listeners.
void NotifyListenerAddedOnIOThread();
void NotifyListenerRemovedOnIOThread();
// Fills the |device| API struct from |device_data|.
void FillDialDevice(const media_router::DialDeviceData& device_data,
api::dial::DialDevice* device) const;
Profile* profile_;
// Used to get its NetLog on the IOThread. It uses the same NetLog as the
// Profile, but the Profile's URLRequestContextGetter isn't ready when DialAPI
// is created.
scoped_refptr<net::URLRequestContextGetter> system_request_context_;
// Created lazily on first access on the IO thread. Does not take ownership of
// |dial_registry_|.
media_router::DialRegistry* dial_registry_;
// Number of dial.onDeviceList event listeners.
int num_on_device_list_listeners_;
// Device data for testing.
std::unique_ptr<media_router::DialDeviceData> test_device_data_;
std::unique_ptr<media_router::DialDeviceDescriptionData>
test_device_description_;
DISALLOW_COPY_AND_ASSIGN(DialAPI);
};
// DiscoverNow function. This function needs a round-trip from the IO thread
// because it needs to grab a pointer to the DIAL API in order to get a
// reference to the DialRegistry while on the IO thread. Then, the result
// must be returned on the UI thread.
class DialDiscoverNowFunction : public AsyncApiFunction {
public:
DialDiscoverNowFunction();
protected:
~DialDiscoverNowFunction() override {}
// AsyncApiFunction:
bool Prepare() override;
void Work() override;
bool Respond() override;
private:
DECLARE_EXTENSION_FUNCTION("dial.discoverNow", DIAL_DISCOVERNOW)
// Pointer to the DIAL API for this profile. We get this on the UI thread.
DialAPI* dial_;
// Result of the discoverNow call to the DIAL registry. This result is
// retrieved on the IO thread but the function result is returned on the UI
// thread.
bool result_;
DISALLOW_COPY_AND_ASSIGN(DialDiscoverNowFunction);
};
class DialFetchDeviceDescriptionFunction : public AsyncExtensionFunction {
public:
DialFetchDeviceDescriptionFunction();
protected:
~DialFetchDeviceDescriptionFunction() override;
// AsyncExtensionFunction:
bool RunAsync() override;
private:
DECLARE_EXTENSION_FUNCTION("dial.fetchDeviceDescription",
DIAL_FETCHDEVICEDESCRIPTION)
void GetDeviceDescriptionUrlOnIOThread(const std::string& label);
void MaybeStartFetch(const GURL& url);
void OnFetchComplete(const media_router::DialDeviceDescriptionData& result);
void OnFetchError(const std::string& result);
std::unique_ptr<api::dial::FetchDeviceDescription::Params> params_;
std::unique_ptr<media_router::DeviceDescriptionFetcher>
device_description_fetcher_;
DialAPI* dial_;
DISALLOW_COPY_AND_ASSIGN(DialFetchDeviceDescriptionFunction);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_DIAL_DIAL_API_H_