switch (strippedTransition) {
    case ui::PAGE_TRANSITION_LINK:
        return WebContentsAdapterClient::LinkNavigation;
    case ui::PAGE_TRANSITION_TYPED:
        return WebContentsAdapterClient::TypedNavigation;
    case ui::PAGE_TRANSITION_FORM_SUBMIT:
        return WebContentsAdapterClient::FormSubmittedNavigation;
    case ui::PAGE_TRANSITION_RELOAD:
        return WebContentsAdapterClient::ReloadNavigation;
    default:
        return WebContentsAdapterClient::OtherNavigation;
    }

/****************************************************************************
**
**
** This file is part of the QtWebEngine module of the Qt Toolkit.
**
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: http://example.com and
** http://example.com.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "content_browser_client_qt.h"

#include "base/files/file_util.h"
#include "base/optional.h"
#include "base/task/post_task.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/tab_contents/form_interaction_tab_helper.h"
#include "components/error_page/common/error.h"
#include "components/error_page/common/localized_error.h"
#include "components/navigation_interception/intercept_navigation_throttle.h"
#include "components/navigation_interception/navigation_params.h"
#include "components/network_hints/browser/simple_network_hints_handler_impl.h"
#include "components/performance_manager/embedder/performance_manager_registry.h"
#include "components/performance_manager/public/performance_manager.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_main_runner.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/browser/file_url_loader.h"
#include "content/public/browser/media_observer.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_user_data.h"
#include "content/public/browser/web_ui_url_loader_factory.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/main_function_params.h"
#include "content/public/common/user_agent.h"
#include "extensions/buildflags/buildflags.h"
#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
#include "net/ssl/client_cert_identity.h"
#include "net/ssl/client_cert_store.h"
#include "services/network/network_service.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
#include "third_party/blink/public/mojom/insecure_input/insecure_input_service.mojom.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_switches.h"
#include "url/url_util_qt.h"

#include "qtwebengine/common/renderer_configuration.mojom.h"
#include "qtwebengine/grit/qt_webengine_resources.h"

#include "profile_adapter.h"
#include "browser_main_parts_qt.h"
#include "browser_message_filter_qt.h"
#include "certificate_error_controller.h"
#include "client_cert_select_controller.h"
#include "devtools_manager_delegate_qt.h"
#include "login_delegate_qt.h"
#include "media_capture_devices_dispatcher.h"
#include "net/cookie_monster_delegate_qt.h"
#include "net/custom_url_loader_factory.h"
#include "net/proxying_restricted_cookie_manager_qt.h"
#include "net/proxying_url_loader_factory_qt.h"
#include "net/system_network_context_manager.h"
#include "ozone/gl_share_context_qt.h"
#include "platform_notification_service_qt.h"
#include "profile_qt.h"
#include "profile_io_data_qt.h"
#include "quota_permission_context_qt.h"
#include "renderer_host/user_resource_controller_host.h"
#include "select_file_dialog_factory_qt.h"
#include "type_conversion.h"
#include "web_contents_adapter_client.h"
#include "web_contents_adapter.h"
#include "web_contents_delegate_qt.h"
#include "web_contents_view_qt.h"
#include "web_engine_context.h"
#include "web_engine_library_info.h"
#include "web_engine_settings.h"
#include "api/qwebenginecookiestore.h"
#include "api/qwebenginecookiestore_p.h"

#if QT_CONFIG(opengl)
#include <QOpenGLContext>
#include <QOpenGLExtraFunctions>
#endif

#if QT_CONFIG(webengine_geolocation)
#include "base/memory/ptr_util.h"
#include "location_provider_qt.h"
#endif

#if QT_CONFIG(webengine_pepper_plugins)
#include "content/public/browser/browser_ppapi_host.h"
#include "ppapi/host/ppapi_host.h"
#include "renderer_host/pepper/pepper_host_factory_qt.h"
#endif

#if QT_CONFIG(webengine_spellchecker)
#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
#include "components/spellcheck/common/spellcheck.mojom.h"
#endif

#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions)
#include "chrome/browser/media/webrtc/webrtc_logging_controller.h"
#endif

#if defined(Q_OS_LINUX)
#include "global_descriptors_qt.h"
#endif

#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "common/extensions/extensions_client_qt.h"
#include "components/guest_view/browser/guest_view_base.h"
#include "extensions/browser/api/mime_handler_private/mime_handler_private.h"
#include "extensions/browser/extension_message_filter.h"
#include "extensions/browser/extension_protocols.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/guest_view/extensions_guest_view_message_filter.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
#include "extensions/browser/guest_view/web_view/web_view_guest.h"
#include "extensions/browser/process_map.h"
#include "extensions/browser/url_loader_factory_manager.h"
#include "extensions/common/constants.h"
#include "extensions/common/manifest_handlers/mime_types_handler.h"
#include "extensions/extension_web_contents_observer_qt.h"
#include "extensions/extensions_browser_client_qt.h"
#include "extensions/pdf_iframe_navigation_throttle_qt.h"
#include "net/plugin_response_interceptor_url_loader_throttle.h"
#endif

#include <QGuiApplication>
#include <QStandardPaths>
#include <qpa/qplatformnativeinterface.h>

QT_BEGIN_NAMESPACE
Q_GUI_EXPORT QOpenGLContext *qt_gl_global_share_context();
QT_END_NAMESPACE

// Implement IsHandledProtocol as declared in //url/url_util_qt.h.
namespace url {
bool IsHandledProtocol(base::StringPiece scheme)
{
    static const char *const kProtocolList[] = {
        url::kHttpScheme,
        url::kHttpsScheme,
#if BUILDFLAG(ENABLE_WEBSOCKETS)
        url::kWsScheme,
        url::kWssScheme,
#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
        url::kFileScheme,
        content::kChromeDevToolsScheme,
#if BUILDFLAG(ENABLE_EXTENSIONS)
        extensions::kExtensionScheme,
#endif
        content::kChromeUIScheme,
        url::kDataScheme,
        url::kAboutScheme,
#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
        url::kFtpScheme,
#endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
        url::kBlobScheme,
        url::kFileSystemScheme,
        url::kQrcScheme,
    };

    for (const char *protocol : kProtocolList) {
        if (scheme == protocol)
            return true;
    }
    if (const auto cs = url::CustomScheme::FindScheme(scheme))
        return true;
    return false;
}
}

namespace QtWebEngineCore {

void MaybeAddThrottle(
    std::unique_ptr<content::NavigationThrottle> maybe_throttle,
    std::vector<std::unique_ptr<content::NavigationThrottle>>* throttles) {
    if (maybe_throttle)
        throttles->push_back(std::move(maybe_throttle));
}

ContentBrowserClientQt::ContentBrowserClientQt()
{
}

ContentBrowserClientQt::~ContentBrowserClientQt()
{
}

std::unique_ptr<content::BrowserMainParts> ContentBrowserClientQt::CreateBrowserMainParts(const content::MainFunctionParams&)
{
    return std::make_unique<BrowserMainPartsQt>();
}

void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost *host)
{
    const int id = host->GetID();
    Profile *profile = Profile::FromBrowserContext(host->GetBrowserContext());

#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions)
    WebRtcLoggingController::AttachToRenderProcessHost(host, WebEngineContext::current()->webRtcLogUploader());
#endif

    // Allow requesting custom schemes.
    const auto policy = content::ChildProcessSecurityPolicy::GetInstance();
    const auto profileAdapter = static_cast<ProfileQt *>(profile)->profileAdapter();
    for (const QByteArray &scheme : profileAdapter->customUrlSchemes())
        policy->GrantRequestScheme(id, scheme.toStdString());

    // FIXME: Add a settings variable to enable/disable the file scheme.
    policy->GrantRequestScheme(id, url::kFileScheme);
    profileAdapter->userResourceController()->renderProcessStartedWithHost(host);
    host->AddFilter(new BrowserMessageFilterQt(id, profile));
#if BUILDFLAG(ENABLE_EXTENSIONS)
    host->AddFilter(new extensions::ExtensionMessageFilter(id, profile));
    host->AddFilter(new extensions::ExtensionsGuestViewMessageFilter(id, profile));
#endif //ENABLE_EXTENSIONS

    bool is_incognito_process = profile->IsOffTheRecord();
    mojo::AssociatedRemote<qtwebengine::mojom::RendererConfiguration> renderer_configuration;
    host->GetChannel()->GetRemoteAssociatedInterface(&renderer_configuration);
    renderer_configuration->SetInitialConfiguration(is_incognito_process);
}

gl::GLShareGroup *ContentBrowserClientQt::GetInProcessGpuShareGroup()
{
    if (!m_shareGroupQt.get())
        m_shareGroupQt = new ShareGroupQt;
    return m_shareGroupQt.get();
}

content::MediaObserver *ContentBrowserClientQt::GetMediaObserver()
{
    return MediaCaptureDevicesDispatcher::GetInstance();
}

void ContentBrowserClientQt::OverrideWebkitPrefs(content::WebContents *webContents, blink::web_pref::WebPreferences *web_prefs)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
    if (guest_view::GuestViewBase::IsGuest(webContents))
        return;

    WebContentsViewQt *view = WebContentsViewQt::from(static_cast<content::WebContentsImpl *>(webContents)->GetView());
    if (!view->client())
        return;
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
    WebContentsDelegateQt* delegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate());
    if (delegate)
        delegate->overrideWebPreferences(webContents, web_prefs);
}

scoped_refptr<content::QuotaPermissionContext> ContentBrowserClientQt::CreateQuotaPermissionContext()
{
    return new QuotaPermissionContextQt;
}

void ContentBrowserClientQt::AllowCertificateError(content::WebContents *webContents,
                                                   int cert_error,
                                                   const net::SSLInfo &ssl_info,
                                                   const GURL &request_url,
                                                   bool /* is_main_frame_request */,
                                                   bool strict_enforcement,
                                                   base::OnceCallback<void(content::CertificateRequestResultType)> callback)
{
    WebContentsDelegateQt* contentsDelegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate());

    QSharedPointer<CertificateErrorController> errorController(new CertificateErrorController(
            cert_error, ssl_info, request_url, strict_enforcement, std::move(callback)));
    contentsDelegate->allowCertificateError(errorController);
}

base::OnceClosure ContentBrowserClientQt::SelectClientCertificate(content::WebContents *webContents,
                                                                  net::SSLCertRequestInfo *certRequestInfo,
                                                                  net::ClientCertIdentityList clientCerts,
                                                                  std::unique_ptr<content::ClientCertificateDelegate> delegate)
{
    if (!clientCerts.empty()) {
        WebContentsDelegateQt* contentsDelegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate());

        QSharedPointer<ClientCertSelectController> certSelectController(
                new ClientCertSelectController(certRequestInfo, std::move(clientCerts), std::move(delegate)));

        contentsDelegate->selectClientCert(certSelectController);
    } else {
        delegate->ContinueWithCertificate(nullptr, nullptr);
    }
    // This is consistent with AwContentBrowserClient and CastContentBrowserClient:
    return base::OnceClosure();
}

std::unique_ptr<net::ClientCertStore> ContentBrowserClientQt::CreateClientCertStore(content::BrowserContext *browser_context)
{
    if (!browser_context)
        return nullptr;

    return ProfileIODataQt::FromBrowserContext(browser_context)->CreateClientCertStore();
}

std::string ContentBrowserClientQt::GetApplicationLocale()
{
    return WebEngineLibraryInfo::getApplicationLocale();
}

std::string ContentBrowserClientQt::GetAcceptLangs(content::BrowserContext *context)
{
    return static_cast<ProfileQt*>(context)->profileAdapter()->httpAcceptLanguage().toStdString();
}

