Merge pull request #2459 from deepak1556/web_view_partition_patch

webview: partition attribute
This commit is contained in:
Cheng Zhao 2015-09-04 17:29:37 +09:00
commit 6f9d822472
15 changed files with 240 additions and 73 deletions

View file

@ -159,11 +159,19 @@ WebContents::WebContents(const mate::Dictionary& options) {
type_ = is_guest ? WEB_VIEW : BROWSER_WINDOW; type_ = is_guest ? WEB_VIEW : BROWSER_WINDOW;
auto browser_context = AtomBrowserMainParts::Get()->browser_context(); content::BrowserContext* browser_context =
AtomBrowserMainParts::Get()->browser_context();
content::WebContents* web_contents; content::WebContents* web_contents;
if (is_guest) { if (is_guest) {
content::SiteInstance* site_instance = content::SiteInstance::CreateForURL( GURL guest_site;
browser_context, GURL("chrome-guest://fake-host")); options.Get("partition", &guest_site);
// use hosts' browser_context when no partition is specified.
if (!guest_site.query().empty()) {
browser_context = AtomBrowserMainParts::Get()
->GetBrowserContextForPartition(guest_site);
}
auto site_instance =
content::SiteInstance::CreateForURL(browser_context, guest_site);
content::WebContents::CreateParams params(browser_context, site_instance); content::WebContents::CreateParams params(browser_context, site_instance);
guest_delegate_.reset(new WebViewGuestDelegate); guest_delegate_.reset(new WebViewGuestDelegate);
params.guest_delegate = guest_delegate_.get(); params.guest_delegate = guest_delegate_.get();

View file

@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/web_view_constants.h"
#include "atom/browser/web_view_manager.h" #include "atom/browser/web_view_manager.h"
#include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/gurl_converter.h"
#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_context.h"
@ -34,16 +35,19 @@ struct Converter<atom::WebViewManager::WebViewInfo> {
return false; return false;
GURL preload_url; GURL preload_url;
if (!options.Get("preloadUrl", &preload_url)) if (!options.Get(atom::web_view::kPreloadUrl, &preload_url))
return false; return false;
if (!preload_url.is_empty() && if (!preload_url.is_empty() &&
!net::FileURLToFilePath(preload_url, &(out->preload_script))) !net::FileURLToFilePath(preload_url, &(out->preload_script)))
return false; return false;
return options.Get("nodeIntegration", &(out->node_integration)) && return options.Get(atom::web_view::kNodeIntegration,
options.Get("plugins", &(out->plugins)) && &(out->node_integration)) &&
options.Get("disableWebSecurity", &(out->disable_web_security)); options.Get(atom::web_view::kPlugins, &(out->plugins)) &&
options.Get(atom::web_view::kPartitionId, &(out->partition_id)) &&
options.Get(atom::web_view::kDisableWebSecurity,
&(out->disable_web_security));
} }
}; };
@ -67,12 +71,15 @@ void AddGuest(int guest_instance_id,
content::WebContents* guest_web_contents, content::WebContents* guest_web_contents,
atom::WebViewManager::WebViewInfo info) { atom::WebViewManager::WebViewInfo info) {
auto manager = GetWebViewManager(embedder); auto manager = GetWebViewManager(embedder);
if (manager) { if (manager)
manager->AddGuest(guest_instance_id, element_instance_id, embedder,
guest_web_contents);
info.guest_instance_id = guest_instance_id; info.guest_instance_id = guest_instance_id;
info.embedder = embedder; info.embedder = embedder;
manager->AddGuest(guest_instance_id, element_instance_id, embedder, auto data = new atom::WebViewManager::WebViewInfoUserData(info);
guest_web_contents, info); guest_web_contents->SetUserData(
} atom::web_view::kWebViewInfoKeyName, data);
} }
void RemoveGuest(content::WebContents* embedder, int guest_instance_id) { void RemoveGuest(content::WebContents* embedder, int guest_instance_id) {

View file

@ -16,6 +16,7 @@
#include "atom/browser/browser.h" #include "atom/browser/browser.h"
#include "atom/browser/native_window.h" #include "atom/browser/native_window.h"
#include "atom/browser/web_view_manager.h" #include "atom/browser/web_view_manager.h"
#include "atom/browser/web_view_constants.h"
#include "atom/browser/window_list.h" #include "atom/browser/window_list.h"
#include "atom/common/options_switches.h" #include "atom/common/options_switches.h"
#include "base/command_line.h" #include "base/command_line.h"
@ -74,8 +75,12 @@ ProcessOwner GetProcessOwner(int process_id,
return OWNER_NATIVE_WINDOW; return OWNER_NATIVE_WINDOW;
// Then search for guest WebContents. // Then search for guest WebContents.
if (WebViewManager::GetInfoForWebContents(web_contents, info)) auto data = static_cast<WebViewManager::WebViewInfoUserData*>(
web_contents->GetUserData(web_view::kWebViewInfoKeyName));
if (data) {
*info = data->web_view_info();
return OWNER_GUEST_WEB_CONTENTS; return OWNER_GUEST_WEB_CONTENTS;
}
return OWNER_NONE; return OWNER_NONE;
} }
@ -155,9 +160,10 @@ void AtomBrowserClient::OverrideWebkitPrefs(
// Custom preferences of guest page. // Custom preferences of guest page.
auto web_contents = content::WebContents::FromRenderViewHost(host); auto web_contents = content::WebContents::FromRenderViewHost(host);
WebViewManager::WebViewInfo info; auto info = static_cast<WebViewManager::WebViewInfoUserData*>(
if (WebViewManager::GetInfoForWebContents(web_contents, &info)) { web_contents->GetUserData(web_view::kWebViewInfoKeyName));
prefs->web_security_enabled = !info.disable_web_security; if (info) {
prefs->web_security_enabled = !info->web_view_info().disable_web_security;
return; return;
} }

View file

@ -26,6 +26,23 @@
namespace atom { namespace atom {
namespace {
const base::FilePath::CharType kStoragePartitionDirname[] = "Partitions";
void GetStoragePartitionConfig(const GURL& partition,
base::FilePath* partition_path,
bool* in_memory,
std::string* id) {
*in_memory = (partition.path() != "/persist");
net::UnescapeRule::Type flags =
net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS;
*id = net::UnescapeURLComponent(partition.query(), flags);
*partition_path = base::FilePath(kStoragePartitionDirname).AppendASCII(*id);
}
} // namespace
// static // static
AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL; AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL;
@ -50,6 +67,21 @@ AtomBrowserMainParts* AtomBrowserMainParts::Get() {
return self_; return self_;
} }
content::BrowserContext* AtomBrowserMainParts::GetBrowserContextForPartition(
const GURL& partition) {
std::string id;
bool in_memory;
base::FilePath partition_path;
GetStoragePartitionConfig(partition, &partition_path, &in_memory, &id);
if (browser_context_map_.contains(id))
return browser_context_map_.get(id);
scoped_ptr<brightray::BrowserContext> browser_context(CreateBrowserContext());
browser_context->Initialize(partition_path.value(), in_memory);
browser_context_map_.set(id, browser_context.Pass());
return browser_context_map_.get(id);
}
void AtomBrowserMainParts::RegisterDestructionCallback( void AtomBrowserMainParts::RegisterDestructionCallback(
const base::Closure& callback) { const base::Closure& callback) {
destruction_callbacks_.push_back(callback); destruction_callbacks_.push_back(callback);

View file

@ -6,10 +6,14 @@
#define ATOM_BROWSER_ATOM_BROWSER_MAIN_PARTS_H_ #define ATOM_BROWSER_ATOM_BROWSER_MAIN_PARTS_H_
#include <list> #include <list>
#include <map>
#include <string>
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/scoped_ptr_hash_map.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "brightray/browser/browser_main_parts.h" #include "brightray/browser/browser_main_parts.h"
#include "content/public/browser/browser_context.h"
class BrowserProcess; class BrowserProcess;
@ -29,6 +33,10 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
static AtomBrowserMainParts* Get(); static AtomBrowserMainParts* Get();
// Returns the BrowserContext associated with the partition.
content::BrowserContext* GetBrowserContextForPartition(
const GURL& partition);
// Register a callback that should be destroyed before JavaScript environment // Register a callback that should be destroyed before JavaScript environment
// gets destroyed. // gets destroyed.
void RegisterDestructionCallback(const base::Closure& callback); void RegisterDestructionCallback(const base::Closure& callback);
@ -70,6 +78,10 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
// List of callbacks should be executed before destroying JS env. // List of callbacks should be executed before destroying JS env.
std::list<base::Closure> destruction_callbacks_; std::list<base::Closure> destruction_callbacks_;
// partition_id => browser_context
base::ScopedPtrHashMap<std::string, scoped_ptr<brightray::BrowserContext>>
browser_context_map_;
static AtomBrowserMainParts* self_; static AtomBrowserMainParts* self_;
DISALLOW_COPY_AND_ASSIGN(AtomBrowserMainParts); DISALLOW_COPY_AND_ASSIGN(AtomBrowserMainParts);

View file

@ -38,12 +38,29 @@ moveLastToFirst = (list) ->
getNextInstanceId = (webContents) -> getNextInstanceId = (webContents) ->
++nextInstanceId ++nextInstanceId
# Generate URL encoded partition id.
getPartitionId = (partition) ->
# Guest site url will be chrome-guest://fake-host/{persist}?{partitionId}
partitionId = "chrome-guest://fake-host/"
if partition
persist = partition.startsWith('persist:')
if persist
partition = partition.substring('persist:'.length)
partitionId += 'persist?'
else
# Just to differentiate from same persistant ID
partition += "_temp"
partitionId += '?'
partitionId += encodeURIComponent(partition)
return partitionId
# Create a new guest instance. # Create a new guest instance.
createGuest = (embedder, params) -> createGuest = (embedder, params) ->
webViewManager ?= process.atomBinding 'web_view_manager' webViewManager ?= process.atomBinding 'web_view_manager'
id = getNextInstanceId embedder id = getNextInstanceId embedder
guest = webContents.create {isGuest: true, embedder} partitionId = getPartitionId params.partition
guest = webContents.create {isGuest: true, partition: partitionId, embedder}
guestInstances[id] = {guest, embedder} guestInstances[id] = {guest, embedder}
# Destroy guest when the embedder is gone or navigated. # Destroy guest when the embedder is gone or navigated.
@ -120,6 +137,7 @@ attachGuest = (embedder, elementInstanceId, guestInstanceId, params) ->
plugins: params.plugins plugins: params.plugins
disableWebSecurity: params.disablewebsecurity disableWebSecurity: params.disablewebsecurity
preloadUrl: params.preload ? '' preloadUrl: params.preload ? ''
partitionId: getPartitionId(params.partition)
guest.attachParams = params guest.attachParams = params
embedderElementsMap[key] = guestInstanceId embedderElementsMap[key] = guestInstanceId

View file

@ -0,0 +1,24 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/web_view_constants.h"
namespace atom {
namespace web_view {
const char kPreloadUrl[] = "preloadUrl";
const char kNodeIntegration[] = "nodeIntegration";
const char kPlugins[] = "plugins";
const char kDisableWebSecurity[] = "disableWebSecurity";
const char kPartitionId[] = "partitionId";
const int kDefaultWidth = 300;
const int kDefaultHeight = 300;
const char kWebViewInfoKeyName[] = "web_view_info";
} // namespace web_view
} // namespace atom

View file

@ -0,0 +1,27 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_WEB_VIEW_CONSTANTS_H_
#define ATOM_BROWSER_WEB_VIEW_CONSTANTS_H_
namespace atom {
namespace web_view {
extern const char kPreloadUrl[];
extern const char kNodeIntegration[];
extern const char kPlugins[];
extern const char kDisableWebSecurity[];
extern const char kPartitionId[];
extern const int kDefaultWidth;
extern const int kDefaultHeight;
extern const char kWebViewInfoKeyName[];
} // namespace web_view
} // namespace atom
#endif // ATOM_BROWSER_WEB_VIEW_CONSTANTS_H_

View file

@ -5,6 +5,7 @@
#include "atom/browser/web_view_guest_delegate.h" #include "atom/browser/web_view_guest_delegate.h"
#include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/web_view_constants.h"
#include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/gurl_converter.h"
#include "content/public/browser/guest_host.h" #include "content/public/browser/guest_host.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
@ -13,13 +14,6 @@
namespace atom { namespace atom {
namespace {
const int kDefaultWidth = 300;
const int kDefaultHeight = 300;
} // namespace
WebViewGuestDelegate::WebViewGuestDelegate() WebViewGuestDelegate::WebViewGuestDelegate()
: guest_opaque_(true), : guest_opaque_(true),
guest_host_(nullptr), guest_host_(nullptr),
@ -178,7 +172,7 @@ gfx::Size WebViewGuestDelegate::GetDefaultSize() const {
return embedder_web_contents_->GetRenderWidgetHostView() return embedder_web_contents_->GetRenderWidgetHostView()
->GetVisibleViewportSize(); ->GetVisibleViewportSize();
} else { } else {
return gfx::Size(kDefaultWidth, kDefaultHeight); return gfx::Size(web_view::kDefaultWidth, web_view::kDefaultHeight);
} }
} }

View file

@ -5,37 +5,12 @@
#include "atom/browser/web_view_manager.h" #include "atom/browser/web_view_manager.h"
#include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
namespace atom { namespace atom {
namespace {
WebViewManager* GetManagerFromWebContents(
const content::WebContents* web_contents) {
auto context = web_contents->GetBrowserContext();
if (!context)
return nullptr;
return static_cast<WebViewManager*>(context->GetGuestManager());
}
} // namespace
// static
bool WebViewManager::GetInfoForWebContents(
const content::WebContents* web_contents, WebViewInfo* info) {
auto manager = GetManagerFromWebContents(web_contents);
if (!manager)
return false;
base::AutoLock auto_lock(manager->lock_);
auto iter = manager->webview_info_map_.find(web_contents);
if (iter == manager->webview_info_map_.end())
return false;
*info = iter->second;
return true;
}
WebViewManager::WebViewManager(content::BrowserContext* context) { WebViewManager::WebViewManager(content::BrowserContext* context) {
} }
@ -45,11 +20,9 @@ WebViewManager::~WebViewManager() {
void WebViewManager::AddGuest(int guest_instance_id, void WebViewManager::AddGuest(int guest_instance_id,
int element_instance_id, int element_instance_id,
content::WebContents* embedder, content::WebContents* embedder,
content::WebContents* web_contents, content::WebContents* web_contents) {
const WebViewInfo& info) {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
web_contents_embdder_map_[guest_instance_id] = { web_contents, embedder }; web_contents_embedder_map_[guest_instance_id] = { web_contents, embedder };
webview_info_map_[web_contents] = info;
// Map the element in embedder to guest. // Map the element in embedder to guest.
int owner_process_id = embedder->GetRenderProcessHost()->GetID(); int owner_process_id = embedder->GetRenderProcessHost()->GetID();
@ -59,12 +32,10 @@ void WebViewManager::AddGuest(int guest_instance_id,
void WebViewManager::RemoveGuest(int guest_instance_id) { void WebViewManager::RemoveGuest(int guest_instance_id) {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
if (!ContainsKey(web_contents_embdder_map_, guest_instance_id)) if (!ContainsKey(web_contents_embedder_map_, guest_instance_id))
return; return;
auto web_contents = web_contents_embdder_map_[guest_instance_id].web_contents; web_contents_embedder_map_.erase(guest_instance_id);
web_contents_embdder_map_.erase(guest_instance_id);
webview_info_map_.erase(web_contents);
// Remove the record of element in embedder too. // Remove the record of element in embedder too.
for (const auto& element : element_instance_id_to_guest_map_) for (const auto& element : element_instance_id_to_guest_map_)
@ -82,15 +53,15 @@ content::WebContents* WebViewManager::GetGuestByInstanceID(
return nullptr; return nullptr;
int guest_instance_id = element_instance_id_to_guest_map_[key]; int guest_instance_id = element_instance_id_to_guest_map_[key];
if (ContainsKey(web_contents_embdder_map_, guest_instance_id)) if (ContainsKey(web_contents_embedder_map_, guest_instance_id))
return web_contents_embdder_map_[guest_instance_id].web_contents; return web_contents_embedder_map_[guest_instance_id].web_contents;
else else
return nullptr; return nullptr;
} }
bool WebViewManager::ForEachGuest(content::WebContents* embedder_web_contents, bool WebViewManager::ForEachGuest(content::WebContents* embedder_web_contents,
const GuestCallback& callback) { const GuestCallback& callback) {
for (auto& item : web_contents_embdder_map_) for (auto& item : web_contents_embedder_map_)
if (item.second.embedder == embedder_web_contents && if (item.second.embedder == embedder_web_contents &&
callback.Run(item.second.web_contents)) callback.Run(item.second.web_contents))
return true; return true;

View file

@ -8,8 +8,10 @@
#include <map> #include <map>
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/supports_user_data.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "content/public/browser/browser_plugin_guest_manager.h" #include "content/public/browser/browser_plugin_guest_manager.h"
#include "content/public/browser/site_instance.h"
namespace content { namespace content {
class BrowserContext; class BrowserContext;
@ -27,12 +29,20 @@ class WebViewManager : public content::BrowserPluginGuestManager {
bool plugins; bool plugins;
bool disable_web_security; bool disable_web_security;
base::FilePath preload_script; base::FilePath preload_script;
GURL partition_id;
}; };
// Finds the WebViewManager attached with |web_contents| and returns the class WebViewInfoUserData : public base::SupportsUserData::Data {
// WebViewInfo of it. public:
static bool GetInfoForWebContents(const content::WebContents* web_contents, explicit WebViewInfoUserData(WebViewInfo info)
WebViewInfo* info); : web_view_info_(info) {}
~WebViewInfoUserData() override {}
WebViewInfo& web_view_info() { return web_view_info_; }
private:
WebViewInfo web_view_info_;
};
explicit WebViewManager(content::BrowserContext* context); explicit WebViewManager(content::BrowserContext* context);
virtual ~WebViewManager(); virtual ~WebViewManager();
@ -40,8 +50,7 @@ class WebViewManager : public content::BrowserPluginGuestManager {
void AddGuest(int guest_instance_id, void AddGuest(int guest_instance_id,
int element_instance_id, int element_instance_id,
content::WebContents* embedder, content::WebContents* embedder,
content::WebContents* web_contents, content::WebContents* web_contents);
const WebViewInfo& info);
void RemoveGuest(int guest_instance_id); void RemoveGuest(int guest_instance_id);
protected: protected:
@ -57,7 +66,7 @@ class WebViewManager : public content::BrowserPluginGuestManager {
content::WebContents* embedder; content::WebContents* embedder;
}; };
// guest_instance_id => (web_contents, embedder) // guest_instance_id => (web_contents, embedder)
std::map<int, WebContentsWithEmbedder> web_contents_embdder_map_; std::map<int, WebContentsWithEmbedder> web_contents_embedder_map_;
struct ElementInstanceKey { struct ElementInstanceKey {
int embedder_process_id; int embedder_process_id;
@ -81,10 +90,6 @@ class WebViewManager : public content::BrowserPluginGuestManager {
// (embedder_process_id, element_instance_id) => guest_instance_id // (embedder_process_id, element_instance_id) => guest_instance_id
std::map<ElementInstanceKey, int> element_instance_id_to_guest_map_; std::map<ElementInstanceKey, int> element_instance_id_to_guest_map_;
typedef std::map<const content::WebContents*, WebViewInfo> WebViewInfoMap;
// web_contents => (guest_instance_id, embedder, ...)
WebViewInfoMap webview_info_map_;
base::Lock lock_; base::Lock lock_;
DISALLOW_COPY_AND_ASSIGN(WebViewManager); DISALLOW_COPY_AND_ASSIGN(WebViewManager);

View file

@ -131,6 +131,24 @@ page is loaded, use the `setUserAgent` method to change the user agent.
If "on", the guest page will have web security disabled. If "on", the guest page will have web security disabled.
### partition
```html
<webview src="https://github.com" partition="persist:github"></webview>
<webview src="http://electron.atom.io" partition="electron"></webview>
```
Sets the storage partition used by the `webview`. If the storage partition ID starts with `persist:`,
the `webview` will use a persistent storage partition available to all `webview` in the app with
the same storage partition ID. if there is no `persist:` prefix, the `webview` will
use an in-memory storage partition. By assigning the same partition ID, multiple `webview`
can share the same storage partition. If the storage partition ID is unset then default storage
of the app will be used.
This value can only be modified before the first navigation, since the storage partition of an active
renderer process cannot change. Subsequent attempts to modify the value will fail with a
DOM exception.
## Methods ## Methods
The `webview` tag has the following methods: The `webview` tag has the following methods:

View file

@ -226,6 +226,8 @@
'atom/browser/ui/x/window_state_watcher.h', 'atom/browser/ui/x/window_state_watcher.h',
'atom/browser/ui/x/x_window_utils.cc', 'atom/browser/ui/x/x_window_utils.cc',
'atom/browser/ui/x/x_window_utils.h', 'atom/browser/ui/x/x_window_utils.h',
'atom/browser/web_view_constants.cc',
'atom/browser/web_view_constants.h',
'atom/browser/web_view_guest_delegate.cc', 'atom/browser/web_view_guest_delegate.cc',
'atom/browser/web_view_guest_delegate.h', 'atom/browser/web_view_guest_delegate.h',
'atom/browser/web_view_manager.cc', 'atom/browser/web_view_manager.cc',

View file

@ -0,0 +1,4 @@
<script>
const item = window.localStorage.getItem('test');
console.log([item, window.localStorage.length].join(' '));
</script>

View file

@ -154,6 +154,45 @@ describe '<webview> tag', ->
webview.src = "data:text/html;base64,#{encoded}" webview.src = "data:text/html;base64,#{encoded}"
document.body.appendChild webview document.body.appendChild webview
describe 'partition attribute', ->
it 'inserts no node symbols when not set', (done) ->
webview.addEventListener 'console-message', (e) ->
assert.equal e.message, 'undefined undefined undefined undefined'
done()
webview.src = "file://#{fixtures}/pages/c.html"
webview.partition = "test"
document.body.appendChild webview
it 'inserts node symbols when set', (done) ->
webview.addEventListener 'console-message', (e) ->
assert.equal e.message, 'function object object'
done()
webview.setAttribute 'nodeintegration', 'on'
webview.src = "file://#{fixtures}/pages/d.html"
webview.partition = "test"
document.body.appendChild webview
it 'isolates storage for different id', (done) ->
listener = (e) ->
assert.equal e.message, " 0"
webview.removeEventListener 'console-message', listener
done()
window.localStorage.setItem 'test', 'one'
webview.addEventListener 'console-message', listener
webview.src = "file://#{fixtures}/pages/partition/one.html"
webview.partition = "test"
document.body.appendChild webview
it 'uses current session storage when no id is provided', (done) ->
listener = (e) ->
assert.equal e.message, "one 1"
webview.removeEventListener 'console-message', listener
done()
window.localStorage.setItem 'test', 'one'
webview.addEventListener 'console-message', listener
webview.src = "file://#{fixtures}/pages/partition/one.html"
document.body.appendChild webview
describe 'new-window event', -> describe 'new-window event', ->
it 'emits when window.open is called', (done) -> it 'emits when window.open is called', (done) ->
webview.addEventListener 'new-window', (e) -> webview.addEventListener 'new-window', (e) ->