Merge pull request #6172 from electron/session-setuseragent
Add Session.setUserAgent(userAgent[, acceptLanguages]) API
This commit is contained in:
commit
fd40f3d344
11 changed files with 120 additions and 58 deletions
|
@ -463,10 +463,11 @@ void App::DisableHardwareAcceleration(mate::Arguments* args) {
|
||||||
void App::ImportCertificate(
|
void App::ImportCertificate(
|
||||||
const base::DictionaryValue& options,
|
const base::DictionaryValue& options,
|
||||||
const net::CompletionCallback& callback) {
|
const net::CompletionCallback& callback) {
|
||||||
auto browser_context = AtomBrowserMainParts::Get()->browser_context();
|
auto browser_context = brightray::BrowserContext::From("", false);
|
||||||
if (!certificate_manager_model_) {
|
if (!certificate_manager_model_) {
|
||||||
std::unique_ptr<base::DictionaryValue> copy = options.CreateDeepCopy();
|
std::unique_ptr<base::DictionaryValue> copy = options.CreateDeepCopy();
|
||||||
CertificateManagerModel::Create(browser_context,
|
CertificateManagerModel::Create(
|
||||||
|
browser_context.get(),
|
||||||
base::Bind(&App::OnCertificateManagerModelCreated,
|
base::Bind(&App::OnCertificateManagerModelCreated,
|
||||||
base::Unretained(this),
|
base::Unretained(this),
|
||||||
base::Passed(©),
|
base::Passed(©),
|
||||||
|
|
|
@ -41,8 +41,10 @@
|
||||||
#include "net/http/http_auth_preferences.h"
|
#include "net/http/http_auth_preferences.h"
|
||||||
#include "net/proxy/proxy_service.h"
|
#include "net/proxy/proxy_service.h"
|
||||||
#include "net/proxy/proxy_config_service_fixed.h"
|
#include "net/proxy/proxy_config_service_fixed.h"
|
||||||
|
#include "net/url_request/static_http_user_agent_settings.h"
|
||||||
#include "net/url_request/url_request_context.h"
|
#include "net/url_request/url_request_context.h"
|
||||||
#include "net/url_request/url_request_context_getter.h"
|
#include "net/url_request/url_request_context_getter.h"
|
||||||
|
#include "ui/base/l10n/l10n_util.h"
|
||||||
|
|
||||||
using content::BrowserThread;
|
using content::BrowserThread;
|
||||||
using content::StoragePartition;
|
using content::StoragePartition;
|
||||||
|
@ -93,6 +95,15 @@ uint32_t GetQuotaMask(const std::vector<std::string>& quota_types) {
|
||||||
return quota_mask;
|
return quota_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetUserAgentInIO(scoped_refptr<net::URLRequestContextGetter> getter,
|
||||||
|
const std::string& accept_lang,
|
||||||
|
const std::string& user_agent) {
|
||||||
|
getter->GetURLRequestContext()->set_http_user_agent_settings(
|
||||||
|
new net::StaticHttpUserAgentSettings(
|
||||||
|
net::HttpUtil::GenerateAcceptLanguageHeader(accept_lang),
|
||||||
|
user_agent));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace mate {
|
namespace mate {
|
||||||
|
@ -455,6 +466,23 @@ void Session::AllowNTLMCredentialsForDomains(const std::string& domains) {
|
||||||
domains));
|
domains));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::SetUserAgent(const std::string& user_agent,
|
||||||
|
mate::Arguments* args) {
|
||||||
|
browser_context_->SetUserAgent(user_agent);
|
||||||
|
|
||||||
|
std::string accept_lang = l10n_util::GetApplicationLocale("");
|
||||||
|
args->GetNext(&accept_lang);
|
||||||
|
|
||||||
|
auto getter = browser_context_->GetRequestContext();
|
||||||
|
getter->GetNetworkTaskRunner()->PostTask(
|
||||||
|
FROM_HERE,
|
||||||
|
base::Bind(&SetUserAgentInIO, getter, accept_lang, user_agent));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Session::GetUserAgent() {
|
||||||
|
return browser_context_->GetUserAgent();
|
||||||
|
}
|
||||||
|
|
||||||
v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
|
v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
|
||||||
if (cookies_.IsEmpty()) {
|
if (cookies_.IsEmpty()) {
|
||||||
auto handle = atom::api::Cookies::Create(isolate, browser_context());
|
auto handle = atom::api::Cookies::Create(isolate, browser_context());
|
||||||
|
@ -520,6 +548,8 @@ void Session::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("clearHostResolverCache", &Session::ClearHostResolverCache)
|
.SetMethod("clearHostResolverCache", &Session::ClearHostResolverCache)
|
||||||
.SetMethod("allowNTLMCredentialsForDomains",
|
.SetMethod("allowNTLMCredentialsForDomains",
|
||||||
&Session::AllowNTLMCredentialsForDomains)
|
&Session::AllowNTLMCredentialsForDomains)
|
||||||
|
.SetMethod("setUserAgent", &Session::SetUserAgent)
|
||||||
|
.SetMethod("getUserAgent", &Session::GetUserAgent)
|
||||||
.SetProperty("cookies", &Session::Cookies)
|
.SetProperty("cookies", &Session::Cookies)
|
||||||
.SetProperty("protocol", &Session::Protocol)
|
.SetProperty("protocol", &Session::Protocol)
|
||||||
.SetProperty("webRequest", &Session::WebRequest);
|
.SetProperty("webRequest", &Session::WebRequest);
|
||||||
|
|
|
@ -57,15 +57,7 @@ class Session: public mate::TrackableObject<Session>,
|
||||||
static void BuildPrototype(v8::Isolate* isolate,
|
static void BuildPrototype(v8::Isolate* isolate,
|
||||||
v8::Local<v8::ObjectTemplate> prototype);
|
v8::Local<v8::ObjectTemplate> prototype);
|
||||||
|
|
||||||
protected:
|
// Methods.
|
||||||
Session(v8::Isolate* isolate, AtomBrowserContext* browser_context);
|
|
||||||
~Session();
|
|
||||||
|
|
||||||
// content::DownloadManager::Observer:
|
|
||||||
void OnDownloadCreated(content::DownloadManager* manager,
|
|
||||||
content::DownloadItem* item) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
|
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
|
||||||
template<CacheAction action>
|
template<CacheAction action>
|
||||||
void DoCacheAction(const net::CompletionCallback& callback);
|
void DoCacheAction(const net::CompletionCallback& callback);
|
||||||
|
@ -80,10 +72,21 @@ class Session: public mate::TrackableObject<Session>,
|
||||||
mate::Arguments* args);
|
mate::Arguments* args);
|
||||||
void ClearHostResolverCache(mate::Arguments* args);
|
void ClearHostResolverCache(mate::Arguments* args);
|
||||||
void AllowNTLMCredentialsForDomains(const std::string& domains);
|
void AllowNTLMCredentialsForDomains(const std::string& domains);
|
||||||
|
void SetUserAgent(const std::string& user_agent, mate::Arguments* args);
|
||||||
|
std::string GetUserAgent();
|
||||||
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
|
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
|
||||||
v8::Local<v8::Value> Protocol(v8::Isolate* isolate);
|
v8::Local<v8::Value> Protocol(v8::Isolate* isolate);
|
||||||
v8::Local<v8::Value> WebRequest(v8::Isolate* isolate);
|
v8::Local<v8::Value> WebRequest(v8::Isolate* isolate);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Session(v8::Isolate* isolate, AtomBrowserContext* browser_context);
|
||||||
|
~Session();
|
||||||
|
|
||||||
|
// content::DownloadManager::Observer:
|
||||||
|
void OnDownloadCreated(content::DownloadManager* manager,
|
||||||
|
content::DownloadItem* item) override;
|
||||||
|
|
||||||
|
private:
|
||||||
// Cached object.
|
// Cached object.
|
||||||
v8::Global<v8::Value> cookies_;
|
v8::Global<v8::Value> cookies_;
|
||||||
v8::Global<v8::Value> protocol_;
|
v8::Global<v8::Value> protocol_;
|
||||||
|
|
|
@ -61,11 +61,9 @@
|
||||||
#include "native_mate/dictionary.h"
|
#include "native_mate/dictionary.h"
|
||||||
#include "native_mate/object_template_builder.h"
|
#include "native_mate/object_template_builder.h"
|
||||||
#include "net/http/http_response_headers.h"
|
#include "net/http/http_response_headers.h"
|
||||||
#include "net/url_request/static_http_user_agent_settings.h"
|
|
||||||
#include "net/url_request/url_request_context.h"
|
#include "net/url_request/url_request_context.h"
|
||||||
#include "third_party/WebKit/public/web/WebInputEvent.h"
|
#include "third_party/WebKit/public/web/WebInputEvent.h"
|
||||||
#include "third_party/WebKit/public/web/WebFindOptions.h"
|
#include "third_party/WebKit/public/web/WebFindOptions.h"
|
||||||
#include "ui/base/l10n/l10n_util.h"
|
|
||||||
|
|
||||||
#include "atom/common/node_includes.h"
|
#include "atom/common/node_includes.h"
|
||||||
|
|
||||||
|
@ -76,15 +74,6 @@ struct PrintSettings {
|
||||||
bool print_background;
|
bool print_background;
|
||||||
};
|
};
|
||||||
|
|
||||||
void SetUserAgentInIO(scoped_refptr<net::URLRequestContextGetter> getter,
|
|
||||||
std::string accept_lang,
|
|
||||||
std::string user_agent) {
|
|
||||||
getter->GetURLRequestContext()->set_http_user_agent_settings(
|
|
||||||
new net::StaticHttpUserAgentSettings(
|
|
||||||
net::HttpUtil::GenerateAcceptLanguageHeader(accept_lang),
|
|
||||||
user_agent));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace mate {
|
namespace mate {
|
||||||
|
@ -811,7 +800,7 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) {
|
||||||
|
|
||||||
std::string user_agent;
|
std::string user_agent;
|
||||||
if (options.Get("userAgent", &user_agent))
|
if (options.Get("userAgent", &user_agent))
|
||||||
SetUserAgent(user_agent);
|
web_contents()->SetUserAgentOverride(user_agent);
|
||||||
|
|
||||||
std::string extra_headers;
|
std::string extra_headers;
|
||||||
if (options.Get("extraHeaders", &extra_headers))
|
if (options.Get("extraHeaders", &extra_headers))
|
||||||
|
@ -898,14 +887,9 @@ bool WebContents::IsCrashed() const {
|
||||||
return web_contents()->IsCrashed();
|
return web_contents()->IsCrashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::SetUserAgent(const std::string& user_agent) {
|
void WebContents::SetUserAgent(const std::string& user_agent,
|
||||||
|
mate::Arguments* args) {
|
||||||
web_contents()->SetUserAgentOverride(user_agent);
|
web_contents()->SetUserAgentOverride(user_agent);
|
||||||
scoped_refptr<net::URLRequestContextGetter> getter =
|
|
||||||
web_contents()->GetBrowserContext()->GetRequestContext();
|
|
||||||
|
|
||||||
auto accept_lang = l10n_util::GetApplicationLocale("");
|
|
||||||
getter->GetNetworkTaskRunner()->PostTask(FROM_HERE,
|
|
||||||
base::Bind(&SetUserAgentInIO, getter, accept_lang, user_agent));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string WebContents::GetUserAgent() {
|
std::string WebContents::GetUserAgent() {
|
||||||
|
|
|
@ -81,7 +81,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||||
void GoForward();
|
void GoForward();
|
||||||
void GoToOffset(int offset);
|
void GoToOffset(int offset);
|
||||||
bool IsCrashed() const;
|
bool IsCrashed() const;
|
||||||
void SetUserAgent(const std::string& user_agent);
|
void SetUserAgent(const std::string& user_agent, mate::Arguments* args);
|
||||||
std::string GetUserAgent();
|
std::string GetUserAgent();
|
||||||
void InsertCSS(const std::string& css);
|
void InsertCSS(const std::string& css);
|
||||||
bool SavePage(const base::FilePath& full_file_path,
|
bool SavePage(const base::FilePath& full_file_path,
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "atom/browser/atom_browser_context.h"
|
#include "atom/browser/atom_browser_context.h"
|
||||||
#include "atom/browser/atom_browser_main_parts.h"
|
|
||||||
#include "atom/common/google_api_key.h"
|
#include "atom/common/google_api_key.h"
|
||||||
|
#include "content/public/browser/browser_thread.h"
|
||||||
#include "content/public/browser/geolocation_provider.h"
|
#include "content/public/browser/geolocation_provider.h"
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
@ -25,6 +25,7 @@ const char* kGeolocationProviderURL =
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AtomAccessTokenStore::AtomAccessTokenStore() {
|
AtomAccessTokenStore::AtomAccessTokenStore() {
|
||||||
|
LOG(ERROR) << "AtomAccessTokenStore";
|
||||||
content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices();
|
content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,21 +34,35 @@ AtomAccessTokenStore::~AtomAccessTokenStore() {
|
||||||
|
|
||||||
void AtomAccessTokenStore::LoadAccessTokens(
|
void AtomAccessTokenStore::LoadAccessTokens(
|
||||||
const LoadAccessTokensCallback& callback) {
|
const LoadAccessTokensCallback& callback) {
|
||||||
AccessTokenMap access_token_map;
|
content::BrowserThread::PostTaskAndReply(
|
||||||
|
content::BrowserThread::UI,
|
||||||
// Equivelent to access_token_map[kGeolocationProviderURL].
|
FROM_HERE,
|
||||||
// Somehow base::string16 is causing compilation errors when used in a pair
|
base::Bind(&AtomAccessTokenStore::GetRequestContextOnUIThread, this),
|
||||||
// of std::map on Linux, this can work around it.
|
base::Bind(&AtomAccessTokenStore::RespondOnOriginatingThread,
|
||||||
std::pair<GURL, base::string16> token_pair;
|
this, callback));
|
||||||
token_pair.first = GURL(kGeolocationProviderURL);
|
|
||||||
access_token_map.insert(token_pair);
|
|
||||||
|
|
||||||
auto browser_context = AtomBrowserMainParts::Get()->browser_context();
|
|
||||||
callback.Run(access_token_map, browser_context->url_request_context_getter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AtomAccessTokenStore::SaveAccessToken(const GURL& server_url,
|
void AtomAccessTokenStore::SaveAccessToken(const GURL& server_url,
|
||||||
const base::string16& access_token) {
|
const base::string16& access_token) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AtomAccessTokenStore::GetRequestContextOnUIThread() {
|
||||||
|
auto browser_context = brightray::BrowserContext::From("", false);
|
||||||
|
request_context_getter_ = browser_context->GetRequestContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomAccessTokenStore::RespondOnOriginatingThread(
|
||||||
|
const LoadAccessTokensCallback& callback) {
|
||||||
|
// Equivelent to access_token_map[kGeolocationProviderURL].
|
||||||
|
// Somehow base::string16 is causing compilation errors when used in a pair
|
||||||
|
// of std::map on Linux, this can work around it.
|
||||||
|
AccessTokenMap access_token_map;
|
||||||
|
std::pair<GURL, base::string16> token_pair;
|
||||||
|
token_pair.first = GURL(kGeolocationProviderURL);
|
||||||
|
access_token_map.insert(token_pair);
|
||||||
|
|
||||||
|
callback.Run(access_token_map, request_context_getter_.get());
|
||||||
|
request_context_getter_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace atom
|
} // namespace atom
|
||||||
|
|
|
@ -9,12 +9,10 @@
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
class AtomBrowserContext;
|
|
||||||
|
|
||||||
class AtomAccessTokenStore : public content::AccessTokenStore {
|
class AtomAccessTokenStore : public content::AccessTokenStore {
|
||||||
public:
|
public:
|
||||||
AtomAccessTokenStore();
|
AtomAccessTokenStore();
|
||||||
virtual ~AtomAccessTokenStore();
|
~AtomAccessTokenStore();
|
||||||
|
|
||||||
// content::AccessTokenStore:
|
// content::AccessTokenStore:
|
||||||
void LoadAccessTokens(
|
void LoadAccessTokens(
|
||||||
|
@ -23,6 +21,11 @@ class AtomAccessTokenStore : public content::AccessTokenStore {
|
||||||
const base::string16& access_token) override;
|
const base::string16& access_token) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void GetRequestContextOnUIThread();
|
||||||
|
void RespondOnOriginatingThread(const LoadAccessTokensCallback& callback);
|
||||||
|
|
||||||
|
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(AtomAccessTokenStore);
|
DISALLOW_COPY_AND_ASSIGN(AtomAccessTokenStore);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -68,16 +68,7 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition,
|
||||||
: brightray::BrowserContext(partition, in_memory),
|
: brightray::BrowserContext(partition, in_memory),
|
||||||
cert_verifier_(new AtomCertVerifier),
|
cert_verifier_(new AtomCertVerifier),
|
||||||
network_delegate_(new AtomNetworkDelegate) {
|
network_delegate_(new AtomNetworkDelegate) {
|
||||||
}
|
// Construct user agent string.
|
||||||
|
|
||||||
AtomBrowserContext::~AtomBrowserContext() {
|
|
||||||
}
|
|
||||||
|
|
||||||
net::NetworkDelegate* AtomBrowserContext::CreateNetworkDelegate() {
|
|
||||||
return network_delegate_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string AtomBrowserContext::GetUserAgent() {
|
|
||||||
Browser* browser = Browser::Get();
|
Browser* browser = Browser::Get();
|
||||||
std::string name = RemoveWhitespace(browser->GetName());
|
std::string name = RemoveWhitespace(browser->GetName());
|
||||||
std::string user_agent;
|
std::string user_agent;
|
||||||
|
@ -91,7 +82,22 @@ std::string AtomBrowserContext::GetUserAgent() {
|
||||||
browser->GetVersion().c_str(),
|
browser->GetVersion().c_str(),
|
||||||
CHROME_VERSION_STRING);
|
CHROME_VERSION_STRING);
|
||||||
}
|
}
|
||||||
return content::BuildUserAgentFromProduct(user_agent);
|
user_agent_ = content::BuildUserAgentFromProduct(user_agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomBrowserContext::~AtomBrowserContext() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomBrowserContext::SetUserAgent(const std::string& user_agent) {
|
||||||
|
user_agent_ = user_agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
net::NetworkDelegate* AtomBrowserContext::CreateNetworkDelegate() {
|
||||||
|
return network_delegate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AtomBrowserContext::GetUserAgent() {
|
||||||
|
return user_agent_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<net::URLRequestJobFactory>
|
std::unique_ptr<net::URLRequestJobFactory>
|
||||||
|
|
|
@ -22,6 +22,8 @@ class AtomBrowserContext : public brightray::BrowserContext {
|
||||||
AtomBrowserContext(const std::string& partition, bool in_memory);
|
AtomBrowserContext(const std::string& partition, bool in_memory);
|
||||||
~AtomBrowserContext() override;
|
~AtomBrowserContext() override;
|
||||||
|
|
||||||
|
void SetUserAgent(const std::string& user_agent);
|
||||||
|
|
||||||
// brightray::URLRequestContextGetter::Delegate:
|
// brightray::URLRequestContextGetter::Delegate:
|
||||||
net::NetworkDelegate* CreateNetworkDelegate() override;
|
net::NetworkDelegate* CreateNetworkDelegate() override;
|
||||||
std::string GetUserAgent() override;
|
std::string GetUserAgent() override;
|
||||||
|
@ -47,6 +49,7 @@ class AtomBrowserContext : public brightray::BrowserContext {
|
||||||
std::unique_ptr<AtomDownloadManagerDelegate> download_manager_delegate_;
|
std::unique_ptr<AtomDownloadManagerDelegate> download_manager_delegate_;
|
||||||
std::unique_ptr<WebViewManager> guest_manager_;
|
std::unique_ptr<WebViewManager> guest_manager_;
|
||||||
std::unique_ptr<AtomPermissionManager> permission_manager_;
|
std::unique_ptr<AtomPermissionManager> permission_manager_;
|
||||||
|
std::string user_agent_;
|
||||||
|
|
||||||
// Managed by brightray::BrowserContext.
|
// Managed by brightray::BrowserContext.
|
||||||
AtomCertVerifier* cert_verifier_;
|
AtomCertVerifier* cert_verifier_;
|
||||||
|
|
|
@ -341,6 +341,23 @@ session.defaultSession.allowNTLMCredentialsForDomains('*example.com, *foobar.com
|
||||||
session.defaultSession.allowNTLMCredentialsForDomains('*')
|
session.defaultSession.allowNTLMCredentialsForDomains('*')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `ses.setUserAgent(userAgent[, acceptLanguages])`
|
||||||
|
|
||||||
|
* `userAgent` String
|
||||||
|
* `acceptLanguages` String (optional)
|
||||||
|
|
||||||
|
Overrides the `userAgent` and `acceptLanguages` for this session.
|
||||||
|
|
||||||
|
The `acceptLanguages` must a comma separated ordered list of language codes, for
|
||||||
|
example `"en-US,fr,de,ko,zh-CN,ja"`.
|
||||||
|
|
||||||
|
This doesn't affect existing `WebContents`, and each `WebContents` can use
|
||||||
|
`webContents.setUserAgent` to override the session-wide user agent.
|
||||||
|
|
||||||
|
#### `ses.getUserAgent()`
|
||||||
|
|
||||||
|
Returns a `String` representing the user agent for this session.
|
||||||
|
|
||||||
#### `ses.webRequest`
|
#### `ses.webRequest`
|
||||||
|
|
||||||
The `webRequest` API set allows to intercept and modify contents of a request at
|
The `webRequest` API set allows to intercept and modify contents of a request at
|
||||||
|
|
2
vendor/brightray
vendored
2
vendor/brightray
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 3a98173c6848bac241074979b24f5a54cc92b5b0
|
Subproject commit 8244628f0c1d0eb15c659d42e882fb5d447c77ba
|
Loading…
Add table
Add a link
Reference in a new issue