void ContentBrowserClientQt::AppendExtraCommandLineSwitches(base::CommandLine* command_line, int child_process_id)
{
    Q_UNUSED(child_process_id);

    url::CustomScheme::SaveSchemes(command_line);

    std::string processType = command_line->GetSwitchValueASCII(switches::kProcessType);
    if (processType == switches::kZygoteProcess)
        command_line->AppendSwitchASCII(switches::kLang, WebEngineLibraryInfo::getApplicationLocale());
}

void ContentBrowserClientQt::GetAdditionalWebUISchemes(std::vector<std::string>* additional_schemes)
{
    ContentBrowserClient::GetAdditionalWebUISchemes(additional_schemes);
    additional_schemes->push_back(content::kChromeDevToolsScheme);
}

void ContentBrowserClientQt::GetAdditionalViewSourceSchemes(std::vector<std::string>* additional_schemes)
{
    ContentBrowserClient::GetAdditionalViewSourceSchemes(additional_schemes);

#if BUILDFLAG(ENABLE_EXTENSIONS)
    additional_schemes->push_back(extensions::kExtensionScheme);
#endif
}

void ContentBrowserClientQt::GetAdditionalAllowedSchemesForFileSystem(std::vector<std::string>* additional_schemes)
{
    ContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem(additional_schemes);
    additional_schemes->push_back(content::kChromeDevToolsScheme);
    additional_schemes->push_back(content::kChromeUIScheme);
}

std::unique_ptr<ui::SelectFilePolicy>
ContentBrowserClientQt::CreateSelectFilePolicy(content::WebContents *web_contents)
{
    return std::make_unique<SelectFilePolicyQt>(web_contents);
}

#if defined(Q_OS_LINUX)
void ContentBrowserClientQt::GetAdditionalMappedFilesForChildProcess(const base::CommandLine& command_line, int child_process_id, content::PosixFileDescriptorInfo* mappings)
{
    const base::FilePath &locale_file_path = ui::ResourceBundle::GetSharedInstance().GetLocaleFilePath(WebEngineLibraryInfo::getResolvedLocale());
    if (locale_file_path.empty() || !base::PathExists(locale_file_path))
        return;

    // Open pak file of the current locale in the Browser process and pass its file descriptor to the sandboxed
    // Renderer Process. FileDescriptorInfo is responsible for closing the file descriptor.
    int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
    base::File locale_file = base::File(locale_file_path, flags);
    mappings->Transfer(kWebEngineLocale, base::ScopedFD(locale_file.TakePlatformFile()));
}
#endif

#if QT_CONFIG(webengine_pepper_plugins)
void ContentBrowserClientQt::DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host)
{
    browser_host->GetPpapiHost()->AddHostFactoryFilter(
                std::make_unique<QtWebEngineCore::PepperHostFactoryQt>(browser_host));
}
#endif

content::DevToolsManagerDelegate* ContentBrowserClientQt::GetDevToolsManagerDelegate()
{
    return new DevToolsManagerDelegateQt;
}

content::PlatformNotificationService *ContentBrowserClientQt::GetPlatformNotificationService(content::BrowserContext *browser_context)
{
    ProfileQt *profile = static_cast<ProfileQt *>(browser_context);
    if (!profile)
        return nullptr;
    return profile->platformNotificationService();
}

// This is a really complicated way of doing absolutely nothing, but Mojo demands it:
class ServiceDriver
        : public blink::mojom::InsecureInputService
        , public content::WebContentsUserData<ServiceDriver>
{
public:
    static void CreateForRenderFrameHost(content::RenderFrameHost *renderFrameHost)
    {
        content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(renderFrameHost);
        if (!web_contents)
            return;
        CreateForWebContents(web_contents);
    }
    static ServiceDriver* FromRenderFrameHost(content::RenderFrameHost *renderFrameHost)
    {
        content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(renderFrameHost);
        if (!web_contents)
            return nullptr;
        return FromWebContents(web_contents);
    }
    static void BindInsecureInputService(content::RenderFrameHost *render_frame_host, mojo::PendingReceiver<blink::mojom::InsecureInputService> receiver)
    {
        CreateForRenderFrameHost(render_frame_host);
        ServiceDriver *driver = FromRenderFrameHost(render_frame_host);

        if (driver)
            driver->BindInsecureInputServiceReceiver(std::move(receiver));
    }
    void BindInsecureInputServiceReceiver(mojo::PendingReceiver<blink::mojom::InsecureInputService> receiver)
    {
        m_receivers.Add(this, std::move(receiver));
    }

    void DidEditFieldInInsecureContext() override
    { }

private:
    WEB_CONTENTS_USER_DATA_KEY_DECL();
    explicit ServiceDriver(content::WebContents* /*web_contents*/) { }
    friend class content::WebContentsUserData<ServiceDriver>;
    mojo::ReceiverSet<blink::mojom::InsecureInputService> m_receivers;
};

WEB_CONTENTS_USER_DATA_KEY_IMPL(ServiceDriver)

void ContentBrowserClientQt::BindHostReceiverForRenderer(content::RenderProcessHost *render_process_host,
                                                         mojo::GenericPendingReceiver receiver)
{
#if QT_CONFIG(webengine_spellchecker)
    if (auto host_receiver = receiver.As<spellcheck::mojom::SpellCheckHost>()) {
        SpellCheckHostChromeImpl::Create(render_process_host->GetID(), std::move(host_receiver));
        return;
    }
#endif
}

static void BindNetworkHintsHandler(content::RenderFrameHost *frame_host,
                                    mojo::PendingReceiver<network_hints::mojom::NetworkHintsHandler> receiver)
{
    network_hints::SimpleNetworkHintsHandlerImpl::Create(frame_host, std::move(receiver));
}

#if BUILDFLAG(ENABLE_EXTENSIONS)
static void BindMimeHandlerService(content::RenderFrameHost *frame_host,
                                   mojo::PendingReceiver<extensions::mime_handler::MimeHandlerService>
                                   receiver) {
    auto *web_contents = content::WebContents::FromRenderFrameHost(frame_host);
    if (!web_contents)
        return;
    auto *guest_view = extensions::MimeHandlerViewGuest::FromWebContents(web_contents);
    if (!guest_view)
        return;
    extensions::MimeHandlerServiceImpl::Create(guest_view->GetStreamWeakPtr(), std::move(receiver));
}

static void BindBeforeUnloadControl(content::RenderFrameHost *frame_host,
                                    mojo::PendingReceiver<extensions::mime_handler::BeforeUnloadControl>
                                    receiver) {
    auto *web_contents = content::WebContents::FromRenderFrameHost(frame_host);
    if (!web_contents)
        return;
    auto *guest_view = extensions::MimeHandlerViewGuest::FromWebContents(web_contents);
    if (!guest_view)
        return;
    guest_view->FuseBeforeUnloadControl(std::move(receiver));
}
#endif

void ContentBrowserClientQt::RegisterBrowserInterfaceBindersForFrame(
        content::RenderFrameHost *render_frame_host,
        mojo::BinderMapWithContext<content::RenderFrameHost *> *map)
{
    Q_UNUSED(render_frame_host);
    map->Add<blink::mojom::InsecureInputService>(base::BindRepeating(&ServiceDriver::BindInsecureInputService));
    map->Add<network_hints::mojom::NetworkHintsHandler>(base::BindRepeating(&BindNetworkHintsHandler));
#if BUILDFLAG(ENABLE_EXTENSIONS)
    map->Add<extensions::mime_handler::MimeHandlerService>(base::BindRepeating(&BindMimeHandlerService));
    map->Add<extensions::mime_handler::BeforeUnloadControl>(base::BindRepeating(&BindBeforeUnloadControl));
    const GURL &site = render_frame_host->GetSiteInstance()->GetSiteURL();
    if (!site.SchemeIs(extensions::kExtensionScheme))
        return;
    content::BrowserContext *browser_context = render_frame_host->GetProcess()->GetBrowserContext();
    auto *extension = extensions::ExtensionRegistry::Get(browser_context)
                        ->enabled_extensions()
                        .GetByID(site.host());
    if (!extension)
        return;
    extensions::ExtensionsBrowserClient::Get()->RegisterBrowserInterfaceBindersForFrame(map,
                                                                                render_frame_host,
                                                                                extension);
#endif
}

void ContentBrowserClientQt::ExposeInterfacesToRenderer(service_manager::BinderRegistry *registry,
                                                        blink::AssociatedInterfaceRegistry *associated_registry,
                                                        content::RenderProcessHost *render_process_host)
{
    Q_UNUSED(associated_registry);
    if (auto *manager = performance_manager::PerformanceManagerRegistry::GetInstance())
        manager->CreateProcessNodeAndExposeInterfacesToRendererProcess(registry, render_process_host);
}

bool ContentBrowserClientQt::CanCreateWindow(
        content::RenderFrameHost* opener,
        const GURL& opener_url,
        const GURL& opener_top_level_frame_url,
        const url::Origin& source_origin,
        content::mojom::WindowContainerType container_type,
        const GURL& target_url,
        const content::Referrer& referrer,
        const std::string& frame_name,
        WindowOpenDisposition disposition,
        const blink::mojom::WindowFeatures& features,
        bool user_gesture,
        bool opener_suppressed,
        bool* no_javascript_access)
{
    Q_UNUSED(opener_url);
    Q_UNUSED(opener_top_level_frame_url);
    Q_UNUSED(source_origin);
    Q_UNUSED(container_type);
    Q_UNUSED(target_url);
    Q_UNUSED(referrer);
    Q_UNUSED(frame_name);
    Q_UNUSED(disposition);
    Q_UNUSED(features);
    Q_UNUSED(opener_suppressed);

    if (no_javascript_access)
        *no_javascript_access = false;

    content::WebContents* webContents = content::WebContents::FromRenderFrameHost(opener);

    WebEngineSettings *settings = nullptr;
    if (webContents) {
        WebContentsDelegateQt* delegate =
                static_cast<WebContentsDelegateQt*>(webContents->GetDelegate());
        if (delegate)
            settings = delegate->webEngineSettings();
    }

    return (settings && settings->getJavaScriptCanOpenWindowsAutomatically()) || user_gesture;
}

#if QT_CONFIG(webengine_geolocation)
std::unique_ptr<device::LocationProvider> ContentBrowserClientQt::OverrideSystemLocationProvider()
{
    return base::WrapUnique(new LocationProviderQt());
}
#endif

bool ContentBrowserClientQt::ShouldEnableStrictSiteIsolation()
{
    // mirroring AwContentBrowserClient, CastContentBrowserClient and
    // HeadlessContentBrowserClient
    return false;
}

bool ContentBrowserClientQt::WillCreateRestrictedCookieManager(network::mojom::RestrictedCookieManagerRole role,
        content::BrowserContext *browser_context,
        const url::Origin & /*origin*/,
        const net::IsolationInfo & /*isolation_info*/,
        bool is_service_worker,
        int process_id,
        int routing_id,
        mojo::PendingReceiver<network::mojom::RestrictedCookieManager> *receiver)
{
    mojo::PendingReceiver<network::mojom::RestrictedCookieManager> orig_receiver = std::move(*receiver);

    mojo::PendingRemote<network::mojom::RestrictedCookieManager> target_rcm_remote;
    *receiver = target_rcm_remote.InitWithNewPipeAndPassReceiver();

    ProxyingRestrictedCookieManagerQt::CreateAndBind(
                ProfileIODataQt::FromBrowserContext(browser_context),
                std::move(target_rcm_remote),
                is_service_worker, process_id, routing_id,
                std::move(orig_receiver));

    return false;  // only made a proxy, still need the actual impl to be made.
}

bool ContentBrowserClientQt::AllowAppCache(const GURL &manifest_url,
                                           const GURL &first_party,
                                           const base::Optional<url::Origin> &top_frame_origin,
                                           content::BrowserContext *context)
{
    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    if (!context || context->ShutdownStarted())
        return false;
    return static_cast<ProfileQt *>(context)->profileAdapter()->cookieStore()->d_func()->canAccessCookies(toQt(first_party), toQt(manifest_url));
}

content::AllowServiceWorkerResult
ContentBrowserClientQt::AllowServiceWorker(const GURL &scope,
                                           const GURL &site_for_cookies,
                                           const base::Optional<url::Origin> & /*top_frame_origin*/,
                                           const GURL & /*script_url*/,
                                           content::BrowserContext *context)
{
    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    if (!context || context->ShutdownStarted())
        return content::AllowServiceWorkerResult::No();
    // FIXME: Chrome also checks if javascript is enabled here to check if has been disabled since the service worker
    // was started.
    return static_cast<ProfileQt *>(context)->profileAdapter()->cookieStore()->d_func()->canAccessCookies(toQt(site_for_cookies), toQt(scope))
         ? content::AllowServiceWorkerResult::Yes()
         : content::AllowServiceWorkerResult::No();
}

// We control worker access to FS and indexed-db using cookie permissions, this is mirroring Chromium's logic.
void ContentBrowserClientQt::AllowWorkerFileSystem(const GURL &url,
                                                   content::BrowserContext *context,
                                                   const std::vector<content::GlobalFrameRoutingId> &/*render_frames*/,
                                                   base::OnceCallback<void(bool)> callback)
{
    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    if (!context || context->ShutdownStarted())
        return std::move(callback).Run(false);
    std::move(callback).Run(
            static_cast<ProfileQt *>(context)->profileAdapter()->cookieStore()->d_func()->canAccessCookies(toQt(url), toQt(url)));
}

bool ContentBrowserClientQt::AllowWorkerIndexedDB(const GURL &url,
                                                  content::BrowserContext *context,
                                                  const std::vector<content::GlobalFrameRoutingId> &/*render_frames*/)
{
    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    if (!context || context->ShutdownStarted())
        return false;
    return static_cast<ProfileQt *>(context)->profileAdapter()->cookieStore()->d_func()->canAccessCookies(toQt(url), toQt(url));
}

static void LaunchURL(const GURL& url,
                      base::OnceCallback<content::WebContents*()> web_contents_getter,
                      ui::PageTransition page_transition, bool is_main_frame, bool has_user_gesture)
{
    Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    content::WebContents* webContents = std::move(web_contents_getter).Run();
    if (!webContents)
        return;

    ProtocolHandlerRegistry* protocolHandlerRegistry =
            ProtocolHandlerRegistryFactory::GetForBrowserContext(
                    webContents->GetBrowserContext());
    if (protocolHandlerRegistry &&
        protocolHandlerRegistry->IsHandledProtocol(url.scheme()))
        return;

    WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate());
    contentsDelegate->launchExternalURL(toQt(url), page_transition, is_main_frame, has_user_gesture);
}

bool ContentBrowserClientQt::HandleExternalProtocol(const GURL &url,
        base::OnceCallback<content::WebContents*()> web_contents_getter,
        int child_id,
        content::NavigationUIData *navigation_data,
        bool is_main_frame,
        ui::PageTransition page_transition,
        bool has_user_gesture,
        const base::Optional<url::Origin> &initiating_origin,
        mojo::PendingRemote<network::mojom::URLLoaderFactory> *out_factory)
{
    Q_UNUSED(child_id);
    Q_UNUSED(navigation_data);
    Q_UNUSED(initiating_origin);
    Q_UNUSED(out_factory);

    base::PostTask(FROM_HERE, {content::BrowserThread::UI},
                   base::BindOnce(&LaunchURL,
                                  url,
                                  std::move(web_contents_getter),
                                  page_transition,
                                  is_main_frame,
                                  has_user_gesture));
    return true;
}

namespace {
// Copied from chrome/browser/chrome_content_browser_client.cc
class ProtocolHandlerThrottle : public blink::URLLoaderThrottle
{
public:
    explicit ProtocolHandlerThrottle(ProtocolHandlerRegistry *protocol_handler_registry)
        : protocol_handler_registry_(protocol_handler_registry)
    {
    }
    ~ProtocolHandlerThrottle() override = default;

    void WillStartRequest(network::ResourceRequest *request, bool *defer) override
    {
        TranslateUrl(&request->url);
    }

    void WillRedirectRequest(net::RedirectInfo *redirect_info,
                             const network::mojom::URLResponseHead &response_head,
                             bool *defer,
                             std::vector<std::string> *to_be_removed_headers,
                             net::HttpRequestHeaders *modified_headers,
                             net::HttpRequestHeaders *modified_cors_exempt_headers) override
    {
        TranslateUrl(&redirect_info->new_url);
    }

private:
    void TranslateUrl(GURL *url)
    {
        if (!protocol_handler_registry_->IsHandledProtocol(url->scheme()))
            return;
        GURL translated_url = protocol_handler_registry_->Translate(*url);
        if (!translated_url.is_empty())
            *url = translated_url;
    }

    ProtocolHandlerRegistry *protocol_handler_registry_;
};
} // namespace

std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
ContentBrowserClientQt::CreateURLLoaderThrottles(
        const network::ResourceRequest &request, content::BrowserContext *browser_context,
        const base::RepeatingCallback<content::WebContents *()> & /*wc_getter*/,
        content::NavigationUIData * /*navigation_ui_data*/, int frame_tree_node_id)
{
    std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
    result.push_back(std::make_unique<ProtocolHandlerThrottle>(
                         ProtocolHandlerRegistryFactory::GetForBrowserContext(browser_context)));
#if BUILDFLAG(ENABLE_EXTENSIONS)
    result.push_back(std::make_unique<PluginResponseInterceptorURLLoaderThrottle>(
                         browser_context, request.destination, frame_tree_node_id));
#endif
    return result;
}

WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition)
{
    if (ui::PageTransitionIsRedirect(transition))
        return WebContentsAdapterClient::RedirectNavigation;

    int32_t qualifier = ui::PageTransitionGetQualifier(transition);

    if (qualifier & ui::PAGE_TRANSITION_FORWARD_BACK)
        return WebContentsAdapterClient::BackForwardNavigation;

    ui::PageTransition strippedTransition = ui::PageTransitionStripQualifier(transition);

}

static bool navigationThrottleCallback(content::WebContents *source,
                                       const navigation_interception::NavigationParams &params)
{
    // We call navigationRequested later in launchExternalUrl for external protocols.
    // The is_external_protocol parameter here is not fully accurate though,
    // and doesn't know about profile specific custom URL schemes.
    ProfileQt *profile = static_cast<ProfileQt *>(source->GetBrowserContext());
    if (params.is_external_protocol() && !profile->profileAdapter()->urlSchemeHandler(toQByteArray(params.url().scheme())))
        return false;

    WebContentsViewQt *view = WebContentsViewQt::from(static_cast<content::WebContentsImpl *>(source)->GetView());
    if (!view->client())
        return false;

    bool navigationAccepted = true;

    WebContentsAdapterClient *client =
        WebContentsViewQt::from(static_cast<content::WebContentsImpl *>(source)->GetView())->client();
    if (!client)
        return false;

    // Redirects might not be reflected in transition_type at this point (see also chrome/.../web_navigation_api_helpers.cc)
    auto transition_type = params.transition_type();
    if (params.is_redirect())
        transition_type = ui::PageTransitionFromInt(transition_type | ui::PAGE_TRANSITION_SERVER_REDIRECT);

    client->navigationRequested(pageTransitionToNavigationType(transition_type),
                                toQt(params.url()),
                                navigationAccepted,
                                params.is_main_frame());
    return !navigationAccepted;
}

std::vector<std::unique_ptr<content::NavigationThrottle>> ContentBrowserClientQt::CreateThrottlesForNavigation(
        content::NavigationHandle *navigation_handle)
{
    std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
    throttles.push_back(std::make_unique<navigation_interception::InterceptNavigationThrottle>(
                            navigation_handle,
                            base::BindRepeating(&navigationThrottleCallback),
                            navigation_interception::SynchronyMode::kSync));

#if BUILDFLAG(ENABLE_EXTENSIONS)
    MaybeAddThrottle(extensions::PDFIFrameNavigationThrottleQt::MaybeCreateThrottleFor(navigation_handle), &throttles);
#endif

    return throttles;
}

bool ContentBrowserClientQt::IsHandledURL(const GURL &url)
{
    return url::IsHandledProtocol(url.scheme());
}

bool ContentBrowserClientQt::HasCustomSchemeHandler(content::BrowserContext *browser_context,
                                                    const std::string &scheme)
{
    if (ProtocolHandlerRegistry *protocol_handler_registry =
              ProtocolHandlerRegistryFactory::GetForBrowserContext(browser_context)) {
        return protocol_handler_registry->IsHandledProtocol(scheme);
    }

    return false;
}

bool ContentBrowserClientQt::HasErrorPage(int httpStatusCode, content::WebContents *contents)
{
    if (contents) {
        WebEngineSettings *settings = nullptr;
        WebContentsDelegateQt *delegate =
                    static_cast<WebContentsDelegateQt*>(contents->GetDelegate());
        if (delegate)
            settings = delegate->webEngineSettings();
        if (settings && !settings->testAttribute(QWebEngineSettings::ErrorPageEnabled))
            return false;
    }
    // Use an internal error page, if we have one for the status code.
    return error_page::LocalizedError::HasStrings(error_page::Error::kHttpErrorDomain, httpStatusCode);
}

std::unique_ptr<content::LoginDelegate> ContentBrowserClientQt::CreateLoginDelegate(
        const net::AuthChallengeInfo &authInfo,
        content::WebContents *web_contents,
        const content::GlobalRequestID & /*request_id*/,
        bool /*is_main_frame*/,
        const GURL &url,
        scoped_refptr<net::HttpResponseHeaders> /*response_headers*/,
        bool first_auth_attempt,
        LoginAuthRequiredCallback auth_required_callback)
{
    auto loginDelegate = std::make_unique<LoginDelegateQt>(authInfo, web_contents, url, first_auth_attempt, std::move(auth_required_callback));
    return loginDelegate;
}

bool ContentBrowserClientQt::ShouldIsolateErrorPage(bool in_main_frame)
{
    Q_UNUSED(in_main_frame);
    return false;
}

bool ContentBrowserClientQt::ShouldUseProcessPerSite(content::BrowserContext* browser_context, const GURL& effective_url)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
     if (effective_url.SchemeIs(extensions::kExtensionScheme))
        return true;
#endif
    return ContentBrowserClient::ShouldUseProcessPerSite(browser_context, effective_url);
}

bool ContentBrowserClientQt::DoesSiteRequireDedicatedProcess(content::BrowserContext *browser_context,
                                                             const GURL &effective_site_url)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
    if (effective_site_url.SchemeIs(extensions::kExtensionScheme))
       return true;
#endif
    return ContentBrowserClient::DoesSiteRequireDedicatedProcess(browser_context, effective_site_url);
}

bool ContentBrowserClientQt::ShouldUseSpareRenderProcessHost(content::BrowserContext *browser_context,
                                                             const GURL &site_url)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
    if (site_url.SchemeIs(extensions::kExtensionScheme))
       return false;
#endif
    return ContentBrowserClient::ShouldUseSpareRenderProcessHost(browser_context, site_url);
}

bool ContentBrowserClientQt::ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(base::StringPiece scheme, bool is_embedded_origin_secure)
{
    if (is_embedded_origin_secure && scheme == content::kChromeUIScheme)
        return true;
#if BUILDFLAG(ENABLE_EXTENSIONS)
    return scheme == extensions::kExtensionScheme;
#else
    return false;
#endif
}

bool ContentBrowserClientQt::DoesSchemeAllowCrossOriginSharedWorker(const std::string &scheme)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
    // Extensions are allowed to start cross-origin shared workers.
    return scheme == extensions::kExtensionScheme;
#else
    return false;
#endif
}

