static bool paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) { HTMLMediaElement* mediaElement = toParentMediaElement(object); if (!mediaElement) return false; static Image* soundLevel3 = platformResource("mediaplayerSoundLevel3"); static Image* soundLevel2 = platformResource("mediaplayerSoundLevel2"); static Image* soundLevel1 = platformResource("mediaplayerSoundLevel1"); static Image* soundLevel0 = platformResource("mediaplayerSoundLevel0"); static Image* soundDisabled = platformResource("mediaplayerSoundDisabled"); if (!hasSource(mediaElement) || !mediaElement->hasAudio()) return paintMediaButton(paintInfo.context, rect, soundDisabled); if (mediaElement->muted() || mediaElement->volume() <= 0) return paintMediaButton(paintInfo.context, rect, soundLevel0); if (mediaElement->volume() <= 0.33) return paintMediaButton(paintInfo.context, rect, soundLevel1); if (mediaElement->volume() <= 0.66) return paintMediaButton(paintInfo.context, rect, soundLevel2); return paintMediaButton(paintInfo.context, rect, soundLevel3); }
bool MediaElementSession::canControlControlsManager(const HTMLMediaElement& element) const { if (!element.hasAudio()) { LOG(Media, "MediaElementSession::canControlControlsManager - returning FALSE: No audio"); return false; } if (element.muted()) { LOG(Media, "MediaElementSession::canControlControlsManager - returning FALSE: Muted"); return false; } if (element.ended()) { LOG(Media, "MediaElementSession::canControlControlsManager - returning FALSE: Ended"); return false; } if (element.document().activeDOMObjectsAreSuspended()) { LOG(Media, "MediaElementSession::canControlControlsManager - returning FALSE: activeDOMObjectsAreSuspended()"); return false; } if (!playbackPermitted(element)) { LOG(Media, "MediaElementSession::canControlControlsManager - returning FALSE: Playback not permitted"); return false; } if (element.isVideo()) { if (!element.renderer()) { LOG(Media, "MediaElementSession::canControlControlsManager - returning FALSE: No renderer"); return false; } if (!element.hasVideo()) { LOG(Media, "MediaElementSession::canControlControlsManager - returning FALSE: No video"); return false; } if (isElementLargeEnoughForMainContent(element)) { LOG(Media, "MediaElementSession::canControlControlsManager - returning TRUE: Is main content"); return true; } } if (m_restrictions & RequireUserGestureToControlControlsManager && !ScriptController::processingUserGestureForMedia()) { LOG(Media, "MediaElementSession::canControlControlsManager - returning FALSE: No user gesture"); return false; } LOG(Media, "MediaElementSession::canControlControlsManager - returning TRUE: All criteria met"); return true; }
static bool paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) { ASSERT(object->node()); HTMLMediaElement* mediaElement = toParentMediaElement(object->node()->shadowHost()); if (!mediaElement) return false; if (!hasSource(mediaElement) || !mediaElement->hasAudio()) return true; static Image* mediaVolumeSliderThumb = platformResource("mediaplayerVolumeSliderThumb"); return paintMediaButton(paintInfo.context, rect, mediaVolumeSliderThumb); }
static bool isMainContent(const HTMLMediaElement& element) { if (!element.hasAudio() || !element.hasVideo()) return false; // Elements which have not yet been laid out, or which are not yet in the DOM, cannot be main content. RenderBox* renderer = downcast<RenderBox>(element.renderer()); if (!renderer) return false; if (!isElementLargeEnoughForMainContent(element)) return false; // Elements which are hidden by style, or have been scrolled out of view, cannot be main content. // But elements which have audio & video and are already playing should not stop playing because // they are scrolled off the page. if (renderer->style().visibility() != VISIBLE || (renderer->visibleInViewportState() != RenderElement::VisibleInViewport && !element.isPlaying()) ) { return false; } // Main content elements must be in the main frame. Document& document = element.document(); if (!document.frame() || !document.frame()->isMainFrame()) return false; MainFrame& mainFrame = document.frame()->mainFrame(); if (!mainFrame.view() || !mainFrame.view()->renderView()) return false; RenderView& mainRenderView = *mainFrame.view()->renderView(); // Hit test the area of the main frame where the element appears, to determine if the element is being obscured. IntRect rectRelativeToView = element.clientRect(); ScrollPosition scrollPosition = mainFrame.view()->documentScrollPositionRelativeToViewOrigin(); IntRect rectRelativeToTopDocument(rectRelativeToView.location() + scrollPosition, rectRelativeToView.size()); HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::AllowChildFrameContent | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(rectRelativeToTopDocument.center()); // Elements which are obscured by other elements cannot be main content. mainRenderView.hitTest(request, result); result.setToNonUserAgentShadowAncestor(); Element* hitElement = result.innerElement(); if (hitElement != &element) return false; return true; }
static bool paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) { HTMLMediaElement* mediaElement = toParentMediaElement(object); if (!mediaElement) return false; static Image* soundFull = platformResource("mediaSoundFull"); static Image* soundNone = platformResource("mediaSoundNone"); static Image* soundDisabled = platformResource("mediaSoundDisabled"); if (!hasSource(mediaElement) || !mediaElement->hasAudio()) return paintMediaButton(paintInfo.context, rect, soundDisabled); return paintMediaButton(paintInfo.context, rect, mediaElement->muted() ? soundNone: soundFull); }
bool RenderTheme::shouldRenderMediaControlPart(ControlPart part, Element* e) { HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(e); switch (part) { case MediaMuteButtonPart: return mediaElement->hasAudio(); case MediaRewindButtonPart: return mediaElement->movieLoadType() != MediaPlayer::LiveStream; case MediaReturnToRealtimeButtonPart: return mediaElement->movieLoadType() == MediaPlayer::LiveStream; case MediaFullscreenButtonPart: return mediaElement->supportsFullscreen(); default: return true; } }
bool MediaElementSession::playbackPermitted(const HTMLMediaElement& element) const { if (pageExplicitlyAllowsElementToAutoplayInline(element)) return true; if (m_restrictions & RequireUserGestureForRateChange && !ScriptController::processingUserGesture()) { LOG(Media, "MediaElementSession::playbackPermitted - returning FALSE"); return false; } if (m_restrictions & RequireUserGestureForAudioRateChange && element.hasAudio() && !ScriptController::processingUserGesture()) { LOG(Media, "MediaElementSession::playbackPermitted - returning FALSE"); return false; } return true; }
bool MediaElementSession::canControlControlsManager(const HTMLMediaElement& element) const { if (!element.hasAudio()) return false; if (!playbackPermitted(element)) return false; RenderBox* renderer = downcast<RenderBox>(element.renderer()); if (!renderer) return false; if (isElementLargeEnoughForMainContent(element)) return true; if (ScriptController::processingUserGestureForMedia()) return true; return false; }
static bool paintMediaVolumeSlider(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) { HTMLMediaElement* mediaElement = toParentMediaElement(object); if (!mediaElement) return false; GraphicsContext* context = paintInfo.context; RenderStyle* style = object->style(); paintRoundedSliderBackground(rect, style, context); // Calculate volume position for white background rectangle. float volume = mediaElement->volume(); if (std::isnan(volume) || volume < 0) return true; if (volume > 1) volume = 1; if (!hasSource(mediaElement) || !mediaElement->hasAudio() || mediaElement->muted()) volume = 0; // Calculate the position relative to the center of the thumb. float fillWidth = 0; if (volume > 0) { float thumbCenter = mediaVolumeSliderThumbWidth / 2; float zoomLevel = style->effectiveZoom(); float positionWidth = volume * (rect.width() - (zoomLevel * thumbCenter)); fillWidth = positionWidth + (zoomLevel * thumbCenter / 2); } Color startColor = Color(195, 195, 195); Color endColor = Color(217, 217, 217); paintSliderRangeHighlight(rect, style, context, 0.0, fillWidth, startColor, endColor); return true; }
PlatformMenuDescription ContextMenuClientImpl::getCustomMenuFromDefaultItems( ContextMenu* defaultMenu) { // Displaying the context menu in this function is a big hack as we don't // have context, i.e. whether this is being invoked via a script or in // response to user input (Mouse event WM_RBUTTONDOWN, // Keyboard events KeyVK_APPS, Shift+F10). Check if this is being invoked // in response to the above input events before popping up the context menu. if (!m_webView->contextMenuAllowed()) return 0; HitTestResult r = defaultMenu->hitTestResult(); Frame* selectedFrame = r.innerNonSharedNode()->document()->frame(); WebContextMenuData data; data.mousePosition = selectedFrame->view()->contentsToWindow(r.point()); // Compute edit flags. data.editFlags = WebContextMenuData::CanDoNone; if (m_webView->focusedWebCoreFrame()->editor()->canUndo()) data.editFlags |= WebContextMenuData::CanUndo; if (m_webView->focusedWebCoreFrame()->editor()->canRedo()) data.editFlags |= WebContextMenuData::CanRedo; if (m_webView->focusedWebCoreFrame()->editor()->canCut()) data.editFlags |= WebContextMenuData::CanCut; if (m_webView->focusedWebCoreFrame()->editor()->canCopy()) data.editFlags |= WebContextMenuData::CanCopy; if (m_webView->focusedWebCoreFrame()->editor()->canPaste()) data.editFlags |= WebContextMenuData::CanPaste; if (m_webView->focusedWebCoreFrame()->editor()->canDelete()) data.editFlags |= WebContextMenuData::CanDelete; // We can always select all... data.editFlags |= WebContextMenuData::CanSelectAll; data.editFlags |= WebContextMenuData::CanTranslate; // Links, Images, Media tags, and Image/Media-Links take preference over // all else. data.linkURL = r.absoluteLinkURL(); if (!r.absoluteImageURL().isEmpty()) { data.srcURL = r.absoluteImageURL(); data.mediaType = WebContextMenuData::MediaTypeImage; } else if (!r.absoluteMediaURL().isEmpty()) { data.srcURL = r.absoluteMediaURL(); // We know that if absoluteMediaURL() is not empty, then this // is a media element. HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(r.innerNonSharedNode()); if (mediaElement->hasTagName(HTMLNames::videoTag)) data.mediaType = WebContextMenuData::MediaTypeVideo; else if (mediaElement->hasTagName(HTMLNames::audioTag)) data.mediaType = WebContextMenuData::MediaTypeAudio; if (mediaElement->error()) data.mediaFlags |= WebContextMenuData::MediaInError; if (mediaElement->paused()) data.mediaFlags |= WebContextMenuData::MediaPaused; if (mediaElement->muted()) data.mediaFlags |= WebContextMenuData::MediaMuted; if (mediaElement->loop()) data.mediaFlags |= WebContextMenuData::MediaLoop; if (mediaElement->supportsSave()) data.mediaFlags |= WebContextMenuData::MediaCanSave; if (mediaElement->hasAudio()) data.mediaFlags |= WebContextMenuData::MediaHasAudio; if (mediaElement->hasVideo()) data.mediaFlags |= WebContextMenuData::MediaHasVideo; if (mediaElement->controls()) data.mediaFlags |= WebContextMenuData::MediaControls; } else if (r.innerNonSharedNode()->hasTagName(HTMLNames::objectTag) || r.innerNonSharedNode()->hasTagName(HTMLNames::embedTag)) { RenderObject* object = r.innerNonSharedNode()->renderer(); if (object && object->isWidget()) { Widget* widget = toRenderWidget(object)->widget(); if (widget && widget->isPluginContainer()) { WebPluginContainerImpl* plugin = static_cast<WebPluginContainerImpl*>(widget); WebString text = plugin->plugin()->selectionAsText(); if (!text.isEmpty()) { data.selectedText = text; data.editFlags |= WebContextMenuData::CanCopy; } data.editFlags &= ~WebContextMenuData::CanTranslate; data.linkURL = plugin->plugin()->linkAtPosition(data.mousePosition); } } } data.isImageBlocked = (data.mediaType == WebContextMenuData::MediaTypeImage) && !r.image(); // If it's not a link, an image, a media element, or an image/media link, // show a selection menu or a more generic page menu. data.frameEncoding = selectedFrame->loader()->writer()->encoding(); // Send the frame and page URLs in any case. data.pageURL = urlFromFrame(m_webView->mainFrameImpl()->frame()); if (selectedFrame != m_webView->mainFrameImpl()->frame()) data.frameURL = urlFromFrame(selectedFrame); if (r.isSelected()) data.selectedText = selectedFrame->editor()->selectedText().stripWhiteSpace(); if (r.isContentEditable()) { data.isEditable = true; if (m_webView->focusedWebCoreFrame()->editor()->isContinuousSpellCheckingEnabled()) { data.isSpellCheckingEnabled = true; // Spellchecking might be enabled for the field, but could be disabled on the node. if (m_webView->focusedWebCoreFrame()->editor()->spellCheckingEnabledInFocusedNode()) data.misspelledWord = selectMisspelledWord(defaultMenu, selectedFrame); } } #if OS(DARWIN) ExceptionCode ec = 0; RefPtr<CSSStyleDeclaration> style = selectedFrame->document()->createCSSStyleDeclaration(); style->setProperty(CSSPropertyDirection, "ltr", false, ec); if (selectedFrame->editor()->selectionHasStyle(style.get()) != FalseTriState) data.writingDirectionLeftToRight |= WebContextMenuData::CheckableMenuItemChecked; style->setProperty(CSSPropertyDirection, "rtl", false, ec); if (selectedFrame->editor()->selectionHasStyle(style.get()) != FalseTriState) data.writingDirectionRightToLeft |= WebContextMenuData::CheckableMenuItemChecked; #endif // OS(DARWIN) // Now retrieve the security info. DocumentLoader* dl = selectedFrame->loader()->documentLoader(); WebDataSource* ds = WebDataSourceImpl::fromDocumentLoader(dl); if (ds) data.securityInfo = ds->response().securityInfo(); // Filter out custom menu elements and add them into the data. populateCustomMenuItems(defaultMenu, &data); WebFrame* selected_web_frame = WebFrameImpl::fromFrame(selectedFrame); if (m_webView->client()) m_webView->client()->showContextMenu(selected_web_frame, data); return 0; }
bool MediaElementSession::playbackPermitted(const HTMLMediaElement& element) const { if (pageExplicitlyAllowsElementToAutoplayInline(element)) return true; if (requiresFullscreenForVideoPlayback(element) && !fullscreenPermitted(element)) { LOG(Media, "MediaElementSession::playbackPermitted - returning FALSE because of fullscreen restriction"); return false; } if (m_restrictions & OverrideUserGestureRequirementForMainContent && updateIsMainContent()) return true; if (m_restrictions & RequireUserGestureForVideoRateChange && element.isVideo() && !ScriptController::processingUserGestureForMedia()) { LOG(Media, "MediaElementSession::playbackPermitted - returning FALSE because of video rate change restriction"); return false; } if (m_restrictions & RequireUserGestureForAudioRateChange && (!element.isVideo() || element.hasAudio()) && !element.muted() && !ScriptController::processingUserGestureForMedia()) { LOG(Media, "MediaElementSession::playbackPermitted - returning FALSE because of audio rate change restriction"); return false; } return true; }
PlatformMenuDescription ContextMenuClientImpl::getCustomMenuFromDefaultItems( ContextMenu* defaultMenu) { // Displaying the context menu in this function is a big hack as we don't // have context, i.e. whether this is being invoked via a script or in // response to user input (Mouse event WM_RBUTTONDOWN, // Keyboard events KeyVK_APPS, Shift+F10). Check if this is being invoked // in response to the above input events before popping up the context menu. if (!m_webView->contextMenuAllowed()) return 0; HitTestResult r = m_webView->page()->contextMenuController()->hitTestResult(); Frame* selectedFrame = r.innerNonSharedNode()->document()->frame(); WebContextMenuData data; data.mousePosition = selectedFrame->view()->contentsToWindow(r.roundedPoint()); // Compute edit flags. data.editFlags = WebContextMenuData::CanDoNone; if (m_webView->focusedWebCoreFrame()->editor()->canUndo()) data.editFlags |= WebContextMenuData::CanUndo; if (m_webView->focusedWebCoreFrame()->editor()->canRedo()) data.editFlags |= WebContextMenuData::CanRedo; if (m_webView->focusedWebCoreFrame()->editor()->canCut()) data.editFlags |= WebContextMenuData::CanCut; if (m_webView->focusedWebCoreFrame()->editor()->canCopy()) data.editFlags |= WebContextMenuData::CanCopy; if (m_webView->focusedWebCoreFrame()->editor()->canPaste()) data.editFlags |= WebContextMenuData::CanPaste; if (m_webView->focusedWebCoreFrame()->editor()->canDelete()) data.editFlags |= WebContextMenuData::CanDelete; // We can always select all... data.editFlags |= WebContextMenuData::CanSelectAll; data.editFlags |= WebContextMenuData::CanTranslate; // Links, Images, Media tags, and Image/Media-Links take preference over // all else. data.linkURL = r.absoluteLinkURL(); if (!r.absoluteImageURL().isEmpty()) { data.srcURL = r.absoluteImageURL(); data.mediaType = WebContextMenuData::MediaTypeImage; } else if (!r.absoluteMediaURL().isEmpty()) { data.srcURL = r.absoluteMediaURL(); // We know that if absoluteMediaURL() is not empty, then this // is a media element. HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(r.innerNonSharedNode()); if (mediaElement->hasTagName(HTMLNames::videoTag)) data.mediaType = WebContextMenuData::MediaTypeVideo; else if (mediaElement->hasTagName(HTMLNames::audioTag)) data.mediaType = WebContextMenuData::MediaTypeAudio; if (mediaElement->error()) data.mediaFlags |= WebContextMenuData::MediaInError; if (mediaElement->paused()) data.mediaFlags |= WebContextMenuData::MediaPaused; if (mediaElement->muted()) data.mediaFlags |= WebContextMenuData::MediaMuted; if (mediaElement->loop()) data.mediaFlags |= WebContextMenuData::MediaLoop; if (mediaElement->supportsSave()) data.mediaFlags |= WebContextMenuData::MediaCanSave; if (mediaElement->hasAudio()) data.mediaFlags |= WebContextMenuData::MediaHasAudio; if (mediaElement->hasVideo()) data.mediaFlags |= WebContextMenuData::MediaHasVideo; if (mediaElement->controls()) data.mediaFlags |= WebContextMenuData::MediaControlRootElement; } else if (r.innerNonSharedNode()->hasTagName(HTMLNames::objectTag) || r.innerNonSharedNode()->hasTagName(HTMLNames::embedTag)) { RenderObject* object = r.innerNonSharedNode()->renderer(); if (object && object->isWidget()) { Widget* widget = toRenderWidget(object)->widget(); if (widget && widget->isPluginContainer()) { data.mediaType = WebContextMenuData::MediaTypePlugin; WebPluginContainerImpl* plugin = static_cast<WebPluginContainerImpl*>(widget); WebString text = plugin->plugin()->selectionAsText(); if (!text.isEmpty()) { data.selectedText = text; data.editFlags |= WebContextMenuData::CanCopy; } data.editFlags &= ~WebContextMenuData::CanTranslate; data.linkURL = plugin->plugin()->linkAtPosition(data.mousePosition); if (plugin->plugin()->supportsPaginatedPrint()) data.mediaFlags |= WebContextMenuData::MediaCanPrint; HTMLPlugInImageElement* pluginElement = static_cast<HTMLPlugInImageElement*>(r.innerNonSharedNode()); data.srcURL = pluginElement->document()->completeURL(pluginElement->url()); data.mediaFlags |= WebContextMenuData::MediaCanSave; // Add context menu commands that are supported by the plugin. if (plugin->plugin()->canRotateView()) data.mediaFlags |= WebContextMenuData::MediaCanRotate; } } } data.isImageBlocked = (data.mediaType == WebContextMenuData::MediaTypeImage) && !r.image(); // If it's not a link, an image, a media element, or an image/media link, // show a selection menu or a more generic page menu. if (selectedFrame->document()->loader()) data.frameEncoding = selectedFrame->document()->encoding(); // Send the frame and page URLs in any case. data.pageURL = urlFromFrame(m_webView->mainFrameImpl()->frame()); if (selectedFrame != m_webView->mainFrameImpl()->frame()) { data.frameURL = urlFromFrame(selectedFrame); RefPtr<HistoryItem> historyItem = selectedFrame->loader()->history()->currentItem(); if (historyItem) data.frameHistoryItem = WebHistoryItem(historyItem); } if (r.isSelected()) { if (!r.innerNonSharedNode()->hasTagName(HTMLNames::inputTag) || !static_cast<HTMLInputElement*>(r.innerNonSharedNode())->isPasswordField()) data.selectedText = selectedFrame->editor()->selectedText().stripWhiteSpace(); } if (r.isContentEditable()) { data.isEditable = true; #if ENABLE(INPUT_SPEECH) if (r.innerNonSharedNode()->hasTagName(HTMLNames::inputTag)) { data.isSpeechInputEnabled = static_cast<HTMLInputElement*>(r.innerNonSharedNode())->isSpeechEnabled(); } #endif // When Chrome enables asynchronous spellchecking, its spellchecker adds spelling markers to misspelled // words and attaches suggestions to these markers in the background. Therefore, when a user right-clicks // a mouse on a word, Chrome just needs to find a spelling marker on the word instread of spellchecking it. if (selectedFrame->settings() && selectedFrame->settings()->asynchronousSpellCheckingEnabled()) { VisibleSelection selection = selectedFrame->selection()->selection(); if (selection.isCaret()) { selection.expandUsingGranularity(WordGranularity); RefPtr<Range> range = selection.toNormalizedRange(); Vector<DocumentMarker*> markers = selectedFrame->document()->markers()->markersInRange(range.get(), DocumentMarker::Spelling | DocumentMarker::Grammar); if (markers.size() == 1) { range->setStart(range->startContainer(), markers[0]->startOffset()); range->setEnd(range->endContainer(), markers[0]->endOffset()); data.misspelledWord = range->text(); if (markers[0]->description().length()) { Vector<String> suggestions; markers[0]->description().split('\n', suggestions); data.dictionarySuggestions = suggestions; } else if (m_webView->spellCheckClient()) { int misspelledOffset, misspelledLength; m_webView->spellCheckClient()->spellCheck(data.misspelledWord, misspelledOffset, misspelledLength, &data.dictionarySuggestions); } selection = VisibleSelection(range.get()); if (selectedFrame->selection()->shouldChangeSelection(selection)) selectedFrame->selection()->setSelection(selection, WordGranularity); } } } else if (m_webView->focusedWebCoreFrame()->editor()->isContinuousSpellCheckingEnabled()) { data.isSpellCheckingEnabled = true; // Spellchecking might be enabled for the field, but could be disabled on the node. if (m_webView->focusedWebCoreFrame()->editor()->isSpellCheckingEnabledInFocusedNode()) { data.misspelledWord = selectMisspelledWord(defaultMenu, selectedFrame); if (m_webView->spellCheckClient()) { int misspelledOffset, misspelledLength; m_webView->spellCheckClient()->spellCheck( data.misspelledWord, misspelledOffset, misspelledLength, &data.dictionarySuggestions); if (!misspelledLength) data.misspelledWord.reset(); } } } HTMLFormElement* form = selectedFrame->selection()->currentForm(); if (form && form->checkValidity() && r.innerNonSharedNode()->hasTagName(HTMLNames::inputTag)) { HTMLInputElement* selectedElement = static_cast<HTMLInputElement*>(r.innerNonSharedNode()); if (selectedElement) { WebSearchableFormData ws = WebSearchableFormData(WebFormElement(form), WebInputElement(selectedElement)); if (ws.url().isValid()) data.keywordURL = ws.url(); } } } #if OS(DARWIN) if (selectedFrame->editor()->selectionHasStyle(CSSPropertyDirection, "ltr") != FalseTriState) data.writingDirectionLeftToRight |= WebContextMenuData::CheckableMenuItemChecked; if (selectedFrame->editor()->selectionHasStyle(CSSPropertyDirection, "rtl") != FalseTriState) data.writingDirectionRightToLeft |= WebContextMenuData::CheckableMenuItemChecked; #endif // OS(DARWIN) // Now retrieve the security info. DocumentLoader* dl = selectedFrame->loader()->documentLoader(); WebDataSource* ds = WebDataSourceImpl::fromDocumentLoader(dl); if (ds) data.securityInfo = ds->response().securityInfo(); data.referrerPolicy = static_cast<WebReferrerPolicy>(selectedFrame->document()->referrerPolicy()); // Filter out custom menu elements and add them into the data. populateCustomMenuItems(defaultMenu, &data); data.node = r.innerNonSharedNode(); WebFrame* selected_web_frame = WebFrameImpl::fromFrame(selectedFrame); if (m_webView->client()) m_webView->client()->showContextMenu(selected_web_frame, data); return 0; }
void ContextMenuClientImpl::showContextMenu(const WebCore::ContextMenu* defaultMenu) { // Displaying the context menu in this function is a big hack as we don't // have context, i.e. whether this is being invoked via a script or in // response to user input (Mouse event WM_RBUTTONDOWN, // Keyboard events KeyVK_APPS, Shift+F10). Check if this is being invoked // in response to the above input events before popping up the context menu. if (!m_webView->contextMenuAllowed()) return; HitTestResult r = m_webView->page()->contextMenuController().hitTestResult(); LocalFrame* selectedFrame = r.innerNodeFrame(); WebContextMenuData data; IntPoint mousePoint = selectedFrame->view()->contentsToWindow(r.roundedPointInInnerNodeFrame()); // FIXME(bokan): crbug.com/371902 - We shouldn't be making these scale // related coordinate transformatios in an ad hoc way. PinchViewport& pinchViewport = selectedFrame->host()->pinchViewport(); mousePoint -= flooredIntSize(pinchViewport.visibleRect().location()); mousePoint.scale(m_webView->pageScaleFactor(), m_webView->pageScaleFactor()); data.mousePosition = mousePoint; // Compute edit flags. data.editFlags = WebContextMenuData::CanDoNone; if (toLocalFrame(m_webView->focusedWebCoreFrame())->editor().canUndo()) data.editFlags |= WebContextMenuData::CanUndo; if (toLocalFrame(m_webView->focusedWebCoreFrame())->editor().canRedo()) data.editFlags |= WebContextMenuData::CanRedo; if (toLocalFrame(m_webView->focusedWebCoreFrame())->editor().canCut()) data.editFlags |= WebContextMenuData::CanCut; if (toLocalFrame(m_webView->focusedWebCoreFrame())->editor().canCopy()) data.editFlags |= WebContextMenuData::CanCopy; if (toLocalFrame(m_webView->focusedWebCoreFrame())->editor().canPaste()) data.editFlags |= WebContextMenuData::CanPaste; if (toLocalFrame(m_webView->focusedWebCoreFrame())->editor().canDelete()) data.editFlags |= WebContextMenuData::CanDelete; // We can always select all... data.editFlags |= WebContextMenuData::CanSelectAll; data.editFlags |= WebContextMenuData::CanTranslate; // Links, Images, Media tags, and Image/Media-Links take preference over // all else. data.linkURL = r.absoluteLinkURL(); if (isHTMLCanvasElement(r.innerNonSharedNode())) { data.mediaType = WebContextMenuData::MediaTypeCanvas; } else if (!r.absoluteImageURL().isEmpty()) { data.srcURL = r.absoluteImageURL(); data.mediaType = WebContextMenuData::MediaTypeImage; data.mediaFlags |= WebContextMenuData::MediaCanPrint; } else if (!r.absoluteMediaURL().isEmpty()) { data.srcURL = r.absoluteMediaURL(); // We know that if absoluteMediaURL() is not empty, then this // is a media element. HTMLMediaElement* mediaElement = toHTMLMediaElement(r.innerNonSharedNode()); if (isHTMLVideoElement(*mediaElement)) data.mediaType = WebContextMenuData::MediaTypeVideo; else if (isHTMLAudioElement(*mediaElement)) data.mediaType = WebContextMenuData::MediaTypeAudio; if (mediaElement->error()) data.mediaFlags |= WebContextMenuData::MediaInError; if (mediaElement->paused()) data.mediaFlags |= WebContextMenuData::MediaPaused; if (mediaElement->muted()) data.mediaFlags |= WebContextMenuData::MediaMuted; if (mediaElement->loop()) data.mediaFlags |= WebContextMenuData::MediaLoop; if (mediaElement->supportsSave()) data.mediaFlags |= WebContextMenuData::MediaCanSave; if (mediaElement->hasAudio()) data.mediaFlags |= WebContextMenuData::MediaHasAudio; // Media controls can be toggled only for video player. If we toggle // controls for audio then the player disappears, and there is no way to // return it back. Don't set this bit for fullscreen video, since // toggling is ignored in that case. if (mediaElement->hasVideo() && !mediaElement->isFullscreen()) data.mediaFlags |= WebContextMenuData::MediaCanToggleControls; if (mediaElement->controls()) data.mediaFlags |= WebContextMenuData::MediaControls; } else if (isHTMLObjectElement(*r.innerNonSharedNode()) || isHTMLEmbedElement(*r.innerNonSharedNode())) { RenderObject* object = r.innerNonSharedNode()->renderer(); if (object && object->isWidget()) { Widget* widget = toRenderWidget(object)->widget(); if (widget && widget->isPluginContainer()) { data.mediaType = WebContextMenuData::MediaTypePlugin; WebPluginContainerImpl* plugin = toWebPluginContainerImpl(widget); WebString text = plugin->plugin()->selectionAsText(); if (!text.isEmpty()) { data.selectedText = text; data.editFlags |= WebContextMenuData::CanCopy; } data.editFlags &= ~WebContextMenuData::CanTranslate; data.linkURL = plugin->plugin()->linkAtPosition(data.mousePosition); if (plugin->plugin()->supportsPaginatedPrint()) data.mediaFlags |= WebContextMenuData::MediaCanPrint; HTMLPlugInElement* pluginElement = toHTMLPlugInElement(r.innerNonSharedNode()); data.srcURL = pluginElement->document().completeURL(pluginElement->url()); data.mediaFlags |= WebContextMenuData::MediaCanSave; // Add context menu commands that are supported by the plugin. if (plugin->plugin()->canRotateView()) data.mediaFlags |= WebContextMenuData::MediaCanRotate; } } } // An image can to be null for many reasons, like being blocked, no image // data received from server yet. data.hasImageContents = (data.mediaType == WebContextMenuData::MediaTypeImage) && r.image() && !(r.image()->isNull()); // If it's not a link, an image, a media element, or an image/media link, // show a selection menu or a more generic page menu. if (selectedFrame->document()->loader()) data.frameEncoding = selectedFrame->document()->encodingName(); // Send the frame and page URLs in any case. data.pageURL = urlFromFrame(m_webView->mainFrameImpl()->frame()); if (selectedFrame != m_webView->mainFrameImpl()->frame()) { data.frameURL = urlFromFrame(selectedFrame); RefPtr<HistoryItem> historyItem = selectedFrame->loader().currentItem(); if (historyItem) data.frameHistoryItem = WebHistoryItem(historyItem); } if (r.isSelected()) { if (!isHTMLInputElement(*r.innerNonSharedNode()) || !toHTMLInputElement(r.innerNonSharedNode())->isPasswordField()) data.selectedText = selectedFrame->selectedText().stripWhiteSpace(); } if (r.isContentEditable()) { data.isEditable = true; // When Chrome enables asynchronous spellchecking, its spellchecker adds spelling markers to misspelled // words and attaches suggestions to these markers in the background. Therefore, when a user right-clicks // a mouse on a word, Chrome just needs to find a spelling marker on the word instead of spellchecking it. if (selectedFrame->settings() && selectedFrame->settings()->asynchronousSpellCheckingEnabled()) { DocumentMarker marker; data.misspelledWord = selectMisspellingAsync(selectedFrame, marker); data.misspellingHash = marker.hash(); if (marker.description().length()) { Vector<String> suggestions; marker.description().split('\n', suggestions); data.dictionarySuggestions = suggestions; } else if (m_webView->spellCheckClient()) { int misspelledOffset, misspelledLength; m_webView->spellCheckClient()->spellCheck(data.misspelledWord, misspelledOffset, misspelledLength, &data.dictionarySuggestions); } } else { data.isSpellCheckingEnabled = toLocalFrame(m_webView->focusedWebCoreFrame())->spellChecker().isContinuousSpellCheckingEnabled(); // Spellchecking might be enabled for the field, but could be disabled on the node. if (toLocalFrame(m_webView->focusedWebCoreFrame())->spellChecker().isSpellCheckingEnabledInFocusedNode()) { data.misspelledWord = selectMisspelledWord(selectedFrame); if (m_webView->spellCheckClient()) { int misspelledOffset, misspelledLength; m_webView->spellCheckClient()->spellCheck( data.misspelledWord, misspelledOffset, misspelledLength, &data.dictionarySuggestions); if (!misspelledLength) data.misspelledWord.reset(); } } } HTMLFormElement* form = selectedFrame->selection().currentForm(); if (form && isHTMLInputElement(*r.innerNonSharedNode())) { HTMLInputElement& selectedElement = toHTMLInputElement(*r.innerNonSharedNode()); WebSearchableFormData ws = WebSearchableFormData(WebFormElement(form), WebInputElement(&selectedElement)); if (ws.url().isValid()) data.keywordURL = ws.url(); } } if (selectedFrame->editor().selectionHasStyle(CSSPropertyDirection, "ltr") != FalseTriState) data.writingDirectionLeftToRight |= WebContextMenuData::CheckableMenuItemChecked; if (selectedFrame->editor().selectionHasStyle(CSSPropertyDirection, "rtl") != FalseTriState) data.writingDirectionRightToLeft |= WebContextMenuData::CheckableMenuItemChecked; // Now retrieve the security info. DocumentLoader* dl = selectedFrame->loader().documentLoader(); WebDataSource* ds = WebDataSourceImpl::fromDocumentLoader(dl); if (ds) data.securityInfo = ds->response().securityInfo(); data.referrerPolicy = static_cast<WebReferrerPolicy>(selectedFrame->document()->referrerPolicy()); // Filter out custom menu elements and add them into the data. populateCustomMenuItems(defaultMenu, &data); // Extract suggested filename for saving file. if (isHTMLAnchorElement(r.URLElement())) { HTMLAnchorElement* anchor = toHTMLAnchorElement(r.URLElement()); data.suggestedFilename = anchor->fastGetAttribute(HTMLNames::downloadAttr); } data.node = r.innerNonSharedNode(); WebLocalFrameImpl* selectedWebFrame = WebLocalFrameImpl::fromFrame(selectedFrame); if (selectedWebFrame->client()) selectedWebFrame->client()->showContextMenu(data); }