Merge pull request #6470 from electron/session-options
Ad options for creating Session
This commit is contained in:
commit
183b599b9c
14 changed files with 189 additions and 133 deletions
|
@ -497,7 +497,7 @@ bool App::IsAccessibilitySupportEnabled() {
|
|||
void App::ImportCertificate(
|
||||
const base::DictionaryValue& options,
|
||||
const net::CompletionCallback& callback) {
|
||||
auto browser_context = brightray::BrowserContext::From("", false);
|
||||
auto browser_context = AtomBrowserContext::From("", false);
|
||||
if (!certificate_manager_model_) {
|
||||
std::unique_ptr<base::DictionaryValue> copy = options.CreateDeepCopy();
|
||||
CertificateManagerModel::Create(
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/net/url_request_async_asar_job.h"
|
||||
#include "atom/browser/net/url_request_buffer_job.h"
|
||||
#include "atom/browser/net/url_request_fetch_job.h"
|
||||
|
@ -192,7 +193,13 @@ void Protocol::BuildPrototype(
|
|||
namespace {
|
||||
|
||||
void RegisterStandardSchemes(
|
||||
const std::vector<std::string>& schemes) {
|
||||
const std::vector<std::string>& schemes, mate::Arguments* args) {
|
||||
if (atom::Browser::Get()->is_ready()) {
|
||||
args->ThrowError("protocol.registerStandardSchemes should be called before "
|
||||
"app is ready");
|
||||
return;
|
||||
}
|
||||
|
||||
auto policy = content::ChildProcessSecurityPolicy::GetInstance();
|
||||
for (const auto& scheme : schemes) {
|
||||
url::AddStandardScheme(scheme.c_str(), url::SCHEME_WITHOUT_PORT);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "atom/browser/api/atom_api_download_item.h"
|
||||
#include "atom/browser/api/atom_api_protocol.h"
|
||||
#include "atom/browser/api/atom_api_web_request.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/atom_permission_manager.h"
|
||||
|
@ -20,6 +21,7 @@
|
|||
#include "atom/common/native_mate_converters/gurl_converter.h"
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/native_mate_converters/value_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/guid.h"
|
||||
|
@ -163,6 +165,8 @@ namespace api {
|
|||
|
||||
namespace {
|
||||
|
||||
const char kPersistPrefix[] = "persist:";
|
||||
|
||||
// The wrapSession funtion which is implemented in JavaScript
|
||||
using WrapSessionCallback = base::Callback<void(v8::Local<v8::Value>)>;
|
||||
WrapSessionCallback g_wrap_session;
|
||||
|
@ -533,10 +537,19 @@ mate::Handle<Session> Session::CreateFrom(
|
|||
|
||||
// static
|
||||
mate::Handle<Session> Session::FromPartition(
|
||||
v8::Isolate* isolate, const std::string& partition, bool in_memory) {
|
||||
auto browser_context = brightray::BrowserContext::From(partition, in_memory);
|
||||
return CreateFrom(isolate,
|
||||
static_cast<AtomBrowserContext*>(browser_context.get()));
|
||||
v8::Isolate* isolate, const std::string& partition,
|
||||
const base::DictionaryValue& options) {
|
||||
scoped_refptr<AtomBrowserContext> browser_context;
|
||||
if (partition.empty()) {
|
||||
browser_context = AtomBrowserContext::From("", false, options);
|
||||
} else if (base::StartsWith(partition, kPersistPrefix,
|
||||
base::CompareCase::SENSITIVE)) {
|
||||
std::string name = partition.substr(8);
|
||||
browser_context = AtomBrowserContext::From(name, false, options);
|
||||
} else {
|
||||
browser_context = AtomBrowserContext::From(partition, true, options);
|
||||
}
|
||||
return CreateFrom(isolate, browser_context.get());
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -576,11 +589,23 @@ void SetWrapSession(const WrapSessionCallback& callback) {
|
|||
|
||||
namespace {
|
||||
|
||||
v8::Local<v8::Value> FromPartition(
|
||||
const std::string& partition, mate::Arguments* args) {
|
||||
if (!atom::Browser::Get()->is_ready()) {
|
||||
args->ThrowError("Session can only be received when app is ready");
|
||||
return v8::Null(args->isolate());
|
||||
}
|
||||
base::DictionaryValue options;
|
||||
args->GetNext(&options);
|
||||
return atom::api::Session::FromPartition(
|
||||
args->isolate(), partition, options).ToV8();
|
||||
}
|
||||
|
||||
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context, void* priv) {
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
mate::Dictionary dict(isolate, exports);
|
||||
dict.SetMethod("fromPartition", &atom::api::Session::FromPartition);
|
||||
dict.SetMethod("fromPartition", &FromPartition);
|
||||
dict.SetMethod("_setWrapSession", &atom::api::SetWrapSession);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "atom/browser/api/trackable_object.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/browser/download_manager.h"
|
||||
#include "native_mate/handle.h"
|
||||
#include "net/base/completion_callback.h"
|
||||
|
@ -47,9 +48,10 @@ class Session: public mate::TrackableObject<Session>,
|
|||
static mate::Handle<Session> CreateFrom(
|
||||
v8::Isolate* isolate, AtomBrowserContext* browser_context);
|
||||
|
||||
// Gets the Session of |partition| and |in_memory|.
|
||||
// Gets the Session of |partition|.
|
||||
static mate::Handle<Session> FromPartition(
|
||||
v8::Isolate* isolate, const std::string& partition, bool in_memory);
|
||||
v8::Isolate* isolate, const std::string& partition,
|
||||
const base::DictionaryValue& options = base::DictionaryValue());
|
||||
|
||||
AtomBrowserContext* browser_context() const { return browser_context_.get(); }
|
||||
|
||||
|
|
|
@ -285,16 +285,11 @@ WebContents::WebContents(v8::Isolate* isolate,
|
|||
std::string partition;
|
||||
mate::Handle<api::Session> session;
|
||||
if (options.Get("session", &session)) {
|
||||
} else if (options.Get("partition", &partition) && !partition.empty()) {
|
||||
bool in_memory = true;
|
||||
if (base::StartsWith(partition, "persist:", base::CompareCase::SENSITIVE)) {
|
||||
in_memory = false;
|
||||
partition = partition.substr(8);
|
||||
}
|
||||
session = Session::FromPartition(isolate, partition, in_memory);
|
||||
} else if (options.Get("partition", &partition)) {
|
||||
session = Session::FromPartition(isolate, partition);
|
||||
} else {
|
||||
// Use the default session if not specified.
|
||||
session = Session::FromPartition(isolate, "", false);
|
||||
session = Session::FromPartition(isolate, "");
|
||||
}
|
||||
session_.Reset(isolate, session.ToV8());
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ void AtomAccessTokenStore::SaveAccessToken(const GURL& server_url,
|
|||
}
|
||||
|
||||
void AtomAccessTokenStore::GetRequestContextOnUIThread() {
|
||||
auto browser_context = brightray::BrowserContext::From("", false);
|
||||
auto browser_context = AtomBrowserContext::From("", false);
|
||||
request_context_getter_ = browser_context->GetRequestContext();
|
||||
}
|
||||
|
||||
|
|
|
@ -63,8 +63,9 @@ std::string RemoveWhitespace(const std::string& str) {
|
|||
|
||||
} // namespace
|
||||
|
||||
AtomBrowserContext::AtomBrowserContext(const std::string& partition,
|
||||
bool in_memory)
|
||||
AtomBrowserContext::AtomBrowserContext(
|
||||
const std::string& partition, bool in_memory,
|
||||
const base::DictionaryValue& options)
|
||||
: brightray::BrowserContext(partition, in_memory),
|
||||
network_delegate_(new AtomNetworkDelegate) {
|
||||
// Construct user agent string.
|
||||
|
@ -82,6 +83,10 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition,
|
|||
CHROME_VERSION_STRING);
|
||||
}
|
||||
user_agent_ = content::BuildUserAgentFromProduct(user_agent);
|
||||
|
||||
// Read options.
|
||||
use_cache_ = true;
|
||||
options.GetBoolean("cache", &use_cache_);
|
||||
}
|
||||
|
||||
AtomBrowserContext::~AtomBrowserContext() {
|
||||
|
@ -144,7 +149,7 @@ net::HttpCache::BackendFactory*
|
|||
AtomBrowserContext::CreateHttpCacheBackendFactory(
|
||||
const base::FilePath& base_path) {
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
if (command_line->HasSwitch(switches::kDisableHttpCache))
|
||||
if (!use_cache_ || command_line->HasSwitch(switches::kDisableHttpCache))
|
||||
return new NoCacheBackend;
|
||||
else
|
||||
return brightray::BrowserContext::CreateHttpCacheBackendFactory(base_path);
|
||||
|
@ -190,14 +195,15 @@ void AtomBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) {
|
|||
pref_registry->RegisterDictionaryPref(prefs::kDevToolsFileSystemPaths);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
namespace brightray {
|
||||
|
||||
// static
|
||||
scoped_refptr<BrowserContext> BrowserContext::Create(
|
||||
const std::string& partition, bool in_memory) {
|
||||
return make_scoped_refptr(new atom::AtomBrowserContext(partition, in_memory));
|
||||
scoped_refptr<AtomBrowserContext> AtomBrowserContext::From(
|
||||
const std::string& partition, bool in_memory,
|
||||
const base::DictionaryValue& options) {
|
||||
auto browser_context = brightray::BrowserContext::Get(partition, in_memory);
|
||||
if (browser_context)
|
||||
return static_cast<AtomBrowserContext*>(browser_context.get());
|
||||
|
||||
return new AtomBrowserContext(partition, in_memory, options);
|
||||
}
|
||||
|
||||
} // namespace brightray
|
||||
} // namespace atom
|
||||
|
|
|
@ -18,8 +18,12 @@ class WebViewManager;
|
|||
|
||||
class AtomBrowserContext : public brightray::BrowserContext {
|
||||
public:
|
||||
AtomBrowserContext(const std::string& partition, bool in_memory);
|
||||
~AtomBrowserContext() override;
|
||||
// Get or create the BrowserContext according to its |partition| and
|
||||
// |in_memory|. The |options| will be passed to constructor when there is no
|
||||
// existing BrowserContext.
|
||||
static scoped_refptr<AtomBrowserContext> From(
|
||||
const std::string& partition, bool in_memory,
|
||||
const base::DictionaryValue& options = base::DictionaryValue());
|
||||
|
||||
void SetUserAgent(const std::string& user_agent);
|
||||
|
||||
|
@ -43,11 +47,17 @@ class AtomBrowserContext : public brightray::BrowserContext {
|
|||
|
||||
AtomNetworkDelegate* network_delegate() const { return network_delegate_; }
|
||||
|
||||
protected:
|
||||
AtomBrowserContext(const std::string& partition, bool in_memory,
|
||||
const base::DictionaryValue& options);
|
||||
~AtomBrowserContext() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<AtomDownloadManagerDelegate> download_manager_delegate_;
|
||||
std::unique_ptr<WebViewManager> guest_manager_;
|
||||
std::unique_ptr<AtomPermissionManager> permission_manager_;
|
||||
std::string user_agent_;
|
||||
bool use_cache_;
|
||||
|
||||
// Managed by brightray::BrowserContext.
|
||||
AtomNetworkDelegate* network_delegate_;
|
||||
|
|
|
@ -20,17 +20,25 @@ const ses = win.webContents.session
|
|||
|
||||
The `session` module has the following methods:
|
||||
|
||||
### session.fromPartition(partition)
|
||||
### session.fromPartition(partition[, options])
|
||||
|
||||
* `partition` String
|
||||
* `options` Object
|
||||
* `cache` Boolean - Whether to enable cache.
|
||||
|
||||
Returns a new `Session` instance from `partition` string.
|
||||
Returns a `Session` instance from `partition` string. When there is an existing
|
||||
`Session` with the same `partition`, it will be returned; othewise a new
|
||||
`Session` instance will be created with `options`.
|
||||
|
||||
If `partition` starts with `persist:`, the page will use a persistent session
|
||||
available to all pages in the app with the same `partition`. if there is no
|
||||
`persist:` prefix, the page will use an in-memory session. If the `partition` is
|
||||
empty then default session of the app will be returned.
|
||||
|
||||
To create a `Session` with `options`, you have to ensure the `Session` with the
|
||||
`partition` has never been used before. There is no way to change the `options`
|
||||
of an existing `Session` object.
|
||||
|
||||
## Properties
|
||||
|
||||
The `session` module has the following properties:
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
const {app, session} = require('electron')
|
||||
const {registerStandardSchemes} = process.atomBinding('protocol')
|
||||
|
||||
exports.registerStandardSchemes = function (schemes) {
|
||||
if (app.isReady()) {
|
||||
console.warn('protocol.registerStandardSchemes should be called before app is ready')
|
||||
return
|
||||
// Global protocol APIs.
|
||||
module.exports = process.atomBinding('protocol')
|
||||
|
||||
// Fallback protocol APIs of default session.
|
||||
Object.setPrototypeOf(module.exports, new Proxy({}, {
|
||||
get (target, property) {
|
||||
if (!app.isReady()) return
|
||||
|
||||
const protocol = session.defaultSession.protocol
|
||||
if (!protocol.hasOwnProperty(property)) return
|
||||
|
||||
// Returning a native function directly would throw error.
|
||||
return (...args) => protocol[property](...args)
|
||||
},
|
||||
|
||||
ownKeys () {
|
||||
if (!app.isReady()) return []
|
||||
|
||||
return Object.getOwnPropertyNames(session.defaultSession.protocol)
|
||||
},
|
||||
|
||||
getOwnPropertyDescriptor (target) {
|
||||
return { configurable: true, enumerable: true }
|
||||
}
|
||||
registerStandardSchemes(schemes)
|
||||
}
|
||||
|
||||
const setupProtocol = function () {
|
||||
let protocol = session.defaultSession.protocol
|
||||
for (let method in protocol) {
|
||||
exports[method] = protocol[method].bind(protocol)
|
||||
}
|
||||
}
|
||||
|
||||
if (app.isReady()) {
|
||||
setupProtocol()
|
||||
} else {
|
||||
app.once('ready', setupProtocol)
|
||||
}
|
||||
}))
|
||||
|
|
|
@ -1,44 +1,22 @@
|
|||
const {EventEmitter} = require('events')
|
||||
const electron = require('electron')
|
||||
const bindings = process.atomBinding('session')
|
||||
|
||||
const PERSIST_PREFIX = 'persist:'
|
||||
const Session = new EventEmitter()
|
||||
|
||||
// Wrapper of binding.fromPartition that checks for ready event.
|
||||
const fromPartition = function (partition, persist) {
|
||||
if (!electron.app.isReady()) {
|
||||
throw new Error('session module can only be used when app is ready')
|
||||
}
|
||||
|
||||
return bindings.fromPartition(partition, persist)
|
||||
}
|
||||
|
||||
// Returns the Session from |partition| string.
|
||||
Session.fromPartition = function (partition = '') {
|
||||
if (partition === '') return exports.defaultSession
|
||||
|
||||
if (partition.startsWith(PERSIST_PREFIX)) {
|
||||
return fromPartition(partition.substr(PERSIST_PREFIX.length), false)
|
||||
} else {
|
||||
return fromPartition(partition, true)
|
||||
}
|
||||
}
|
||||
const {app} = require('electron')
|
||||
const {fromPartition, _setWrapSession} = process.atomBinding('session')
|
||||
|
||||
// Returns the default session.
|
||||
Object.defineProperty(Session, 'defaultSession', {
|
||||
Object.defineProperties(exports, {
|
||||
defaultSession: {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return fromPartition('', false)
|
||||
get () { return fromPartition('') }
|
||||
},
|
||||
fromPartition: {
|
||||
enumerable: true,
|
||||
value: fromPartition
|
||||
}
|
||||
})
|
||||
|
||||
const wrapSession = function (session) {
|
||||
// Wraps native Session class.
|
||||
_setWrapSession(function (session) {
|
||||
// Session is an EventEmitter.
|
||||
Object.setPrototypeOf(session, EventEmitter.prototype)
|
||||
Session.emit('session-created', session)
|
||||
}
|
||||
|
||||
bindings._setWrapSession(wrapSession)
|
||||
|
||||
module.exports = Session
|
||||
app.emit('session-created', session)
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const {app, ipcMain, session, webContents, BrowserWindow} = require('electron')
|
||||
const {app, ipcMain, webContents, BrowserWindow} = require('electron')
|
||||
const {getAllWebContents} = process.atomBinding('web_contents')
|
||||
const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllWebContents()
|
||||
|
||||
|
@ -86,6 +86,7 @@ const startBackgroundPages = function (manifest) {
|
|||
}
|
||||
|
||||
const contents = webContents.create({
|
||||
partition: 'persist:__chrome_extension',
|
||||
isBackgroundPage: true,
|
||||
commandLineSwitches: ['--background-page']
|
||||
})
|
||||
|
@ -284,6 +285,39 @@ app.on('web-contents-created', function (event, webContents) {
|
|||
})
|
||||
})
|
||||
|
||||
// The chrome-extension: can map a extension URL request to real file path.
|
||||
const chromeExtensionHandler = function (request, callback) {
|
||||
const parsed = url.parse(request.url)
|
||||
if (!parsed.hostname || !parsed.path) return callback()
|
||||
|
||||
const manifest = manifestMap[parsed.hostname]
|
||||
if (!manifest) return callback()
|
||||
|
||||
const page = backgroundPages[parsed.hostname]
|
||||
if (page && parsed.path === `/${page.name}`) {
|
||||
return callback({
|
||||
mimeType: 'text/html',
|
||||
data: page.html
|
||||
})
|
||||
}
|
||||
|
||||
fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) {
|
||||
if (err) {
|
||||
return callback(-6) // FILE_NOT_FOUND
|
||||
} else {
|
||||
return callback(content)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
app.on('session-created', function (ses) {
|
||||
ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
|
||||
if (error) {
|
||||
console.error(`Unable to register chrome-extension protocol: ${error}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// The persistent path of "DevTools Extensions" preference file.
|
||||
let loadedExtensionsPath = null
|
||||
|
||||
|
@ -309,38 +343,6 @@ app.on('will-quit', function () {
|
|||
|
||||
// We can not use protocol or BrowserWindow until app is ready.
|
||||
app.once('ready', function () {
|
||||
// The chrome-extension: can map a extension URL request to real file path.
|
||||
const chromeExtensionHandler = function (request, callback) {
|
||||
const parsed = url.parse(request.url)
|
||||
if (!parsed.hostname || !parsed.path) return callback()
|
||||
|
||||
const manifest = manifestMap[parsed.hostname]
|
||||
if (!manifest) return callback()
|
||||
|
||||
const page = backgroundPages[parsed.hostname]
|
||||
if (page && parsed.path === `/${page.name}`) {
|
||||
return callback({
|
||||
mimeType: 'text/html',
|
||||
data: page.html
|
||||
})
|
||||
}
|
||||
|
||||
fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) {
|
||||
if (err) {
|
||||
return callback(-6) // FILE_NOT_FOUND
|
||||
} else {
|
||||
return callback(content)
|
||||
}
|
||||
})
|
||||
}
|
||||
session.on('session-created', function (ses) {
|
||||
ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
|
||||
if (error) {
|
||||
console.error(`Unable to register chrome-extension protocol: ${error}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Load persisted extensions.
|
||||
loadedExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions')
|
||||
try {
|
||||
|
|
|
@ -3,12 +3,8 @@ const http = require('http')
|
|||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
const ipcRenderer = require('electron').ipcRenderer
|
||||
const remote = require('electron').remote
|
||||
|
||||
const ipcMain = remote.ipcMain
|
||||
const session = remote.session
|
||||
const BrowserWindow = remote.BrowserWindow
|
||||
const {ipcRenderer, remote} = require('electron')
|
||||
const {ipcMain, session, BrowserWindow} = remote
|
||||
|
||||
describe('session module', function () {
|
||||
this.timeout(10000)
|
||||
|
@ -35,7 +31,30 @@ describe('session module', function () {
|
|||
w = null
|
||||
})
|
||||
|
||||
describe('session.cookies', function () {
|
||||
describe('session.defaultSession', function () {
|
||||
it('returns the default session', function () {
|
||||
assert.equal(session.defaultSession, session.fromPartition(''))
|
||||
})
|
||||
})
|
||||
|
||||
describe('session.fromPartition(partition, options)', function () {
|
||||
it('returns existing session with same partition', function () {
|
||||
assert.equal(session.fromPartition('test'), session.fromPartition('test'))
|
||||
})
|
||||
|
||||
it('created session is ref-counted', function () {
|
||||
const partition = 'test2'
|
||||
const userAgent = 'test-agent'
|
||||
const ses1 = session.fromPartition(partition)
|
||||
ses1.setUserAgent(userAgent)
|
||||
assert.equal(ses1.getUserAgent(), userAgent)
|
||||
ses1.destroy()
|
||||
const ses2 = session.fromPartition(partition)
|
||||
assert.notEqual(ses2.getUserAgent(), userAgent)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ses.cookies', function () {
|
||||
it('should get cookies', function (done) {
|
||||
var server = http.createServer(function (req, res) {
|
||||
res.setHeader('Set-Cookie', ['0=0'])
|
||||
|
@ -141,7 +160,7 @@ describe('session module', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('session.clearStorageData(options)', function () {
|
||||
describe('ses.clearStorageData(options)', function () {
|
||||
fixtures = path.resolve(__dirname, 'fixtures')
|
||||
it('clears localstorage data', function (done) {
|
||||
ipcMain.on('count', function (event, count) {
|
||||
|
@ -163,7 +182,7 @@ describe('session module', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('session will-download event', function () {
|
||||
describe('will-download event', function () {
|
||||
var w = null
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -280,7 +299,7 @@ describe('session module', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('session.protocol', function () {
|
||||
describe('ses.protocol', function () {
|
||||
const partitionName = 'temp'
|
||||
const protocolName = 'sp'
|
||||
const partitionProtocol = session.fromPartition(partitionName).protocol
|
||||
|
|
2
vendor/brightray
vendored
2
vendor/brightray
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 3b993f9fd7ffdd0e92bb77521a8b7f32af5eba5b
|
||||
Subproject commit 91abdb01a1825c12522fd5fc2349a7ba9a091a48
|
Loading…
Reference in a new issue