blob: 9fead2690e7c941e7274a60b21f283a0a14c3261 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of $LIBRARYNAME;
$if DART2JS
$(ANNOTATIONS)class $CLASSNAME$EXTENDS$IMPLEMENTS native "Window,DOMWindow" {
$else
$(ANNOTATIONS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
$endif
/**
* Executes a [callback] after the immediate execution stack has completed.
*
* This differs from using Timer.run(callback)
* because Timer will run in about 4-15 milliseconds, depending on browser,
* depending on load. [setImmediate], in contrast, makes browser-specific
* changes in behavior to attempt to run immediately after the current
* frame unwinds, causing the future to complete after all processing has
* completed for the current event, but before any subsequent events.
*/
void setImmediate(TimeoutHandler callback) {
_addMicrotaskCallback(callback);
}
/**
* Lookup a port by its [name]. Return null if no port is
* registered under [name].
*/
SendPortSync lookupPort(String name) {
var portStr = document.documentElement.attributes['dart-port:$name'];
if (portStr == null) {
return null;
}
var port = json.parse(portStr);
return _deserialize(port);
}
/**
* Register a [port] on this window under the given [name]. This
* port may be retrieved by any isolate (or JavaScript script)
* running in this window.
*/
void registerPort(String name, var port) {
var serialized = _serialize(port);
document.documentElement.attributes['dart-port:$name'] =
json.stringify(serialized);
}
/**
* Returns a Future that completes just before the window is about to
* repaint so the user can draw an animation frame.
*
* If you need to later cancel this animation, use [requestAnimationFrame]
* instead.
*
* The [Future] completes to a timestamp that represents a floating
* point value of the number of milliseconds that have elapsed since the page
* started to load (which is also the timestamp at this call to
* animationFrame).
*
* Note: The code that runs when the future completes should call
* [animationFrame] again for the animation to continue.
*/
Future<num> get animationFrame {
var completer = new Completer<num>();
requestAnimationFrame((time) {
completer.complete(time);
});
return completer.future;
}
$if DART2JS
Document get document => JS('Document', '#.document', this);
WindowBase _open2(url, name) => JS('Window', '#.open(#,#)', this, url, name);
WindowBase _open3(url, name, options) =>
JS('Window', '#.open(#,#,#)', this, url, name, options);
WindowBase open(String url, String name, [String options]) {
if (options == null) {
return _DOMWindowCrossFrame._createSafe(_open2(url, name));
} else {
return _DOMWindowCrossFrame._createSafe(_open3(url, name, options));
}
}
// API level getter and setter for Location.
// TODO: The cross domain safe wrapper can be inserted here or folded into
// _LocationWrapper.
Location get location {
// Firefox work-around for Location. The Firefox location object cannot be
// made to behave like a Dart object so must be wrapped.
var result = _location;
if (_isDartLocation(result)) return result; // e.g. on Chrome.
if (null == _location_wrapper) {
_location_wrapper = new _LocationWrapper(result);
}
return _location_wrapper;
}
// TODO: consider forcing users to do: window.location.assign('string').
/**
* Sets the window's location, which causes the browser to navigate to the new
* location. [value] may be a Location object or a string.
*/
void set location(value) {
if (value is _LocationWrapper) {
_location = value._ptr;
} else {
_location = value;
}
}
_LocationWrapper _location_wrapper; // Cached wrapped Location object.
// Native getter and setter to access raw Location object.
dynamic get _location => JS('Location|=Object', '#.location', this);
void set _location(value) {
JS('void', '#.location = #', this, value);
}
// Prevent compiled from thinking 'location' property is available for a Dart
// member.
@JSName('location')
_protect_location() native;
static _isDartLocation(thing) {
// On Firefox the code that implements 'is Location' fails to find the patch
// stub on Object.prototype and throws an exception.
try {
return thing is Location;
} catch (e) {
return false;
}
}
/**
* Called to draw an animation frame and then request the window to repaint
* after [callback] has finished (creating the animation).
*
* Use this method only if you need to later call [cancelAnimationFrame]. If
* not, the preferred Dart idiom is to set animation frames by calling
* [animationFrame], which returns a Future.
*
* Returns a non-zero valued integer to represent the request id for this
* request. This value only needs to be saved if you intend to call
* [cancelAnimationFrame] so you can specify the particular animation to
* cancel.
*
* Note: The supplied [callback] needs to call [requestAnimationFrame] again
* for the animation to continue.
*/
@DomName('DOMWindow.requestAnimationFrame')
int requestAnimationFrame(RequestAnimationFrameCallback callback) {
_ensureRequestAnimationFrame();
return _requestAnimationFrame(callback);
}
void cancelAnimationFrame(int id) {
_ensureRequestAnimationFrame();
_cancelAnimationFrame(id);
}
@JSName('requestAnimationFrame')
int _requestAnimationFrame(RequestAnimationFrameCallback callback) native;
@JSName('cancelAnimationFrame')
void _cancelAnimationFrame(int id) native;
_ensureRequestAnimationFrame() {
if (JS('bool',
'!!(#.requestAnimationFrame && #.cancelAnimationFrame)', this, this))
return;
JS('void',
r"""
(function($this) {
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var i = 0; i < vendors.length && !$this.requestAnimationFrame; ++i) {
$this.requestAnimationFrame = $this[vendors[i] + 'RequestAnimationFrame'];
$this.cancelAnimationFrame =
$this[vendors[i]+'CancelAnimationFrame'] ||
$this[vendors[i]+'CancelRequestAnimationFrame'];
}
if ($this.requestAnimationFrame && $this.cancelAnimationFrame) return;
$this.requestAnimationFrame = function(callback) {
return window.setTimeout(function() {
callback(Date.now());
}, 16 /* 16ms ~= 60fps */);
};
$this.cancelAnimationFrame = function(id) { clearTimeout(id); }
})(#)""",
this);
}
/**
* Gets an instance of the Indexed DB factory to being using Indexed DB.
*
* Use [IdbFactory.supported] to check if Indexed DB is supported on the
* current platform.
*/
@SupportedBrowser(SupportedBrowser.CHROME, '23.0')
@SupportedBrowser(SupportedBrowser.FIREFOX, '15.0')
@SupportedBrowser(SupportedBrowser.IE, '10.0')
@Experimental
IdbFactory get indexedDB =>
JS('IdbFactory|Null', // If not supported, returns `null`.
'#.indexedDB || #.webkitIndexedDB || #.mozIndexedDB',
this, this, this);
@DomName('Window.console')
Console get console => Console._safeConsole;
/// Checks if _setImmediate is supported.
static bool get _supportsSetImmediate =>
JS('bool', '!!(window.setImmediate)');
// Set immediate implementation for IE
void _setImmediate(void callback()) {
JS('void', '#.setImmediate(#)', this, convertDartClosureToJS(callback, 0));
}
$else
/// Checks if _setImmediate is supported.
static bool get _supportsSetImmediate => false;
/// Dartium stub for IE's setImmediate.
void _setImmediate(void callback()) {
throw new UnsupportedError('setImmediate is not supported');
}
$endif
/**
* Access a sandboxed file system of the specified `size`. If `persistent` is
* true, the application will request permission from the user to create
* lasting storage. This storage cannot be freed without the user's
* permission. Returns a [Future] whose value stores a reference to the
* sandboxed file system for use. Because the file system is sandboxed,
* applications cannot access file systems created in other web pages.
*/
Future<FileSystem> requestFileSystem(int size, {bool persistent: false}) {
return _requestFileSystem(persistent? 1 : 0, size);
}
@DomName('DOMWindow.convertPointFromNodeToPage')
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
Point convertPointFromNodeToPage(Node node, Point point) {
var result = _convertPointFromNodeToPage(node,
new _DomPoint(point.x, point.y));
return new Point(result.x, result.y);
}
@DomName('DOMWindow.convertPointFromPageToNode')
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
Point convertPointFromPageToNode(Node node, Point point) {
var result = _convertPointFromPageToNode(node,
new _DomPoint(point.x, point.y));
return new Point(result.x, result.y);
}
/**
* Checks whether [convertPointFromNodeToPage] and
* [convertPointFromPageToNode] are supported on the current platform.
*/
static bool get supportsPointConversions => _DomPoint.supported;
$!MEMBERS
@DomName('DOMWindow.beforeunloadEvent')
@DocsEditable
static const EventStreamProvider<BeforeUnloadEvent> beforeUnloadEvent =
const _BeforeUnloadEventStreamProvider('beforeunload');
@DomName('DOMWindow.onbeforeunload')
@DocsEditable
Stream<Event> get onBeforeUnload => beforeUnloadEvent.forTarget(this);
}
/**
* Event object that is fired before the window is closed.
*
* The standard window close behavior can be prevented by setting the
* [returnValue]. This will display a dialog to the user confirming that they
* want to close the page.
*/
abstract class BeforeUnloadEvent implements Event {
/**
* If set to a non-null value, a dialog will be presented to the user
* confirming that they want to close the page.
*/
String returnValue;
}
class _BeforeUnloadEvent extends _WrappedEvent implements BeforeUnloadEvent {
String _returnValue;
_BeforeUnloadEvent(Event base): super(base);
String get returnValue => _returnValue;
void set returnValue(String value) {
_returnValue = value;
$if DART2JS
// FF and IE use the value as the return value, Chrome will return this from
// the event callback function.
if (JS('bool', '("returnValue" in #)', wrapped)) {
JS('void', '#.returnValue = #', wrapped, value);
}
$endif
}
}
class _BeforeUnloadEventStreamProvider implements
EventStreamProvider<BeforeUnloadEvent> {
final String _eventType;
const _BeforeUnloadEventStreamProvider(this._eventType);
Stream<BeforeUnloadEvent> forTarget(EventTarget e, {bool useCapture: false}) {
var controller = new StreamController(sync: true);
var stream = new _EventStream(e, _eventType, useCapture);
stream.listen((event) {
var wrapped = new _BeforeUnloadEvent(event);
controller.add(wrapped);
return wrapped.returnValue;
});
return controller.stream;
}
String getEventType(EventTarget target) {
return _eventType;
}
}