/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.waveprotocol.wave.client.clipboard;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.IFrameElement;
import com.google.gwt.dom.client.Node;
import org.waveprotocol.wave.client.common.util.DomHelper;
import org.waveprotocol.wave.client.editor.selection.html.NativeSelectionUtil;
import org.waveprotocol.wave.model.document.util.Point;
/**
* Firefox implementation of the paste buffer. We cannot use a standard div set
* to contentEditable because pasting any javascript will automatically
* execute it. Instead, use an offscreen iframe whose document is set to
* "designMode". This is roughly equivalent to contentEditable with the
* exception of being more sandbox-like and prevents script execution.
*
* NOTE(user): There's actually a lot of blackmagic in this class. In setupDom,
* the real contentDocument of the iframe is actually created asynchronously. In
* this code are actually using a transient element inside a transient
* contentDocument, that just happens to work.
*
* Why don't we get contentDocument().getBody() after a delay then?
*
* If we get the use the real element, i.e. assign element from
* iframe.getContentDocument().getBody() asynchronously we'll suffers from paste
* self-xss. This seems strange, because we're using designMode in an IFrame,
* which is supposed to guard against javascript execution. If we make this
* iframe visible and paste in html that executes js directly, we are protected
* from js execution. However, if we are doing it programmatically inside a copy
* event, we are not protected.
*
* Tested on: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.2)
* Gecko/20100115 Firefox/3.6 GTB7.0
*
*/
class PasteBufferImplFirefox extends PasteBufferImpl {
private final IFrameElement iframe;
/**
* Protected empty constructor. Will be created by factory constructor in
* PasteBufferImpl.
*/
protected PasteBufferImplFirefox() {
iframe = Document.get().createIFrameElement();
}
@Override
protected void setupDom() {
Document.get().getBody().appendChild(iframe);
positionPasteBuffer(iframe);
element = iframe.getContentDocument().getBody();
setDesignMode(iframe.getContentDocument());
}
private static native void setDesignMode(Document doc) /*-{
doc.designMode = "on";
}-*/;
@Override
public void prepareForPaste() {
super.prepareForPaste();
// N.B.(davidbyttow): In FF3, focus is not implicitly set by setting the
// selection when appending a DOM element dynamically. So we must explicitly
// set the focus.
DomHelper.focus(iframe);
NativeSelectionUtil.setCaret(Point.<Node>end(element));
}
}