Commit 3828a6ec authored by graouts@apple.com's avatar graouts@apple.com

Manage the presentation of the snapshotted plug-in using JavaScript

https://bugs.webkit.org/show_bug.cgi?id=115548

Reviewed by Dean Jackson.

Source/WebCore:

* Resources/plugIns.js:
(createOverlay):
Implement the createOverlay(shadowRoot, titleText, subtitleText) method
that is called from WebCore (HTMLPlugInImageElement::didAddUserAgentShadowRoot)
to allow the injected script to customize the shadow root for a snapshotted
plug-in. This is a default implementation, clients are expected to customize
this by providing their own JS file with enhanced behavior.

* css/CSSDefaultStyleSheets.cpp:
(WebCore::CSSDefaultStyleSheets::ensureDefaultStyleSheetsForElement):
Since we no longer have a default shadow tree generated in C++, it makes little
sense for clients to extend the default snapshotted plug-in stylesheet, so we
only insert it if no custom one is provided by the chrome client.

* css/plugIns.css:
Better styling of the default snapshotted plug-in overlay look by using CSS
flex boxes. Also using more explicit selector as an optimization.

* dom/Document.cpp:
(WebCore::Document::ensurePlugInsInjectedScript):
Expose a new method to allow HTMLPlugInImageElement instances to ensure that
the JavaScript code required to customize the snapshotted plug-in's shadow root
is indeed injected in the current document. The actual injection would only
happen once per document so all snapshotted plug-ins share the same scripting
context.

* dom/Document.h:
Expose the new ensurePlugInsInjectedScript method and the m_hasInjectedPlugInsScript
property used to ensure injection happens only once per document.

* html/HTMLPlugInImageElement.cpp:
(WebCore::titleText):
(WebCore::subtitleText):
Store the localized strings for each mime-type in a static hash map as it can be
costly to retrieve them each time from the client. It is expected the chrome client
will want to provide localized strings taking into account the snapshotted plug-in's
mime-type, so we're adding this as a parameter.
(WebCore::HTMLPlugInImageElement::checkSnapshotStatus):
Dispatch a "resize" event to the shadow root to notify the injected script that the
snapshotted plug-in's metrics have changed and to allow the overlay to update itself
as a result.
(WebCore::HTMLPlugInImageElement::didAddUserAgentShadowRoot):
Remove all the DOM generation code in favor of an approach where we create a shared
DOM scripting world in which we inject JavaScript code that will perform the same
task but will additionally be provided by the client in order to provide a completely
custom overlay for the snapshotted plug-in. The sole contract is for the JavaScript
to implement a createOverlay(shadowRoot, titleText, subtitleText) method.
(WebCore::HTMLPlugInImageElement::partOfSnapshotOverlay):
Renamed method to be generic to the overlay as opposed to text labels and use the
element with CSS class name "snapshot-overlay" as the comparison node.

* html/HTMLPlugInImageElement.h:
(HTMLPlugInImageElement):
Removing a couple of unused members since we no longer generate the shadow DOM from C++
and rename the partOfSnapshotLabel method to partOfSnapshotOverlay.

* page/ChromeClient.h:
(WebCore::ChromeClient::plugInStartLabelTitle):
(WebCore::ChromeClient::plugInStartLabelSubtitle):
(WebCore::ChromeClient::plugInExtraScript):
Pass in the mime-type to plugInStartLabelTitle and plugInStartLabelSubtitle and expose
a new plugInExtraScript method to allow the chrome client to provide a custom JS file
for the management of the shadow root.

* rendering/RenderSnapshottedPlugIn.cpp:
(WebCore::RenderSnapshottedPlugIn::handleEvent):
Update the terminology from "label" to "overlay" per the changes made in HTMLPlugInImageElement.

Source/WebKit2:

Expose a new plugInExtraScript method to support the injection of
a JS file from the chrome client to customize the rendering of a
snapshotted plug-in's shadow tree. Additionally, it is expected
the chrome client will want to provide localized strings taking
into account the snapshotted plug-in's mime-type, so we're adding
this as a parameter to both plugInStartLabelTitle and
plugInStartLabelSubtitle methods.

