|  | // Copyright 2016 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | import {DragWrapperDelegate} from 'chrome://resources/js/drag_wrapper.js'; | 
|  |  | 
|  | import {Service} from './service.js'; | 
|  |  | 
|  |  | 
|  | declare global { | 
|  | interface HTMLElementEventMap { | 
|  | 'drag-and-drop-load-error': CustomEvent<chrome.developerPrivate.LoadError>; | 
|  | } | 
|  | } | 
|  |  | 
|  | export class DragAndDropHandler implements DragWrapperDelegate { | 
|  | dragEnabled: boolean; | 
|  | private eventTarget_: EventTarget; | 
|  |  | 
|  | constructor(dragEnabled: boolean, target: EventTarget) { | 
|  | this.dragEnabled = dragEnabled; | 
|  | this.eventTarget_ = target; | 
|  | } | 
|  |  | 
|  | shouldAcceptDrag(e: DragEvent): boolean { | 
|  | // 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; | 
|  | } | 
|  |  | 
|  | doDragEnter() { | 
|  | Service.getInstance().notifyDragInstallInProgress(); | 
|  | this.eventTarget_.dispatchEvent(new CustomEvent('extension-drag-started')); | 
|  | } | 
|  |  | 
|  | doDragLeave() { | 
|  | this.fireDragEnded_(); | 
|  | } | 
|  |  | 
|  | doDragOver(e: DragEvent) { | 
|  | e.preventDefault(); | 
|  | } | 
|  |  | 
|  | doDrop(e: DragEvent) { | 
|  | this.fireDragEnded_(); | 
|  | if (e.dataTransfer!.files.length !== 1) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let handled = false; | 
|  |  | 
|  | // Files lack a check if they're a directory, but we can find out through | 
|  | // its item entry. | 
|  | const item = e.dataTransfer!.items[0]; | 
|  | if (item.kind === 'file' && item.webkitGetAsEntry()!.isDirectory) { | 
|  | handled = true; | 
|  | this.handleDirectoryDrop_(); | 
|  | } else if (/\.(crx|user\.js|zip)$/i.test(e.dataTransfer!.files[0].name)) { | 
|  | // Only process files that look like extensions. Other files should | 
|  | // navigate the browser normally. | 
|  | handled = true; | 
|  | this.handleFileDrop_(); | 
|  | } | 
|  |  | 
|  | if (handled) { | 
|  | e.preventDefault(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Handles a dropped file. | 
|  | */ | 
|  | private handleFileDrop_() { | 
|  | Service.getInstance().installDroppedFile(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Handles a dropped directory. | 
|  | */ | 
|  | private handleDirectoryDrop_() { | 
|  | Service.getInstance().loadUnpackedFromDrag().catch(loadError => { | 
|  | this.eventTarget_.dispatchEvent( | 
|  | new CustomEvent('drag-and-drop-load-error', {detail: loadError})); | 
|  | }); | 
|  | } | 
|  |  | 
|  | private fireDragEnded_() { | 
|  | this.eventTarget_.dispatchEvent(new CustomEvent('extension-drag-ended')); | 
|  | } | 
|  | } |