Bug 593891 - Part 3 : Fennec nsICapturePicker implementation [r=mfinkle]

This commit is contained in:
Fabrice Desré 2011-09-26 17:25:41 -07:00
parent d79d54482e
commit 5a5295587e
13 changed files with 403 additions and 2 deletions

View File

@ -0,0 +1,174 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Fabrice Desré <fabrice@mozilla.com>, Original author
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var CaptureDialog = {
_video: null,
_container: null,
_lastOrientationEvent: null,
init: function() {
document.getElementsByAttribute('command', 'cmd_ok')[0].focus();
this._video = document.getElementById("capturepicker-video");
this._container = document.getElementById("capturepicker-container");
window.addEventListener("resize", this, false);
window.addEventListener("deviceorientation", this, false);
this.handleEvent({ type: "resize" });
},
setPreviewOrientation: function(aEvent) {
if (window.innerWidth < window.innerHeight) {
if (aEvent.beta > 0)
this._video.style.MozTransform = "rotate(90deg)";
else
this._video.style.MozTransform = "rotate(-90deg)";
this._container.classList.add("vertical");
}
else {
if (aEvent.gamma > 0)
this._video.style.MozTransform = "rotate(180deg)";
else
this._video.style.MozTransform = "";
this._container.classList.remove("vertical");
}
},
handleEvent: function(aEvent) {
if (aEvent.type == "deviceorientation") {
if (!this._lastOrientationEvent)
this.setPreviewOrientation(aEvent);
this._lastOrientationEvent = aEvent;
}
else if (aEvent.type == "resize") {
if (this._lastOrientationEvent)
this.setPreviewOrientation(this._lastOrientationEvent);
}
},
cancel: function() {
this.doClose(false);
},
capture: function() {
this.doClose(true);
},
doClose: function(aResult) {
window.removeEventListener("resize", this, false);
window.removeEventListener("deviceorientation", this, false);
let dialog = document.getElementById("capturepicker-dialog");
dialog.arguments.result = aResult;
if (aResult)
dialog.arguments.path = this.saveFrame();
document.getElementById("capturepicker-video").setAttribute("src", "");
dialog.close();
},
saveFrame: function() {
let Cc = Components.classes;
let Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
let width = 320;
let height = 240;
let rotation = 0;
if (window.innerWidth < window.innerHeight) {
width = 240;
height = 320;
if (this._lastOrientationEvent.beta > 0)
rotation = Math.PI / 2;
else
rotation = -Math.PI / 2;
}
else {
if (this._lastOrientationEvent.gamma > 0)
rotation = Math.PI;
}
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
//ctx.save();
if (rotation != 0) {
if (rotation == Math.PI / 2)
ctx.translate(width, 0);
else if (rotation == -Math.PI / 2)
ctx.translate(-width, 0);
else
ctx.translate(width, height);
ctx.rotate(rotation);
}
ctx.drawImage(document.getElementById("capturepicker-video"), 0, 0, 320, 240);
//ctx.restore();
let url = canvas.toDataURL("image/png");
let tmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
let file = tmpDir.clone();
file.append("capture.png");
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
persist.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES | Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
let mDone = false;
let mFailed = false;
let progressListener = {
onProgressChange: function() {
/* Ignore progress callback */
},
onStateChange: function(aProgress, aRequest, aStateFlag, aStatus) {
if (aStateFlag & Ci.nsIWebProgressListener.STATE_STOP) {
mDone = true;
}
}
}
persist.progressListener = progressListener;
let source = Services.io.newURI(url, "UTF8", null);;
persist.saveURI(source, null, null, null, null, file);
// don't wait more than 3 seconds for a successful save
window.setTimeout(function() {
mDone = true;
mFailed = true;
}, 3000);
while (!mDone)
Services.tm.currentThread.processNextEvent(true);
return (mFailed ? null : file.path);
}
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0"?>
<!DOCTYPE dialog SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="capturepicker-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementById('capturepicker-dialog').CaptureDialog.init()"
script="chrome://browser/content/CaptureDialog.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('capturepicker-dialog').CaptureDialog.capture();"/>
<command id="cmd_cancel" oncommand="document.getElementById('capturepicker-dialog').CaptureDialog.cancel();"/>
</commandset>
<vbox class="prompt-header" flex="1">
<description id="capturepicker-title" class="prompt-title" crop="center" flex="1"/>
<separator id="capturepicker-separator" class="prompt-line"/>
<hbox flex="1" pack="center">
<vbox id="capturepicker-container" pack="center">
<video xmlns="http://www.w3.org/1999/xhtml" id="capturepicker-video"
width="320" height="240"
src="moz-device:?width=320&amp;height=240&amp;type=video/x-raw-yuv"
autoplay="true"> </video>
</vbox>
</hbox>
</vbox>
<hbox id="capturepicker-buttons-box" class="prompt-buttons">
<button class="prompt-button" label="&ok.label;" command="cmd_ok"/>
<button class="prompt-button" label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</dialog>

View File

@ -0,0 +1,20 @@
var CapturePickerUI = {
init: function() {
this.messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
this.messageManager.addMessageListener("CapturePicker:Show", this);
},
receiveMessage: function(aMessage) {
switch (aMessage.name) {
case "CapturePicker:Show":
let params = { result: true };
let dialog = importDialog(null, "chrome://browser/content/CaptureDialog.xul", params);
document.getElementById("capturepicker-title").appendChild(document.createTextNode(aMessage.json.title));
dialog.waitForClose();
return { value: params.result, path: params.path };
break;
}
// prevents warning from the script loader
return null;
}
};

View File

@ -114,7 +114,8 @@ XPCOMUtils.defineLazyGetter(this, "CommonUI", function() {
["WeaveGlue", "chrome://browser/content/sync.js"],
#endif
["WebappsUI", "chrome://browser/content/WebappsUI.js"],
["SSLExceptions", "chrome://browser/content/exceptions.js"]
["SSLExceptions", "chrome://browser/content/exceptions.js"],
["CapturePickerUI", "chrome://browser/content/CapturePickerUI.js"]
].forEach(function (aScript) {
let [name, script] = aScript;
XPCOMUtils.defineLazyGetter(window, name, function() {

View File

@ -491,6 +491,7 @@ var BrowserUI = {
FullScreenVideo.init();
NewTabPopup.init();
WebappsUI.init();
CapturePickerUI.init();
// If some add-ons were disabled during during an application update, alert user
let addonIDs = AddonManager.getStartupChanges("disabled");

View File

@ -80,6 +80,9 @@ chrome.jar:
content/fullscreen-video.js (content/fullscreen-video.js)
content/fullscreen-video.xhtml (content/fullscreen-video.xhtml)
content/netError.xhtml (content/netError.xhtml)
content/CapturePickerUI.js (content/CapturePickerUI.js)
content/CaptureDialog.js (content/CaptureDialog.js)
content/CaptureDialog.xul (content/CaptureDialog.xul)
% override chrome://global/content/config.xul chrome://browser/content/config.xul
% override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml

View File

@ -0,0 +1,118 @@
/* -*- Mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is CapturePicker.js
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kyle Huey <me@kylehuey.com>
* Fabrice Desré <fabrice@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
function CapturePicker() {
this.messageManager = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
}
CapturePicker.prototype = {
_file: null,
_mode: -1,
_result: -1,
_shown: false,
_title: "",
_type: "",
_window: null,
//
// nsICapturePicker
//
init: function(aWindow, aTitle, aMode) {
this._window = aWindow;
this._title = aTitle;
this._mode = aMode;
},
show: function() {
if (this._shown)
throw Cr.NS_ERROR_UNEXPECTED;
this._shown = true;
let res = this.messageManager.sendSyncMessage("CapturePicker:Show", { title: this._title, mode: this._mode, type: this._type })[0];
if (res.value)
this._file = res.path;
return (res.value ? Ci.nsICapturePicker.RETURN_OK : Ci.nsICapturePicker.RETURN_CANCEL);
},
modeMayBeAvailable: function(aMode) {
if (aMode != Ci.nsICapturePicker.MODE_STILL)
return false;
return true;
},
get file() {
if (this._file) {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(this._file);
let utils = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
return utils.wrapDOMFile(file);
} else {
throw Cr.NS_ERROR_FAILURE;
}
},
get type() {
return this._type;
},
set type(aNewType) {
if (this._shown)
throw Cr.NS_ERROR_UNEXPECTED;
else
this._type = aNewType;
},
// QI
QueryInterface: XPCOMUtils.generateQI([Ci.nsICapturePicker]),
// XPCOMUtils factory
classID: Components.ID("{cb5a47f0-b58c-4fc3-b61a-358ee95f8238}"),
};
var components = [ CapturePicker ];
const NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

View File

@ -75,6 +75,7 @@ EXTRA_COMPONENTS = \
LoginManager.js \
LoginManagerPrompter.js \
BlocklistPrompt.js \
CapturePicker.js \
$(NULL)
ifdef MOZ_SAFE_BROWSING

View File

@ -120,3 +120,8 @@ category app-startup SafeBrowsing service,@mozilla.org/safebrowsing/application;
component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js
contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
#endif
# CapturePicker.js
component {cb5a47f0-b58c-4fc3-b61a-358ee95f8238} CapturePicker.js
contract @mozilla.org/capturepicker;1 {cb5a47f0-b58c-4fc3-b61a-358ee95f8238}

View File

@ -613,5 +613,6 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/UpdatePrompt.js
#endif
@BINPATH@/components/XPIDialogService.js
@BINPATH@/components/CapturePicker.js
@BINPATH@/components/browsercomps.xpt
@BINPATH@/extensions/feedback@mobile.mozilla.org.xpi

View File

@ -1617,4 +1617,18 @@ setting {
}
}
/* Capture picker ------------------------------------------------------------- */
#capturepicker-video {
border: @border_width_tiny@ solid white;
}
#capturepicker-container {
margin: @margin_normal@;
}
#capturepicker-container.vertical {
height: 330px;
}
%include tablet.css

View File

@ -1596,4 +1596,18 @@ setting:hover:active {
list-style-image: url("chrome://browser/skin/images/handle-end.png");
}
/* Capture picker ------------------------------------------------------------- */
#capturepicker-video {
border: @border_width_tiny@ solid white;
}
#capturepicker-container {
margin: @margin_normal@;
}
#capturepicker-container.vertical {
height: 330px;
}
%include ../tablet.css

View File

@ -1997,4 +1997,18 @@ setting {
-moz-padding-start: @padding_large@;
}
/* Capture picker ------------------------------------------------------------- */
#capturepicker-video {
border: @border_width_tiny@ solid white;
}
#capturepicker-container {
margin: @margin_normal@;
}
#capturepicker-container.vertical {
height: 330px;
}
%include ../tablet.css