author | Yury <async.processingjs@yahoo.com> |
Sat, 25 Aug 2012 17:18:44 -0400 | |
changeset 103426 | 2fee87b2367aa9f2b1e156f2e0e0b6d5ab802c18 |
parent 103425 | 8a9316111cf6ac23874f412b67f090848e21ab08 |
child 103427 | 7c2aaefc3891ee3cafa971006dac064aed05901c |
push id | 13983 |
push user | ryanvm@gmail.com |
push date | Sat, 25 Aug 2012 21:18:37 +0000 |
treeherder | mozilla-inbound@7c2aaefc3891 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | johns, jaws, margaret, joshmoz |
bugs | 776208 |
milestone | 17.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/base/content/browser-plugins.js +++ b/browser/base/content/browser-plugins.js @@ -145,66 +145,86 @@ var gPluginHandler = { self.addLinkClickCallback(updateLink, "openPluginUpdatePage"); /* FALLTHRU */ case "PluginVulnerableNoUpdate": case "PluginClickToPlay": self._handleClickToPlayEvent(plugin); break; + case "PluginPlayPreview": + self._handlePlayPreviewEvent(plugin); + break; + case "PluginDisabled": let manageLink = doc.getAnonymousElementByAttribute(plugin, "class", "managePluginsLink"); self.addLinkClickCallback(manageLink, "managePlugins"); break; } // Hide the in-content UI if it's too big. The crashed plugin handler already did this. if (event.type != "PluginCrashed") { let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox"); /* overlay might be null, so only operate on it if it exists */ if (overlay != null && self.isTooSmall(plugin, overlay)) overlay.style.visibility = "hidden"; } }, + canActivatePlugin: function PH_canActivatePlugin(objLoadingContent) { + return !objLoadingContent.activated && + objLoadingContent.pluginFallbackType !== Ci.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW; + }, + activatePlugins: function PH_activatePlugins(aContentWindow) { let browser = gBrowser.getBrowserForDocument(aContentWindow.document); browser._clickToPlayPluginsActivated = true; let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let plugins = cwu.plugins; for (let plugin of plugins) { let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); - if (!objLoadingContent.activated) + if (gPluginHandler.canActivatePlugin(objLoadingContent)) objLoadingContent.playPlugin(); } let notification = PopupNotifications.getNotification("click-to-play-plugins", browser); if (notification) notification.remove(); }, activateSinglePlugin: function PH_activateSinglePlugin(aContentWindow, aPlugin) { let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent); - if (!objLoadingContent.activated) + if (gPluginHandler.canActivatePlugin(objLoadingContent)) objLoadingContent.playPlugin(); let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let haveUnplayedPlugins = cwu.plugins.some(function(plugin) { let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); - return (plugin != aPlugin && !objLoadingContent.activated); + return (plugin != aPlugin && gPluginHandler.canActivatePlugin(objLoadingContent)); }); let browser = gBrowser.getBrowserForDocument(aContentWindow.document); let notification = PopupNotifications.getNotification("click-to-play-plugins", browser); if (notification && !haveUnplayedPlugins) { browser._clickToPlayDoorhangerShown = false; notification.remove(); } }, + stopPlayPreview: function PH_stopPlayPreview(aPlugin, aPlayPlugin) { + let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent); + if (objLoadingContent.activated) + return; + + if (aPlayPlugin) + objLoadingContent.playPlugin(); + else + objLoadingContent.cancelPlayPreview(); + }, + newPluginInstalled : function(event) { // browser elements are anonymous so we can't just use target. var browser = event.originalTarget; // clear the plugin list, now that at least one plugin has been installed browser.missingPlugins = null; var notificationBox = gBrowser.getNotificationBox(browser); var notification = notificationBox.getNotificationWithValue("missing-plugins"); @@ -285,32 +305,72 @@ var gPluginHandler = { gPluginHandler.activateSinglePlugin(aEvent.target.ownerDocument.defaultView.top, aPlugin); }, true); } if (!browser._clickToPlayDoorhangerShown) gPluginHandler._showClickToPlayNotification(browser); }, + _handlePlayPreviewEvent: function PH_handlePlayPreviewEvent(aPlugin) { + let doc = aPlugin.ownerDocument; + let previewContent = doc.getAnonymousElementByAttribute(aPlugin, "class", "previewPluginContent"); + if (!previewContent) { + // the XBL binding is not attached (element is display:none), fallback to click-to-play logic + gPluginHandler.stopPlayPreview(aPlugin, false); + return; + } + let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0]; + if (!iframe) { + // lazy initialization of the iframe + iframe = doc.createElementNS("http://www.w3.org/1999/xhtml", "iframe"); + iframe.className = "previewPluginContentFrame"; + previewContent.appendChild(iframe); + + // Force a style flush, so that we ensure our binding is attached. + aPlugin.clientTop; + } + let pluginInfo = getPluginInfo(aPlugin); + let playPreviewUri = "data:application/x-moz-playpreview;," + pluginInfo.mimetype; + iframe.src = playPreviewUri; + + // MozPlayPlugin event can be dispatched from the extension chrome + // code to replace the preview content with the native plugin + previewContent.addEventListener("MozPlayPlugin", function playPluginHandler(aEvent) { + if (!aEvent.isTrusted) + return; + + previewContent.removeEventListener("MozPlayPlugin", playPluginHandler, true); + + let playPlugin = !aEvent.detail; + gPluginHandler.stopPlayPreview(aPlugin, playPlugin); + + // cleaning up: removes overlay iframe from the DOM + let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0]; + if (iframe) + previewContent.removeChild(iframe); + }, true); + }, + reshowClickToPlayNotification: function PH_reshowClickToPlayNotification() { if (!Services.prefs.getBoolPref("plugins.click_to_play")) return; let browser = gBrowser.selectedBrowser; let pluginsPermission = Services.perms.testPermission(browser.currentURI, "plugins"); if (pluginsPermission == Ci.nsIPermissionManager.DENY_ACTION) return; let contentWindow = browser.contentWindow; let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let pluginNeedsActivation = cwu.plugins.some(function(plugin) { let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); - return !objLoadingContent.activated; + return gPluginHandler.canActivatePlugin(objLoadingContent); }); if (pluginNeedsActivation) gPluginHandler._showClickToPlayNotification(browser); }, _showClickToPlayNotification: function PH_showClickToPlayNotification(aBrowser) { aBrowser._clickToPlayDoorhangerShown = true; let contentWindow = aBrowser.contentWindow;
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1015,16 +1015,17 @@ var gBrowserInit = { gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false); gBrowser.addEventListener("PluginNotFound", gPluginHandler, true); gBrowser.addEventListener("PluginCrashed", gPluginHandler, true); gBrowser.addEventListener("PluginBlocklisted", gPluginHandler, true); gBrowser.addEventListener("PluginOutdated", gPluginHandler, true); gBrowser.addEventListener("PluginDisabled", gPluginHandler, true); gBrowser.addEventListener("PluginClickToPlay", gPluginHandler, true); + gBrowser.addEventListener("PluginPlayPreview", gPluginHandler, true); gBrowser.addEventListener("PluginVulnerableUpdatable", gPluginHandler, true); gBrowser.addEventListener("PluginVulnerableNoUpdate", gPluginHandler, true); gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true); #ifdef XP_MACOSX gBrowser.addEventListener("npapi-carbon-event-model-failure", gPluginHandler, true); #endif Services.obs.addObserver(gPluginHandler.pluginCrashed, "plugin-crashed", false);
--- a/content/base/public/nsIObjectLoadingContent.idl +++ b/content/base/public/nsIObjectLoadingContent.idl @@ -16,17 +16,17 @@ interface nsIURI; #include "nsNPAPIPluginInstance.h" %} [ptr] native nsNPAPIPluginInstancePtr(nsNPAPIPluginInstance); /** * This interface represents a content node that loads objects. */ -[scriptable, uuid(8ed953b4-5022-4a49-bed4-6818f85dc113)] +[scriptable, uuid(a812424b-4820-4e28-96c8-dd2b69e36496)] interface nsIObjectLoadingContent : nsISupports { /** * See notes in nsObjectLoadingContent.h */ const unsigned long TYPE_LOADING = 0; const unsigned long TYPE_IMAGE = 1; const unsigned long TYPE_PLUGIN = 2; @@ -50,16 +50,18 @@ interface nsIObjectLoadingContent : nsIS // Blocked by content policy const unsigned long PLUGIN_USER_DISABLED = 7; // The plugin is disabled until the user clicks on it const unsigned long PLUGIN_CLICK_TO_PLAY = 8; // The plugin is vulnerable (update available) const unsigned long PLUGIN_VULNERABLE_UPDATABLE = 9; // The plugin is vulnerable (no update available) const unsigned long PLUGIN_VULNERABLE_NO_UPDATE = 10; + // The plugin is in play preview mode + const unsigned long PLUGIN_PLAY_PREVIEW = 11; /** * The actual mime type (the one we got back from the network * request) for the element. */ readonly attribute ACString actualType; /** @@ -108,26 +110,31 @@ interface nsIObjectLoadingContent : nsIS /** * This method will play a plugin that has been stopped by the * click-to-play plugins feature. */ void playPlugin(); /** - * This attribute will return true if the plugin has been activated - * and false if the plugin is still in the click-to-play state. + * This attribute will return true if the plugin has been activated and + * false if the plugin is still in the click-to-play or play preview state. */ readonly attribute boolean activated; [noscript] void stopPluginInstance(); [noscript] void syncStartPluginInstance(); [noscript] void asyncStartPluginInstance(); /** * The URL of the data/src loaded in the object. This may be null (i.e. * an <embed> with no src). */ readonly attribute nsIURI srcURI; readonly attribute unsigned long pluginFallbackType; + + /** + * This method will disable the play-preview plugin state. + */ + void cancelPlayPreview(); };
--- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -183,16 +183,19 @@ nsPluginErrorEvent::Run() type = NS_LITERAL_STRING("PluginVulnerableUpdatable"); break; case nsObjectLoadingContent::eFallbackVulnerableNoUpdate: type = NS_LITERAL_STRING("PluginVulnerableNoUpdate"); break; case nsObjectLoadingContent::eFallbackClickToPlay: type = NS_LITERAL_STRING("PluginClickToPlay"); break; + case nsObjectLoadingContent::eFallbackPlayPreview: + type = NS_LITERAL_STRING("PluginPlayPreview"); + break; case nsObjectLoadingContent::eFallbackUnsupported: type = NS_LITERAL_STRING("PluginNotFound"); break; case nsObjectLoadingContent::eFallbackDisabled: type = NS_LITERAL_STRING("PluginDisabled"); break; case nsObjectLoadingContent::eFallbackBlocklisted: type = NS_LITERAL_STRING("PluginBlocklisted"); @@ -654,16 +657,17 @@ nsObjectLoadingContent::nsObjectLoadingC : mPendingInstantiateEvent(nullptr) , mChannel(nullptr) , mType(eType_Loading) , mFallbackType(eFallbackAlternate) , mChannelLoaded(false) , mInstantiating(false) , mNetworkCreated(true) , mActivated(false) + , mPlayPreviewCanceled(false) , mIsStopping(false) , mIsLoading(false) , mSrcStreamLoading(false) {} nsObjectLoadingContent::~nsObjectLoadingContent() { // Should have been unbound from the tree at this point, and InDocCheckEvent // keeps us alive @@ -1036,16 +1040,18 @@ nsObjectLoadingContent::ObjectState() co case eType_Null: switch (mFallbackType) { case eFallbackSuppressed: return NS_EVENT_STATE_SUPPRESSED; case eFallbackUserDisabled: return NS_EVENT_STATE_USERDISABLED; case eFallbackClickToPlay: return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY; + case eFallbackPlayPreview: + return NS_EVENT_STATE_TYPE_PLAY_PREVIEW; case eFallbackDisabled: return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_DISABLED; case eFallbackBlocklisted: return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_BLOCKED; case eFallbackCrashed: return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_CRASHED; case eFallbackUnsupported: { // Check to see if plugins are blocked on this platform. @@ -1422,16 +1428,17 @@ nsObjectLoadingContent::UpdateObjectPara // eAllowPluginSkipChannel, meaning it is possible that we have a URI, but // are not going to open a channel for it. The old objLC code did this (in a // less obviously-intended way), so it's probably best not to change our // behavior at this point. // if (stateInvalid) { newType = eType_Null; + newMime.Truncate(); } else if (useChannel) { // If useChannel is set above, we considered it in setting newMime newType = GetTypeOfContent(newMime); LOG(("OBJLC [%p]: Using channel type", this)); } else if (((caps & eAllowPluginSkipChannel) || !newURI) && (GetTypeOfContent(newMime) == eType_Plugin)) { newType = eType_Plugin; LOG(("OBJLC [%p]: Skipping loading channel, type plugin", this)); @@ -1640,16 +1647,25 @@ nsObjectLoadingContent::LoadObject(bool // reject plugins fallbackType = eFallbackUserDisabled; } else { fallbackType = eFallbackSuppressed; } } } + // Items resolved as Image/Document will not be checked for previews, as well + // as invalid plugins (they will not have the mContentType set). + if ((mType == eType_Null || mType == eType_Plugin) && ShouldPreview()) { + // If plugin preview exists, we shall use it + LOG(("OBJLC [%p]: Using plugin preview", this)); + mType = eType_Null; + fallbackType = eFallbackPlayPreview; + } + // If we're a plugin but shouldn't start yet, load fallback with // reason click-to-play instead FallbackType clickToPlayReason; if (mType == eType_Plugin && !ShouldPlay(clickToPlayReason)) { LOG(("OBJLC [%p]: Marking plugin as click-to-play", this)); mType = eType_Null; fallbackType = clickToPlayReason; } @@ -2446,28 +2462,53 @@ nsObjectLoadingContent::PlayPlugin() mActivated = true; return LoadObject(true, true); } NS_IMETHODIMP nsObjectLoadingContent::GetActivated(bool *aActivated) { FallbackType reason; - *aActivated = ShouldPlay(reason); + *aActivated = ShouldPlay(reason) && !ShouldPreview(); return NS_OK; } NS_IMETHODIMP nsObjectLoadingContent::GetPluginFallbackType(uint32_t* aPluginFallbackType) { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); *aPluginFallbackType = mFallbackType; return NS_OK; } +NS_IMETHODIMP +nsObjectLoadingContent::CancelPlayPreview() +{ + if (!nsContentUtils::IsCallerChrome()) + return NS_ERROR_NOT_AVAILABLE; + + if (mPlayPreviewCanceled || mActivated) + return NS_OK; + + mPlayPreviewCanceled = true; + return LoadObject(true, true); +} + +bool +nsObjectLoadingContent::ShouldPreview() +{ + if (mPlayPreviewCanceled || mActivated) + return false; + + nsRefPtr<nsPluginHost> pluginHost = + already_AddRefed<nsPluginHost>(nsPluginHost::GetInst()); + + return pluginHost->IsPluginPlayPreviewForType(mContentType.get()); +} + bool nsObjectLoadingContent::ShouldPlay(FallbackType &aReason) { // mActivated is true if we've been activated via PlayPlugin() (e.g. user has // clicked through). Otherwise, only play if click-to-play is off or if page // is whitelisted nsRefPtr<nsPluginHost> pluginHost =
--- a/content/base/src/nsObjectLoadingContent.h +++ b/content/base/src/nsObjectLoadingContent.h @@ -77,17 +77,20 @@ class nsObjectLoadingContent : public ns eFallbackSuppressed = nsIObjectLoadingContent::PLUGIN_SUPPRESSED, // Blocked by content policy eFallbackUserDisabled = nsIObjectLoadingContent::PLUGIN_USER_DISABLED, // The plugin is disabled until the user clicks on it eFallbackClickToPlay = nsIObjectLoadingContent::PLUGIN_CLICK_TO_PLAY, // The plugin is vulnerable (update available) eFallbackVulnerableUpdatable = nsIObjectLoadingContent::PLUGIN_VULNERABLE_UPDATABLE, // The plugin is vulnerable (no update available) - eFallbackVulnerableNoUpdate = nsIObjectLoadingContent::PLUGIN_VULNERABLE_NO_UPDATE + eFallbackVulnerableNoUpdate = nsIObjectLoadingContent::PLUGIN_VULNERABLE_NO_UPDATE, + // The plugin is disabled and play preview content is displayed until + // the extension code enables it by sending the MozPlayPlugin event + eFallbackPlayPreview = nsIObjectLoadingContent::PLUGIN_PLAY_PREVIEW }; nsObjectLoadingContent(); virtual ~nsObjectLoadingContent(); NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSIFRAMELOADEROWNER @@ -286,16 +289,21 @@ class nsObjectLoadingContent : public ns /** * If this object is allowed to play plugin content, or if it would display * click-to-play instead. * NOTE that this does not actually check if the object is a loadable plugin */ bool ShouldPlay(FallbackType &aReason); /** + * If the object should display preview content for the current mContentType + */ + bool ShouldPreview(); + + /** * Helper to check if our current URI passes policy * * @param aContentPolicy [out] The result of the content policy decision * * @return true if call succeeded and NS_CP_ACCEPTED(*aContentPolicy) */ bool CheckLoadPolicy(int16_t *aContentPolicy); @@ -418,16 +426,19 @@ class nsObjectLoadingContent : public ns // created using NS_FROM_PARSER_NETWORK flag. If the element is modified, // it may lose the flag. bool mNetworkCreated : 1; // Used to keep track of whether or not a plugin has been explicitly // activated by PlayPlugin(). (see ShouldPlay()) bool mActivated : 1; + // Used to keep track of whether or not a plugin is blocked by play-preview. + bool mPlayPreviewCanceled : 1; + // Protects DoStopPlugin from reentry (bug 724781). bool mIsStopping : 1; // Protects LoadObject from re-entry bool mIsLoading : 1; // Used to track when we might try to instantiate a plugin instance based on // a src data stream being delivered to this object. When this is true we
--- a/content/events/public/nsEventStates.h +++ b/content/events/public/nsEventStates.h @@ -247,16 +247,18 @@ private: // Handler for click to play plugin (vulnerable w/no update) #define NS_EVENT_STATE_VULNERABLE_NO_UPDATE NS_DEFINE_EVENT_STATE_MACRO(41) // Platform does not support plugin content (some mobile platforms) #define NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM NS_DEFINE_EVENT_STATE_MACRO(42) // Element is ltr (for :dir pseudo-class) #define NS_EVENT_STATE_LTR NS_DEFINE_EVENT_STATE_MACRO(43) // Element is rtl (for :dir pseudo-class) #define NS_EVENT_STATE_RTL NS_DEFINE_EVENT_STATE_MACRO(44) +// Handler for play preview plugin +#define NS_EVENT_STATE_TYPE_PLAY_PREVIEW NS_DEFINE_EVENT_STATE_MACRO(45) /** * NOTE: do not go over 63 without updating nsEventStates::InternalType! */ #define DIRECTION_STATES (NS_EVENT_STATE_LTR | NS_EVENT_STATE_RTL) #define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \
--- a/dom/plugins/base/nsIPluginHost.idl +++ b/dom/plugins/base/nsIPluginHost.idl @@ -7,17 +7,17 @@ #include "nsISupports.idl" #include "nsIPluginTag.idl" %{C++ #define MOZ_PLUGIN_HOST_CONTRACTID \ "@mozilla.org/plugin/host;1" %} -[scriptable, uuid(28F1F9E1-CD23-4FE2-BCC8-BBB0B2D49A4A)] +[scriptable, uuid(fdb56ce3-89ac-4293-be64-9f4be88004cc)] interface nsIPluginHost : nsISupports { /** * Causes the plugins directory to be searched again for new plugin * libraries. * * @param reloadPages - indicates whether currently visible pages should * also be reloaded @@ -67,10 +67,19 @@ interface nsIPluginHost : nsISupports * @param plugin: the plugin to query, such as one returned by * nsIPluginHost.getPluginTags. * @param domain: the domain to test. If this argument is null, test if data * is stored for any site. The base domain for the given domain * will be determined; if any data for the base domain or its * subdomains is found, return true. */ boolean siteHasData(in nsIPluginTag plugin, in AUTF8String domain); + + /** + * Registers the play preview plugin mode for specific mime type + * + * @param mimeType - specified mime type + */ + void registerPlayPreviewMimeType(in AUTF8String mimeType); + + void unregisterPlayPreviewMimeType(in AUTF8String mimeType); };
--- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -1304,16 +1304,27 @@ nsPluginHost::IsPluginClickToPlayForType (plugin->HasFlag(NS_PLUGIN_FLAG_CLICKTOPLAY) || mPluginsClickToPlay)) { return true; } else { return false; } } +bool +nsPluginHost::IsPluginPlayPreviewForType(const char* aMimeType) +{ + for (uint32_t i = 0; i < mPlayPreviewMimeTypes.Length(); i++) { + nsCString mt = mPlayPreviewMimeTypes[i]; + if (PL_strcasecmp(mt.get(), aMimeType) == 0) + return true; + } + return false; +} + nsresult nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState) { nsPluginTag *plugin = FindPluginForType(aMimeType, true); if (plugin) { nsCOMPtr<nsIBlocklistService> blocklist = do_GetService("@mozilla.org/extensions/blocklist;1"); if (blocklist) { // The EmptyString()s are so we use the currently running application @@ -1811,16 +1822,37 @@ nsPluginHost::EnumerateSiteData(const ns break; } } return NS_OK; } NS_IMETHODIMP +nsPluginHost::RegisterPlayPreviewMimeType(const nsACString& mimeType) +{ + mPlayPreviewMimeTypes.AppendElement(mimeType); + return NS_OK; +} + +NS_IMETHODIMP +nsPluginHost::UnregisterPlayPreviewMimeType(const nsACString& mimeType) +{ + nsCAutoString mimeTypeToRemove(mimeType); + for (uint32_t i = mPlayPreviewMimeTypes.Length(); i > 0;) { + nsCString mt = mPlayPreviewMimeTypes[--i]; + if (PL_strcasecmp(mt.get(), mimeTypeToRemove.get()) == 0) { + mPlayPreviewMimeTypes.RemoveElementAt(i); + break; + } + } + return NS_OK; +} + +NS_IMETHODIMP nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain, uint64_t flags, int64_t maxAge) { // maxAge must be either a nonnegative integer or -1. NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1); // Caller may give us a tag object that is no longer live. if (!IsLiveTag(plugin)) {
--- a/dom/plugins/base/nsPluginHost.h +++ b/dom/plugins/base/nsPluginHost.h @@ -81,16 +81,17 @@ public: nsresult UnloadPlugins(); nsresult SetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsIPluginInstanceOwner *aOwner); nsresult IsPluginEnabledForType(const char* aMimeType); nsresult IsPluginEnabledForExtension(const char* aExtension, const char* &aMimeType); bool IsPluginClickToPlayForType(const char *aMimeType); + bool IsPluginPlayPreviewForType(const char *aMimeType); nsresult GetBlocklistStateForType(const char *aMimeType, uint32_t *state); nsresult GetPluginCount(uint32_t* aPluginCount); nsresult GetPlugins(uint32_t aPluginCount, nsIDOMPlugin** aPluginArray); nsresult GetURL(nsISupports* pluginInst, const char* url, const char* target, @@ -276,16 +277,17 @@ private: nsresult EnsurePrivateDirServiceProvider(); void OnPluginInstanceDestroyed(nsPluginTag* aPluginTag); nsRefPtr<nsPluginTag> mPlugins; nsRefPtr<nsPluginTag> mCachedPlugins; nsRefPtr<nsInvalidPluginTag> mInvalidPlugins; + nsTArray<nsCString> mPlayPreviewMimeTypes; bool mPluginsLoaded; bool mDontShowBadPluginMessage; // set by pref plugin.override_internal_types bool mOverrideInternalTypes; // set by pref plugin.disable bool mPluginsDisabled;
--- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -149,16 +149,18 @@ CSS_STATE_PSEUDO_CLASS(mozSuppressed, ": NS_EVENT_STATE_SUPPRESSED) CSS_STATE_PSEUDO_CLASS(mozLoading, ":-moz-loading", NS_EVENT_STATE_LOADING) CSS_STATE_PSEUDO_CLASS(mozTypeUnsupported, ":-moz-type-unsupported", NS_EVENT_STATE_TYPE_UNSUPPORTED) CSS_STATE_PSEUDO_CLASS(mozTypeUnsupportedPlatform, ":-moz-type-unsupported-platform", NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM) CSS_STATE_PSEUDO_CLASS(mozHandlerClickToPlay, ":-moz-handler-clicktoplay", NS_EVENT_STATE_TYPE_CLICK_TO_PLAY) +CSS_STATE_PSEUDO_CLASS(mozHandlerPlayPreview, ":-moz-handler-playpreview", + NS_EVENT_STATE_TYPE_PLAY_PREVIEW) CSS_STATE_PSEUDO_CLASS(mozHandlerVulnerableUpdatable, ":-moz-handler-vulnerable-updatable", NS_EVENT_STATE_VULNERABLE_UPDATABLE) CSS_STATE_PSEUDO_CLASS(mozHandlerVulnerableNoUpdate, ":-moz-handler-vulnerable-no-update", NS_EVENT_STATE_VULNERABLE_NO_UPDATE) CSS_STATE_PSEUDO_CLASS(mozHandlerDisabled, ":-moz-handler-disabled", NS_EVENT_STATE_HANDLER_DISABLED) CSS_STATE_PSEUDO_CLASS(mozHandlerBlocked, ":-moz-handler-blocked", NS_EVENT_STATE_HANDLER_BLOCKED)
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2182,16 +2182,17 @@ Tab.prototype = { this.browser.addEventListener("DOMContentLoaded", this, true); this.browser.addEventListener("DOMLinkAdded", this, true); this.browser.addEventListener("DOMTitleChanged", this, true); this.browser.addEventListener("DOMWindowClose", this, true); this.browser.addEventListener("DOMWillOpenModalDialog", this, true); this.browser.addEventListener("scroll", this, true); this.browser.addEventListener("MozScrolledAreaChanged", this, true); this.browser.addEventListener("PluginClickToPlay", this, true); + this.browser.addEventListener("PluginPlayPreview", this, true); this.browser.addEventListener("PluginNotFound", this, true); this.browser.addEventListener("pageshow", this, true); Services.obs.addObserver(this, "before-first-paint", false); Services.prefs.addObserver("browser.ui.zoom.force-user-scalable", this, false); if (!aParams.delayLoad) { let flags = "flags" in aParams ? aParams.flags : Ci.nsIWebNavigation.LOAD_FLAGS_NONE; @@ -2275,16 +2276,17 @@ Tab.prototype = { this.browser.removeProgressListener(this); this.browser.removeEventListener("DOMContentLoaded", this, true); this.browser.removeEventListener("DOMLinkAdded", this, true); this.browser.removeEventListener("DOMTitleChanged", this, true); this.browser.removeEventListener("DOMWindowClose", this, true); this.browser.removeEventListener("DOMWillOpenModalDialog", this, true); this.browser.removeEventListener("scroll", this, true); this.browser.removeEventListener("PluginClickToPlay", this, true); + this.browser.removeEventListener("PluginPlayPreview", this, true); this.browser.removeEventListener("PluginNotFound", this, true); this.browser.removeEventListener("MozScrolledAreaChanged", this, true); Services.obs.removeObserver(this, "before-first-paint"); Services.prefs.removeObserver("browser.ui.zoom.force-user-scalable", this); // Make sure the previously selected panel remains selected. The selected panel of a deck is // not stable when panels are removed. @@ -2837,16 +2839,62 @@ Tab.prototype = { tab.clickToPlayPluginsActivated = true; PluginHelper.playAllPlugins(win); NativeWindow.doorhanger.hide("ask-to-play-plugins", tab.id); }, true); break; } + case "PluginPlayPreview": { + let plugin = aEvent.target; + + // Force a style flush, so that we ensure our binding is attached. + plugin.clientTop; + + let doc = plugin.ownerDocument; + let previewContent = doc.getAnonymousElementByAttribute(plugin, "class", "previewPluginContent"); + if (!previewContent) { + // If the plugin is hidden, fallback to click-to-play logic + PluginHelper.stopPlayPreview(plugin, false); + break; + } + let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0]; + if (!iframe) { + // lazy initialization of the iframe + iframe = doc.createElementNS("http://www.w3.org/1999/xhtml", "iframe"); + iframe.className = "previewPluginContentFrame"; + previewContent.appendChild(iframe); + + // Force a style flush, so that we ensure our binding is attached. + plugin.clientTop; + } + let mimeType = PluginHelper.getPluginMimeType(plugin); + let playPreviewUri = "data:application/x-moz-playpreview;," + mimeType; + iframe.src = playPreviewUri; + + // MozPlayPlugin event can be dispatched from the extension chrome + // code to replace the preview content with the native plugin + previewContent.addEventListener("MozPlayPlugin", function playPluginHandler(e) { + if (!e.isTrusted) + return; + + previewContent.removeEventListener("MozPlayPlugin", playPluginHandler, true); + + let playPlugin = !aEvent.detail; + PluginHelper.stopPlayPreview(plugin, playPlugin); + + // cleaning up: removes overlay iframe from the DOM + let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0]; + if (iframe) + previewContent.removeChild(iframe); + }, true); + break; + } + case "PluginNotFound": { let plugin = aEvent.target; plugin.clientTop; // force style flush // On devices where we don't support Flash, there will be a "Learn More..." link in // the missing plugin error message. let learnMoreLink = plugin.ownerDocument.getAnonymousElementByAttribute(plugin, "class", "unsupportedLearnMoreLink"); if (learnMoreLink) { @@ -5232,20 +5280,32 @@ var PluginHelper = { if (!plugins || !plugins.length) return; plugins.forEach(this.playPlugin); }, playPlugin: function(plugin) { let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); - if (!objLoadingContent.activated) + if (!objLoadingContent.activated && + objLoadingContent.pluginFallbackType !== Ci.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW) objLoadingContent.playPlugin(); }, + stopPlayPreview: function(plugin, playPlugin) { + let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); + if (objLoadingContent.activated) + return; + + if (playPlugin) + objLoadingContent.playPlugin(); + else + objLoadingContent.cancelPlayPreview(); + }, + getPluginPreference: function getPluginPreference() { let pluginDisable = Services.prefs.getBoolPref("plugin.disable"); if (pluginDisable) return "0"; let clickToPlay = Services.prefs.getBoolPref("plugins.click_to_play"); return clickToPlay ? "2" : "1"; }, @@ -5272,16 +5332,32 @@ var PluginHelper = { // Is the <object>'s size too small to hold what we want to show? let pluginRect = plugin.getBoundingClientRect(); // XXX bug 446693. The text-shadow on the submitted-report text at // the bottom causes scrollHeight to be larger than it should be. let overflows = (overlay.scrollWidth > pluginRect.width) || (overlay.scrollHeight - 5 > pluginRect.height); return overflows; + }, + + getPluginMimeType: function (plugin) { + var tagMimetype; + if (plugin instanceof HTMLAppletElement) { + tagMimetype = "application/x-java-vm"; + } else { + tagMimetype = plugin.QueryInterface(Components.interfaces.nsIObjectLoadingContent) + .actualType; + + if (tagMimetype == "") { + tagMimetype = plugin.type; + } + } + + return tagMimetype; } }; var PermissionsHelper = { _permissonTypes: ["password", "geolocation", "popup", "indexedDB", "offline-app", "desktop-notification", "plugins"], _permissionStrings: {
--- a/toolkit/mozapps/plugins/content/pluginProblem.xml +++ b/toolkit/mozapps/plugins/content/pluginProblem.xml @@ -51,12 +51,13 @@ <!-- link href set at runtime --> <html:div class="msg msgReload">&reloadPlugin.pre;<html:a class="reloadLink" href="">&reloadPlugin.middle;</html:a>&reloadPlugin.post;</html:div> </html:div> <xul:spacer flex="1"/> <html:div class="msg msgBottomLinks"> <html:span class="helpIcon" role="link"/> </html:div> </xul:vbox> + <html:div class="previewPluginContent"><!-- iframe and its src will be set at runtime --></html:div> <html:div style="display:none;"><children/></html:div> </content> </binding> </bindings>
--- a/toolkit/mozapps/plugins/content/pluginProblemBinding.css +++ b/toolkit/mozapps/plugins/content/pluginProblemBinding.css @@ -3,26 +3,29 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */ embed:-moz-handler-disabled, embed:-moz-handler-blocked, embed:-moz-handler-crashed, embed:-moz-handler-clicktoplay, +embed:-moz-handler-playpreview, embed:-moz-handler-vulnerable-updatable, embed:-moz-handler-vulnerable-no-update, applet:-moz-handler-disabled, applet:-moz-handler-blocked, applet:-moz-handler-crashed, applet:-moz-handler-clicktoplay, +applet:-moz-handler-playpreview, applet:-moz-handler-vulnerable-updatable, applet:-moz-handler-vulnerable-no-update, object:-moz-has-handlerref:-moz-handler-disabled, object:-moz-has-handlerref:-moz-handler-blocked, object:-moz-handler-crashed, object:-moz-handler-clicktoplay, +object:-moz-handler-playpreview, object:-moz-handler-vulnerable-updatable, object:-moz-handler-vulnerable-no-update { display: inline-block; overflow: hidden; -moz-binding: url('chrome://mozapps/content/plugins/pluginProblem.xml#pluginProblem') !important; }
--- a/toolkit/mozapps/plugins/content/pluginProblemContent.css +++ b/toolkit/mozapps/plugins/content/pluginProblemContent.css @@ -42,16 +42,37 @@ html|applet:not([height]), html|applet[h direction: ltr; unicode-bidi: embed; } .mainBox[chromedir="rtl"] { direction: rtl; } +:-moz-handler-playpreview .mainBox { + display: none; +} + +.previewPluginContent { + display: none; +} + +.previewPluginContent > iframe { + width: inherit; + height: inherit; + border: none; +} + +:-moz-handler-playpreview .previewPluginContent { + display: block; + width: inherit; + height: inherit; + overflow: hidden; +} + .msg { display: none; } :-moz-type-unsupported .msgUnsupported, :-moz-type-unsupported-platform .msgUnsupportedPlatform, :-moz-handler-clicktoplay .msgClickToPlay, :-moz-handler-vulnerable-updatable .msgVulnerableUpdatable,