Commit 50ebc130 authored by rdevlin.cronin's avatar rdevlin.cronin Committed by Commit bot

[WebUI] ClosureCompile cr.ui.DragWrapper; create a real handler class

Pull out the extensions code drag-and-drop handler in order to reuse it
in the MD extensions page. In the process, closure compiler DragWrapper
and create a real interface for the DragWrapperHandler, which previously
was only specified in comments. Update drag wrapper handler implementors
with @override.

BUG=529395
CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2164693002
Cr-Commit-Position: refs/heads/master@{#407242}
parent 3359cdec
...@@ -11,5 +11,14 @@ ...@@ -11,5 +11,14 @@
], ],
'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
}, },
{
'target_name': 'drag_and_drop_handler',
'dependencies': [
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
'<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:drag_wrapper',
],
'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
},
] ]
} }
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
cr.define('extensions', function() {
'use strict';
/**
* @param {boolean} dragEnabled
* @param {!EventTarget} target
* @constructor
* @implements cr.ui.DragWrapperDelegate
*/
function DragAndDropHandler(dragEnabled, target) {
this.dragEnabled = dragEnabled;
/** @private {!EventTarget} */
this.eventTarget_ = target;
}
// TODO(devlin): Un-chrome.send-ify this implementation.
DragAndDropHandler.prototype = {
/** @type {boolean} */
dragEnabled: false,
/** @override */
shouldAcceptDrag: function(e) {
// External Extension installation can be disabled globally, e.g. while a
// different overlay is already showing.
if (!this.dragEnabled)
return false;
// We can't access filenames during the 'dragenter' event, so we have to
// wait until 'drop' to decide whether to do something with the file or
// not.
// See: http://www.w3.org/TR/2011/WD-html5-20110113/dnd.html#concept-dnd-p
return !!e.dataTransfer.types &&
e.dataTransfer.types.indexOf('Files') > -1;
},
/** @override */
doDragEnter: function() {
chrome.send('startDrag');
this.eventTarget_.dispatchEvent(
new CustomEvent('extension-drag-started'));
},
/** @override */
doDragLeave: function() {
this.fireDragEnded_();
chrome.send('stopDrag');
},
/** @override */
doDragOver: function(e) {
e.preventDefault();
},
/** @override */
doDrop: function(e) {
this.fireDragEnded_();
if (e.dataTransfer.files.length != 1)
return;
var toSend = '';
// Files lack a check if they're a directory, but we can find out through
// its item entry.
for (var i = 0; i < e.dataTransfer.items.length; ++i) {
if (e.dataTransfer.items[i].kind == 'file' &&
e.dataTransfer.items[i].webkitGetAsEntry().isDirectory) {
toSend = 'installDroppedDirectory';
break;
}
}
// Only process files that look like extensions. Other files should
// navigate the browser normally.
if (!toSend &&
/\.(crx|user\.js|zip)$/i.test(e.dataTransfer.files[0].name)) {
toSend = 'installDroppedFile';
}
if (toSend) {
e.preventDefault();
chrome.send(toSend);
}
},
/** @private */
fireDragEnded_: function() {
this.eventTarget_.dispatchEvent(new CustomEvent('extension-drag-ended'));
}
};
return {
DragAndDropHandler: DragAndDropHandler,
};
});
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<include src="../../../../ui/webui/resources/js/cr/ui/focus_row.js"> <include src="../../../../ui/webui/resources/js/cr/ui/focus_row.js">
<include src="../../../../ui/webui/resources/js/cr/ui/focus_grid.js"> <include src="../../../../ui/webui/resources/js/cr/ui/focus_grid.js">
<include src="../uber/uber_utils.js"> <include src="../uber/uber_utils.js">
<include src="drag_and_drop_handler.js">
<include src="extension_code.js"> <include src="extension_code.js">
<include src="extension_commands_overlay.js"> <include src="extension_commands_overlay.js">
<include src="extension_error_overlay.js"> <include src="extension_error_overlay.js">
...@@ -26,76 +27,6 @@ var webuiResponded = false; ...@@ -26,76 +27,6 @@ var webuiResponded = false;
cr.define('extensions', function() { cr.define('extensions', function() {
var ExtensionList = extensions.ExtensionList; var ExtensionList = extensions.ExtensionList;
// Implements the DragWrapper handler interface.
var dragWrapperHandler = {
/** @override */
shouldAcceptDrag: function(e) {
// External Extension installation can be disabled globally, e.g. while a
// different overlay is already showing.
if (!ExtensionSettings.getInstance().dragEnabled_)
return false;
// We can't access filenames during the 'dragenter' event, so we have to
// wait until 'drop' to decide whether to do something with the file or
// not.
// See: http://www.w3.org/TR/2011/WD-html5-20110113/dnd.html#concept-dnd-p
return (e.dataTransfer.types &&
e.dataTransfer.types.indexOf('Files') > -1);
},
/** @override */
doDragEnter: function() {
chrome.send('startDrag');
ExtensionSettings.showOverlay($('drop-target-overlay'));
},
/** @override */
doDragLeave: function() {
this.hideDropTargetOverlay_();
chrome.send('stopDrag');
},
/** @override */
doDragOver: function(e) {
e.preventDefault();
},
/** @override */
doDrop: function(e) {
this.hideDropTargetOverlay_();
if (e.dataTransfer.files.length != 1)
return;
var toSend = null;
// Files lack a check if they're a directory, but we can find out through
// its item entry.
for (var i = 0; i < e.dataTransfer.items.length; ++i) {
if (e.dataTransfer.items[i].kind == 'file' &&
e.dataTransfer.items[i].webkitGetAsEntry().isDirectory) {
toSend = 'installDroppedDirectory';
break;
}
}
// Only process files that look like extensions. Other files should
// navigate the browser normally.
if (!toSend &&
/\.(crx|user\.js|zip)$/i.test(e.dataTransfer.files[0].name)) {
toSend = 'installDroppedFile';
}
if (toSend) {
e.preventDefault();
chrome.send(toSend);
}
},
/**
* Hide the current overlay if it is the drop target overlay.
* @private
*/
hideDropTargetOverlay_: function() {
var currentOverlay = ExtensionSettings.getCurrentOverlay();
if (currentOverlay && currentOverlay.id === 'drop-target-overlay')
ExtensionSettings.showOverlay(null);
}
};
/** /**
* ExtensionSettings class * ExtensionSettings class
* @class * @class
...@@ -107,8 +38,6 @@ cr.define('extensions', function() { ...@@ -107,8 +38,6 @@ cr.define('extensions', function() {
cr.addSingletonGetter(ExtensionSettings); cr.addSingletonGetter(ExtensionSettings);
ExtensionSettings.prototype = { ExtensionSettings.prototype = {
__proto__: HTMLDivElement.prototype,
/** /**
* The drag-drop wrapper for installing external Extensions, if available. * The drag-drop wrapper for installing external Extensions, if available.
* null if external Extension installation is not available. * null if external Extension installation is not available.
...@@ -182,9 +111,20 @@ cr.define('extensions', function() { ...@@ -182,9 +111,20 @@ cr.define('extensions', function() {
this.handleUpdateExtensionNow_.bind(this)); this.handleUpdateExtensionNow_.bind(this));
if (!loadTimeData.getBoolean('offStoreInstallEnabled')) { if (!loadTimeData.getBoolean('offStoreInstallEnabled')) {
this.dragWrapper_ = new cr.ui.DragWrapper(document.documentElement, var dragTarget = document.documentElement;
dragWrapperHandler); /** @private {extensions.DragAndDropHandler} */
this.dragEnabled_ = true; this.dragWrapperHandler_ =
new extensions.DragAndDropHandler(true, dragTarget);
dragTarget.addEventListener('extension-drag-started', function() {
ExtensionSettings.showOverlay($('drop-target-overlay'));
});
dragTarget.addEventListener('extension-drag-ended', function() {
var overlay = ExtensionSettings.getCurrentOverlay();
if (overlay && overlay.id === 'drop-target-overlay')
ExtensionSettings.showOverlay(null);
});
this.dragWrapper_ =
new cr.ui.DragWrapper(dragTarget, this.dragWrapperHandler_);
} }
extensions.PackExtensionOverlay.getInstance().initializePage(); extensions.PackExtensionOverlay.getInstance().initializePage();
...@@ -428,8 +368,10 @@ cr.define('extensions', function() { ...@@ -428,8 +368,10 @@ cr.define('extensions', function() {
// drag-drop when there is any overlay showing other than the usual overlay // drag-drop when there is any overlay showing other than the usual overlay
// shown when drag-drop is started. // shown when drag-drop is started.
var settings = ExtensionSettings.getInstance(); var settings = ExtensionSettings.getInstance();
if (settings.dragWrapper_) if (settings.dragWrapper_) {
settings.dragEnabled_ = !node || node == $('drop-target-overlay'); assert(settings.dragWrapperHandler_).dragEnabled =
!node || node == $('drop-target-overlay');
}
uber.invokeMethodOnParent(node ? 'beginInterceptingEvents' : uber.invokeMethodOnParent(node ? 'beginInterceptingEvents' :
'stopInterceptingEvents'); 'stopInterceptingEvents');
......
...@@ -19,6 +19,7 @@ cr.define('ntp', function() { ...@@ -19,6 +19,7 @@ cr.define('ntp', function() {
* @param {boolean} animate If true, animates into existence. * @param {boolean} animate If true, animates into existence.
* @constructor * @constructor
* @extends {HTMLLIElement} * @extends {HTMLLIElement}
* @implements {cr.ui.DragWrapperDelegate}
*/ */
function NavDot(page, title, titleIsEditable, animate) { function NavDot(page, title, titleIsEditable, animate) {
var dot = cr.doc.createElement('li'); var dot = cr.doc.createElement('li');
...@@ -189,12 +190,7 @@ cr.define('ntp', function() { ...@@ -189,12 +190,7 @@ cr.define('ntp', function() {
return this.page_.shouldAcceptDrag(e); return this.page_.shouldAcceptDrag(e);
}, },
/** /** @override */
* A drag has entered the navigation dot. If the user hovers long enough,
* we will navigate to the relevant page.
* @param {Event} e The MouseOver event for the drag.
* @private
*/
doDragEnter: function(e) { doDragEnter: function(e) {
var self = this; var self = this;
function navPageClearTimeout() { function navPageClearTimeout() {
...@@ -206,14 +202,9 @@ cr.define('ntp', function() { ...@@ -206,14 +202,9 @@ cr.define('ntp', function() {
this.doDragOver(e); this.doDragOver(e);
}, },
/** /** @override */
* A dragged element has moved over the navigation dot. Show the correct
* indicator and prevent default handling so the <input> won't act as a drag
* target.
* @param {Event} e The MouseOver event for the drag.
* @private
*/
doDragOver: function(e) { doDragOver: function(e) {
// Prevent default handling so the <input> won't act as a drag target.
e.preventDefault(); e.preventDefault();
if (!this.dragWrapper_.isCurrentDragTarget) if (!this.dragWrapper_.isCurrentDragTarget)
...@@ -222,12 +213,7 @@ cr.define('ntp', function() { ...@@ -222,12 +213,7 @@ cr.define('ntp', function() {
this.page_.setDropEffect(e.dataTransfer); this.page_.setDropEffect(e.dataTransfer);
}, },
/** /** @override */
* A dragged element has been dropped on the navigation dot. Tell the page
* to append it.
* @param {Event} e The MouseOver event for the drag.
* @private
*/
doDrop: function(e) { doDrop: function(e) {
e.stopPropagation(); e.stopPropagation();
var tile = ntp.getCurrentlyDraggingTile(); var tile = ntp.getCurrentlyDraggingTile();
...@@ -238,11 +224,7 @@ cr.define('ntp', function() { ...@@ -238,11 +224,7 @@ cr.define('ntp', function() {
this.cancelDelayedSwitch_(); this.cancelDelayedSwitch_();
}, },
/** /** @override */
* The drag has left the navigation dot.
* @param {Event} e The MouseOver event for the drag.
* @private
*/
doDragLeave: function(e) { doDragLeave: function(e) {
this.cancelDelayedSwitch_(); this.cancelDelayedSwitch_();
}, },
......
...@@ -359,6 +359,7 @@ cr.define('ntp', function() { ...@@ -359,6 +359,7 @@ cr.define('ntp', function() {
* of the tile grid. * of the tile grid.
* @constructor * @constructor
* @extends {HTMLDivElement} * @extends {HTMLDivElement}
* @implements {cr.ui.DragWrapperDelegate}
*/ */
function TilePage(gridValues) { function TilePage(gridValues) {
var el = cr.doc.createElement('div'); var el = cr.doc.createElement('div');
...@@ -1145,18 +1146,12 @@ cr.define('ntp', function() { ...@@ -1145,18 +1146,12 @@ cr.define('ntp', function() {
return this.dragWrapper_.isCurrentDragTarget; return this.dragWrapper_.isCurrentDragTarget;
}, },
/** /** @override */
* Thunk for dragleave events fired on |tileGrid_|.
* @param {Event} e A MouseEvent for the drag.
*/
doDragLeave: function(e) { doDragLeave: function(e) {
this.cleanupDrag(); this.cleanupDrag();
}, },
/** /** @override */
* Performs all actions necessary when a drag enters the tile page.
* @param {Event} e A mouseover event for the drag enter.
*/
doDragEnter: function(e) { doDragEnter: function(e) {
// Applies the mask so doppleganger tiles disappear into the fog. // Applies the mask so doppleganger tiles disappear into the fog.
this.updateMask_(); this.updateMask_();
...@@ -1175,11 +1170,7 @@ cr.define('ntp', function() { ...@@ -1175,11 +1170,7 @@ cr.define('ntp', function() {
this.doDragOver(e); this.doDragOver(e);
}, },
/** /** @override */
* Performs all actions necessary when the user moves the cursor during
* a drag over the tile page.
* @param {Event} e A mouseover event for the drag over.
*/
doDragOver: function(e) { doDragOver: function(e) {
e.preventDefault(); e.preventDefault();
...@@ -1190,10 +1181,7 @@ cr.define('ntp', function() { ...@@ -1190,10 +1181,7 @@ cr.define('ntp', function() {
this.updateDropIndicator_(newDragIndex); this.updateDropIndicator_(newDragIndex);
}, },
/** /** @override */
* Performs all actions necessary when the user completes a drop.
* @param {Event} e A mouseover event for the drag drop.
*/
doDrop: function(e) { doDrop: function(e) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
......
...@@ -12,6 +12,8 @@ cr.define('ntp', function() { ...@@ -12,6 +12,8 @@ cr.define('ntp', function() {
/** /**
* @constructor * @constructor
* @extends {HTMLDivElement}
* @implements {cr.ui.DragWrapperDelegate}
*/ */
function Trash(trash) { function Trash(trash) {
trash.__proto__ = Trash.prototype; trash.__proto__ = Trash.prototype;
...@@ -39,10 +41,7 @@ cr.define('ntp', function() { ...@@ -39,10 +41,7 @@ cr.define('ntp', function() {
return tile.firstChild.canBeRemoved(); return tile.firstChild.canBeRemoved();
}, },
/** /** @override */
* Drag over handler.
* @param {Event} e The drag event.
*/
doDragOver: function(e) { doDragOver: function(e) {
ntp.getCurrentlyDraggingTile().dragClone.classList.add( ntp.getCurrentlyDraggingTile().dragClone.classList.add(
'hovering-on-trash'); 'hovering-on-trash');
...@@ -50,18 +49,12 @@ cr.define('ntp', function() { ...@@ -50,18 +49,12 @@ cr.define('ntp', function() {
e.preventDefault(); e.preventDefault();
}, },
/** /** @override */
* Drag enter handler.
* @param {Event} e The drag event.
*/
doDragEnter: function(e) { doDragEnter: function(e) {
this.doDragOver(e); this.doDragOver(e);
}, },
/** /** @override */
* Drop handler.
* @param {Event} e The drag event.
*/
doDrop: function(e) { doDrop: function(e) {
e.preventDefault(); e.preventDefault();
...@@ -70,10 +63,7 @@ cr.define('ntp', function() { ...@@ -70,10 +63,7 @@ cr.define('ntp', function() {
tile.landedOnTrash = true; tile.landedOnTrash = true;
}, },
/** /** @override */
* Drag leave handler.
* @param {Event} e The drag event.
*/
doDragLeave: function(e) { doDragLeave: function(e) {
ntp.getCurrentlyDraggingTile().dragClone.classList.remove( ntp.getCurrentlyDraggingTile().dragClone.classList.remove(
'hovering-on-trash'); 'hovering-on-trash');
......
...@@ -19,6 +19,14 @@ ...@@ -19,6 +19,14 @@
], ],
'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'],
}, },
{
'target_name': 'drag_wrapper',
'dependencies': [
'../../compiled_resources2.gyp:assert',
'../../compiled_resources2.gyp:cr',
],
'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{ {
'target_name': 'focus_grid', 'target_name': 'focus_grid',
'dependencies': [ 'dependencies': [
......
...@@ -8,22 +8,45 @@ ...@@ -8,22 +8,45 @@
* handle the nitty gritty of nested drag enters and leaves. * handle the nitty gritty of nested drag enters and leaves.
*/ */
cr.define('cr.ui', function() { cr.define('cr.ui', function() {
/** @interface */
var DragWrapperDelegate = function() {};
// TODO(devlin): The only method this "delegate" actually needs is
// shouldAcceptDrag(); the rest can be events emitted by the DragWrapper.
DragWrapperDelegate.prototype = {
/**
* @param {MouseEvent} e The event for the drag.
* @return {boolean} Whether the drag should be accepted. If false,
* subsequent methods (doDrag*) will not be called.
*/
shouldAcceptDrag: assertNotReached,
/** @param {MouseEvent} e */
doDragEnter: assertNotReached,
/** @param {MouseEvent} e */
doDragLeave: assertNotReached,
/** @param {MouseEvent} e */
doDragOver: assertNotReached,
/** @param {MouseEvent} e */
doDrop: assertNotReached,
};
/** /**
* Creates a DragWrapper which listens for drag target events on |target| and * Creates a DragWrapper which listens for drag target events on |target| and
* delegates event handling to |handler|. The |handler| must implement: * delegates event handling to |delegate|.
* shouldAcceptDrag * @param {!Element} target
* doDragEnter * @param {!cr.ui.DragWrapperDelegate} delegate
* doDragLeave
* doDragOver
* doDrop
* @constructor * @constructor
*/ */
function DragWrapper(target, handler) { function DragWrapper(target, delegate) {
this.initialize(target, handler); this.initialize(target, delegate);
} }
DragWrapper.prototype = { DragWrapper.prototype = {
initialize: function(target, handler) { initialize: function(target, delegate) {
target.addEventListener('dragenter', target.addEventListener('dragenter',
this.onDragEnter_.bind(this)); this.onDragEnter_.bind(this));
target.addEventListener('dragover', this.onDragOver_.bind(this)); target.addEventListener('dragover', this.onDragOver_.bind(this));
...@@ -31,7 +54,7 @@ cr.define('cr.ui', function() { ...@@ -31,7 +54,7 @@ cr.define('cr.ui', function() {
target.addEventListener('dragleave', this.onDragLeave_.bind(this)); target.addEventListener('dragleave', this.onDragLeave_.bind(this));
this.target_ = target; this.target_ = target;
this.handler_ = handler; this.delegate_ = delegate;
}, },
/** /**
...@@ -55,20 +78,20 @@ cr.define('cr.ui', function() { ...@@ -55,20 +78,20 @@ cr.define('cr.ui', function() {
}, },
/** /**
* Handler for dragenter events fired on |target_|. * Delegate for dragenter events fired on |target_|.
* @param {Event} e A MouseEvent for the drag. * @param {MouseEvent} e A MouseEvent for the drag.
* @private * @private
*/ */
onDragEnter_: function(e) { onDragEnter_: function(e) {
if (++this.dragEnters_ == 1) { if (++this.dragEnters_ == 1) {
if (this.handler_.shouldAcceptDrag(e)) { if (this.delegate_.shouldAcceptDrag(e)) {
this.target_.classList.add('drag-target'); this.target_.classList.add('drag-target');
this.handler_.doDragEnter(e); this.delegate_.doDragEnter(e);
} }
} else { } else {
// Sometimes we'll get an enter event over a child element without an // Sometimes we'll get an enter event over a child element without an
// over event following it. In this case we have to still call the // over event following it. In this case we have to still call the
// drag over handler so that we make the necessary updates (one visible // drag over delegate so that we make the necessary updates (one visible
// symptom of not doing this is that the cursor's drag state will // symptom of not doing this is that the cursor's drag state will
// flicker during drags). // flicker during drags).
this.onDragOver_(e); this.onDragOver_(e);
...@@ -83,7 +106,7 @@ cr.define('cr.ui', function() { ...@@ -83,7 +106,7 @@ cr.define('cr.ui', function() {
onDragOver_: function(e) { onDragOver_: function(e) {
if (!this.target_.classList.contains('drag-target')) if (!this.target_.classList.contains('drag-target'))
return; return;
this.handler_.doDragOver(e); this.delegate_.doDragOver(e);
}, },
/** /**
...@@ -96,7 +119,7 @@ cr.define('cr.ui', function() { ...@@ -96,7 +119,7 @@ cr.define('cr.ui', function() {
if (!this.target_.classList.contains('drag-target')) if (!this.target_.classList.contains('drag-target'))
return; return;
this.target_.classList.remove('drag-target'); this.target_.classList.remove('drag-target');
this.handler_.doDrop(e); this.delegate_.doDrop(e);
}, },
/** /**
...@@ -109,11 +132,12 @@ cr.define('cr.ui', function() { ...@@ -109,11 +132,12 @@ cr.define('cr.ui', function() {
return; return;
this.target_.classList.remove('drag-target'); this.target_.classList.remove('drag-target');
this.handler_.doDragLeave(e); this.delegate_.doDragLeave(e);
}, },
}; };
return { return {
DragWrapper: DragWrapper DragWrapper: DragWrapper,
DragWrapperDelegate: DragWrapperDelegate,
}; };
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment