diff options
-rw-r--r-- | chrome/browser/about_flags.cc | 1 | ||||
-rw-r--r-- | chrome/browser/chrome_content_browser_client.cc | 76 | ||||
-rw-r--r-- | chrome/browser/chrome_content_browser_client.h | 12 | ||||
-rw-r--r-- | chrome/browser/chrome_worker_message_filter.cc | 96 | ||||
-rw-r--r-- | chrome/browser/chrome_worker_message_filter.h | 48 | ||||
-rw-r--r-- | chrome/browser/ui/webui/workers_ui.cc | 4 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | content/browser/mock_content_browser_client.cc | 21 | ||||
-rw-r--r-- | content/browser/mock_content_browser_client.h | 12 | ||||
-rw-r--r-- | content/browser/worker_host/worker_process_host.cc | 12 | ||||
-rw-r--r-- | content/browser/worker_host/worker_process_host.h | 3 | ||||
-rw-r--r-- | content/public/browser/content_browser_client.h | 23 | ||||
-rw-r--r-- | content/shell/shell_content_browser_client.cc | 21 | ||||
-rw-r--r-- | content/shell/shell_content_browser_client.h | 12 |
14 files changed, 168 insertions, 175 deletions
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 05094e2..aa0efa8 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -20,7 +20,6 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "content/browser/user_metrics.h" -#include "content/public/common/content_switches.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/gl/gl_switches.h" diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index dcab99b..eef9057 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -17,7 +17,6 @@ #include "chrome/browser/chrome_benchmarking_message_filter.h" #include "chrome/browser/chrome_plugin_message_filter.h" #include "chrome/browser/chrome_quota_permission_context.h" -#include "chrome/browser/chrome_worker_message_filter.h" #include "chrome/browser/content_settings/cookie_settings.h" #include "chrome/browser/content_settings/tab_specific_content_settings.h" #include "chrome/browser/download/download_util.h" @@ -331,11 +330,6 @@ void ChromeContentBrowserClient::PluginProcessHostCreated( host->AddFilter(new ChromePluginMessageFilter(host)); } -void ChromeContentBrowserClient::WorkerProcessHostCreated( - WorkerProcessHost* host) { - host->AddFilter(new ChromeWorkerMessageFilter(host)); -} - content::WebUIFactory* ChromeContentBrowserClient::GetWebUIFactory() { return ChromeWebUIFactory::GetInstance(); } @@ -722,6 +716,76 @@ bool ChromeContentBrowserClient::AllowSaveLocalState( return !io_data->clear_local_state_on_exit()->GetValue(); } +bool ChromeContentBrowserClient::AllowWorkerDatabase( + int worker_route_id, + const GURL& url, + const string16& name, + const string16& display_name, + unsigned long estimated_size, + WorkerProcessHost* worker_process_host) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ProfileIOData* io_data = reinterpret_cast<ProfileIOData*>( + worker_process_host->resource_context()->GetUserData(NULL)); + CookieSettings* cookie_settings = io_data->GetCookieSettings(); + bool allow = cookie_settings->IsSettingCookieAllowed(url, url); + + // Record access to database for potential display in UI: Find the worker + // instance and forward the message to all attached documents. + WorkerProcessHost::Instances::const_iterator i; + for (i = worker_process_host->instances().begin(); + i != worker_process_host->instances().end(); ++i) { + if (i->worker_route_id() != worker_route_id) + continue; + const WorkerDocumentSet::DocumentInfoSet& documents = + i->worker_document_set()->documents(); + for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = + documents.begin(); doc != documents.end(); ++doc) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind( + &TabSpecificContentSettings::WebDatabaseAccessed, + doc->render_process_id(), doc->render_view_id(), + url, name, display_name, !allow)); + } + break; + } + + return allow; +} + +bool ChromeContentBrowserClient::AllowWorkerFileSystem( + int worker_route_id, + const GURL& url, + WorkerProcessHost* worker_process_host) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ProfileIOData* io_data = reinterpret_cast<ProfileIOData*>( + worker_process_host->resource_context()->GetUserData(NULL)); + CookieSettings* cookie_settings = io_data->GetCookieSettings(); + bool allow = cookie_settings->IsSettingCookieAllowed(url, url); + + // Record access to file system for potential display in UI: Find the worker + // instance and forward the message to all attached documents. + WorkerProcessHost::Instances::const_iterator i; + for (i = worker_process_host->instances().begin(); + i != worker_process_host->instances().end(); ++i) { + if (i->worker_route_id() != worker_route_id) + continue; + const WorkerDocumentSet::DocumentInfoSet& documents = + i->worker_document_set()->documents(); + for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = + documents.begin(); doc != documents.end(); ++doc) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind( + &TabSpecificContentSettings::FileSystemAccessed, + doc->render_process_id(), doc->render_view_id(), url, !allow)); + } + break; + } + + return allow; +} + net::URLRequestContext* ChromeContentBrowserClient::OverrideRequestContextForURL( const GURL& url, const content::ResourceContext& context) { diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index 14aba9d..2366fe3 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h @@ -26,7 +26,6 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient { virtual void BrowserRenderProcessHostCreated( BrowserRenderProcessHost* host) OVERRIDE; virtual void PluginProcessHostCreated(PluginProcessHost* host) OVERRIDE; - virtual void WorkerProcessHostCreated(WorkerProcessHost* host) OVERRIDE; virtual content::WebUIFactory* GetWebUIFactory() OVERRIDE; virtual bool ShouldUseProcessPerSite(content::BrowserContext* browser_context, const GURL& effective_url) OVERRIDE; @@ -64,6 +63,17 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient { net::CookieOptions* options) OVERRIDE; virtual bool AllowSaveLocalState( const content::ResourceContext& context) OVERRIDE; + virtual bool AllowWorkerDatabase( + int worker_route_id, + const GURL& url, + const string16& name, + const string16& display_name, + unsigned long estimated_size, + WorkerProcessHost* worker_process_host) OVERRIDE; + virtual bool AllowWorkerFileSystem( + int worker_route_id, + const GURL& url, + WorkerProcessHost* worker_process_host) OVERRIDE; virtual net::URLRequestContext* OverrideRequestContextForURL( const GURL& url, const content::ResourceContext& context) OVERRIDE; virtual QuotaPermissionContext* CreateQuotaPermissionContext() OVERRIDE; diff --git a/chrome/browser/chrome_worker_message_filter.cc b/chrome/browser/chrome_worker_message_filter.cc deleted file mode 100644 index 5ed2c68..0000000 --- a/chrome/browser/chrome_worker_message_filter.cc +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/chrome_worker_message_filter.h" - -#include "base/bind.h" -#include "chrome/browser/content_settings/cookie_settings.h" -#include "chrome/browser/content_settings/tab_specific_content_settings.h" -#include "chrome/browser/profiles/profile_io_data.h" -#include "content/browser/resource_context.h" -#include "content/browser/worker_host/worker_process_host.h" -#include "content/common/worker_messages.h" - -using content::BrowserThread; - -ChromeWorkerMessageFilter::ChromeWorkerMessageFilter(WorkerProcessHost* process) - : process_(process) { - ProfileIOData* io_data = reinterpret_cast<ProfileIOData*>( - process->resource_context()->GetUserData(NULL)); - cookie_settings_ = io_data->GetCookieSettings(); -} - -ChromeWorkerMessageFilter::~ChromeWorkerMessageFilter() { -} - -bool ChromeWorkerMessageFilter::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(ChromeWorkerMessageFilter, message) - IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase, OnAllowDatabase) - IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowFileSystem, OnAllowFileSystem) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - - return handled; -} - -bool ChromeWorkerMessageFilter::Send(IPC::Message* message) { - return process_->Send(message); -} - -void ChromeWorkerMessageFilter::OnAllowDatabase(int worker_route_id, - const GURL& url, - const string16& name, - const string16& display_name, - unsigned long estimated_size, - bool* result) { - *result = cookie_settings_->IsSettingCookieAllowed(url, url); - - // Record access to database for potential display in UI: Find the worker - // instance and forward the message to all attached documents. - WorkerProcessHost::Instances::const_iterator i; - for (i = process_->instances().begin(); i != process_->instances().end(); - ++i) { - if (i->worker_route_id() != worker_route_id) - continue; - const WorkerDocumentSet::DocumentInfoSet& documents = - i->worker_document_set()->documents(); - for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = - documents.begin(); doc != documents.end(); ++doc) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind( - &TabSpecificContentSettings::WebDatabaseAccessed, - doc->render_process_id(), doc->render_view_id(), - url, name, display_name, !*result)); - } - break; - } -} - -void ChromeWorkerMessageFilter::OnAllowFileSystem(int worker_route_id, - const GURL& url, - bool* result) { - *result = cookie_settings_->IsSettingCookieAllowed(url, url); - - // Record access to file system for potential display in UI: Find the worker - // instance and forward the message to all attached documents. - WorkerProcessHost::Instances::const_iterator i; - for (i = process_->instances().begin(); i != process_->instances().end(); - ++i) { - if (i->worker_route_id() != worker_route_id) - continue; - const WorkerDocumentSet::DocumentInfoSet& documents = - i->worker_document_set()->documents(); - for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc = - documents.begin(); doc != documents.end(); ++doc) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind( - &TabSpecificContentSettings::FileSystemAccessed, - doc->render_process_id(), doc->render_view_id(), url, !*result)); - } - break; - } -} diff --git a/chrome/browser/chrome_worker_message_filter.h b/chrome/browser/chrome_worker_message_filter.h deleted file mode 100644 index e5a0bd62..0000000 --- a/chrome/browser/chrome_worker_message_filter.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_CHROME_WORKER_MESSAGE_FILTER_H_ -#define CHROME_BROWSER_CHROME_WORKER_MESSAGE_FILTER_H_ -#pragma once - -#include "base/string16.h" -#include "ipc/ipc_channel_proxy.h" - -class CookieSettings; -class GURL; -class WorkerProcessHost; - -// This class filters out incoming Chrome-specific IPC messages for the renderer -// process on the IPC thread. -class ChromeWorkerMessageFilter : public IPC::ChannelProxy::MessageFilter, - public IPC::Message::Sender { - public: - explicit ChromeWorkerMessageFilter(WorkerProcessHost* process); - - // BrowserMessageFilter methods: - virtual bool OnMessageReceived(const IPC::Message& message); - - // IPC::Message::Sender methods: - virtual bool Send(IPC::Message* message); - - private: - virtual ~ChromeWorkerMessageFilter(); - - void OnAllowDatabase(int worker_route_id, - const GURL& url, - const string16& name, - const string16& display_name, - unsigned long estimated_size, - bool* result); - void OnAllowFileSystem(int worker_route_id, - const GURL& url, - bool* result); - - WorkerProcessHost* process_; - scoped_refptr<CookieSettings> cookie_settings_; - - DISALLOW_COPY_AND_ASSIGN(ChromeWorkerMessageFilter); -}; - -#endif // CHROME_BROWSER_CHROME_WORKER_MESSAGE_FILTER_H_ diff --git a/chrome/browser/ui/webui/workers_ui.cc b/chrome/browser/ui/webui/workers_ui.cc index df424dc..fe76d9c 100644 --- a/chrome/browser/ui/webui/workers_ui.cc +++ b/chrome/browser/ui/webui/workers_ui.cc @@ -22,7 +22,6 @@ #include "content/browser/worker_host/worker_service.h" #include "content/browser/worker_host/worker_service_observer.h" #include "content/common/devtools_messages.h" -#include "content/common/worker_messages.h" #include "grit/generated_resources.h" #include "grit/workers_resources.h" #include "ui/base/resource/resource_bundle.h" @@ -153,7 +152,8 @@ static void TerminateWorker(int worker_process_id, int worker_route_id) { for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS); !iter.Done(); ++iter) { if (iter->id() == worker_process_id) { - (*iter)->Send(new WorkerMsg_TerminateWorkerContext(worker_route_id)); + WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter); + worker->TerminateWorker(worker_route_id); return; } } diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 059b48b..eec1f15 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -370,8 +370,6 @@ 'browser/chrome_plugin_service_filter.h', 'browser/chrome_quota_permission_context.cc', 'browser/chrome_quota_permission_context.h', - 'browser/chrome_worker_message_filter.cc', - 'browser/chrome_worker_message_filter.h', 'browser/chromeos/accessibility_util.cc', 'browser/chromeos/accessibility_util.h', 'browser/chromeos/audio_handler.cc', diff --git a/content/browser/mock_content_browser_client.cc b/content/browser/mock_content_browser_client.cc index 98b8ecd..c36e92a 100644 --- a/content/browser/mock_content_browser_client.cc +++ b/content/browser/mock_content_browser_client.cc @@ -50,10 +50,6 @@ void MockContentBrowserClient::PluginProcessHostCreated( PluginProcessHost* host) { } -void MockContentBrowserClient::WorkerProcessHostCreated( - WorkerProcessHost* host) { -} - WebUIFactory* MockContentBrowserClient::GetWebUIFactory() { // Return an empty factory so callsites don't have to check for NULL. return EmptyWebUIFactory::GetInstance(); @@ -147,6 +143,23 @@ bool MockContentBrowserClient::AllowSaveLocalState( return true; } +bool MockContentBrowserClient::AllowWorkerDatabase( + int worker_route_id, + const GURL& url, + const string16& name, + const string16& display_name, + unsigned long estimated_size, + WorkerProcessHost* worker_process_host) { + return true; +} + +bool MockContentBrowserClient::AllowWorkerFileSystem( + int worker_route_id, + const GURL& url, + WorkerProcessHost* worker_process_host) { + return true; +} + QuotaPermissionContext* MockContentBrowserClient::CreateQuotaPermissionContext() { return NULL; diff --git a/content/browser/mock_content_browser_client.h b/content/browser/mock_content_browser_client.h index 23d53b1..e8cb53d 100644 --- a/content/browser/mock_content_browser_client.h +++ b/content/browser/mock_content_browser_client.h @@ -33,7 +33,6 @@ class MockContentBrowserClient : public ContentBrowserClient { virtual void BrowserRenderProcessHostCreated( BrowserRenderProcessHost* host) OVERRIDE; virtual void PluginProcessHostCreated(PluginProcessHost* host) OVERRIDE; - virtual void WorkerProcessHostCreated(WorkerProcessHost* host) OVERRIDE; virtual WebUIFactory* GetWebUIFactory() OVERRIDE; virtual GURL GetEffectiveURL(content::BrowserContext* browser_context, const GURL& url) OVERRIDE; @@ -71,6 +70,17 @@ class MockContentBrowserClient : public ContentBrowserClient { net::CookieOptions* options) OVERRIDE; virtual bool AllowSaveLocalState( const content::ResourceContext& context) OVERRIDE; + virtual bool AllowWorkerDatabase( + int worker_route_id, + const GURL& url, + const string16& name, + const string16& display_name, + unsigned long estimated_size, + WorkerProcessHost* worker_process_host) OVERRIDE; + virtual bool AllowWorkerFileSystem( + int worker_route_id, + const GURL& url, + WorkerProcessHost* worker_process_host) OVERRIDE; virtual net::URLRequestContext* OverrideRequestContextForURL( const GURL& url, const content::ResourceContext& context) OVERRIDE; virtual QuotaPermissionContext* CreateQuotaPermissionContext() OVERRIDE; diff --git a/content/browser/worker_host/worker_process_host.cc b/content/browser/worker_host/worker_process_host.cc index 7d2fbfb..96bcba3 100644 --- a/content/browser/worker_host/worker_process_host.cc +++ b/content/browser/worker_host/worker_process_host.cc @@ -234,8 +234,6 @@ bool WorkerProcessHost::Init(int render_process_id) { base::PLATFORM_FILE_WRITE); } - // Call the embedder first so that their IPC filters have priority. - content::GetContentClient()->browser()->WorkerProcessHostCreated(this); CreateMessageFilters(render_process_id); return true; @@ -373,13 +371,15 @@ void WorkerProcessHost::OnAllowDatabase(int worker_route_id, const string16& display_name, unsigned long estimated_size, bool* result) { - *result = true; + *result = content::GetContentClient()->browser()->AllowWorkerDatabase( + worker_route_id, url, name, display_name, estimated_size, this); } void WorkerProcessHost::OnAllowFileSystem(int worker_route_id, const GURL& url, bool* result) { - *result = true; + *result = content::GetContentClient()->browser()->AllowWorkerFileSystem( + worker_route_id, url, this); } void WorkerProcessHost::RelayMessage( @@ -514,6 +514,10 @@ void WorkerProcessHost::DocumentDetached(WorkerMessageFilter* filter, } } +void WorkerProcessHost::TerminateWorker(int worker_route_id) { + Send(new WorkerMsg_TerminateWorkerContext(worker_route_id)); +} + WorkerProcessHost::WorkerInstance::WorkerInstance( const GURL& url, const string16& name, diff --git a/content/browser/worker_host/worker_process_host.h b/content/browser/worker_host/worker_process_host.h index 9608262..a3c4a13 100644 --- a/content/browser/worker_host/worker_process_host.h +++ b/content/browser/worker_host/worker_process_host.h @@ -131,6 +131,9 @@ class WorkerProcessHost : public BrowserChildProcessHost { void DocumentDetached(WorkerMessageFilter* filter, unsigned long long document_id); + // Terminates the given worker, i.e. based on a UI action. + void TerminateWorker(int worker_route_id); + typedef std::list<WorkerInstance> Instances; const Instances& instances() const { return instances_; } diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index 727e6c9..6ab3c87 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -119,11 +119,6 @@ class ContentBrowserClient { // embedder's IPC filters have priority. virtual void PluginProcessHostCreated(PluginProcessHost* host) = 0; - // Notifies that a WorkerProcessHost has been created. This is called - // before the content layer adds its own message filters, so that the - // embedder's IPC filters have priority. - virtual void WorkerProcessHostCreated(WorkerProcessHost* host) = 0; - // Gets the WebUIFactory which will be responsible for generating WebUIs. virtual WebUIFactory* GetWebUIFactory() = 0; @@ -206,6 +201,24 @@ class ContentBrowserClient { virtual bool AllowSaveLocalState( const content::ResourceContext& context) = 0; + // Allow the embedder to control if access to web database by a worker is + // allowed. + // This is called on the IO thread. + virtual bool AllowWorkerDatabase(int worker_route_id, + const GURL& url, + const string16& name, + const string16& display_name, + unsigned long estimated_size, + WorkerProcessHost* worker_process_host) = 0; + + // Allow the embedder to control if access to file system by a worker is + // allowed. + // This is called on the IO thread. + virtual bool AllowWorkerFileSystem( + int worker_route_id, + const GURL& url, + WorkerProcessHost* worker_process_host) = 0; + // Allows the embedder to override the request context based on the URL for // certain operations, like cookie access. Returns NULL to indicate the // regular request context should be used. diff --git a/content/shell/shell_content_browser_client.cc b/content/shell/shell_content_browser_client.cc index 2a31c9a..74ff4aa 100644 --- a/content/shell/shell_content_browser_client.cc +++ b/content/shell/shell_content_browser_client.cc @@ -64,10 +64,6 @@ void ShellContentBrowserClient::PluginProcessHostCreated( PluginProcessHost* host) { } -void ShellContentBrowserClient::WorkerProcessHostCreated( - WorkerProcessHost* host) { -} - WebUIFactory* ShellContentBrowserClient::GetWebUIFactory() { // Return an empty factory so callsites don't have to check for NULL. return EmptyWebUIFactory::GetInstance(); @@ -162,6 +158,23 @@ bool ShellContentBrowserClient::AllowSaveLocalState( return true; } +bool ShellContentBrowserClient::AllowWorkerDatabase( + int worker_route_id, + const GURL& url, + const string16& name, + const string16& display_name, + unsigned long estimated_size, + WorkerProcessHost* worker_process_host) { + return true; +} + +bool ShellContentBrowserClient::AllowWorkerFileSystem( + int worker_route_id, + const GURL& url, + WorkerProcessHost* worker_process_host) { + return true; +} + QuotaPermissionContext* ShellContentBrowserClient::CreateQuotaPermissionContext() { return NULL; diff --git a/content/shell/shell_content_browser_client.h b/content/shell/shell_content_browser_client.h index e8aa52f..3c60622 100644 --- a/content/shell/shell_content_browser_client.h +++ b/content/shell/shell_content_browser_client.h @@ -45,7 +45,6 @@ class ShellContentBrowserClient : public ContentBrowserClient virtual void BrowserRenderProcessHostCreated( BrowserRenderProcessHost* host) OVERRIDE; virtual void PluginProcessHostCreated(PluginProcessHost* host) OVERRIDE; - virtual void WorkerProcessHostCreated(WorkerProcessHost* host) OVERRIDE; virtual WebUIFactory* GetWebUIFactory() OVERRIDE; virtual GURL GetEffectiveURL(content::BrowserContext* browser_context, const GURL& url) OVERRIDE; @@ -84,6 +83,17 @@ class ShellContentBrowserClient : public ContentBrowserClient net::CookieOptions* options) OVERRIDE; virtual bool AllowSaveLocalState( const content::ResourceContext& context) OVERRIDE; + virtual bool AllowWorkerDatabase( + int worker_route_id, + const GURL& url, + const string16& name, + const string16& display_name, + unsigned long estimated_size, + WorkerProcessHost* worker_process_host) OVERRIDE; + virtual bool AllowWorkerFileSystem( + int worker_route_id, + const GURL& url, + WorkerProcessHost* worker_process_host) OVERRIDE; virtual net::URLRequestContext* OverrideRequestContextForURL( const GURL& url, const content::ResourceContext& context) OVERRIDE; virtual QuotaPermissionContext* CreateQuotaPermissionContext() OVERRIDE; |