Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Firefox clipboard commands #2601

Merged
merged 10 commits into from Nov 25, 2017
4 changes: 2 additions & 2 deletions background_scripts/commands.coffee
Expand Up @@ -340,8 +340,8 @@ commandDescriptions =
toggleViewSource: ["View page source", { noRepeat: true }]

copyCurrentUrl: ["Copy the current URL to the clipboard", { noRepeat: true }]
openCopiedUrlInCurrentTab: ["Open the clipboard's URL in the current tab", { background: true, noRepeat: true }]
openCopiedUrlInNewTab: ["Open the clipboard's URL in a new tab", { background: true, repeatLimit: 20 }]
openCopiedUrlInCurrentTab: ["Open the clipboard's URL in the current tab", { noRepeat: true }]
openCopiedUrlInNewTab: ["Open the clipboard's URL in a new tab", { repeatLimit: 20 }]

enterInsertMode: ["Enter insert mode", { noRepeat: true }]
passNextKey: ["Pass the next key to the page"]
Expand Down
29 changes: 29 additions & 0 deletions content_scripts/hud.coffee
Expand Up @@ -9,6 +9,8 @@ HUD =
findMode: null
abandon: -> @hudUI?.hide false

pasteListener: null # Set by @pasteFromClipboard to handle the value returned by pasteResponse

# This HUD is styled to precisely mimick the chrome HUD on Mac. Use the "has_popup_and_link_hud.html"
# test harness to tweak these styles to match Chrome's. One limitation of our HUD display is that
# it doesn't sit on top of horizontal scrollbars like Chrome's HUD does.
Expand Down Expand Up @@ -82,6 +84,33 @@ HUD =
@findMode.exit()
postExit?()

# These commands manage copying and pasting from the clipboard in the HUD frame.
# NOTE(mrmr1993): We need this to copy and paste on Firefox:
# * an element can't be focused in the background page, so copying/pasting doesn't work
# * we don't want to disrupt the focus in the page, in case the page is listening for focus/blur events.
# * the HUD shouldn't be active for this frame while any of the copy/paste commands are running.
copyToClipboard: (text) ->
DomUtils.documentComplete =>
@init()
@hudUI?.postMessage {name: "copyToClipboard", data: text}

pasteFromClipboard: (@pasteListener) ->
DomUtils.documentComplete =>
@init()
# Show the HUD frame, so Firefox will actually perform the paste.
@hudUI.toggleIframeElementClasses "vimiumUIComponentHidden", "vimiumUIComponentVisible"
@tween.fade 0, 0
@hudUI.postMessage {name: "pasteFromClipboard"}

pasteResponse: ({data}) ->
# Hide the HUD frame again.
@hudUI.toggleIframeElementClasses "vimiumUIComponentVisible", "vimiumUIComponentHidden"
@unfocusIfFocused()
@pasteListener data

unfocusIfFocused: ->
document.activeElement.blur() if document.activeElement == @hudUI?.iframeElement

class Tween
opacity: 0
intervalId: -1
Expand Down
2 changes: 1 addition & 1 deletion content_scripts/link_hints.coffee
Expand Up @@ -31,7 +31,7 @@ COPY_LINK_URL =
indicator: "Copy link URL to Clipboard"
linkActivator: (link) ->
if link.href?
chrome.runtime.sendMessage handler: "copyToClipboard", data: link.href
HUD.copyToClipboard link.href
url = link.href
url = url[0..25] + "...." if 28 < url.length
HUD.showForDuration "Yanked #{url}", 2000
Expand Down
11 changes: 10 additions & 1 deletion content_scripts/mode_normal.coffee
Expand Up @@ -91,10 +91,19 @@ NormalModeCommands =

copyCurrentUrl: ->
chrome.runtime.sendMessage { handler: "getCurrentTabUrl" }, (url) ->
chrome.runtime.sendMessage { handler: "copyToClipboard", data: url }
HUD.copyToClipboard url
url = url[0..25] + "...." if 28 < url.length
HUD.showForDuration("Yanked #{url}", 2000)

openCopiedUrlInNewTab: (count) ->
HUD.pasteFromClipboard (url) ->
for i in [0...count] by 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might change this to use mkRepeatCommand in main.coffee (to keep the count logic in as few places as possible).