void ContentBrowserClientQt::OverrideURLLoaderFactoryParams(content::BrowserContext *browser_context,
                                                            const url::Origin &origin,
                                                            bool is_for_isolated_world,
                                                            network::mojom::URLLoaderFactoryParams *factory_params)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
    extensions::URLLoaderFactoryManager::OverrideURLLoaderFactoryParams(
                browser_context, origin, is_for_isolated_world, factory_params);
#endif
}

std::string ContentBrowserClientQt::getUserAgent()
{
    // Mention the Chromium version we're based on to get passed stupid UA-string-based feature detection (several WebRTC demos need this)
    return content::BuildUserAgentFromProduct("QtWebEngine/" QTWEBENGINECORE_VERSION_STR " Chrome/" CHROMIUM_VERSION);
}

std::string ContentBrowserClientQt::GetProduct()
{
    QString productName(qApp->applicationName() % '/' % qApp->applicationVersion());
    return productName.toStdString();
}

scoped_refptr<network::SharedURLLoaderFactory> ContentBrowserClientQt::GetSystemSharedURLLoaderFactory()
{
    if (!SystemNetworkContextManager::GetInstance())
        return nullptr;
    return SystemNetworkContextManager::GetInstance()->GetSharedURLLoaderFactory();
}

network::mojom::NetworkContext *ContentBrowserClientQt::GetSystemNetworkContext()
{
    if (!SystemNetworkContextManager::GetInstance())
        return nullptr;
    return SystemNetworkContextManager::GetInstance()->GetContext();
}

void ContentBrowserClientQt::OnNetworkServiceCreated(network::mojom::NetworkService *network_service)
{
    if (!SystemNetworkContextManager::GetInstance())
        SystemNetworkContextManager::CreateInstance();

    // Need to set up global NetworkService state before anything else uses it.
    SystemNetworkContextManager::GetInstance()->OnNetworkServiceCreated(network_service);
}

void ContentBrowserClientQt::ConfigureNetworkContextParams(
        content::BrowserContext *context,
        bool in_memory,
        const base::FilePath &relative_partition_path,
        network::mojom::NetworkContextParams *network_context_params,
        cert_verifier::mojom::CertVerifierCreationParams *cert_verifier_creation_params)
{
    ProfileIODataQt::FromBrowserContext(context)->ConfigureNetworkContextParams(in_memory, relative_partition_path,
                                                                                network_context_params, cert_verifier_creation_params);

    mojo::PendingRemote<network::mojom::CookieManager> cookie_manager_remote;
    network_context_params->cookie_manager = cookie_manager_remote.InitWithNewPipeAndPassReceiver();
    ProfileIODataQt::FromBrowserContext(context)->cookieDelegate()->setMojoCookieManager(std::move(cookie_manager_remote));
}

std::vector<base::FilePath> ContentBrowserClientQt::GetNetworkContextsParentDirectory()
{
    return {
        toFilePath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)),
        toFilePath(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) };
}

void ContentBrowserClientQt::RegisterNonNetworkNavigationURLLoaderFactories(int frame_tree_node_id,
                                                                            ukm::SourceIdObj ukm_source_id,
                                                                            NonNetworkURLLoaderFactoryMap *factories)
{
    content::WebContents *web_contents = content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
    Profile *profile = Profile::FromBrowserContext(web_contents->GetBrowserContext());
    ProfileAdapter *profileAdapter = static_cast<ProfileQt *>(profile)->profileAdapter();

    for (const QByteArray &scheme : profileAdapter->customUrlSchemes())
        factories->emplace(scheme.toStdString(), CreateCustomURLLoaderFactory(profileAdapter));

#if BUILDFLAG(ENABLE_EXTENSIONS)
    factories->emplace(
        extensions::kExtensionScheme,
        extensions::CreateExtensionNavigationURLLoaderFactory(profile, ukm_source_id,
                                                              !!extensions::WebViewGuest::FromWebContents(web_contents)));
#endif
}

void ContentBrowserClientQt::RegisterNonNetworkWorkerMainResourceURLLoaderFactories(content::BrowserContext *browser_context,
                                                                                NonNetworkURLLoaderFactoryMap *factories)
{
    Profile *profile = Profile::FromBrowserContext(browser_context);
    ProfileAdapter *profileAdapter = static_cast<ProfileQt *>(profile)->profileAdapter();

    for (const QByteArray &scheme : profileAdapter->customUrlSchemes())
        factories->emplace(scheme.toStdString(), CreateCustomURLLoaderFactory(profileAdapter));

#if BUILDFLAG(ENABLE_EXTENSIONS)
    factories->emplace(
        extensions::kExtensionScheme,
        extensions::CreateExtensionWorkerMainResourceURLLoaderFactory(browser_context));
#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
}

void ContentBrowserClientQt::RegisterNonNetworkServiceWorkerUpdateURLLoaderFactories(content::BrowserContext* browser_context,
                                                                                NonNetworkURLLoaderFactoryMap* factories)
{
    DCHECK(browser_context);
    DCHECK(factories);
#if BUILDFLAG(ENABLE_EXTENSIONS)
    factories->emplace(
        extensions::kExtensionScheme,
        extensions::CreateExtensionServiceWorkerScriptURLLoaderFactory(browser_context));
#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
}

void ContentBrowserClientQt::RegisterNonNetworkSubresourceURLLoaderFactories(int render_process_id, int render_frame_id,
                                                                             NonNetworkURLLoaderFactoryMap *factories)
{
    content::RenderProcessHost *process_host = content::RenderProcessHost::FromID(render_process_id);
    Profile *profile = Profile::FromBrowserContext(process_host->GetBrowserContext());
    ProfileAdapter *profileAdapter = static_cast<ProfileQt *>(profile)->profileAdapter();

    for (const QByteArray &scheme : profileAdapter->customUrlSchemes())
        factories->emplace(scheme.toStdString(), CreateCustomURLLoaderFactory(profileAdapter));

    content::RenderFrameHost *frame_host = content::RenderFrameHost::FromID(render_process_id, render_frame_id);
    content::WebContents *web_contents = content::WebContents::FromRenderFrameHost(frame_host);
    GURL url;
    if (web_contents)
        url = web_contents->GetVisibleURL();

    // Install file scheme if necessary:
    bool install_file_scheme = false;
    if (web_contents) {
        const auto *settings = static_cast<WebContentsDelegateQt *>(web_contents->GetResponsibleWebContents()->GetDelegate())->webEngineSettings();
        if (settings->testAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls)) {
            for (const auto &local_scheme : url::GetLocalSchemes()) {
                if (url.SchemeIs(local_scheme)) {
                    install_file_scheme = true;
                    break;
                }
            }
        }
    }

    if (install_file_scheme && factories->find(url::kFileScheme) == factories->end()) {
        auto file_factory = content::CreateFileURLLoaderFactory(profile->GetPath(),
                                                                profile->GetSharedCorsOriginAccessList());
        factories->emplace(url::kFileScheme, std::move(file_factory));
    }

#if BUILDFLAG(ENABLE_EXTENSIONS)
    auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id, render_frame_id);
    if (factory)
        factories->emplace(extensions::kExtensionScheme, std::move(factory));

    if (!web_contents)
        return;

    extensions::ExtensionWebContentsObserverQt *web_observer =
            extensions::ExtensionWebContentsObserverQt::FromWebContents(web_contents);
    if (!web_observer)
        return;

    const extensions::Extension *extension = web_observer->GetExtensionFromFrame(frame_host, false);
    if (!extension)
        return;

    std::vector<std::string> allowed_webui_hosts;
    if ((extension->is_extension() || extension->is_platform_app()) &&
            extensions::Manifest::IsComponentLocation(extension->location())) {
        // Components of chrome that are implemented as extensions or platform apps
        // are allowed to use chrome://resources/ and chrome://theme/ URLs.
        allowed_webui_hosts.emplace_back(content::kChromeUIResourcesHost);
    }
    if (!allowed_webui_hosts.empty()) {
        factories->emplace(content::kChromeUIScheme,
                           content::CreateWebUIURLLoaderFactory(frame_host,
                                                                content::kChromeUIScheme,
                                                                std::move(allowed_webui_hosts)));
    }
#endif
}

base::flat_set<std::string> ContentBrowserClientQt::GetPluginMimeTypesWithExternalHandlers(
        content::BrowserContext *browser_context)
{
    base::flat_set<std::string> mime_types;
#if BUILDFLAG(ENABLE_EXTENSIONS)
    ProfileQt *profile = static_cast<ProfileQt *>(browser_context);
    for (const std::string &extension_id : MimeTypesHandler::GetMIMETypeAllowlist()) {
        const extensions::Extension *extension =
            extensions::ExtensionRegistry::Get(browser_context)
                ->enabled_extensions()
                .GetByID(extension_id);
        // The allowed extension may not be installed, so we have to nullptr
        if (!extension ||
            (profile->IsOffTheRecord() && !extensions::util::IsIncognitoEnabled(
                                              extension_id, browser_context))) {
            continue;
        }
        if (MimeTypesHandler *handler = MimeTypesHandler::GetHandler(extension)) {
            for (const auto &supported_mime_type : handler->mime_type_set())
                mime_types.insert(supported_mime_type);
        }
    }
#endif
    return mime_types;
}

bool ContentBrowserClientQt::WillCreateURLLoaderFactory(
        content::BrowserContext *browser_context,
        content::RenderFrameHost *frame,
        int render_process_id,
        URLLoaderFactoryType type,
        const url::Origin &request_initiator,
        base::Optional<int64_t> navigation_id,
        ukm::SourceIdObj ukm_source_id,
        mojo::PendingReceiver<network::mojom::URLLoaderFactory> *factory_receiver,
        mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient> *header_client,
        bool *bypass_redirect_checks,
        bool *disable_secure_dns,
        network::mojom::URLLoaderFactoryOverridePtr *factory_override)
{
    auto adapter = static_cast<ProfileQt *>(browser_context)->profileAdapter();
    int process_id = type == URLLoaderFactoryType::kNavigation ? 0 : render_process_id;
    auto proxied_receiver = std::move(*factory_receiver);
    mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_url_loader_factory;
    *factory_receiver = pending_url_loader_factory.InitWithNewPipeAndPassReceiver();
    // Will manage its own lifetime
    new ProxyingURLLoaderFactoryQt(adapter, process_id, std::move(proxied_receiver), std::move(pending_url_loader_factory));
    return true;
}

void ContentBrowserClientQt::SiteInstanceGotProcess(content::SiteInstance *site_instance)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
    content::BrowserContext *context = site_instance->GetBrowserContext();
    extensions::ExtensionRegistry *registry = extensions::ExtensionRegistry::Get(context);
    const extensions::Extension *extension = registry->enabled_extensions().GetExtensionOrAppByURL(site_instance->GetSiteURL());
    if (!extension)
        return;

    extensions::ProcessMap *processMap = extensions::ProcessMap::Get(context);
    processMap->Insert(extension->id(), site_instance->GetProcess()->GetID(), site_instance->GetId());
#endif
}

void ContentBrowserClientQt::SiteInstanceDeleting(content::SiteInstance *site_instance)
{
#if BUILDFLAG(ENABLE_EXTENSIONS)
    // Don't do anything if we're shutting down.
    if (content::BrowserMainRunner::ExitedMainMessageLoop() || !site_instance->HasProcess())
       return;

    content::BrowserContext *context = site_instance->GetBrowserContext();
    extensions::ExtensionRegistry *registry = extensions::ExtensionRegistry::Get(context);
    const extensions::Extension *extension = registry->enabled_extensions().GetExtensionOrAppByURL(site_instance->GetSiteURL());
    if (!extension)
        return;

    extensions::ProcessMap *processMap = extensions::ProcessMap::Get(context);
    processMap->Remove(extension->id(), site_instance->GetProcess()->GetID(), site_instance->GetId());
#endif
}

