the Chromium logo

The Chromium Projects

Using delegates to Resolve Dependency Issues

Chromium Repo Dependency Checks

The Chromium repository has strict restrictions on which modules are able to depend on the top level //ui, //chrome, or //content directories.

Directly from the //chromeos README:

These limitations are enforced using "-chrome" in the DEPS file and apply to the //chromeos, //ash/webui, and other low-level directories in the repository.

When these rules are violated (by including a file from //chrome/…), attempts to upload the offending CL will display the following message:

Running Python 3 presubmit upload checks ...
** Presubmit ERRORS: 1 **
You added one or more #includes that violate checkdeps rules.
  ash/webui/file_with_violation.cc
      Illegal include: "chrome/browser/utils.h"
    Because of "-chrome" from ash/webui's include_rules.

Presubmit checks took 5.4s to calculate.
There were Python 3 presubmit errors.

The diagram below gives a rough visualization of the separation of components in ChromeOS.

ChromeOS Dependency Chart ChromeOS architecture pre-LaCrOs

Delegate pattern to use Chrome from Ash

There will be numerous occasions where services in //ash/webui will need access to functions and data only available in //chrome (to access the logged-in user’s Profile for example).

To avoid repo dependency violations, in these situations we can utilize the delegate pattern to get the data/invoke the actions needed. The delegate pattern (summary) in the diagram below shows a simplified version of a function in //ash/webui using a delegate to invoke a function in //chrome.

Sample delegate pattern diagram Sample delegate pattern diagram

Creating a delegate

The below steps will use the existing Scan App implementation in //ash/webui to demonstrate examples of the delegate pattern.

Define the functions in the header

Here the functions are defined which will be invoked by our //ash/webui service. This header file will be created in //ash/webui so the service’s .cc file will be allowed to call/depend on it.

class ServiceDelegate {
 public:
  virtual ~ServiceDelegate() = default;

  virtual std::string GetStringFromChromeBrowser() = 0;
};

Scan app example

Create the implementation class

Since functions in //chrome can’t be called from //ash/webui, the functions defined in the delegate header need to be implemented by a file located in //chrome.

.h file

class ChromeServiceDelegate : public ServiceDelegate {
 public:
  ChromeServiceDelegate() override;

  std::string GetStringFromChromeBrowser() override;
};

Scan app example

.cc file

ChromeServiceDelegate::ChromeServiceDelegate() = default;

std::string ChromeServiceDelegate::GetStringFromChromeBrowser() {
  return chrome::ChromeBrowserFunction();
}

Scan app example

Pass the delegate implementation to the service

Probably the most difficult part in the process, the service in //ash/webui needs to be given access to an instance of the delegate implementation created in //chrome. This requires the app to be created/launched from //chrome. Here are two examples of correctly setting up and passing the impl: Scan app example via chrome_web_ui_controller_factory.cc Diagnostics Log Controller example via chrome_browser_main_parts_ash.cc

Invoke the delegate function from the service

Once the delegate impl is passed to the service and stored as a private local variable, any function defined in the delegate header can now be invoked without worry of dependency conflicts.

.h file

class Service {
 private:
  std::string UseStringFromChrome();

  // Provides browser functionality from //chrome to the Service UI.
  std::unique_ptr<ServiceDelegate> service_delegate_;
};

Scan app example

.cc file

std::string Service::GetStringFromChromeBrowser() {
  service_delegate_->GetStringFromChromeBrowser();
}

Scan app example

Example CL