chrome.runtime.sendMessage { handler: "openUrlInNewTab", url }

openCopiedUrlInCurrentTab: ->
HUD.pasteFromClipboard (url) ->
chrome.runtime.sendMessage { handler: "openUrlInCurrentTab", url }

# Mode changes.
enterInsertMode: ->
# If a focusable element receives the focus, then we exit and leave the permanently-installed insert-mode
Expand Down
2 changes: 1 addition & 1 deletion content_scripts/mode_visual.coffee
Expand Up @@ -312,7 +312,7 @@ class VisualMode extends KeyHandlerMode
yank: (args = {}) ->
@yankedText = @selection.toString()
@exit()
chrome.runtime.sendMessage handler: "copyToClipboard", data: @yankedText
HUD.copyToClipboard @yankedText

message = @yankedText.replace /\s+/g, " "
message = message[...12] + "..." if 15 < @yankedText.length
Expand Down
9 changes: 5 additions & 4 deletions lib/clipboard.coffee
@@ -1,8 +1,9 @@
Clipboard =
_createTextArea: ->
textArea = document.createElement "textarea"
_createTextArea: (tagName = "textarea") ->
textArea = document.createElement tagName
textArea.style.position = "absolute"
textArea.style.left = "-100%"
textArea.contentEditable = "true"
textArea

# http://groups.google.com/group/chromium-extensions/browse_thread/thread/49027e7f3b04f68/f6ab2457dee5bf55
Expand All @@ -16,11 +17,11 @@ Clipboard =
document.body.removeChild(textArea)

paste: ->
textArea = @_createTextArea()
textArea = @_createTextArea "div" # Use a <div> so Firefox pastes rich text.
document.body.appendChild(textArea)
textArea.focus()
document.execCommand("Paste")
value = textArea.value
value = textArea.innerText
document.body.removeChild(textArea)
value

Expand Down
1 change: 1 addition & 0 deletions manifest.json
Expand Up @@ -32,6 +32,7 @@
"bookmarks",
"history",
"clipboardRead",
"clipboardWrite",
"storage",
"sessions",
"notifications",
Expand Down
2 changes: 1 addition & 1 deletion pages/help_dialog.coffee
Expand Up @@ -83,7 +83,7 @@ HelpDialog =
commandNameElement.textContent = command.command
commandNameElement.title = "Click to copy \"#{command.command}\" to clipboard."
commandNameElement.addEventListener "click", ->
chrome.runtime.sendMessage handler: "copyToClipboard", data: commandNameElement.textContent
HUD.copyToClipboard commandNameElement.textContent
HUD.showForDuration("Yanked #{commandNameElement.textContent}.", 2000)

@showAdvancedCommands(@getShowAdvancedCommands())
Expand Down
14 changes: 14 additions & 0 deletions pages/hud.coffee
Expand Up @@ -95,5 +95,19 @@ handlers =
" (No matches)"
countElement.textContent = if showMatchText then countText else ""

copyToClipboard: (data) ->
focusedElement = document.activeElement
Clipboard.copy data
focusedElement?.focus()
window.parent.focus()
UIComponentServer.postMessage {name: "unfocusIfFocused", data}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfocusIfFocused, above, does not take data, does it? I'll remove data here; let me know if that's incorrect, @mrmr1993.


pasteFromClipboard: ->
focusedElement = document.activeElement
data = Clipboard.paste()
focusedElement?.focus()
window.parent.focus()
UIComponentServer.postMessage {name: "pasteResponse", data}

UIComponentServer.registerHandler ({data}) -> handlers[data.name ? data]? data
FindModeHistory.init()
1 change: 1 addition & 0 deletions pages/hud.html
Expand Up @@ -7,6 +7,7 @@
<script type="text/javascript" src="../lib/settings.js"></script>
<script type="text/javascript" src="../lib/keyboard_utils.js"></script>
<script type="text/javascript" src="../lib/find_mode_history.js"></script>
<script type="text/javascript" src="../lib/clipboard.js"></script>
<script type="text/javascript" src="ui_component_server.js"></script>
<script type="text/javascript" src="hud.js"></script>
</head>
Expand Down