content::WebContentsViewDelegate *ContentBrowserClientQt::GetWebContentsViewDelegate(content::WebContents *web_contents)
{
    FormInteractionTabHelper::CreateForWebContents(web_contents);
    if (auto *registry = performance_manager::PerformanceManagerRegistry::GetInstance())
        registry->MaybeCreatePageNodeForWebContents(web_contents);

     return nullptr;
}

content::ContentBrowserClient::AllowWebBluetoothResult
ContentBrowserClientQt::AllowWebBluetooth(content::BrowserContext *browser_context,
                                          const url::Origin &requesting_origin,
                                          const url::Origin &embedding_origin)
{
    DCHECK(browser_context);
    return content::ContentBrowserClient::AllowWebBluetoothResult::BLOCK_GLOBALLY_DISABLED;
}

} // namespace QtWebEngineCore


switch(type) {
	case ERA:
		type_str = "era";
		break;
	case SCENARIO:
		type_str = "scenario";
		break;
	case MODIFICATION:
		type_str = "modification";
	}

/*
	Part of the Battle for Wesnoth Project http://example.com

*/

#include "game_initialization/depcheck.hpp"

#include <algorithm>

#include "serialization/string_utils.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "utils/general.hpp"

#include "gui/dialogs/depcheck_confirm_change.hpp"
#include "gui/dialogs/depcheck_select_new.hpp"
#include "gui/dialogs/message.hpp"
#include "game_config_view.hpp"

static lg::log_domain log_mp_create_depcheck("mp/create/depcheck");
#define DBG_MP LOG_STREAM(debug, log_mp_create_depcheck)

namespace
{
// helper function
void copy_keys(config& out, const config& in, const std::string& type, bool copy_force_key = false)
{
	if(in.has_attribute("allow_" + type)) {
		out["allow_" + type] = in["allow_" + type];
	} else if(in.has_attribute("disallow_" + type)) {
		out["disallow_" + type] = in["disallow_" + type];
	}

	if(in.has_attribute("ignore_incompatible_" + type)) {
		out["ignore_incompatible_" + type] = in["ignore_incompatible_" + type];
	}

	if(copy_force_key) {
		if(in.has_attribute("force_" + type)) {
			out["force_" + type] = in["force_" + type];
		}
	}
}
} // anonymous namespace

namespace ng
{
namespace depcheck
{
manager::manager(const game_config_view& gamecfg, bool mp)
	: depinfo_()
	, era_()
	, scenario_()
	, mods_()
	, prev_era_()
	, prev_scenario_()
	, prev_mods_()
{
	DBG_MP << "Initializing the dependency manager" << std::endl;

	for(const config& cfg : gamecfg.child_range("modification")) {
		component_availability type = cfg["type"].to_enum<component_availability>(component_availability::HYBRID);

		if((type != component_availability::MP || mp) && (type != component_availability::SP || !mp)) {
			config info;
			info["id"] = cfg["id"];
			info["name"] = cfg["name"];

			copy_keys(info, cfg, "scenario");
			copy_keys(info, cfg, "era");
			copy_keys(info, cfg, "modification");

			depinfo_.add_child("modification", std::move(info));
		}
	}

	for(const config& cfg : gamecfg.child_range("era")) {
		component_availability type = cfg["type"].to_enum<component_availability>(component_availability::MP);

		if((type != component_availability::MP || mp) && (type != component_availability::SP || !mp)) {
			config info;
			info["id"] = cfg["id"];
			info["name"] = cfg["name"];

			copy_keys(info, cfg, "scenario");
			copy_keys(info, cfg, "modification", true);

			depinfo_.add_child("era", std::move(info));
		}
	}

	for(const config& cfg : gamecfg.child_range("multiplayer")) {
		if(cfg["allow_new_game"].to_bool(true)) {
			config info;
			info["id"] = cfg["id"];
			info["name"] = cfg["name"];

			copy_keys(info, cfg, "era");
			copy_keys(info, cfg, "modification", true);

			depinfo_.add_child("scenario", std::move(info));
		}
	}

	for(const config& cfg : gamecfg.child_range("campaign")) {
		config info;
		info["id"] = cfg["id"];
		info["name"] = cfg["name"];
		info["allow_era_choice"] = cfg["allow_era_choice"];

		copy_keys(info, cfg, "era");
		copy_keys(info, cfg, "modification", true);

		depinfo_.add_child("scenario", std::move(info));
	}
}

void manager::save_state()
{
	DBG_MP << "Saving current state" << std::endl;
	prev_era_ = era_;
	prev_scenario_ = scenario_;
	prev_mods_ = mods_;
}

void manager::revert()
{
	DBG_MP << "Restoring previous state" << std::endl;
	era_ = prev_era_;
	scenario_ = prev_scenario_;
	mods_ = prev_mods_;
}

bool manager::exists(const elem& e) const
{
	for(const config& cfg : depinfo_.child_range(e.type)) {
		if(cfg["id"] == e.id) {
			return true;
		}
	}

	return false;
}

std::string manager::find_name_for(const elem& e) const
{
	const config& cfg = depinfo_.find_child(e.type, "id", e.id);
	return cfg["name"];
}

std::vector<std::string> manager::get_required_not_installed(const elem& e) const
{
	std::vector<std::string> result;

	std::vector<std::string> items = get_required(e);

	for(const std::string& str : items) {
		if(!exists(elem(str, "modification"))) {
			result.push_back(str);
		}
	}

	return result;
}

std::vector<std::string> manager::get_required(const elem& e) const
{
	std::vector<std::string> result;

	if(e.type == "modification") {
		return result;
	}

	config data = depinfo_.find_child(e.type, "id", e.id);

	if(data.has_attribute("force_modification")) {
		result = utils::split(data["force_modification"].str(), ',');
	}

	return result;
}

std::vector<std::string> manager::get_required_not_enabled(const elem& e) const
{
	std::vector<std::string> required = get_required(e);
	std::vector<std::string> result;

	for(std::string str : required) {
		if(!utils::contains(mods_, str)) {
			result.push_back(str);
		}
	}

	return result;
}

std::vector<std::string> manager::get_conflicting_enabled(const elem& e) const
{
	std::vector<std::string> result;

	for(const std::string& mod : mods_) {
		if(does_conflict(elem(mod, "modification"), e)) {
			result.push_back(mod);
		}
	}

	return result;
}

bool manager::does_conflict(const elem& elem1, const elem& elem2, bool directonly) const
{
	if(elem1 == elem2) {
		return false;
	}

	// We ignore nonexistent elements at this point, they will generate
	// errors in change_era()/change_scenario() anyways.
	if(!exists(elem1) || !exists(elem2)) {
		return false;
	}

	config data1 = depinfo_.find_child(elem1.type, "id", elem1.id);
	config data2 = depinfo_.find_child(elem2.type, "id", elem2.id);

	// Whether we should skip the check entirely
	if(data1.has_attribute("ignore_incompatible_" + elem2.type)) {
		std::vector<std::string> ignored = utils::split(data1["ignore_incompatible_" + elem2.type]);

		if(utils::contains(ignored, elem2.id)) {
			return false;
		}
	}

	if(data2.has_attribute("ignore_incompatible_" + elem1.type)) {
		std::vector<std::string> ignored = utils::split(data2["ignore_incompatible_" + elem1.type]);

		if(utils::contains(ignored, elem1.id)) {
			return false;
		}
	}

	if((elem1.type == "era" && data2["allow_era_choice"].to_bool(false)) ||(elem2.type == "era" && data1["allow_era_choice"].to_bool(false))) {
		return false;
	}

	bool result = false;

	// Checking for direct conflicts between elem1 and elem2
	if(data1.has_attribute("allow_" + elem2.type)) {
		std::vector<std::string> allowed = utils::split(data1["allow_" + elem2.type]);

		result = !utils::contains(allowed, elem2.id) && !does_require(elem1, elem2);
	} else if(data1.has_attribute("disallow_" + elem2.type)) {
		std::vector<std::string> disallowed = utils::split(data1["disallow_" + elem2.type]);

		result = utils::contains(disallowed, elem2.id);
	}

	if(data2.has_attribute("allow_" + elem1.type)) {
		std::vector<std::string> allowed = utils::split(data2["allow_" + elem1.type]);

		result = result || (!utils::contains(allowed, elem1.id) && !does_require(elem2, elem1));
	} else if(data2.has_attribute("disallow_" + elem1.type)) {
		std::vector<std::string> disallowed = utils::split(data2["disallow_" + elem1.type]);

		result = result || utils::contains(disallowed, elem1.id);
	}

	if(result) {
		return true;
	}

	// Checking for indirect conflicts (i.e. conflicts between dependencies)
	if(!directonly) {
		std::vector<std::string> req1 = get_required(elem1), req2 = get_required(elem2);

		for(const std::string& s : req1) {
			elem m(s, "modification");

			if(does_conflict(elem2, m, true)) {
				return true;
			}
		}

		for(const std::string& s : req2) {
			elem m(s, "modification");

			if(does_conflict(elem1, m, true)) {
				return true;
			}
		}

		for(const std::string& id1 : req1) {
			elem m1(id1, "modification");

			for(const std::string& id2 : req2) {
				elem m2(id2, "modification");

				if(does_conflict(m1, m2)) {
					return true;
				}
			}
		}
	}

	return false;
}

bool manager::does_require(const elem& elem1, const elem& elem2) const
{
	if(elem2.type != "modification") {
		return false;
	}

	config data = depinfo_.find_child(elem1.type, "id", elem1.id);

	if(data.has_attribute("force_modification")) {
		std::vector<std::string> required = utils::split(data["force_modification"]);

		return utils::contains(required, elem2.id);
	}

	return false;
}

void manager::try_era(const std::string& id, bool force)
{
	save_state();

	if(force) {
		era_ = id;
	} else if(!change_era(id)) {
		revert();
	}
}

void manager::try_scenario(const std::string& id, bool force)
{
	save_state();

	if(force) {
		scenario_ = id;
	} else if(!change_scenario(id)) {
		revert();
	}
}

void manager::try_modifications(const std::vector<std::string>& ids, bool force)
{
	save_state();

	if(force) {
		mods_ = ids;
	} else if(!change_modifications(ids)) {
		revert();
	}
}

void manager::try_modification_by_index(int index, bool activate, bool force)
{
	std::string id = depinfo_.child("modification", index)["id"];
	std::vector<std::string> mods_copy = mods_;

	if(activate) {
		if(std::find(mods_copy.begin(), mods_copy.end(), id) == mods_copy.end()) {
			mods_copy.push_back(id);
		}
	} else {
		std::vector<std::string>::iterator pos = std::find(mods_copy.begin(), mods_copy.end(), id);
		if(pos != mods_copy.end()) {
			mods_copy.erase(pos);
		}
	}

	try_modifications(mods_copy, force);
}

void manager::try_era_by_index(int index, bool force)
{
	try_era(depinfo_.child("era", index)["id"], force);
}

void manager::try_scenario_by_index(int index, bool force)
{
	try_scenario(depinfo_.child("scenario", index)["id"], force);
}

int manager::get_era_index() const
{
	int result = 0;
	for(const config& i : depinfo_.child_range("era")) {
		if(i["id"] == era_) {
			return result;
		}

		result++;
	}

	return -1;
}

int manager::get_scenario_index() const
{
	int result = 0;

	for(const config& i : depinfo_.child_range("scenario")) {
		if(i["id"] == scenario_) {
			return result;
		}

		result++;
	}

	return -1;
}

bool manager::is_modification_active(int index) const
{
	std::string id = depinfo_.child("modification", index)["id"];
	return std::find(mods_.begin(), mods_.end(), id) != mods_.end();
}

bool manager::is_modification_active(const std::string id) const
{
	return std::find(mods_.begin(), mods_.end(), id) != mods_.end();
}

bool manager::enable_mods_dialog(const std::vector<std::string>& mods, const std::string& requester)
{
	std::vector<std::string> items;
	for(const std::string& mod : mods) {
		items.push_back(depinfo_.find_child("modification", "id", mod)["name"]);
	}

	return gui2::dialogs::depcheck_confirm_change::execute(true, items, requester);
}

bool manager::disable_mods_dialog(const std::vector<std::string>& mods, const std::string& requester)
{
	std::vector<std::string> items;
	for(const std::string& mod : mods) {
		items.push_back(depinfo_.find_child("modification", "id", mod)["name"]);
	}

	return gui2::dialogs::depcheck_confirm_change::execute(false, items, requester);
}

std::string manager::change_era_dialog(const std::vector<std::string>& eras)
{
	std::vector<std::string> items;
	for(const std::string& era : eras) {
		items.push_back(depinfo_.find_child("era", "id", era)["name"]);
	}

	gui2::dialogs::depcheck_select_new dialog(ERA, items);

	if(dialog.show()) {
		return eras[dialog.result()];
	}

	return "";
}

std::string manager::change_scenario_dialog(const std::vector<std::string>& scenarios)
{
	std::vector<std::string> items;
	for(const std::string& scenario : scenarios) {
		items.push_back(depinfo_.find_child("scenario", "id", scenario)["name"]);
	}

	gui2::dialogs::depcheck_select_new dialog(SCENARIO, items);
	if(dialog.show()) {
		return scenarios[dialog.result()];
	}

	return "";
}

void manager::failure_dialog(const std::string& msg)
{
	gui2::show_message(_("Failed to resolve dependencies"), msg, _("OK"));
}

void manager::insert_element(component_type type, const config& data, int index)
{
	std::string type_str;

	depinfo_.add_child_at(type_str, data, index);
}

bool manager::change_scenario(const std::string& id)
{
	// Checking for missing dependencies
	if(!get_required_not_installed(elem(id, "scenario")).empty()) {
		std::string msg = _("Scenario can't be activated. Some dependencies are missing: ");

		msg += utils::join(get_required_not_installed(elem(id, "scenario")), ", ");

		failure_dialog(msg);
		return false;
	}

	scenario_ = id;

	elem scen = elem(id, "scenario");
	std::string scen_name = find_name_for(scen);

	// Firstly, we check if we have to enable/disable any mods
	std::vector<std::string> req = get_required_not_enabled(scen);
	std::vector<std::string> con = get_conflicting_enabled(scen);

	if(!req.empty()) {
		if(!enable_mods_dialog(req, scen_name)) {
			return false;
		}
	}

	if(!con.empty()) {
		if(!disable_mods_dialog(con, scen_name)) {
			return false;
		}
	}

	std::vector<std::string> newmods = req;
	for(const std::string& i : mods_) {
		if(!utils::contains(con, i)) {
			newmods.push_back(i);
		}
	}

	mods_ = newmods;

	// Now checking if the currently selected era conflicts the scenario
	// and changing era if necessary
	if(!does_conflict(scen, elem(era_, "era"))) {
		return true;
	}

	std::vector<std::string> compatible;
	for(const config& i : depinfo_.child_range("era")) {
		if(!does_conflict(scen, elem(i["id"], "era"))) {
			compatible.push_back(i["id"]);
		}
	}

	if(!compatible.empty()) {
		era_ = change_era_dialog(compatible);
	} else {
		failure_dialog(_("No compatible eras found."));
		return false;
	}

	if(era_.empty()) {
		return false;
	}

	return change_era(era_);
}

bool manager::change_era(const std::string& id)
{
	// Checking for missing dependencies
	if(!get_required_not_installed(elem(id, "era")).empty()) {
		std::string msg = _("Era can't be activated. Some dependencies are missing: ");

		msg += utils::join(get_required_not_installed(elem(id, "era")), ", ");
		failure_dialog(msg);
		return false;
	}

	era_ = id;

	elem era = elem(id, "era");
	std::string era_name = find_name_for(era);

	std::vector<std::string> req = get_required_not_enabled(era);
	std::vector<std::string> con = get_conflicting_enabled(era);

	// Firstly, we check if we have to enable/disable any mods
	if(!req.empty()) {
		if(!enable_mods_dialog(req, era_name)) {
			return false;
		}
	}

	if(!con.empty()) {
		if(!disable_mods_dialog(con, era_name)) {
			return false;
		}
	}

	std::vector<std::string> newmods = req;
	for(const std::string& i : mods_) {
		if(!utils::contains(con, i)) {
			newmods.push_back(i);
		}
	}

	mods_ = newmods;

	// Now checking if the currently selected scenario conflicts the era
	// and changing scenario if necessary
	if(!does_conflict(era, elem(scenario_, "scenario"))) {
		return true;
	}

	std::vector<std::string> compatible;
	for(const config& i : depinfo_.child_range("scenario")) {
		if(!does_conflict(era, elem(i["id"], "scenario"))) {
			compatible.push_back(i["id"]);
		}
	}

	if(!compatible.empty()) {
		scenario_ = change_scenario_dialog(compatible);
	} else {
		failure_dialog(_("No compatible scenarios found."));
		return false;
	}

	if(scenario_.empty()) {
		return false;
	}

	return change_scenario(scenario_);
}

bool manager::change_modifications(const std::vector<std::string>& modifications)
{
	// Checking if the selected combination of mods is valid at all
	std::vector<std::string> filtered;
	for(const std::string& i : modifications) {
		bool ok = true;
		elem ei(i, "modification");

		for(const std::string& j : filtered) {
			ok = ok && !does_conflict(ei, elem(j, "modification"));
		}

		if(ok) {
			filtered.push_back(i);
		}
	}

	if(filtered.size() != modifications.size()) {
		failure_dialog(_("Not all of the chosen modifications are compatible."
						 " Some of them will be disabled."));
	}

	mods_ = filtered;

	// Checking if the currently selected era is compatible with the set
	// modifications, and changing era if necessary
	std::vector<std::string> compatible;
	for(const config& c : depinfo_.child_range("era")) {
		elem era(c["id"], "era");
		bool ok = true;

		for(const std::string& s : mods_) {
			ok = ok && !does_conflict(era, elem(s, "modification"));
		}

		if(ok) {
			compatible.push_back(era.id);
		}
	}

	if(!utils::contains(compatible, era_)) {
		if(!compatible.empty()) {
			era_ = change_era_dialog(compatible);
		} else {
			failure_dialog(_("No compatible eras found."));
			return false;
		}

		if(era_.empty()) {
			return false;
		}

		if(!change_era(era_)) {
			return false;
		}
	} else {
		if(!change_era(era_)) {
			return false;
		}
	}

	compatible.clear();

	// Checking if the currently selected scenario is compatible with
	// the set modifications, and changing scenario if necessary
	for(const config& c : depinfo_.child_range("scenario")) {
		elem scen(c["id"], "scenario");
		bool ok = true;
		for(const std::string& s : mods_) {
			ok = ok && !does_conflict(scen, elem(s, "modification"));
		}

		if(ok) {
			compatible.push_back(scen.id);
		}
	}

	if(!utils::contains(compatible, scenario_)) {
		if(!compatible.empty()) {
			scenario_ = change_scenario_dialog(compatible);
		} else {
			failure_dialog(_("No compatible scenarios found."));
			return false;
		}

		if(scenario_.empty()) {
			return false;
		}

		return change_scenario(scenario_);
	} else {
		if(!change_scenario(scenario_)) {
			return false;
		}
	}

	return true;
}

} // namespace depcheck

} // namespace ng


