зеркало из https://github.com/mozilla/gecko-dev.git
Bug 719731 - Get rid of window mediator's getMostRecentBrowserWindow across Tilt codebase; r=rcampbell
This commit is contained in:
Родитель
50995502c6
Коммит
a5a3b1aa44
|
@ -126,10 +126,10 @@ Tilt.prototype = {
|
|||
|
||||
// create a visualizer instance for the current tab
|
||||
this.visualizers[id] = new TiltVisualizer({
|
||||
parentNode: this.chromeWindow.gBrowser.selectedBrowser.parentNode,
|
||||
chromeWindow: this.chromeWindow,
|
||||
contentWindow: this.chromeWindow.gBrowser.selectedBrowser.contentWindow,
|
||||
parentNode: this.chromeWindow.gBrowser.selectedBrowser.parentNode,
|
||||
requestAnimationFrame: this.chromeWindow.mozRequestAnimationFrame,
|
||||
inspectorUI: this.chromeWindow.InspectorUI,
|
||||
notifications: this.NOTIFICATIONS
|
||||
});
|
||||
|
||||
|
@ -183,7 +183,7 @@ Tilt.prototype = {
|
|||
let pageYOffset = content.pageYOffset * presenter.transforms.zoom;
|
||||
|
||||
Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.DESTROYING, null);
|
||||
TiltUtils.setDocumentZoom(presenter.transforms.zoom);
|
||||
TiltUtils.setDocumentZoom(this.chromeWindow, presenter.transforms.zoom);
|
||||
|
||||
controller.removeEventListeners();
|
||||
controller.arcball.reset([-pageXOffset, -pageYOffset]);
|
||||
|
|
|
@ -530,18 +530,6 @@ TiltUtils.destroyObject = function TU_destroyObject(aScope)
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the most recent browser window.
|
||||
*
|
||||
* @return {Window} the window
|
||||
*/
|
||||
TiltUtils.getBrowserWindow = function TU_getBrowserWindow()
|
||||
{
|
||||
return Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getMostRecentWindow("navigator:browser");
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the unique ID of a window object.
|
||||
*
|
||||
|
@ -564,30 +552,36 @@ TiltUtils.getWindowId = function TU_getWindowId(aWindow)
|
|||
/**
|
||||
* Gets the markup document viewer zoom for the currently selected browser.
|
||||
*
|
||||
* @param {Window} aChromeWindow
|
||||
* the top-level browser window
|
||||
*
|
||||
* @return {Number} the zoom ammount
|
||||
*/
|
||||
TiltUtils.getDocumentZoom = function TU_getDocumentZoom() {
|
||||
return TiltUtils.getBrowserWindow()
|
||||
.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
|
||||
TiltUtils.getDocumentZoom = function TU_getDocumentZoom(aChromeWindow) {
|
||||
return aChromeWindow.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the markup document viewer zoom for the currently selected browser.
|
||||
*
|
||||
* @param {Window} aChromeWindow
|
||||
* the top-level browser window
|
||||
*
|
||||
* @param {Number} the zoom ammount
|
||||
*/
|
||||
TiltUtils.setDocumentZoom = function TU_getDocumentZoom(aZoom) {
|
||||
TiltUtils.getBrowserWindow()
|
||||
.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = aZoom;
|
||||
TiltUtils.setDocumentZoom = function TU_setDocumentZoom(aChromeWindow, aZoom) {
|
||||
aChromeWindow.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = aZoom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs a garbage collection.
|
||||
*
|
||||
* @param {Window} aChromeWindow
|
||||
* the top-level browser window
|
||||
*/
|
||||
TiltUtils.gc = function TU_gc()
|
||||
TiltUtils.gc = function TU_gc(aChromeWindow)
|
||||
{
|
||||
TiltUtils.getBrowserWindow()
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
aChromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
};
|
||||
|
|
|
@ -91,10 +91,10 @@ let EXPORTED_SYMBOLS = ["TiltVisualizer"];
|
|||
*
|
||||
* @param {Object} aProperties
|
||||
* an object containing the following properties:
|
||||
* {Element} parentNode: the parent node to hold the visualization
|
||||
* {Window} chromeWindow: a reference to the top level window
|
||||
* {Window} contentWindow: the content window holding the visualized doc
|
||||
* {Element} parentNode: the parent node to hold the visualization
|
||||
* {Function} requestAnimationFrame: responsible with scheduling loops
|
||||
* {InspectorUI} inspectorUI: necessary instance of the InspectorUI
|
||||
* {Object} notifications: necessary notifications for Tilt
|
||||
* {Function} onError: optional, function called if initialization failed
|
||||
* {Function} onLoad: optional, function called if initialization worked
|
||||
|
@ -104,6 +104,11 @@ function TiltVisualizer(aProperties)
|
|||
// make sure the properties parameter is a valid object
|
||||
aProperties = aProperties || {};
|
||||
|
||||
/**
|
||||
* Save a reference to the top-level window.
|
||||
*/
|
||||
this.chromeWindow = aProperties.chromeWindow;
|
||||
|
||||
/**
|
||||
* The canvas element used for rendering the visualization.
|
||||
*/
|
||||
|
@ -116,9 +121,9 @@ function TiltVisualizer(aProperties)
|
|||
* Visualization logic and drawing loop.
|
||||
*/
|
||||
this.presenter = new TiltVisualizer.Presenter(this.canvas,
|
||||
aProperties.chromeWindow,
|
||||
aProperties.contentWindow,
|
||||
aProperties.requestAnimationFrame,
|
||||
aProperties.inspectorUI,
|
||||
aProperties.notifications,
|
||||
aProperties.onError || null,
|
||||
aProperties.onLoad || null);
|
||||
|
@ -163,9 +168,12 @@ TiltVisualizer.prototype = {
|
|||
if (this.presenter) {
|
||||
TiltUtils.destroyObject(this.presenter);
|
||||
}
|
||||
|
||||
let chromeWindow = this.chromeWindow;
|
||||
|
||||
TiltUtils.destroyObject(this);
|
||||
TiltUtils.clearCache();
|
||||
TiltUtils.gc();
|
||||
TiltUtils.gc(chromeWindow);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -174,12 +182,12 @@ TiltVisualizer.prototype = {
|
|||
*
|
||||
* @param {HTMLCanvasElement} aCanvas
|
||||
* the canvas element used for rendering
|
||||
* @param {Object} aContentWindow
|
||||
* @param {Window} aChromeWindow
|
||||
* a reference to the top-level window
|
||||
* @param {Window} aContentWindow
|
||||
* the content window holding the document to be visualized
|
||||
* @param {Function} aRequestAnimationFrame
|
||||
* function responsible with scheduling loop frames
|
||||
* @param {InspectorUI} aInspectorUI
|
||||
* necessary instance of the InspectorUI
|
||||
* @param {Object} aNotifications
|
||||
* necessary notifications for Tilt
|
||||
* @param {Function} onError
|
||||
|
@ -188,13 +196,23 @@ TiltVisualizer.prototype = {
|
|||
* function called if initialization worked
|
||||
*/
|
||||
TiltVisualizer.Presenter = function TV_Presenter(
|
||||
aCanvas, aContentWindow, aRequestAnimationFrame, aInspectorUI, aNotifications,
|
||||
aCanvas, aChromeWindow, aContentWindow, aRequestAnimationFrame, aNotifications,
|
||||
onError, onLoad)
|
||||
{
|
||||
/**
|
||||
* A canvas overlay used for drawing the visualization.
|
||||
*/
|
||||
this.canvas = aCanvas;
|
||||
|
||||
/**
|
||||
* Save a reference to the top-level window, to access InspectorUI or Tilt.
|
||||
*/
|
||||
this.chromeWindow = aChromeWindow;
|
||||
|
||||
/**
|
||||
* The content window generating the visualization
|
||||
*/
|
||||
this.contentWindow = aContentWindow;
|
||||
this.inspectorUI = aInspectorUI;
|
||||
this.tiltUI = aInspectorUI.chromeWin.Tilt;
|
||||
|
||||
/**
|
||||
* Shortcut for accessing notifications strings.
|
||||
|
@ -235,7 +253,7 @@ TiltVisualizer.Presenter = function TV_Presenter(
|
|||
* Modified by events in the controller through delegate functions.
|
||||
*/
|
||||
this.transforms = {
|
||||
zoom: TiltUtils.getDocumentZoom(),
|
||||
zoom: TiltUtils.getDocumentZoom(aChromeWindow),
|
||||
offset: vec3.create(), // mesh offset, aligned to the viewport center
|
||||
translation: vec3.create(), // scene translation, on the [x, y, z] axis
|
||||
rotation: quat4.create() // scene rotation, expressed as a quaternion
|
||||
|
@ -525,13 +543,13 @@ TiltVisualizer.Presenter.prototype = {
|
|||
// if there's no initial selection made, highlight the required node
|
||||
if (!this._initialSelection) {
|
||||
this._initialSelection = true;
|
||||
this.highlightNode(this.inspectorUI.selection);
|
||||
this.highlightNode(this.chromeWindow.InspectorUI.selection);
|
||||
}
|
||||
|
||||
if (!this._initialMeshConfiguration) {
|
||||
this._initialMeshConfiguration = true;
|
||||
|
||||
let zoom = TiltUtils.getDocumentZoom();
|
||||
let zoom = this.transforms.zoom;
|
||||
let width = Math.min(aData.meshWidth * zoom, renderer.width);
|
||||
let height = Math.min(aData.meshHeight * zoom, renderer.height);
|
||||
|
||||
|
@ -605,7 +623,7 @@ TiltVisualizer.Presenter.prototype = {
|
|||
*/
|
||||
onResize: function TVP_onResize(e)
|
||||
{
|
||||
let zoom = TiltUtils.getDocumentZoom();
|
||||
let zoom = TiltUtils.getDocumentZoom(this.chromeWindow);
|
||||
let width = e.target.innerWidth * zoom;
|
||||
let height = e.target.innerHeight * zoom;
|
||||
|
||||
|
@ -724,7 +742,9 @@ TiltVisualizer.Presenter.prototype = {
|
|||
vec3.set([x, y + h, z * STACK_THICKNESS], highlight.v3);
|
||||
|
||||
this._currentSelection = aNodeIndex;
|
||||
this.inspectorUI.inspectNode(node, this.contentWindow.innerHeight < y ||
|
||||
|
||||
this.chromeWindow.InspectorUI.inspectNode(node,
|
||||
this.contentWindow.innerHeight < y ||
|
||||
this.contentWindow.pageYOffset > 0);
|
||||
|
||||
Services.obs.notifyObservers(null, this.NOTIFICATIONS.HIGHLIGHTING, null);
|
||||
|
@ -796,7 +816,7 @@ TiltVisualizer.Presenter.prototype = {
|
|||
}
|
||||
}, false);
|
||||
|
||||
let zoom = TiltUtils.getDocumentZoom();
|
||||
let zoom = TiltUtils.getDocumentZoom(this.chromeWindow);
|
||||
let width = this.renderer.width * zoom;
|
||||
let height = this.renderer.height * zoom;
|
||||
let mesh = this.meshStacks;
|
||||
|
@ -971,26 +991,34 @@ TiltVisualizer.Presenter.prototype = {
|
|||
*/
|
||||
TiltVisualizer.Controller = function TV_Controller(aCanvas, aPresenter)
|
||||
{
|
||||
/**
|
||||
* A canvas overlay on which mouse and keyboard event listeners are attached.
|
||||
*/
|
||||
this.canvas = aCanvas;
|
||||
|
||||
/**
|
||||
* Save a reference to the presenter to modify its model-view transforms.
|
||||
*/
|
||||
this.presenter = aPresenter;
|
||||
|
||||
/**
|
||||
* The initial controller dimensions and offset, in pixels.
|
||||
*/
|
||||
this.left = aPresenter.contentWindow.pageXOffset || 0;
|
||||
this.top = aPresenter.contentWindow.pageYOffset || 0;
|
||||
this.zoom = aPresenter.transforms.zoom;
|
||||
this.left = (aPresenter.contentWindow.pageXOffset || 0) * this.zoom;
|
||||
this.top = (aPresenter.contentWindow.pageYOffset || 0) * this.zoom;
|
||||
this.width = aCanvas.width;
|
||||
this.height = aCanvas.height;
|
||||
|
||||
this.left *= TiltUtils.getDocumentZoom();
|
||||
this.top *= TiltUtils.getDocumentZoom();
|
||||
|
||||
/**
|
||||
* Arcball used to control the visualization using the mouse.
|
||||
*/
|
||||
this.arcball = new TiltVisualizer.Arcball(this.width, this.height, 0,
|
||||
[this.width + this.left < aPresenter.maxTextureSize ? -this.left : 0,
|
||||
this.height + this.top < aPresenter.maxTextureSize ? -this.top : 0]);
|
||||
this.arcball = new TiltVisualizer.Arcball(
|
||||
this.presenter.chromeWindow, this.width, this.height, 0,
|
||||
[
|
||||
this.width + this.left < aPresenter.maxTextureSize ? -this.left : 0,
|
||||
this.height + this.top < aPresenter.maxTextureSize ? -this.top : 0
|
||||
]);
|
||||
|
||||
/**
|
||||
* Object containing the rotation quaternion and the translation amount.
|
||||
|
@ -1192,7 +1220,7 @@ TiltVisualizer.Controller.prototype = {
|
|||
onKeyUp: function TVC_onKeyUp(e)
|
||||
{
|
||||
let code = e.keyCode || e.which;
|
||||
let tilt = this.presenter.tiltUI;
|
||||
let tilt = this.presenter.chromeWindow.Tilt;
|
||||
|
||||
if (code === e.DOM_VK_ESCAPE) {
|
||||
tilt.destroy(tilt.currentWindowId, true);
|
||||
|
@ -1221,7 +1249,7 @@ TiltVisualizer.Controller.prototype = {
|
|||
*/
|
||||
onResize: function TVC_onResize(e)
|
||||
{
|
||||
let zoom = TiltUtils.getDocumentZoom();
|
||||
let zoom = TiltUtils.getDocumentZoom(this.presenter.chromeWindow);
|
||||
let width = e.target.innerWidth * zoom;
|
||||
let height = e.target.innerHeight * zoom;
|
||||
|
||||
|
@ -1256,6 +1284,8 @@ TiltVisualizer.Controller.prototype = {
|
|||
* in the Graphics Interface ’92 Proceedings. It features good behavior
|
||||
* easy implementation, cheap execution.
|
||||
*
|
||||
* @param {Window} aChromeWindow
|
||||
* a reference to the top-level window
|
||||
* @param {Number} aWidth
|
||||
* the width of canvas
|
||||
* @param {Number} aHeight
|
||||
|
@ -1268,8 +1298,13 @@ TiltVisualizer.Controller.prototype = {
|
|||
* optional, initial quaternion rotation
|
||||
*/
|
||||
TiltVisualizer.Arcball = function TV_Arcball(
|
||||
aWidth, aHeight, aRadius, aInitialTrans, aInitialRot)
|
||||
aChromeWindow, aWidth, aHeight, aRadius, aInitialTrans, aInitialRot)
|
||||
{
|
||||
/**
|
||||
* Save a reference to the top-level window to set/remove intervals.
|
||||
*/
|
||||
this.chromeWindow = aChromeWindow;
|
||||
|
||||
/**
|
||||
* Values retaining the current horizontal and vertical mouse coordinates.
|
||||
*/
|
||||
|
@ -1725,17 +1760,17 @@ TiltVisualizer.Arcball.prototype = {
|
|||
this.onResetStart = null;
|
||||
}
|
||||
|
||||
let func = this._nextResetIntervalStep.bind(this);
|
||||
|
||||
this.cancelMouseEvents();
|
||||
this.cancelKeyEvents();
|
||||
this._cancelResetInterval();
|
||||
|
||||
let window = TiltUtils.getBrowserWindow();
|
||||
let func = this._nextResetIntervalStep.bind(this);
|
||||
|
||||
this._save();
|
||||
this._resetFinalTranslation = vec3.create(aFinalTranslation);
|
||||
this._resetFinalRotation = quat4.create(aFinalRotation);
|
||||
this._resetInterval = window.setInterval(func, ARCBALL_RESET_INTERVAL);
|
||||
this._resetInterval =
|
||||
this.chromeWindow.setInterval(func, ARCBALL_RESET_INTERVAL);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1744,9 +1779,8 @@ TiltVisualizer.Arcball.prototype = {
|
|||
_cancelResetInterval: function TVA__cancelResetInterval()
|
||||
{
|
||||
if (this._resetInterval) {
|
||||
let window = TiltUtils.getBrowserWindow();
|
||||
this.chromeWindow.clearInterval(this._resetInterval);
|
||||
|
||||
window.clearInterval(this._resetInterval);
|
||||
this._resetInterval = null;
|
||||
this._save();
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ function isExpectedUpdate(update1, update2) {
|
|||
}
|
||||
|
||||
function test() {
|
||||
let arcball1 = new TiltVisualizer.Arcball(123, 456);
|
||||
let arcball1 = new TiltVisualizer.Arcball(window, 123, 456);
|
||||
|
||||
is(arcball1.width, 123,
|
||||
"The first arcball width wasn't set correctly.");
|
||||
|
@ -38,7 +38,7 @@ function test() {
|
|||
"The first arcball radius wasn't implicitly set correctly.");
|
||||
|
||||
|
||||
let arcball2 = new TiltVisualizer.Arcball(987, 654);
|
||||
let arcball2 = new TiltVisualizer.Arcball(window, 987, 654);
|
||||
|
||||
is(arcball2.width, 987,
|
||||
"The second arcball width wasn't set correctly.");
|
||||
|
@ -48,7 +48,7 @@ function test() {
|
|||
"The second arcball radius wasn't implicitly set correctly.");
|
||||
|
||||
|
||||
let arcball3 = new TiltVisualizer.Arcball(512, 512);
|
||||
let arcball3 = new TiltVisualizer.Arcball(window, 512, 512);
|
||||
|
||||
let sphereVec = vec3.create();
|
||||
arcball3.pointToSphere(123, 456, 256, 512, 512, sphereVec);
|
||||
|
|
|
@ -43,8 +43,4 @@ function test() {
|
|||
"Not all members of the destroyed object were deleted.");
|
||||
is(typeof someObject.func, "undefined",
|
||||
"Not all function members of the destroyed object were deleted.");
|
||||
|
||||
|
||||
is(TiltUtils.getBrowserWindow(), window,
|
||||
"The getBrowserWindow() function didn't return the correct window.");
|
||||
}
|
||||
|
|
|
@ -19,8 +19,9 @@ function test() {
|
|||
let webGLLoad = false;
|
||||
|
||||
let visualizer = new TiltVisualizer({
|
||||
parentNode: gBrowser.selectedBrowser.parentNode,
|
||||
chromeWindow: window,
|
||||
contentWindow: gBrowser.selectedBrowser.contentWindow,
|
||||
parentNode: gBrowser.selectedBrowser.parentNode,
|
||||
requestAnimationFrame: window.mozRequestAnimationFrame,
|
||||
inspectorUI: window.InspectorUI,
|
||||
|
||||
|
|
|
@ -9,17 +9,11 @@
|
|||
const ZOOM = 2;
|
||||
const RESIZE = 50;
|
||||
|
||||
function setZoom(value) {
|
||||
gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = value;
|
||||
}
|
||||
|
||||
function getZoom() {
|
||||
return gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
|
||||
}
|
||||
|
||||
function test() {
|
||||
TiltUtils.setDocumentZoom(Math.random());
|
||||
is(getZoom(), TiltUtils.getDocumentZoom(),
|
||||
let random = Math.random() * 10;
|
||||
|
||||
TiltUtils.setDocumentZoom(window, random);
|
||||
ok(isApprox(TiltUtils.getDocumentZoom(window), random),
|
||||
"The getDocumentZoom utility function didn't return the expected results.");
|
||||
|
||||
|
||||
|
@ -38,7 +32,7 @@ function test() {
|
|||
createTilt({
|
||||
onInspectorOpen: function()
|
||||
{
|
||||
setZoom(ZOOM);
|
||||
TiltUtils.setDocumentZoom(window, ZOOM);
|
||||
},
|
||||
onTiltOpen: function(instance)
|
||||
{
|
||||
|
@ -53,33 +47,33 @@ function test() {
|
|||
let arcball = instance.controller.arcball;
|
||||
|
||||
ok(isApprox(contentWindow.innerWidth * ZOOM, renderer.width, 1),
|
||||
"The renderer width wasn't set correctly.");
|
||||
"The renderer width wasn't set correctly before the resize.");
|
||||
ok(isApprox(contentWindow.innerHeight * ZOOM, renderer.height, 1),
|
||||
"The renderer height wasn't set correctly.");
|
||||
"The renderer height wasn't set correctly before the resize.");
|
||||
|
||||
ok(isApprox(contentWindow.innerWidth * ZOOM, arcball.width, 1),
|
||||
"The arcball width wasn't set correctly.");
|
||||
"The arcball width wasn't set correctly before the resize.");
|
||||
ok(isApprox(contentWindow.innerHeight * ZOOM, arcball.height, 1),
|
||||
"The arcball height wasn't set correctly.");
|
||||
"The arcball height wasn't set correctly before the resize.");
|
||||
|
||||
|
||||
window.resizeBy(-RESIZE * ZOOM, -RESIZE * ZOOM);
|
||||
|
||||
executeSoon(function() {
|
||||
ok(isApprox(contentWindow.innerWidth + RESIZE, initialWidth, 1),
|
||||
"The content window width wasn't set correctly.");
|
||||
"The content window width wasn't set correctly after the resize.");
|
||||
ok(isApprox(contentWindow.innerHeight + RESIZE, initialHeight, 1),
|
||||
"The content window height wasn't set correctly.");
|
||||
"The content window height wasn't set correctly after the resize.");
|
||||
|
||||
ok(isApprox(contentWindow.innerWidth * ZOOM, renderer.width, 1),
|
||||
"The renderer width wasn't set correctly.");
|
||||
"The renderer width wasn't set correctly after the resize.");
|
||||
ok(isApprox(contentWindow.innerHeight * ZOOM, renderer.height, 1),
|
||||
"The renderer height wasn't set correctly.");
|
||||
"The renderer height wasn't set correctly after the resize.");
|
||||
|
||||
ok(isApprox(contentWindow.innerWidth * ZOOM, arcball.width, 1),
|
||||
"The arcball width wasn't set correctly.");
|
||||
"The arcball width wasn't set correctly after the resize.");
|
||||
ok(isApprox(contentWindow.innerHeight * ZOOM, arcball.height, 1),
|
||||
"The arcball height wasn't set correctly.");
|
||||
"The arcball height wasn't set correctly after the resize.");
|
||||
|
||||
|
||||
window.resizeBy(RESIZE * ZOOM, RESIZE * ZOOM);
|
||||
|
|
|
@ -29,15 +29,15 @@ const DEFAULT_HTML = "data:text/html," +
|
|||
"</head>" +
|
||||
"<body>" +
|
||||
"<div id='first-law'>" +
|
||||
"A robot may not injure a human being or, through inaction, allow a" +
|
||||
"A robot may not injure a human being or, through inaction, allow a " +
|
||||
"human being to come to harm." +
|
||||
"</div>" +
|
||||
"<div>" +
|
||||
"A robot must obey the orders given to it by human beings, except" +
|
||||
"A robot must obey the orders given to it by human beings, except " +
|
||||
"where such orders would conflict with the First Law." +
|
||||
"</div>" +
|
||||
"<div>" +
|
||||
"A robot must protect its own existence as long as such protection" +
|
||||
"A robot must protect its own existence as long as such protection " +
|
||||
"does not conflict with the First or Second Laws." +
|
||||
"</div>" +
|
||||
"<body>" +
|
||||
|
|
Загрузка…
Ссылка в новой задаче