diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 2c5538998b57..66315f40dcfd 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -103,6 +103,7 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); static const char *kPrefJavaMIME = "plugin.java.mime"; +static const char *kPrefYoutubeRewrite = "plugins.rewrite_youtube_embeds"; using namespace mozilla; using namespace mozilla::dom; @@ -714,7 +715,8 @@ nsObjectLoadingContent::nsObjectLoadingContent() , mActivated(false) , mIsStopping(false) , mIsLoading(false) - , mScriptRequested(false) {} + , mScriptRequested(false) + , mRewrittenYoutubeEmbed(false) {} nsObjectLoadingContent::~nsObjectLoadingContent() { @@ -1482,7 +1484,7 @@ nsObjectLoadingContent::CheckJavaCodebase() } bool -nsObjectLoadingContent::IsYoutubeEmbed() +nsObjectLoadingContent::ShouldRewriteYoutubeEmbed(nsIURI* aURI) { nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); @@ -1500,25 +1502,41 @@ nsObjectLoadingContent::IsYoutubeEmbed() NS_WARNING("Could not get TLD service!"); return false; } + nsAutoCString currentBaseDomain; - bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(mURI, 0, currentBaseDomain)); + bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(aURI, 0, currentBaseDomain)); if (!ok) { - // Data URIs won't parse correctly, so just fail silently here. + // Data URIs (commonly used for things like svg embeds) won't parse + // correctly, so just fail silently here. return false; } + // See if URL is referencing youtube - nsAutoCString domain("youtube.com"); - if (!StringEndsWith(domain, currentBaseDomain)) { + if (!currentBaseDomain.EqualsLiteral("youtube.com")) { return false; } + + // We should only rewrite URLs with paths starting with "/v/", as we shouldn't + // touch object nodes with "/embed/" urls that already do that right thing. + nsAutoCString path; + aURI->GetPath(path); + if (!StringBeginsWith(path, NS_LITERAL_CSTRING("/v/"))) { + return false; + } + // See if requester is planning on using the JS API. nsAutoCString uri; - mURI->GetSpec(uri); - // Only log urls that are rewritable, e.g. not using enablejsapi=1 + aURI->GetSpec(uri); if (uri.Find("enablejsapi=1", true, 0, -1) != kNotFound) { return false; } - return true; + + // If we've made it this far, we've got a rewritable embed. Log it in + // telemetry. + Telemetry::Accumulate(Telemetry::YOUTUBE_REWRITABLE_EMBED_SEEN, 1); + + // Even if node is rewritable, only rewrite if the pref tells us we should. + return Preferences::GetBool(kPrefYoutubeRewrite); } bool @@ -1769,6 +1787,7 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI) NS_NOTREACHED("Unrecognized plugin-loading tag"); } + mRewrittenYoutubeEmbed = false; // Note that the baseURI changing could affect the newURI, even if uriStr did // not change. if (!uriStr.IsEmpty()) { @@ -1776,6 +1795,19 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI) uriStr, thisContent->OwnerDoc(), newBaseURI); + if (ShouldRewriteYoutubeEmbed(newURI)) { + // Switch out video access url formats, which should possibly allow HTML5 + // video loading. + uriStr.ReplaceSubstring(NS_LITERAL_STRING("/v/"), + NS_LITERAL_STRING("/embed/")); + rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(newURI), + uriStr, + thisContent->OwnerDoc(), + newBaseURI); + mRewrittenYoutubeEmbed = true; + newMime = NS_LITERAL_CSTRING("text/html"); + } + if (NS_SUCCEEDED(rv)) { NS_TryToSetImmutable(newURI); } else { @@ -1939,9 +1971,9 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI) newType = eType_Null; newMime.Truncate(); } else if (newChannel) { - // If newChannel is set above, we considered it in setting newMime - newType = GetTypeOfContent(newMime); - LOG(("OBJLC [%p]: Using channel type", this)); + // If newChannel 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; @@ -2170,11 +2202,6 @@ nsObjectLoadingContent::LoadObject(bool aNotify, return NS_OK; } - // Check whether this is a youtube embed. - if (IsYoutubeEmbed()) { - Telemetry::Accumulate(Telemetry::YOUTUBE_REWRITABLE_EMBED_SEEN, 1); - } - // // Security checks // diff --git a/dom/base/nsObjectLoadingContent.h b/dom/base/nsObjectLoadingContent.h index 0c317d222a5e..8d788c9846b9 100644 --- a/dom/base/nsObjectLoadingContent.h +++ b/dom/base/nsObjectLoadingContent.h @@ -331,6 +331,12 @@ class nsObjectLoadingContent : public nsImageLoadingContent */ virtual nsContentPolicyType GetContentPolicyType() const = 0; + // True if object represents an object/embed tag pointing to a flash embed + // for a youtube video. When possible (see IsRewritableYoutubeEmbed function + // comments for details), we change these to try to load HTML5 versions of + // videos. + bool mRewrittenYoutubeEmbed : 1; + private: // Object parameter changes returned by UpdateObjectParameters @@ -519,7 +525,27 @@ class nsObjectLoadingContent : public nsImageLoadingContent */ nsPluginFrame* GetExistingFrame(); - bool IsYoutubeEmbed(); + /** + * Used for identifying whether we can rewrite a youtube flash embed to + * possibly use HTML5 instead. + * + * Returns true if plugin.rewrite_youtube_embeds pref is true and the + * element this nsObjectLoadingContent instance represents: + * + * - is an embed or object node + * - has a URL pointing at the youtube.com domain, using "/v/" style video + * path reference, and without enablejsapi=1 in the path + * + * Having the enablejsapi flag means the document that contains the element + * could possibly be manipulating the youtube video elsewhere on the page + * via javascript. We can't rewrite these kinds of elements without possibly + * breaking content, which we want to avoid. + * + * If we can rewrite the URL, we change the "/v/" to "/embed/", and change + * our type to eType_Document so that we render similarly to an iframe + * embed. + */ + bool ShouldRewriteYoutubeEmbed(nsIURI* uri); // Helper class for SetupProtoChain class SetupProtoChainRunner final : public nsIRunnable diff --git a/dom/html/HTMLSharedObjectElement.cpp b/dom/html/HTMLSharedObjectElement.cpp index cf954be6150e..4d6a29a3cfbf 100644 --- a/dom/html/HTMLSharedObjectElement.cpp +++ b/dom/html/HTMLSharedObjectElement.cpp @@ -345,6 +345,11 @@ HTMLSharedObjectElement::GetCapabilities() const if (mNodeInfo->Equals(nsGkAtoms::embed)) { capabilities |= eSupportSVG | eSupportImages; } + // If this is a rewritten youtube flash embed, add documents to capabilities + // so that we can render HTML5 if possible. + if (mRewrittenYoutubeEmbed) { + capabilities |= eSupportDocuments; + } return capabilities; } diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 7e7ac9b322d0..5b428d2c9dc5 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -5140,3 +5140,7 @@ pref("webextensions.tests", false); // Allow customization of the fallback directory for file uploads pref("dom.input.fallbackUploadDir", ""); + +// Turn rewriting of youtube embeds on/off +pref("plugins.rewrite_youtube_embeds", true); +