void NoteTrackMenuTable::OnChangeOctave(wxCommandEvent &event)
{
   NoteTrack *const pTrack = static_cast<NoteTrack*>(mpData->pTrack);

   wxASSERT(event.GetId() == OnUpOctaveID
      || event.GetId() == OnDownOctaveID);

   const bool bDown = (OnDownOctaveID == event.GetId());
   pTrack->ShiftNoteRange((bDown) ? -12 : 12);

   AudacityProject *const project = &mpData->project;
   ProjectHistory::Get( *project )
      .ModifyState(false);
   mpData->result = RefreshCode::RefreshAll;
}

/**********************************************************************

Audacity: A Digital Audio Editor

NoteTrackControls.cpp

Paul Licameli split from TrackPanel.cpp

**********************************************************************/

#ifdef USE_MIDI
#include "NoteTrackControls.h"

#include "NoteTrackButtonHandle.h"

#include "../../ui/PlayableTrackButtonHandles.h"
#include "NoteTrackSliderHandles.h"

#include "../../../../HitTestResult.h"
#include "../../../../TrackArtist.h"
#include "../../../../TrackPanel.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../NoteTrack.h"
#include "../../../../widgets/PopupMenuTable.h"
#include "../../../../Project.h"
#include "../../../../ProjectHistory.h"
#include "../../../../RefreshCode.h"
#include "../../../../prefs/ThemePrefs.h"

#include <mutex>
#include <wx/frame.h>

NoteTrackControls::~NoteTrackControls()
{
}

std::vector<UIHandlePtr> NoteTrackControls::HitTest
(const TrackPanelMouseState & st,
 const AudacityProject *pProject)
{
   // Hits are mutually exclusive, results single
   std::vector<UIHandlePtr> results;
   const wxMouseState &state = st.state;
   const wxRect &rect = st.rect;
   if (state.ButtonIsDown(wxMOUSE_BTN_ANY)) {
      auto track = std::static_pointer_cast<NoteTrack>(FindTrack());
      auto result = [&]{
         UIHandlePtr result;
         if (NULL != (result = MuteButtonHandle::HitTest(
            mMuteHandle, state, rect, pProject, track)))
            return result;

         if (NULL != (result = SoloButtonHandle::HitTest(
            mSoloHandle, state, rect, pProject, track)))
            return result;
#ifdef EXPERIMENTAL_MIDI_OUT
         if (NULL != (result = VelocitySliderHandle::HitTest(
            mVelocityHandle, state, rect, track)))
            return result;
         if (NULL != (result = NoteTrackButtonHandle::HitTest(
            mClickHandle, state, rect, track)))
            return result;
#endif
         return result;
      }();
      if (result) {
         results.push_back(result);
         return results;
      }
   }

   return NoteTrackControlsBase::HitTest(st, pProject);
}

class NoteTrackMenuTable : public PopupMenuTable
{
   NoteTrackMenuTable()
      : PopupMenuTable{ "NoteTrack" }
   {}
   DECLARE_POPUP_MENU(NoteTrackMenuTable);

public:
   static NoteTrackMenuTable &Instance();

private:
   void InitUserData(void *pUserData) override
   {
      mpData = static_cast<NoteTrackControlsBase::InitMenuData*>(pUserData);
   }

   void DestroyMenu() override
   {
      mpData = nullptr;
   }

   NoteTrackControlsBase::InitMenuData *mpData{};

   void OnChangeOctave(wxCommandEvent &);
};

NoteTrackMenuTable &NoteTrackMenuTable::Instance()
{
   static NoteTrackMenuTable instance;
   return instance;
}

enum {
   OnUpOctaveID = 30000,
   OnDownOctaveID,
};

/// Scrolls the note track up or down by an octave

BEGIN_POPUP_MENU(NoteTrackMenuTable)
   BeginSection( "Basic" );
      AppendItem( "Up", OnUpOctaveID, XXO("Up &Octave"), POPUP_MENU_FN( OnChangeOctave ) );
      AppendItem( "Down", OnDownOctaveID, XXO("Down Octa&ve"), POPUP_MENU_FN( OnChangeOctave ) );
   EndSection();
END_POPUP_MENU()

PopupMenuTable *NoteTrackControls::GetMenuExtension(Track *)
{
#if defined(USE_MIDI)
   return &NoteTrackMenuTable::Instance();
#else
   return NULL;
#endif
}