* WebProcess/InjectedBundle/API/c/WKBundlePage.h:
* WebProcess/InjectedBundle/InjectedBundlePageUIClient.cpp:
(WebKit::InjectedBundlePageUIClient::plugInStartLabelTitle):
(WebKit::InjectedBundlePageUIClient::plugInStartLabelSubtitle):
(WebKit::InjectedBundlePageUIClient::plugInExtraScript):
* WebProcess/InjectedBundle/InjectedBundlePageUIClient.h:
(InjectedBundlePageUIClient):
* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::plugInStartLabelTitle):
(WebKit::WebChromeClient::plugInStartLabelSubtitle):
(WebKit::WebChromeClient::plugInExtraScript):
* WebProcess/WebCoreSupport/WebChromeClient.h:
(WebChromeClient):

Tools:

Take into account the new plugInExtraScript method added to support
the injection of a JS file from the chrome client to customize the
rendering of a snapshotted plug-in's shadow tree.

* WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp:
(WTR::InjectedBundlePage::InjectedBundlePage):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@149586 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 09cef8dd
2013-05-06 Antoine Quint <graouts@apple.com>
Manage the presentation of the snapshotted plug-in using JavaScript
https://bugs.webkit.org/show_bug.cgi?id=115548
Reviewed by Dean Jackson.
* Resources/plugIns.js:
(createOverlay):
Implement the createOverlay(shadowRoot, titleText, subtitleText) method
that is called from WebCore (HTMLPlugInImageElement::didAddUserAgentShadowRoot)
to allow the injected script to customize the shadow root for a snapshotted
plug-in. This is a default implementation, clients are expected to customize
this by providing their own JS file with enhanced behavior.
* css/CSSDefaultStyleSheets.cpp:
(WebCore::CSSDefaultStyleSheets::ensureDefaultStyleSheetsForElement):
Since we no longer have a default shadow tree generated in C++, it makes little
sense for clients to extend the default snapshotted plug-in stylesheet, so we
only insert it if no custom one is provided by the chrome client.
* css/plugIns.css:
Better styling of the default snapshotted plug-in overlay look by using CSS
flex boxes. Also using more explicit selector as an optimization.
* dom/Document.cpp:
(WebCore::Document::ensurePlugInsInjectedScript):
Expose a new method to allow HTMLPlugInImageElement instances to ensure that
the JavaScript code required to customize the snapshotted plug-in's shadow root
is indeed injected in the current document. The actual injection would only
happen once per document so all snapshotted plug-ins share the same scripting
context.
* dom/Document.h:
Expose the new ensurePlugInsInjectedScript method and the m_hasInjectedPlugInsScript
property used to ensure injection happens only once per document.
* html/HTMLPlugInImageElement.cpp:
(WebCore::titleText):
(WebCore::subtitleText):
Store the localized strings for each mime-type in a static hash map as it can be
costly to retrieve them each time from the client. It is expected the chrome client
will want to provide localized strings taking into account the snapshotted plug-in's
mime-type, so we're adding this as a parameter.
(WebCore::HTMLPlugInImageElement::checkSnapshotStatus):
Dispatch a "resize" event to the shadow root to notify the injected script that the
snapshotted plug-in's metrics have changed and to allow the overlay to update itself
as a result.
(WebCore::HTMLPlugInImageElement::didAddUserAgentShadowRoot):
Remove all the DOM generation code in favor of an approach where we create a shared
DOM scripting world in which we inject JavaScript code that will perform the same
task but will additionally be provided by the client in order to provide a completely
custom overlay for the snapshotted plug-in. The sole contract is for the JavaScript
to implement a createOverlay(shadowRoot, titleText, subtitleText) method.
(WebCore::HTMLPlugInImageElement::partOfSnapshotOverlay):
Renamed method to be generic to the overlay as opposed to text labels and use the
element with CSS class name "snapshot-overlay" as the comparison node.
* html/HTMLPlugInImageElement.h:
(HTMLPlugInImageElement):
Removing a couple of unused members since we no longer generate the shadow DOM from C++
and rename the partOfSnapshotLabel method to partOfSnapshotOverlay.
* page/ChromeClient.h:
(WebCore::ChromeClient::plugInStartLabelTitle):
(WebCore::ChromeClient::plugInStartLabelSubtitle):
(WebCore::ChromeClient::plugInExtraScript):
Pass in the mime-type to plugInStartLabelTitle and plugInStartLabelSubtitle and expose
a new plugInExtraScript method to allow the chrome client to provide a custom JS file
for the management of the shadow root.
* rendering/RenderSnapshottedPlugIn.cpp:
(WebCore::RenderSnapshottedPlugIn::handleEvent):
Update the terminology from "label" to "overlay" per the changes made in HTMLPlugInImageElement.
2013-05-06 Robert Hogan <robert@webkit.org>
REGRESSION(r140907) - Backport blink r149612 to fix vertical-align and rowspan issue
......
// FIXME: Fill up with useful code for https://webkit.org/b/115548.
undefined;
\ No newline at end of file
// Function called from WebCore.
function createOverlay(shadowRoot, titleText, subtitleText)
{
// Generate the following structure:
//
// <div pseudo="-webkit-snapshotted-plugin-content">
// <div class="snapshot-overlay" aria-label="[Title]: [Subtitle]" role="button">
// <div class="snapshot-label">
// <div class="snapshot-title">[Title]</div>
// <div class="snapshot-subtitle">[Subtitle]</div>
// </div>
// </div>
// </div>
var shadowContainer = document.createElement("div");
shadowContainer.setAttribute("pseudo", "-webkit-snapshotted-plugin-content");
var overlay = shadowContainer.appendChild(document.createElement("div"));
overlay.setAttribute("aria-label", titleText + ": " + subtitleText);
overlay.setAttribute("role", "button");
overlay.className = "snapshot-overlay";
var snapshotLabel = overlay.appendChild(document.createElement("div"));
snapshotLabel.className = "snapshot-label";
var title = snapshotLabel.appendChild(document.createElement("div"));
title.className = "snapshot-title";
title.textContent = titleText;
var subtitle = snapshotLabel.appendChild(document.createElement("div"));
subtitle.className = "snapshot-subtitle";
subtitle.textContent = subtitleText;
shadowRoot.appendChild(shadowContainer);
};
......@@ -201,7 +201,9 @@ void CSSDefaultStyleSheets::ensureDefaultStyleSheetsForElement(Element* element,
#endif
if (!plugInsStyleSheet && (element->hasTagName(objectTag) || element->hasTagName(embedTag))) {
String plugInsRules = String(plugInsUserAgentStyleSheet, sizeof(plugInsUserAgentStyleSheet)) + RenderTheme::themeForPage(element->document()->page())->extraPlugInsStyleSheet() + element->document()->page()->chrome()->client()->plugInExtraStyleSheet();
String plugInsRules = RenderTheme::themeForPage(element->document()->page())->extraPlugInsStyleSheet() + element->document()->page()->chrome()->client()->plugInExtraStyleSheet();
if (plugInsRules.isEmpty())
plugInsRules = String(plugInsUserAgentStyleSheet, sizeof(plugInsUserAgentStyleSheet));
plugInsStyleSheet = parseUASheet(plugInsRules);
defaultStyle->addRulesFromSheet(plugInsStyleSheet, screenEval());
changedDefaultStyle = true;
......
......@@ -29,20 +29,19 @@
* with the following structure:
*
* <object>
* #ShadowRoot
* <div class="snapshot-container">
* <div class="snapshot-overlay"></div> <!-- e.g. for dimming content -->
* <div class="snapshot-label">
* <div class="snapshot-title">Snapshotted Plug-In</div>
* <div class="snapshot-subtitle">Click to restart</div>
* #ShadowRoot
* <div pseudo="-webkit-snapshotted-plugin-content">
* <div class="snapshot-overlay" aria-label="[Title]: [Subtitle]" role="button">
* <div class="snapshot-label">
* <div class="snapshot-title">[Title]</div>
* <div class="snapshot-subtitle">[Subtitle]</div>
* </div>
* </div>
* </div>
* </div>
* </div>
*
*/
object::-webkit-snapshotted-plugin-content,
embed::-webkit-snapshotted-plugin-content
embed::-webkit-snapshotted-plugin-content,
object::-webkit-snapshotted-plugin-content
{
position: relative;
display: inline-block;
......@@ -50,41 +49,44 @@ embed::-webkit-snapshotted-plugin-content
height: 100%;
}
object::-webkit-snapshotted-plugin-content .snapshot-container,
embed::-webkit-snapshotted-plugin-content .snapshot-container
embed::-webkit-snapshotted-plugin-content > .snapshot-overlay,
object::-webkit-snapshotted-plugin-content > .snapshot-overlay
{
position: absolute;
width: 100%;
height: 100%;
top: 5px;
right: 5px;
bottom: 5px;
left: 5px;
background-color: rgba(255, 255, 255, 0.75);
cursor: pointer;
display: -webkit-flex;
-webkit-justify-content: center;
-webkit-align-items: center;
}
object::-webkit-snapshotted-plugin-content .snapshot-container .snapshot-overlay,
embed::-webkit-snapshotted-plugin-content .snapshot-container .snapshot-overlay
embed::-webkit-snapshotted-plugin-content > .snapshot-overlay > .snapshot-label,
object::-webkit-snapshotted-plugin-content > .snapshot-overlay > .snapshot-label
{
display: none;
color: black;
-webkit-user-select: none;
}
object::-webkit-snapshotted-plugin-content .snapshot-container .snapshot-label,
embed::-webkit-snapshotted-plugin-content .snapshot-container .snapshot-label
embed::-webkit-snapshotted-plugin-content > .snapshot-overlay > .snapshot-label > div,
object::-webkit-snapshotted-plugin-content > .snapshot-overlay > .snapshot-label > div
{
position: absolute;
background-color: white;
color: black;
margin: 1em;
padding: 0.25em 2em;
text-align: center;
cursor: pointer;
-webkit-user-select: none;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
object::-webkit-snapshotted-plugin-content .snapshot-container .snapshot-label .snapshot-title,
embed::-webkit-snapshotted-plugin-content .snapshot-container .snapshot-label .snapshot-title
embed::-webkit-snapshotted-plugin-content > .snapshot-overlay > .snapshot-label > .snapshot-title,
object::-webkit-snapshotted-plugin-content > .snapshot-overlay > .snapshot-label > .snapshot-title
{
font-weight: bold;
}
object::-webkit-snapshotted-plugin-content .snapshot-container .snapshot-label .snapshot-subtitle,
embed::-webkit-snapshotted-plugin-content .snapshot-container .snapshot-label .snapshot-subtitle
embed::-webkit-snapshotted-plugin-content > .snapshot-overlay > .snapshot-label > .snapshot-subtitle,
object::-webkit-snapshotted-plugin-content > .snapshot-overlay > .snapshot-label > .snapshot-subtitle
{
font-style: italic;
color: #444;
......
......@@ -129,6 +129,7 @@
#include "PageTransitionEvent.h"
#include "PlatformKeyboardEvent.h"
#include "PlatformLocale.h"
#include "PlugInsResources.h"
#include "PluginDocument.h"
#include "PointerLockController.h"
#include "PopStateEvent.h"
......@@ -149,6 +150,8 @@
#include "ScriptElement.h"
#include "ScriptEventListener.h"
#include "ScriptRunner.h"
#include "ScriptSourceCode.h"
#include "ScriptValue.h"
#include "ScrollingCoordinator.h"
#include "SecurityOrigin.h"
#include "SecurityPolicy.h"
......@@ -487,6 +490,7 @@ Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML)
, m_fontloader(0)
#endif
, m_didAssociateFormControlsTimer(this, &Document::didAssociateFormControlsTimerFired)
, m_hasInjectedPlugInsScript(false)
{
m_printing = false;
m_paginatedForScreen = false;
......@@ -6050,4 +6054,19 @@ void Document::didAssociateFormControlsTimerFired(Timer<Document>* timer)
m_associatedFormControls.clear();
}
void Document::ensurePlugInsInjectedScript(DOMWrapperWorld* world)
{
if (m_hasInjectedPlugInsScript)
return;
// Use the JS file provided by the Chrome client, or fallback to the default one.
String jsString = page()->chrome()->client()->plugInExtraScript();
if (!jsString)
jsString = plugInsJavaScript;
page()->mainFrame()->script()->evaluateInWorld(ScriptSourceCode(jsString), world);
m_hasInjectedPlugInsScript = true;
}
} // namespace WebCore
......@@ -78,6 +78,7 @@ class DOMImplementation;
class DOMNamedFlowCollection;
class DOMSelection;
class DOMWindow;
class DOMWrapperWorld;
class Database;
class DatabaseThread;
class DocumentFragment;
......@@ -1203,6 +1204,8 @@ public:
PassRefPtr<FontLoader> fontloader();
#endif
void ensurePlugInsInjectedScript(DOMWrapperWorld*);
protected:
Document(Frame*, const KURL&, bool isXHTML, bool isHTML);
......@@ -1581,6 +1584,7 @@ private:
Timer<Document> m_didAssociateFormControlsTimer;
HashSet<RefPtr<Element> > m_associatedFormControls;
bool m_hasInjectedPlugInsScript;
};
inline void Document::notifyRemovePendingSheetIfNeeded()
......
......@@ -30,6 +30,7 @@
#include "HTMLDivElement.h"
#include "HTMLImageLoader.h"
#include "Image.h"
#include "JSDocumentFragment.h"
#include "LocalizedStrings.h"
#include "Logging.h"
#include "MouseEvent.h"
......@@ -49,18 +50,20 @@
#include "ShadowRoot.h"
#include "StyleResolver.h"
#include "Text.h"
#include <JavaScriptCore/APICast.h>
#include <JavaScriptCore/JSBase.h>
#include <wtf/CurrentTime.h>
#include <wtf/HashMap.h>
#include <wtf/text/StringHash.h>
namespace WebCore {
using namespace HTMLNames;
typedef Vector<RefPtr<HTMLPlugInImageElement> > HTMLPlugInImageElementList;
typedef HashMap<String, String> MimeTypeToLocalizedStringMap;
static const int sizingTinyDimensionThreshold = 40;
static const int sizingSmallWidthThreshold = 250;
static const int sizingMediumWidthThreshold = 450;
static const int sizingMediumHeightThreshold = 300;
static const float sizingFullPageAreaRatioThreshold = 0.96;
static const float autostartSoonAfterUserGestureThreshold = 5.0;
......@@ -68,6 +71,34 @@ static const float autostartSoonAfterUserGestureThreshold = 5.0;
static const double simulatedMouseClickTimerDelay = .75;
static const double removeSnapshotTimerDelay = 1.5;
static const String titleText(Page* page, String mimeType)
{
DEFINE_STATIC_LOCAL(MimeTypeToLocalizedStringMap, mimeTypeToLabelTitleMap, ());
String titleText = mimeTypeToLabelTitleMap.get(mimeType);
if (!titleText.isEmpty())
return titleText;
titleText = page->chrome()->client()->plugInStartLabelTitle(mimeType);
if (titleText.isEmpty())
titleText = snapshottedPlugInLabelTitle();
mimeTypeToLabelTitleMap.set(mimeType, titleText);
return titleText;
};
static const String subtitleText(Page* page, String mimeType)
{
DEFINE_STATIC_LOCAL(MimeTypeToLocalizedStringMap, mimeTypeToLabelSubtitleMap, ());
String subtitleText = mimeTypeToLabelSubtitleMap.get(mimeType);
if (!subtitleText.isEmpty())
return subtitleText;
subtitleText = page->chrome()->client()->plugInStartLabelSubtitle(mimeType);
if (subtitleText.isEmpty())
subtitleText = snapshottedPlugInLabelSubtitle();
mimeTypeToLabelSubtitleMap.set(mimeType, subtitleText);
return subtitleText;
};
HTMLPlugInImageElement::HTMLPlugInImageElement(const QualifiedName& tagName, Document* document, bool createdByParser, PreferPlugInsForImagesOption preferPlugInsForImagesOption)
: HTMLPlugInElement(tagName, document)
// m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay
......@@ -318,29 +349,6 @@ void HTMLPlugInImageElement::updateSnapshot(PassRefPtr<Image> image)
renderer()->repaint();
}
static AtomicString classNameForShadowRoot(const Node* node)
{
DEFINE_STATIC_LOCAL(const AtomicString, plugInTinySizeClassName, ("tiny", AtomicString::ConstructFromLiteral));
DEFINE_STATIC_LOCAL(const AtomicString, plugInSmallSizeClassName, ("small", AtomicString::ConstructFromLiteral));
DEFINE_STATIC_LOCAL(const AtomicString, plugInMediumSizeClassName, ("medium", AtomicString::ConstructFromLiteral));
DEFINE_STATIC_LOCAL(const AtomicString, plugInLargeSizeClassName, ("large", AtomicString::ConstructFromLiteral));
RenderBox* renderBox = static_cast<RenderBox*>(node->renderer());
LayoutUnit width = renderBox->contentWidth();
LayoutUnit height = renderBox->contentHeight();
if (width < sizingTinyDimensionThreshold || height < sizingTinyDimensionThreshold)
return plugInTinySizeClassName;
if (width < sizingSmallWidthThreshold)
return plugInSmallSizeClassName;
if (width < sizingMediumWidthThreshold || height < sizingMediumHeightThreshold)
return plugInMediumSizeClassName;
return plugInLargeSizeClassName;
}
void HTMLPlugInImageElement::checkSnapshotStatus()
{
if (!renderer()->isSnapshottedPlugIn()) {
......@@ -349,69 +357,49 @@ void HTMLPlugInImageElement::checkSnapshotStatus()
return;
}
ShadowRoot* root = userAgentShadowRoot();
if (!root)
return;
Element* shadowContainer = toElement(root->firstChild());
shadowContainer->setAttribute(classAttr, classNameForShadowRoot(this));
// Notify the shadow root that the size changed so that we may update the overlay layout.
ensureUserAgentShadowRoot()->dispatchEvent(Event::create(eventNames().resizeEvent, true, false));
}
void HTMLPlugInImageElement::didAddUserAgentShadowRoot(ShadowRoot* root)
{
Document* doc = document();
m_shadowContainer = HTMLDivElement::create(doc);
m_shadowContainer->setPseudo(AtomicString("-webkit-snapshotted-plugin-content", AtomicString::ConstructFromLiteral));
RefPtr<Element> container = HTMLDivElement::create(doc);
container->setAttribute(classAttr, AtomicString("snapshot-container", AtomicString::ConstructFromLiteral));
Page* page = document()->page();
if (!page)
return;
String mimeType = loadedMimeType();
RefPtr<Element> overlay = HTMLDivElement::create(doc);
overlay->setAttribute(classAttr, AtomicString("snapshot-overlay", AtomicString::ConstructFromLiteral));
container->appendChild(overlay, ASSERT_NO_EXCEPTION);
DEFINE_STATIC_LOCAL(RefPtr<DOMWrapperWorld>, isolatedWorld, (DOMWrapperWorld::create(JSDOMWindow::commonVM())));
document()->ensurePlugInsInjectedScript(isolatedWorld.get());
m_snapshotLabel = HTMLDivElement::create(doc);
m_snapshotLabel->setAttribute(classAttr, AtomicString("snapshot-label", AtomicString::ConstructFromLiteral));
ScriptController* scriptController = page->mainFrame()->script();
JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController->globalObject(isolatedWorld.get()));
JSC::ExecState* exec = globalObject->globalExec();
String titleText = snapshottedPlugInLabelTitle();
String subtitleText = snapshottedPlugInLabelSubtitle();
if (document()->page()) {
String clientTitleText = document()->page()->chrome()->client()->plugInStartLabelTitle();
if (!clientTitleText.isEmpty())
titleText = clientTitleText;
String clientSubtitleText = document()->page()->chrome()->client()->plugInStartLabelSubtitle();
if (!clientSubtitleText.isEmpty())
subtitleText = clientSubtitleText;
}
JSC::JSLockHolder lock(exec);
RefPtr<Element> title = HTMLDivElement::create(doc);
title->setAttribute(classAttr, AtomicString("snapshot-title", AtomicString::ConstructFromLiteral));
title->appendChild(doc->createTextNode(titleText), ASSERT_NO_EXCEPTION);
m_snapshotLabel->appendChild(title, ASSERT_NO_EXCEPTION);
JSC::MarkedArgumentBuffer argList;
argList.append(toJS(exec, globalObject, root));
argList.append(jsString(exec, titleText(page, mimeType)));
argList.append(jsString(exec, subtitleText(page, mimeType)));
RefPtr<Element> subTitle = HTMLDivElement::create(doc);
subTitle->setAttribute(classAttr, AtomicString("snapshot-subtitle", AtomicString::ConstructFromLiteral));
subTitle->appendChild(doc->createTextNode(subtitleText), ASSERT_NO_EXCEPTION);
m_snapshotLabel->appendChild(subTitle, ASSERT_NO_EXCEPTION);
container->appendChild(m_snapshotLabel, ASSERT_NO_EXCEPTION);
// It is expected the JS file provides a createOverlay(shadowRoot, title, subtitle) function.
JSC::JSObject* overlay = globalObject->get(exec, JSC::Identifier(exec, "createOverlay")).toObject(exec);
JSC::CallData callData;
JSC::CallType callType = overlay->methodTable()->getCallData(overlay, callData);
if (callType == JSC::CallTypeNone)
return;
// Make this into a button for accessibility clients.
String combinedText = titleText;
if (!combinedText.isEmpty() && !subtitleText.isEmpty())
combinedText.append(" ");
combinedText.append(subtitleText);
container->setAttribute(aria_labelAttr, combinedText);
container->setAttribute(roleAttr, "button");
JSC::JSObject* thisObj = globalObject->methodTable()->toThisObject(globalObject, exec);
m_shadowContainer->appendChild(container, ASSERT_NO_EXCEPTION);
root->appendChild(m_shadowContainer, ASSERT_NO_EXCEPTION);
JSC::call(exec, overlay, callType, callData, thisObj, argList);
}
bool HTMLPlugInImageElement::partOfSnapshotLabel(Node* node)
bool HTMLPlugInImageElement::partOfSnapshotOverlay(Node* node)
{
return node && (node == m_snapshotLabel.get() || node->isDescendantOf(m_snapshotLabel.get()));
DEFINE_STATIC_LOCAL(AtomicString, selector, (".snapshot-overlay", AtomicString::ConstructFromLiteral));
RefPtr<Element> snapshotLabel = ensureUserAgentShadowRoot()->querySelector(selector, ASSERT_NO_EXCEPTION);
return node && snapshotLabel && (node == snapshotLabel.get() || node->isDescendantOf(snapshotLabel.get()));
}
void HTMLPlugInImageElement::swapRendererTimerFired(Timer<HTMLPlugInImageElement>*)
......
......@@ -83,7 +83,7 @@ public:
void subframeLoaderDidCreatePlugIn(const Widget*);
void setIsPrimarySnapshottedPlugIn(bool);
bool partOfSnapshotLabel(Node*);
bool partOfSnapshotOverlay(Node*);
bool needsCheckForSizeChange() const { return m_needsCheckForSizeChange; }
void setNeedsCheckForSizeChange() { m_needsCheckForSizeChange = true; }
......@@ -155,8 +155,6 @@ private:
Timer<HTMLPlugInImageElement> m_swapRendererTimer;
Timer<HTMLPlugInImageElement> m_removeSnapshotTimer;
RefPtr<Image> m_snapshotImage;
RefPtr<Element> m_shadowContainer;
RefPtr<Element> m_snapshotLabel;
bool m_createdDuringUserGesture;
bool m_isRestartedPlugin;
bool m_needsCheckForSizeChange;
......
......@@ -369,9 +369,10 @@ public:
virtual bool isEmptyChromeClient() const { return false; }
virtual String plugInStartLabelTitle() const { return String(); }
virtual String plugInStartLabelSubtitle() const { return String(); }
virtual String plugInStartLabelTitle(const String& mimeType) const { UNUSED_PARAM(mimeType); return String(); }
virtual String plugInStartLabelSubtitle(const String& mimeType) const { UNUSED_PARAM(mimeType); return String(); }
virtual String plugInExtraStyleSheet() const { return String(); }
virtual String plugInExtraScript() const { return String(); }
// FIXME: Port should return true using heuristic based on scrollable(RenderBox).
virtual bool shouldAutoscrollForDragAndDrop(RenderBox*) const { return false; }
......
......@@ -173,9 +173,9 @@ void RenderSnapshottedPlugIn::handleEvent(Event* event)
if (event->type() == eventNames().clickEvent || (m_isPotentialMouseActivation && event->type() == eventNames().mouseupEvent)) {
m_isPotentialMouseActivation = false;
bool clickWasOnLabel = plugInImageElement()->partOfSnapshotLabel(event->target()->toNode());
plugInImageElement()->setDisplayState(clickWasOnLabel ? HTMLPlugInElement::Restarting : HTMLPlugInElement::RestartingWithPendingMouseClick);
plugInImageElement()->userDidClickSnapshot(mouseEvent, !clickWasOnLabel);
bool clickWasOnOverlay = plugInImageElement()->partOfSnapshotOverlay(event->target()->toNode());
plugInImageElement()->setDisplayState(clickWasOnOverlay ? HTMLPlugInElement::Restarting : HTMLPlugInElement::RestartingWithPendingMouseClick);
plugInImageElement()->userDidClickSnapshot(mouseEvent, !clickWasOnOverlay);
event->setDefaultHandled();
} else if (event->type() == eventNames().mousedownEvent) {
m_isPotentialMouseActivation = true;
......
2013-05-06 Antoine Quint <graouts@apple.com>
Manage the presentation of the snapshotted plug-in using JavaScript
https://bugs.webkit.org/show_bug.cgi?id=115548
Reviewed by Dean Jackson.
Expose a new plugInExtraScript method to support the injection of
a JS file from the chrome client to customize the rendering of a
snapshotted plug-in's shadow tree. Additionally, it is expected
the chrome client will want to provide localized strings taking
into account the snapshotted plug-in's mime-type, so we're adding
this as a parameter to both plugInStartLabelTitle and
plugInStartLabelSubtitle methods.
* WebProcess/InjectedBundle/API/c/WKBundlePage.h:
* WebProcess/InjectedBundle/InjectedBundlePageUIClient.cpp:
(WebKit::InjectedBundlePageUIClient::plugInStartLabelTitle):
(WebKit::InjectedBundlePageUIClient::plugInStartLabelSubtitle):
(WebKit::InjectedBundlePageUIClient::plugInExtraScript):
* WebProcess/InjectedBundle/InjectedBundlePageUIClient.h:
(InjectedBundlePageUIClient):
* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::plugInStartLabelTitle):
(WebKit::WebChromeClient::plugInStartLabelSubtitle):
(WebKit::WebChromeClient::plugInExtraScript):
* WebProcess/WebCoreSupport/WebChromeClient.h:
(WebChromeClient):
2013-05-05 Chris Fleizach <cfleizach@apple.com>
WEB SPEECH: deny file-read-data /Library/Speech/Synthesizers
......
......@@ -250,9 +250,10 @@ typedef WKBundlePageUIElementVisibility (*WKBundlePageMenuBarIsVisibleCallback)(
typedef WKBundlePageUIElementVisibility (*WKBundlePageToolbarsAreVisibleCallback)(WKBundlePageRef page, const void *clientInfo);
typedef void (*WKBundlePageReachedAppCacheOriginQuotaCallback)(WKBundlePageRef page, WKSecurityOriginRef origin, int64_t totalBytesNeeded, const void *clientInfo);
typedef uint64_t (*WKBundlePageExceededDatabaseQuotaCallback)(WKBundlePageRef page, WKSecurityOriginRef origin, WKStringRef databaseName, WKStringRef databaseDisplayName, uint64_t currentQuotaBytes, uint64_t currentOriginUsageBytes, uint64_t currentDatabaseUsageBytes, uint64_t expectedUsageBytes, const void *clientInfo);
typedef WKStringRef (*WKBundlePagePlugInCreateStartLabelTitleCallback)(const void *clientInfo);
typedef WKStringRef (*WKBundlePagePlugInCreateStartLabelSubtitleCallback)(const void *clientInfo);
typedef WKStringRef (*WKBundlePagePlugInCreateStartLabelTitleCallback)(WKStringRef mimeType, const void *clientInfo);
typedef WKStringRef (*WKBundlePagePlugInCreateStartLabelSubtitleCallback)(WKStringRef mimeType, const void *clientInfo);
typedef WKStringRef (*WKBundlePagePlugInCreateExtraStyleSheetCallback)(const void *clientInfo);
typedef WKStringRef (*WKBundlePagePlugInCreateExtraScriptCallback)(const void *clientInfo);
struct WKBundlePageUIClient {
int version;
......@@ -280,6 +281,7 @@ struct WKBundlePageUIClient {
WKBundlePagePlugInCreateStartLabelTitleCallback createPlugInStartLabelTitle;
WKBundlePagePlugInCreateStartLabelSubtitleCallback createPlugInStartLabelSubtitle;
WKBundlePagePlugInCreateExtraStyleSheetCallback createPlugInExtraStyleSheet;
WKBundlePagePlugInCreateExtraScriptCallback createPlugInExtraScript;
};
typedef struct WKBundlePageUIClient WKBundlePageUIClient;
......
......@@ -163,21 +163,21 @@ uint64_t InjectedBundlePageUIClient::didExceedDatabaseQuota(WebPage* page, WebSe
return m_client.didExceedDatabaseQuota(toAPI(page), toAPI(origin), toAPI(databaseName.impl()), toAPI(databaseDisplayName.impl()), currentQuotaBytes, currentOriginUsageBytes, currentDatabaseUsageBytes, expectedUsageBytes, m_client.clientInfo);
}
String InjectedBundlePageUIClient::plugInStartLabelTitle() const
String InjectedBundlePageUIClient::plugInStartLabelTitle(const String& mimeType) const
{
if (!m_client.createPlugInStartLabelTitle)
return String();
RefPtr<WebString> title = adoptRef(toImpl(m_client.createPlugInStartLabelTitle(m_client.clientInfo)));
RefPtr<WebString> title = adoptRef(toImpl(m_client.createPlugInStartLabelTitle(toAPI(mimeType.impl()), m_client.clientInfo)));
return title ? title->string() : String();
}
String InjectedBundlePageUIClient::plugInStartLabelSubtitle() const
String InjectedBundlePageUIClient::plugInStartLabelSubtitle(const String& mimeType) const