// drawing related
#include "../../../../widgets/ASlider.h"
#include "../../../../TrackInfo.h"
#include "../../../../TrackPanelDrawingContext.h"
#include "../../../../ViewInfo.h"

using TCPLine = TrackInfo::TCPLine;

#ifdef USE_MIDI
enum : int {
   // PRL:  was it correct to include the margin?
   kMidiCellWidth = ( ( kTrackInfoWidth + kLeftMargin ) / 4) - 2,
   kMidiCellHeight = kTrackInfoBtnSize
};
#endif

#include "NoteTrackButtonHandle.h"
#include "NoteTrackSliderHandles.h"

namespace {
void GetMidiControlsHorizontalBounds( const wxRect &rect, wxRect &dest )
{
   dest.x = rect.x + 1; // To center slightly
   // PRL: TODO: kMidiCellWidth is defined in terms of the other constant
   // kTrackInfoWidth but I am trying to avoid use of that constant.
   // Can cell width be computed from dest.width instead?
   dest.width = kMidiCellWidth * 4;
}

void SliderDrawFunction
( LWSlider *(*Selector)
    (const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow*),
  wxDC *dc, const wxRect &rect, const Track *pTrack,
  wxWindow *pParent,
  bool captured, bool highlight )
{
   wxRect sliderRect = rect;
   TrackInfo::GetSliderHorizontalBounds( rect.GetTopLeft(), sliderRect );
   auto nt = static_cast<const NoteTrack*>( pTrack );
   Selector( sliderRect, nt, captured, pParent )->OnPaint(*dc, highlight);
}

#ifdef EXPERIMENTAL_MIDI_OUT
void VelocitySliderDrawFunction
( TrackPanelDrawingContext &context,
  const wxRect &rect, const Track *pTrack )
{
   auto dc = &context.dc;
   auto target = dynamic_cast<VelocitySliderHandle*>( context.target.get() );
   bool hit = target && target->GetTrack().get() == pTrack;
   bool captured = hit && target->IsClicked();

   const auto artist = TrackArtist::Get( context );
   auto pParent = FindProjectFrame( artist->parent->GetProject() );

   SliderDrawFunction(
      &NoteTrackControls::VelocitySlider, dc, rect, pTrack,
      pParent, captured, hit);
}
#endif

void MidiControlsDrawFunction
( TrackPanelDrawingContext &context,
  const wxRect &rect, const Track *pTrack )
{
   auto target = dynamic_cast<NoteTrackButtonHandle*>( context.target.get() );
   bool hit = target && target->GetTrack().get() == pTrack;
   auto channel = hit ? target->GetChannel() : -1;
   auto &dc = context.dc;
   wxRect midiRect = rect;
   GetMidiControlsHorizontalBounds(rect, midiRect);
   NoteTrack::DrawLabelControls
      ( static_cast<const NoteTrack *>(pTrack), dc, midiRect, channel );
}
}

static const struct NoteTrackTCPLines
   : TCPLines { NoteTrackTCPLines() {
   (TCPLines&)*this =
      NoteTrackControlsBase::StaticTCPLines();
   insert( end(), {
      { TCPLine::kItemMidiControlsRect, kMidiCellHeight * 4, 0,
        MidiControlsDrawFunction },
#ifdef EXPERIMENTAL_MIDI_OUT
      { TCPLine::kItemVelocity, kTrackInfoSliderHeight, kTrackInfoSliderExtra,
        VelocitySliderDrawFunction },
#endif
   } );
} } noteTrackTCPLines;

void NoteTrackControls::GetVelocityRect(const wxPoint &topleft, wxRect & dest)
{
   TrackInfo::GetSliderHorizontalBounds( topleft, dest );
   auto results = CalcItemY( noteTrackTCPLines, TCPLine::kItemVelocity );
   dest.y = topleft.y + results.first;
   dest.height = results.second;
}

void NoteTrackControls::GetMidiControlsRect(const wxRect & rect, wxRect & dest)
{
   GetMidiControlsHorizontalBounds( rect, dest );
   auto results = TrackInfo::CalcItemY(
      noteTrackTCPLines, TCPLine::kItemMidiControlsRect );
   dest.y = rect.y + results.first;
   dest.height = results.second;
}

unsigned NoteTrackControls::DefaultNoteTrackHeight()
{
   return TrackInfo::DefaultTrackHeight( noteTrackTCPLines );
}

const TCPLines &NoteTrackControls::GetTCPLines() const
{
   return noteTrackTCPLines;
};

namespace {

#ifdef EXPERIMENTAL_MIDI_OUT
   std::unique_ptr<LWSlider>
     gVelocityCaptured
   , gVelocity
   ;
#endif

}

#ifdef EXPERIMENTAL_MIDI_OUT
LWSlider * NoteTrackControls::VelocitySlider
(const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow *pParent)
{
   static std::once_flag flag;
   std::call_once( flag, [] {
      wxCommandEvent dummy;
      ReCreateVelocitySlider( dummy );
      wxTheApp->Bind(EVT_THEME_CHANGE, ReCreateVelocitySlider);
   } );

   wxPoint pos = sliderRect.GetPosition();
   float velocity = t ? t->GetVelocity() : 0.0;

   gVelocity->Move(pos);
   gVelocity->Set(velocity);
   gVelocityCaptured->Move(pos);
   gVelocityCaptured->Set(velocity);

   auto slider = (captured ? gVelocityCaptured : gVelocity).get();
   slider->SetParent( pParent );
   return slider;
}
#endif

void NoteTrackControls::ReCreateVelocitySlider( wxEvent &evt )
{
   evt.Skip();
#ifdef EXPERIMENTAL_MIDI_OUT
   wxPoint point{ 0, 0 };
   wxRect sliderRect;
   GetVelocityRect(point, sliderRect);

   /* i18n-hint: Title of the Velocity slider, used to adjust the volume of note tracks */
   gVelocity = std::make_unique<LWSlider>(nullptr, XO("Velocity"),
      wxPoint(sliderRect.x, sliderRect.y),
      wxSize(sliderRect.width, sliderRect.height),
      VEL_SLIDER);
   gVelocity->SetDefaultValue(0.0);
   gVelocityCaptured = std::make_unique<LWSlider>(nullptr, XO("Velocity"),
      wxPoint(sliderRect.x, sliderRect.y),
      wxSize(sliderRect.width, sliderRect.height),
      VEL_SLIDER);
   gVelocityCaptured->SetDefaultValue(0.0);
#endif
}

using DoGetNoteTrackControls = DoGetControls::Override< NoteTrack >;
template<> template<> auto DoGetNoteTrackControls::Implementation() -> Function {
   return [](NoteTrack &track) {
      return std::make_shared<NoteTrackControls>( track.SharedPointer() );
   };
}
static DoGetNoteTrackControls registerDoGetNoteTrackControls;

#include "../../../ui/TrackView.h"

using GetDefaultNoteTrackHeight = GetDefaultTrackHeight::Override< NoteTrack >;
template<> template<>
auto GetDefaultNoteTrackHeight::Implementation() -> Function {
   return [](NoteTrack &) {
      return NoteTrackControls::DefaultNoteTrackHeight();
   };
}
static GetDefaultNoteTrackHeight registerGetDefaultNoteTrackHeight;

#endif


/*Header-MicMac-eLiSe-25/06/2007

    MicMac : Multi Image Correspondances par Methodes Automatiques de Correlation
    eLiSe  : ELements of an Image Software Environnement

    www.micmac.ign.fr

    Author : Marc Pierrot Deseilligny
    Contributors : Gregoire Maillet, Didier Boldo.

[1] M. Pierrot-Deseilligny, N. Paparoditis.
    "A multiresolution and optimization-based image matching approach:
    An application to surface reconstruction from SPOT5-HRS stereo imagery."
    In IAPRS vol XXXVI-1/W41 in ISPRS Workshop On Topographic Mapping From Space
    (With Special Emphasis on Small Satellites), Ankara, Turquie, 02-2006.

[2] M. Pierrot-Deseilligny, "MicMac, un lociel de mise en correspondance
    d'images, adapte au contexte geograhique" to appears in
    Bulletin d'information de l'Institut Geographique National, 2007.

Francais :

   MicMac est un logiciel de mise en correspondance d'image adapte
   au contexte de recherche en information geographique. Il s'appuie sur
   la bibliotheque de manipulation d'image eLiSe. Il est distibue sous la

English :

    MicMac is an open source software specialized in image matching
    for research in geographic information. MicMac is built on the

Header-MicMac-eLiSe-25/06/2007*/

#include "StdAfx.h"
#include <algorithm>
#include "TapasCampari.h"

class cAppliLiquor;
class cIntervLiquor;

// NKS-Set-OfFile

class cIntervLiquor
{
     public :

         cIntervLiquor(cAppliLiquor * anAppli,int aBegin,int aEnd,int aProf);
         int Num()   const  {return mNum;}
         int Begin()   const  {return mBegin;}
         int End()   const  {return mEnd;}
         const std::string & NameOri() const {return mNameOri;}
         const std::string & NameMerge() const {return mNameMerge;}
         const std::string & PatLOF() const {return mPatLOF;}
         void SetF1(cIntervLiquor * aIL) {mF1=aIL;}
         void SetF2(cIntervLiquor * aIL) {mF2=aIL;}
         cIntervLiquor * F1() {return mF1;}
         cIntervLiquor * F2() {return mF2;}
         bool IsTerminal() const {return (mF1==0) && (mF2==0) ;}
         bool Done() const {return mDone;}
         bool Exe(int aExe)
         {
              if (aExe==1) return ! mDone;
              if (aExe==2) return true;
              if (aExe<=0) return false;
              return false;
         }

     private :
         static int      TheCpt;

         cAppliLiquor *  mAppli;
         int             mBegin;
         int             mEnd;
         int             mProf;
         int             mNum;
         cIntervLiquor * mF1;
         cIntervLiquor * mF2;
         std::string     mNameLOF; //  Name liste of file
         std::string     mPatLOF; //  Name liste of file
         std::string     mNameOri; //  Name liste of file
         std::string     mNameMerge; //  Name liste of file
         bool            mDone;
};

class cAppliLiquor : public cAppli_Tapas_Campari
{
    public :
        cAppliLiquor(int argc,char ** argv);
        const std::string & Dir() {return mEASF.mDir;}
        const std::string & Name(int aK) {return mVNames->at(aK);}
        const std::string & TimeStamp(int aK) {return mVTimeStamp->at(aK);}

        std::string NameExp(int aK);
        std::string NameExp(const std::string &);

    private :
        cIntervLiquor * SplitRecInterv(int aDeb,int aEnd,int aProf);
        std::string ComTerm(const  cIntervLiquor&) const;
        void DoComRec(int aLevel);

        std::string mFullName;
        std::string mCalib;
        cElemAppliSetFile mEASF;
        const std::vector<std::string> * mVNames;
        const std::vector<std::string> * mVTimeStamp;
        std::vector<std::list<cIntervLiquor*> > mInterv;

        int                              mNbIm;
        int                              mSzLim;
        int                              mOverlapMin;  // Il faut un peu de redondance
        Pt2di                            mIntervOverlap;  // Si redondance trop grande, risque de divergence au raccord
        double                           mOverlapProp; // entre les 2, il peut sembler logique d'avoir  une raccord prop
        int                              mExe;
        std::string                      mSH;
        std::string                      mSHTapas;
        bool                             mExpTxt;
        std::string                      mParamCommon;
        std::string                      mParamTapas;
        std::string                      mParamCampari;
        std::string                      mKeyName;
};

int cIntervLiquor::TheCpt=0;

cIntervLiquor::cIntervLiquor(cAppliLiquor * anAppli,int aBegin,int aEnd,int aProf) :
   mAppli (anAppli),
   mBegin (aBegin),
   mEnd   (aEnd),
   mProf  (aProf),
   mNum   (TheCpt++),
   mF1    (0),
   mF2    (0),
   mNameLOF    ("Liquor_LOF_"+ToString(mNum) + ".xml"),
   mPatLOF     (" NKS-Set-OfFile@"+mNameLOF + " "),
   mNameOri    ( (mProf==0) ? "LIQUOR_Final"  : ( "Liquor_Cmp_"+ ToString(mNum))),
   mNameMerge  ("Liquor_Merge_" + ToString(mNum)),
   mDone       (ELISE_fp::exist_file("Ori-"+mNameOri+"/Residus.xml"))
{
   cListOfName aLON;
   for (int aK=mBegin ; aK<mEnd ; aK++)
   {
      aLON.Name().push_back(mAppli->Name(aK));
   }
   MakeFileXML(aLON,mNameLOF);
}

std::string cAppliLiquor::NameExp(int aK)
{
    return NameExp(Name(aK));
}

std::string cAppliLiquor::NameExp(const std::string & aName)
{
   std::string aRes = aName;

   if (EAMIsInit(&mKeyName))
      aRes = mEASF.mICNM->Assoc1To1(mKeyName,aRes,true);

   return aRes;
}

cAppliLiquor::cAppliLiquor(int argc,char ** argv)  :
    mSzLim          (40),
    mIntervOverlap  (3,40),
    mOverlapProp    (0.1),
    mExe            (2),
	mExpTxt         (false)
{

    ElInitArgMain
     (
           argc,argv,
           LArgMain() << EAMC(mFullName,"Full name (Dir+Pat)", eSAM_IsPatFile)
                      << EAMC(mCalib,"Calibration Dir",eSAM_IsExistDirOri),
           LArgMain() << EAM(mSzLim,"SzInit",true,"Sz of initial interval (Def=50)")
                      << EAM(mOverlapProp,"OverLap",true,"Prop overlap (Def=0.1) ")
                      << EAM(mIntervOverlap,"IOL",true,"Interval Overlap Def(3,40) image / (4,8) Blocs")
                      << EAM(mExe,"Exe",true,"Execute commands 2 always, 1 if dont exist, 0 never")
                      << EAM(mSH,"SH",true,"Set of Homogue")
                      << EAM(mSHTapas,"SHTapas",true,"Set of Homologue special for Tapas")
                      << EAM(mExpTxt,"ExpTxt",true,"Homol in txt, Def=false")
                      << EAM(mKeyName,"KeyName",true,"Key Name for print")
                      << ArgATP()
    );

    if (MMVisualMode) return;

    mEASF.Init(mFullName);
    mVNames = mEASF.SetIm();
    mNbIm = (int)mVNames->size();
    StdCorrecNameOrient(mCalib,Dir());

    std::string aComPB;
    AddParamBloc(aComPB);
    if (mWithBlock)
    {
         InitAllImages(mEASF.mPat,mEASF.mICNM);
         mVNames = & BlocImagesByTime();
         mVTimeStamp = & BlocTimeStamps();
         if (! EAMIsInit(&mIntervOverlap))
         {
              mIntervOverlap = Pt2di(4,10);
         }
         mIntervOverlap = Pt2di(1,1) +mIntervOverlap * NbInBloc();

    }
    mParamCommon  =  StrParamBloc();

    if (EAMIsInit(&mSHTapas))
    {
       if (mSHTapas!="")
       {
          mParamTapas += BlQUOTE(" SH="+mSHTapas);
       }
    }
    else
    {
       mParamTapas    += BlQUOTE(StrInitOfEAM(&mSH));
    }
    mParamCampari  += BlQUOTE(StrInitOfEAM(&mSH));

    mParamCommon  += std::string(" SauvAutom=NONE ");
    mParamCommon  +=  std::string(" ExpTxt=") + ToString(mExpTxt);
/*
{
    for (const auto & aS : *mVNames)
        std::cout << "NNN= " << aS << "\n";
    std::cout << "BBBBB " << mWithBlock << "\n";
    std::cout << "ARGBL " << mParamCommon  << "\n";

    getchar();
}
*/

    SplitRecInterv(0,mNbIm,0);

    for (int aLevel = (int)(mInterv.size() - 1);  aLevel>=0 ;  aLevel--)
    {
         DoComRec(aLevel);
    }
}

void  cAppliLiquor::DoComRec(int aLevel)
{
   bool DoPrint = (mExe<0);
   if (DoPrint)
      std::cout << "==================== Lev=" << aLevel << " =============================\n";
   std::list<std::string> aLComMerge;
   for (const auto & anII : mInterv[aLevel])
   {
        cIntervLiquor & anIL = *anII;
        if (anIL.Exe(mExe) && (!anIL.IsTerminal()))
        {
            std::string aComMerge =    MM3dBinFile("Morito")
                                    + "Ori-"+ anIL.F1()->NameOri() + std::string("/Orientation.*xml ")
                                    + "Ori-"+ anIL.F2()->NameOri() + std::string("/Orientation.*xml ")
                                    +  anIL.NameMerge();

            if (anIL.Exe(mExe))
            {
               aLComMerge.push_back(aComMerge);
            }
         }
         if (DoPrint)
         {

             std::cout << " " << anIL.Num() << " "
                       << "["  <<  anIL.Begin()  << "," <<    anIL.End()  << "]"
                       << " " <<  NameExp(anIL.Begin())
                       << " " <<  NameExp(anIL.End()-1) << " "
                       << "\n";
         }
   }
   if (mExe)
      cEl_GPAO::DoComInParal(aLComMerge);

   std::list<std::string> aLComComp;
   for
   (
        std::list<cIntervLiquor*>::iterator II=mInterv[aLevel].begin();
        II!=mInterv[aLevel].end();
        II++
   )
   {
        cIntervLiquor & anIL = **II;
        std::string aCom;
        if (anIL.IsTerminal())
        {
             aCom =  ComTerm(anIL);
        }
        else
        {
            aCom =     MM3dBinFile("Campari")
                                +  anIL.PatLOF()
                                +  anIL.NameMerge() + " "
                                +  anIL.NameOri()  + " "
                                +  mParamCommon
                                +  mParamCampari
                                +  " SigmaTieP=2.0 ";
        }

        if (aLevel==0)
        {
        }
        if (anIL.Exe(mExe))
           aLComComp.push_back(aCom);
   }
   if (mExe)
      cEl_GPAO::DoComInParal(aLComComp);
}

/*
void cAppliLiquor::DoComTerm()
{
   std::list<std::string> aLComInit;
   for
   (
        std::list<cIntervLiquor*>::iterator II=mInterv.back().begin();
        II!=mInterv.back().end();
        II++
   )
   {
        std::string aCom = ComTerm(**II) ;
        aLComInit.push_back(aCom);
        std::cout << aCom << "\n";
   }
   if (mExe)
      cEl_GPAO::DoComInParal(aLComInit);
}
*/

cIntervLiquor * cAppliLiquor::SplitRecInterv(int aDeb,int aEnd,int aProf)
{
   cIntervLiquor * aRes =  new cIntervLiquor(this,aDeb,aEnd,aProf);
   {
       std::cout << (aRes->Done() ?  "1 " :  "0 " ) ;
       for (int aK=0 ; aK< aProf +1 ; aK++)
           std::cout << " +-+ ";
       std::cout << "SplitRecInterv " << aDeb << " " << aEnd << " " << aProf << "\n";
   }
   int aLarg = aEnd-aDeb;
   if (aLarg < mSzLim)
   {

   }
   else
   {
         int anOverlap = ElMax(mIntervOverlap.x,ElMin(mIntervOverlap.y,round_ni(aLarg*mOverlapProp)));
         int aNewLarg = round_up((aLarg + anOverlap)/2.0);

         int aNewEnd = ElMin(aDeb+aNewLarg,aEnd);
         int aNewDeb = ElMax(aEnd-aNewLarg,aDeb);

         bool DoSplit = (aNewEnd < aEnd) && (aNewDeb >aDeb);
         if (mWithBlock)
         {
            while ((aNewEnd < mNbIm) &&  (TimeStamp(aNewEnd-1)==TimeStamp(aNewEnd)))
                  aNewEnd++;
            while ((aNewDeb>0) &&  (TimeStamp(aNewDeb)==TimeStamp(aNewDeb-1)))
                  aNewDeb --;
            DoSplit = DoSplit &&  (aNewEnd < aEnd) && (aNewDeb >aDeb);
            if (DoSplit)
            {
                 if ( (LongestBloc(aDeb,aNewEnd)!=NbInBloc()) || (LongestBloc(aNewDeb,aEnd)!=NbInBloc()) )
                    DoSplit =false;
            }
         }

         if (DoSplit)
         {
            aRes->SetF1(SplitRecInterv(aDeb,aNewEnd,aProf+1));
            aRes->SetF2(SplitRecInterv(aNewDeb,aEnd,aProf+1));
         }
   }

   // Incremente la liste des
   for (int aP = (int)mInterv.size() ; aP<=aProf ; aP++)
   {
      std::list<cIntervLiquor*> aL;
      mInterv.push_back(aL);
   }
   mInterv[aProf].push_back(aRes);

   return aRes;
}

/*
std::string  cAppliLiquor::StrImMinMax(const  cIntervLiquor& anIL) const
{
   std::string aN1  = (*mVNames)[anIL.Begin()];
   std::string aN2  = (*mVNames)[anIL.End()-1];
   return  std::string(" ImMinMax=[" +aN1+ "," + aN2 + "] ");
}
*/

std::string cAppliLiquor::ComTerm(const  cIntervLiquor& anIL) const
{

   std::string aOut = anIL.NameOri();

   std::string aCom = MM3dBinFile("Tapas")
                      + " Figee "
                      + anIL.PatLOF()
                      + std::string(" InCal=" + mCalib)
                      + std::string(" ImInit=MIDLE ")
                      + std::string(" Out=" + aOut + " ")
                      + std::string(" RefineAll=false ")
                      + mParamCommon
                      + mParamTapas
                      ;

   return aCom;
}

int Liquor_main(int argc,char ** argv)
{
    cAppliLiquor anAppli(argc,argv);

    BanniereLiquor();
    return EXIT_SUCCESS;
}

/*Footer-MicMac-eLiSe-25/06/2007

Ce logiciel est un programme informatique servant Ã  la mise en
correspondances d'images pour la reconstruction du relief.

respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions

En contrepartie de l'accessibilitÃ© au code source et des droits de copie,
de modification et de redistribution accordÃ©s par cette licence, il n'est
offert aux utilisateurs qu'une garantie limitÃ©e.  Pour les mÃªmes raisons,
seule une responsabilitÃ© restreinte pÃ¨se sur l'auteur du programme,  le
titulaire des droits patrimoniaux et les concÃ©dants successifs.

A cet Ã©gard  l'attention de l'utilisateur est attirÃ©e sur les risques
associÃ©s au chargement,  Ã  l'utilisation,  Ã  la modification et/ou au
dÃ©veloppement et Ã  la reproduction du logiciel par l'utilisateur Ã©tant
donnÃ© sa spÃ©cificitÃ© de logiciel libre, qui peut le rendre complexe Ã
manipuler et qui le rÃ©serve donc Ã  des dÃ©veloppeurs et des professionnels
avertis possÃ©dant  des  connaissances  informatiques approfondies.  Les
utilisateurs sont donc invitÃ©s Ã  charger  et  tester  l'adÃ©quation  du
logiciel Ã  leurs besoins dans des conditions permettant d'assurer la
sÃ©curitÃ© de leurs systÃ¨mes et ou de leurs donnÃ©es et, plus gÃ©nÃ©ralement,
Ã  l'utiliser et l'exploiter dans les mÃªmes conditions de sÃ©curitÃ©.

Le fait que vous puissiez accÃ©der Ã  cet en-tÃªte signifie que vous avez
termes.
Footer-MicMac-eLiSe-25/06/2007*/